├── .cargo └── config.toml ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── README.tpl ├── bors.toml ├── examples ├── array.rs ├── async.rs ├── boxed_dyn.rs ├── boxed_slice.rs ├── clike.rs ├── dyn.rs ├── function.rs ├── optionlike.rs ├── raw.rs ├── slice.rs ├── str.rs └── struct.rs ├── justfile ├── rustfmt.toml ├── src ├── debug.rs ├── error.rs ├── lib.rs ├── schema │ ├── array.rs │ ├── box.rs │ ├── boxed_dyn.rs │ ├── boxed_slice.rs │ ├── data.rs │ ├── enum.rs │ ├── field.rs │ ├── fields.rs │ ├── function.rs │ ├── mod.rs │ ├── name.rs │ ├── offset.rs │ ├── pointer.rs │ ├── slice.rs │ ├── str_impl.rs │ ├── struct.rs │ ├── variant.rs │ └── variants.rs └── value │ ├── array.rs │ ├── box.rs │ ├── boxed_dyn.rs │ ├── boxed_slice.rs │ ├── enum.rs │ ├── field.rs │ ├── fields.rs │ ├── function.rs │ ├── iter.rs │ ├── mod.rs │ ├── pointer.rs │ ├── slice_impl.rs │ ├── str_impl.rs │ ├── struct.rs │ └── variant.rs └── tests └── reflect.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustdocflags = ["-Cdebuginfo=2"] -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This file incorporates work [0] covered by the following copyright and 2 | # permission notice: 3 | # 4 | # Copyright 2019 The Fuchsia Authors. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | # [0] https://github.com/google/zerocopy/blob/main/.github/workflows/ci.yml 30 | 31 | name: CI 32 | 33 | on: 34 | pull_request: 35 | push: 36 | branches: 37 | - staging 38 | - trying 39 | 40 | env: 41 | CARGO_TERM_COLOR: always 42 | RUSTFLAGS: -Dwarnings 43 | # `CRATE_NIGHTLY_XXX` are flags that we add to `XXX` only on the nightly 44 | # toolchain. 45 | CRATE_NIGHTLY_RUSTFLAGS: -Zrandomize-layout 46 | 47 | jobs: 48 | build_test: 49 | runs-on: ubuntu-latest 50 | 51 | strategy: 52 | matrix: 53 | toolchain: [ "stable", "nightly" ] 54 | target: [ "i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu" ] 55 | 56 | name: Build & Test (toolchain:${{ matrix.toolchain }}, target:${{ matrix.target }}) 57 | 58 | steps: 59 | - uses: actions/checkout@v3 60 | 61 | # We use toolchain descriptors ("msrv", "stable", and "nightly") in the 62 | # matrix. This step converts the current descriptor to a particular 63 | # toolchain version by looking up the corresponding key in `Cargo.toml`. It 64 | # sets the `CRATE_TOOLCHAIN` environment variable for future steps to use. 65 | # 66 | # Note that all metadata is stored in the `Cargo.toml` at 67 | # the repository root. 68 | - name: Set toolchain version 69 | run: | 70 | set -e 71 | # Usage: msrv 72 | function msrv { 73 | cargo metadata --format-version 1 | jq -r ".packages[] | select(.name == \"$1\").rust_version" 74 | } 75 | case ${{ matrix.toolchain }} in 76 | msrv) 77 | CRATE_TOOLCHAIN="$(msrv deflect)" 78 | ;; 79 | stable) 80 | CRATE_TOOLCHAIN="stable" 81 | ;; 82 | nightly) 83 | CRATE_TOOLCHAIN="nightly" 84 | ;; 85 | *) 86 | echo 'Unrecognized toolchain: ${{ matrix.toolchain }}' | tee -a $GITHUB_STEP_SUMMARY >&2 87 | exit 1 88 | ;; 89 | esac 90 | echo "Found that the '${{ matrix.toolchain }}' toolchain is $CRATE_TOOLCHAIN" | tee -a $GITHUB_STEP_SUMMARY 91 | echo "CRATE_TOOLCHAIN=$CRATE_TOOLCHAIN" >> $GITHUB_ENV 92 | - name: Configure environment variables 93 | run: | 94 | set -e 95 | if [[ '${{ matrix.toolchain }}' == 'nightly' ]]; then 96 | RUSTFLAGS="$RUSTFLAGS $CRATE_NIGHTLY_RUSTFLAGS" 97 | MIRIFLAGS="$MIRIFLAGS $CRATE_NIGHTLY_MIRIFLAGS" 98 | echo "Using nightly toolchain; setting RUSTFLAGS='$RUSTFLAGS' and MIRIFLAGS='$MIRIFLAGS'" | tee -a $GITHUB_STEP_SUMMARY 99 | echo "RUSTFLAGS=$RUSTFLAGS" >> $GITHUB_ENV 100 | echo "MIRIFLAGS=$MIRIFLAGS" >> $GITHUB_ENV 101 | else 102 | echo "Using non-nightly toolchain; not modifying RUSTFLAGS='$RUSTFLAGS' or MIRIFLAGS='$MIRIFLAGS'" | tee -a $GITHUB_STEP_SUMMARY 103 | fi 104 | - name: Install Rust with toolchain ${{ env.CRATE_TOOLCHAIN }} and target ${{ matrix.target }} 105 | uses: actions-rs/toolchain@v1 106 | with: 107 | toolchain: ${{ env.CRATE_TOOLCHAIN }} 108 | target: ${{ matrix.target }} 109 | components: clippy 110 | 111 | - name: Rust Cache 112 | uses: Swatinem/rust-cache@v2.0.0 113 | 114 | # When building tests for the i686 target, we need certain libraries which 115 | # are not installed by default; `gcc-multilib` includes these libraries. 116 | - name: Install gcc-multilib 117 | run: sudo apt-get install gcc-multilib 118 | if: ${{ contains(matrix.target, 'i686') }} 119 | 120 | - name: Check 121 | run: cargo +${{ env.CRATE_TOOLCHAIN }} check --target ${{ matrix.target }} --all-targets --verbose 122 | 123 | - name: Test 124 | run: cargo +${{ env.CRATE_TOOLCHAIN }} test --target ${{ matrix.target }} --verbose 125 | # Only run tests when targetting x86 (32- or 64-bit) - we're executing on 126 | # x86_64, so we can't run tests for any non-x86 target. 127 | if: ${{ contains(matrix.target, 'x86_64') || contains(matrix.target, 'i686') }} 128 | 129 | check_fmt: 130 | runs-on: ubuntu-latest 131 | name: cargo fmt 132 | steps: 133 | - uses: actions/checkout@v3 134 | - name: Install Rust (nightly) 135 | uses: actions-rs/toolchain@v1 136 | with: 137 | toolchain: nightly 138 | override: true 139 | components: rustfmt 140 | - name: "`cargo fmt --check`" 141 | run: | 142 | set -e 143 | cargo fmt --check 144 | 145 | check_clippy: 146 | runs-on: ubuntu-latest 147 | name: cargo clippy 148 | steps: 149 | - uses: actions/checkout@v3 150 | - name: Install Rust (nightly) 151 | uses: actions-rs/toolchain@v1 152 | with: 153 | toolchain: nightly 154 | override: true 155 | components: clippy 156 | - name: Rust Cache 157 | uses: Swatinem/rust-cache@v2.0.0 158 | - name: "`cargo clippy`" 159 | run: | 160 | set -e 161 | cargo clippy --all-targets 162 | 163 | check_readme: 164 | runs-on: ubuntu-latest 165 | name: Check README is correctly generated. 166 | steps: 167 | - uses: actions/checkout@v3 168 | - name: Rust Cache 169 | uses: Swatinem/rust-cache@v2.0.0 170 | - uses: extractions/setup-just@v1 171 | - name: Check MSRVs match 172 | run: | 173 | set -e 174 | cargo install cargo-readme --version 3.2.0 175 | diff <(just generate-readme) README.md 176 | exit $? 177 | 178 | ci-success: 179 | name: ci 180 | if: ${{ success() }} 181 | needs: 182 | - build_test 183 | - check_fmt 184 | - check_clippy 185 | - check_readme 186 | runs-on: ubuntu-latest 187 | steps: 188 | - name: CI succeeded 189 | run: exit 0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## [Unreleased] - ReleaseDate 4 | - Initial release. 5 | 6 | 7 | [Unreleased]: https://github.com/tokio-rs/async-backtrace/compare/71f11b1ef824b01657fd833eb205bd566174f700...HEAD 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deflect" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | repository = "https://github.com/jswrenn/deflect" 7 | documentation = "https://docs.rs/deflect/" 8 | description = "Reflection in Rust via DWARF debug info." 9 | readme = "README.md" 10 | rust-version = "1.66.0" 11 | 12 | [package.metadata.release] 13 | dev-version = false 14 | pre-release-commit-message = "chore: Release version {{version}}" 15 | allow-branch = ["main"] 16 | pre-release-replacements = [ 17 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}"}, 18 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 19 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}"}, 20 | {file="CHANGELOG.md", search="", replace="\n\n## [Unreleased] - ReleaseDate", exactly=1}, 21 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/jswrenn/deflect/compare/{{tag_name}}...HEAD", exactly=1}, 22 | ] 23 | tag-name = "v{{version}}" 24 | 25 | [dependencies] 26 | addr2line = "0.17.0" 27 | anyhow = "1.0" 28 | dashmap = "5.4.0" 29 | memmap2 = "0.5.7" 30 | once_cell = "1.16.0" 31 | procmaps = "0.4.1" 32 | rustc-demangle = "0.1.21" 33 | thiserror = "1.0.37" 34 | itertools = "0.10.5" 35 | 36 | [dev-dependencies] 37 | quickcheck = "1.0" 38 | quickcheck_macros = "1.0" 39 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Deflect Contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # deflect 4 | 5 | **[EXPERIMENTAL]** 6 | Deflect brings reflection to Rust using [DWARF] debug info. 7 | 8 | Deflect can be used to recover the concrete types of trait objects, inspect 9 | the internal state of `async` generators, and pretty-print arbitrary data. 10 | 11 | [DWARF]: https://en.wikipedia.org/wiki/DWARF 12 | 13 | ## Example 14 | Use the [`Reflect`] trait to debug or recursively destructure any value. 15 | 16 | ```rust 17 | pub struct Foo { 18 | a: u8 19 | } 20 | 21 | // initialize the debuginfo provider 22 | let context = deflect::default_provider()?; 23 | 24 | // create some type-erased data 25 | let erased: Box = Box::new(Foo { a: 42 }); 26 | 27 | // cast it to `&dyn Reflect` 28 | let reflectable: &dyn deflect::Reflect = &erased; 29 | 30 | // reflect it! 31 | let value: deflect::Value = reflectable.reflect(&context)?; 32 | 33 | // pretty-print the reflected value 34 | assert_eq!(value.to_string(), "box Foo { a: 42 }"); 35 | 36 | // downcast into a `BoxedDyn` value 37 | let value: deflect::value::BoxedDyn = value.try_into()?; 38 | 39 | // dereference the boxed value 40 | let value: deflect::Value = value.deref()?; 41 | 42 | // downcast into a `Struct` value 43 | let value: deflect::value::Struct = value.try_into()?; 44 | 45 | // get the field `a` by name 46 | let Some(field) = value.field("a")? else { 47 | panic!("no field named `a`!") 48 | }; 49 | 50 | // get the value of the field 51 | let value = field.value()?; 52 | 53 | // downcast into a `u8` 54 | let value: u8 = value.try_into()?; 55 | 56 | // check that it's equal to `42`! 57 | assert_eq!(value, 42); 58 | ``` 59 | See the `examples` directory of this crate's source for additional examples. 60 | 61 | ## Limitations 62 | The current implementation of [`default_provider`] only works when DWARF 63 | debuginfo is stored in the program's binary. It will not work if DWARF 64 | debug info is split into other files. Pull requests are welcome. 65 | 66 | This crate is highly experimental. It is not suitable as a critical 67 | component of any system. The initial releases of this crate require 68 | significant polish. Pull requests are welcome. Its known soundness holes 69 | include ignorance of `UnsafeCell`. Don't reflect into types containing 70 | `UnsafeCell`. 71 | 72 | Additionally, the particulars of how Rust encodes DWARF debug info my change 73 | over time. This crate will do its best to keep up with those changes. Again, 74 | pull requests are welcome. 75 | 76 | ## License 77 | 78 | This project is licensed under the Apache License, Version 2.0, or the MIT 79 | license, at your option. 80 | 81 | ### Contribution 82 | 83 | Unless you explicitly state otherwise, any contribution intentionally submitted 84 | for inclusion in deflect by you, shall be licensed as MIT and Apache 2.0, 85 | without any additional terms or conditions. 86 | -------------------------------------------------------------------------------- /README.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | # {{crate}} 4 | 5 | {{readme}} 6 | 7 | ## License 8 | 9 | This project is licensed under the Apache License, Version 2.0, or the MIT 10 | license, at your option. 11 | 12 | ### Contribution 13 | 14 | Unless you explicitly state otherwise, any contribution intentionally submitted 15 | for inclusion in deflect by you, shall be licensed as MIT and Apache 2.0, 16 | without any additional terms or conditions. 17 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = ["ci"] 2 | -------------------------------------------------------------------------------- /examples/array.rs: -------------------------------------------------------------------------------- 1 | use deflect::Reflect; 2 | use std::error::Error; 3 | 4 | fn main() -> Result<(), Box> { 5 | let erased: &dyn Reflect = &[1, 2, 3, 4]; 6 | let context = deflect::default_provider()?; 7 | let value: deflect::Value = erased.reflect(&context)?; 8 | let value: deflect::value::Array = value.try_into()?; 9 | println!("{value:#}"); 10 | Ok(()) 11 | } 12 | -------------------------------------------------------------------------------- /examples/async.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code, clippy::disallowed_names)] 2 | 3 | use deflect::Reflect; 4 | use std::error::Error; 5 | use std::sync::Arc; 6 | use std::task::{RawWaker, RawWakerVTable, Waker}; 7 | 8 | macro_rules! waker_vtable { 9 | ($ty:ident) => { 10 | &RawWakerVTable::new( 11 | clone_arc_raw::<$ty>, 12 | wake_arc_raw::<$ty>, 13 | wake_by_ref_arc_raw::<$ty>, 14 | drop_arc_raw::<$ty>, 15 | ) 16 | }; 17 | } 18 | 19 | pub trait ArcWake { 20 | fn wake(self: Arc); 21 | 22 | fn wake_by_ref(arc_self: &Arc) { 23 | arc_self.clone().wake() 24 | } 25 | 26 | fn into_waker(wake: Arc) -> Waker 27 | where 28 | Self: Sized, 29 | { 30 | let ptr = Arc::into_raw(wake) as *const (); 31 | 32 | unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable!(Self))) } 33 | } 34 | } 35 | 36 | unsafe fn increase_refcount(data: *const ()) { 37 | // Retain Arc by creating a copy 38 | let arc: Arc = Arc::from_raw(data as *const T); 39 | let arc_clone = arc.clone(); 40 | // Forget the Arcs again, so that the refcount isn't decrased 41 | let _ = Arc::into_raw(arc); 42 | let _ = Arc::into_raw(arc_clone); 43 | } 44 | 45 | unsafe fn clone_arc_raw(data: *const ()) -> RawWaker { 46 | increase_refcount::(data); 47 | RawWaker::new(data, waker_vtable!(T)) 48 | } 49 | 50 | unsafe fn drop_arc_raw(data: *const ()) { 51 | // Drop Arc 52 | let _: Arc = Arc::from_raw(data as *const T); 53 | } 54 | 55 | unsafe fn wake_arc_raw(data: *const ()) { 56 | let arc: Arc = Arc::from_raw(data as *const T); 57 | ArcWake::wake(arc); 58 | } 59 | 60 | unsafe fn wake_by_ref_arc_raw(data: *const ()) { 61 | let arc: Arc = Arc::from_raw(data as *const T); 62 | ArcWake::wake_by_ref(&arc); 63 | let _ = Arc::into_raw(arc); 64 | } 65 | 66 | use std::future::Future; 67 | use std::pin::Pin; 68 | use std::sync::atomic::{self, AtomicUsize}; 69 | use std::task::{Context, Poll}; 70 | 71 | struct Counter { 72 | wakes: AtomicUsize, 73 | } 74 | 75 | impl ArcWake for Counter { 76 | fn wake(self: Arc) { 77 | Self::wake_by_ref(&self) 78 | } 79 | fn wake_by_ref(arc_self: &Arc) { 80 | arc_self.wakes.fetch_add(1, atomic::Ordering::SeqCst); 81 | } 82 | } 83 | 84 | struct WakeOnceThenComplete(bool, u8); 85 | 86 | impl Future for WakeOnceThenComplete { 87 | type Output = u8; 88 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 89 | if self.0 { 90 | Poll::Ready(self.1) 91 | } else { 92 | cx.waker().wake_by_ref(); 93 | self.0 = true; 94 | Poll::Pending 95 | } 96 | } 97 | } 98 | 99 | fn poll_once>(fut: Pin<&mut F>) -> Poll { 100 | let mut fut = Box::pin(fut); 101 | let counter = Arc::new(Counter { 102 | wakes: AtomicUsize::new(0), 103 | }); 104 | let waker = ArcWake::into_waker(counter); 105 | let mut cx = Context::from_waker(&waker); 106 | fut.as_mut().poll(&mut cx) 107 | } 108 | 109 | fn base() -> WakeOnceThenComplete { 110 | WakeOnceThenComplete(false, 1) 111 | } 112 | 113 | async fn await1_level1() -> u8 { 114 | base().await 115 | } 116 | 117 | async fn await2_level1() -> u8 { 118 | base().await + base().await 119 | } 120 | 121 | async fn await3_level1() -> u8 { 122 | base().await + base().await + base().await 123 | } 124 | 125 | async fn await3_level2() -> u8 { 126 | let foo = Box::pin(await3_level1()); 127 | let bar = await3_level1(); 128 | let baz = await3_level1(); 129 | foo.await + bar.await + baz.await 130 | } 131 | 132 | async fn await3_level3() -> u8 { 133 | await3_level2().await + await3_level2().await + await3_level2().await 134 | } 135 | 136 | async fn await3_level4() -> u8 { 137 | await3_level3().await + await3_level3().await + await3_level3().await 138 | } 139 | 140 | async fn await3_level5() -> u8 { 141 | let foo = await3_level4(); 142 | let bar = await3_level4(); 143 | let baz = await3_level4(); 144 | 145 | let x = foo.await; 146 | let y = x + bar.await; 147 | 148 | y + baz.await 149 | } 150 | 151 | fn poll(_f: F) -> impl Reflect { 152 | ::poll 153 | } 154 | 155 | fn main() -> Result<(), Box> { 156 | let mut task = Box::pin(await3_level5()); 157 | let mut i = 0; 158 | loop { 159 | let res = poll_once(task.as_mut()); 160 | let erased: &dyn Reflect = &task; 161 | let context = deflect::default_provider()?; 162 | 163 | let value = erased.reflect(&context)?; 164 | 165 | print!("\x1B[2J\x1B[1;1H"); 166 | println!("STEP {i}"); 167 | println!("{value:#}"); 168 | 169 | if res.is_ready() { 170 | break; 171 | } 172 | 173 | i += 1; 174 | 175 | //std::io::stdin().read_line(&mut String::new()).unwrap(); 176 | std::thread::sleep(std::time::Duration::from_millis(100)); 177 | } 178 | 179 | Ok(()) 180 | } 181 | -------------------------------------------------------------------------------- /examples/boxed_dyn.rs: -------------------------------------------------------------------------------- 1 | use deflect::Reflect; 2 | use std::error::Error; 3 | 4 | fn main() -> Result<(), Box> { 5 | #[allow(dead_code)] 6 | struct Foo { 7 | a: u8, 8 | } 9 | 10 | // initialize the debuginfo provider 11 | let context = deflect::default_provider()?; 12 | 13 | // create some type-erased data 14 | let data: Box = Box::new(Foo { a: 42 }); 15 | 16 | // cast it to `&dyn Reflect` 17 | let erased: &dyn Reflect = &data; 18 | 19 | // reflect it! 20 | let value: deflect::Value = erased.reflect(&context)?; 21 | 22 | // pretty-print the reflected value 23 | assert_eq!(value.to_string(), "box Foo { a: 42 }"); 24 | 25 | // downcast into a `BoxedDyn` value 26 | let value: deflect::value::BoxedDyn = value.try_into()?; 27 | 28 | // dereference the boxed value 29 | let value: deflect::Value = value.deref()?; 30 | // downcast into a `Struct` value 31 | let value: deflect::value::Struct = value.try_into()?; 32 | 33 | // pretty-print the reflected value 34 | assert_eq!(value.to_string(), "Foo { a: 42 }"); 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /examples/boxed_slice.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | fn main() -> Result<(), Box> { 4 | let data = vec![1, 2, 3].into_boxed_slice(); 5 | let erased: &dyn deflect::Reflect = &data; 6 | let context = deflect::default_provider()?; 7 | let value: deflect::Value = erased.reflect(&context)?; 8 | let value: deflect::value::BoxedSlice = value.try_into()?; 9 | assert_eq!(value.to_string(), "box [1, 2, 3][..]"); 10 | Ok(()) 11 | } 12 | -------------------------------------------------------------------------------- /examples/clike.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use deflect::Reflect; 4 | 5 | #[repr(u64)] 6 | enum TestCLikeEnum { 7 | A = 400, 8 | B, 9 | C, 10 | } 11 | 12 | fn main() -> Result<(), Box> { 13 | let erased: &dyn Reflect = &TestCLikeEnum::B; 14 | let context = deflect::default_provider()?; 15 | let value: deflect::Value = erased.reflect(&context)?; 16 | let value: deflect::value::Enum = value.try_into()?; 17 | println!("{value}"); 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /examples/dyn.rs: -------------------------------------------------------------------------------- 1 | use deflect::Reflect; 2 | 3 | fn main() -> Result<(), Box> { 4 | struct Struct; 5 | 6 | trait Trait { 7 | fn foo(&self) {} 8 | fn bar(&self) {} 9 | } 10 | 11 | impl Trait for Struct {} 12 | 13 | let raw = &Struct as &dyn Trait; 14 | let erased: &dyn Reflect = &raw; 15 | let context = deflect::default_provider()?; 16 | let value = erased.reflect(&context)?; 17 | let value: deflect::value::Struct<_> = value.try_into()?; 18 | 19 | println!("{value:#}"); 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /examples/function.rs: -------------------------------------------------------------------------------- 1 | use deflect::Reflect; 2 | 3 | fn foo(val: u8) -> u32 { 4 | val as _ 5 | } 6 | 7 | struct Wrapper(F); 8 | 9 | fn run() -> Result<(), Box> { 10 | let erased: &dyn Reflect = &Wrapper(foo); 11 | let context = deflect::default_provider()?; 12 | let value = erased.reflect(&context)?; 13 | println!("{value:#}"); 14 | Ok(()) 15 | } 16 | 17 | fn main() { 18 | if let Err(err) = run() { 19 | println!("{err}"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/optionlike.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use deflect::Reflect; 4 | 5 | enum OptionLike { 6 | Some(std::num::NonZeroU8), 7 | None, 8 | } 9 | 10 | fn main() -> Result<(), Box> { 11 | let erased: &dyn Reflect = &OptionLike::Some(std::num::NonZeroU8::new(42).unwrap()); 12 | let context = deflect::default_provider()?; 13 | let value = erased.reflect(&context)?; 14 | println!("{value:#}"); 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /examples/raw.rs: -------------------------------------------------------------------------------- 1 | use deflect::Reflect; 2 | 3 | fn main() -> Result<(), Box> { 4 | let raw = &1 as *const i32; 5 | let erased: &dyn Reflect = &raw; 6 | let context = deflect::default_provider()?; 7 | let value = erased.reflect(&context)?; 8 | println!("{value:}"); 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /examples/slice.rs: -------------------------------------------------------------------------------- 1 | use deflect::Reflect; 2 | 3 | fn main() -> Result<(), Box> { 4 | let erased: &dyn Reflect = &[1, 2, 3, 4].as_slice(); 5 | let context = deflect::default_provider()?; 6 | let value = erased.reflect(&context)?; 7 | println!("{value:#}"); 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /examples/str.rs: -------------------------------------------------------------------------------- 1 | use deflect::Reflect; 2 | 3 | fn main() -> Result<(), Box> { 4 | let erased: &dyn Reflect = &"Hello World"; 5 | let context = deflect::default_provider()?; 6 | let value = erased.reflect(&context)?; 7 | println!("{value:#}"); 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /examples/struct.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use deflect::Reflect; 4 | 5 | struct Example { 6 | foo: bool, 7 | bar: &'static str, 8 | baz: f64, 9 | } 10 | 11 | fn main() -> Result<(), Box> { 12 | let value = Example { 13 | foo: true, 14 | bar: "Hello World!", 15 | baz: 42.0, 16 | }; 17 | 18 | let erased: &dyn Reflect = &value; 19 | 20 | let dbginfo = deflect::default_provider()?; 21 | let reflection = erased.reflect(&dbginfo)?; 22 | 23 | assert_eq!( 24 | reflection.to_string(), 25 | r#"Example { foo: true, bar: "Hello World!", baz: 42 }"# 26 | ); 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | readme: 2 | just generate-readme > README.md 3 | 4 | generate-readme: 5 | cargo readme -t README.tpl --no-indent-headings 6 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | wrap_comments = true 2 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | borrow::Cow, 3 | cell::{Cell, RefCell}, 4 | fmt::{self, Formatter}, 5 | }; 6 | 7 | struct DebugExpression<'dwarf, R> 8 | where 9 | R: crate::gimli::Reader, 10 | { 11 | unit: &'dwarf crate::gimli::Unit, 12 | expression: Cell>>, 13 | } 14 | 15 | impl<'dwarf, R> DebugExpression<'dwarf, R> 16 | where 17 | R: crate::gimli::Reader, 18 | { 19 | fn new( 20 | unit: &'dwarf crate::gimli::Unit, 21 | expression: crate::gimli::Expression, 22 | ) -> Self { 23 | let expression = Cell::new(Some(expression)); 24 | Self { unit, expression } 25 | } 26 | } 27 | 28 | impl<'dwarf, R> fmt::Debug for DebugExpression<'dwarf, R> 29 | where 30 | R: crate::gimli::Reader, 31 | { 32 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 33 | let mut debug_list = f.debug_list(); 34 | let expression = self.expression.take().ok_or(fmt::Error)?; 35 | let mut ops = expression.operations(self.unit.encoding()); 36 | while let Some(op) = ops.next().map_err(crate::fmt_err)? { 37 | debug_list.entry(&op); 38 | } 39 | 40 | Ok(()) 41 | } 42 | } 43 | 44 | // ----- 45 | pub(crate) struct DebugEntry<'entry, 'dwarf, R> 46 | where 47 | R: crate::gimli::Reader, 48 | { 49 | dwarf: &'dwarf crate::gimli::Dwarf, 50 | unit: &'dwarf crate::gimli::Unit, 51 | entry: &'entry crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R, usize>, 52 | } 53 | 54 | impl<'entry, 'dwarf, R> DebugEntry<'entry, 'dwarf, R> 55 | where 56 | R: crate::gimli::Reader, 57 | { 58 | pub(crate) fn new( 59 | dwarf: &'dwarf crate::gimli::Dwarf, 60 | unit: &'dwarf crate::gimli::Unit, 61 | entry: &'entry crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R, usize>, 62 | ) -> Self { 63 | Self { dwarf, unit, entry } 64 | } 65 | } 66 | 67 | impl<'entry, 'dwarf, R> fmt::Debug for DebugEntry<'entry, 'dwarf, R> 68 | where 69 | R: crate::gimli::Reader, 70 | { 71 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 72 | let mut debug_struct = f.debug_struct(&dw_tag_to_string(self.entry.tag())); 73 | let mut attrs = self.entry.attrs(); 74 | while let Some(attr) = attrs.next().map_err(crate::fmt_err)? { 75 | let name = attr.name(); 76 | if name == crate::gimli::DW_AT_frame_base { 77 | continue; 78 | } 79 | let value = attr.value(); 80 | if let Ok(value_as_string) = self.dwarf.attr_string(self.unit, value) { 81 | if let Ok(value_as_string) = value_as_string.to_string_lossy() { 82 | if name == crate::gimli::DW_AT_MIPS_linkage_name { 83 | let value_as_string = rustc_demangle::demangle(&value_as_string); 84 | debug_struct.field(&dw_at_to_string(name), &value_as_string); 85 | } else { 86 | debug_struct.field(&dw_at_to_string(name), &value_as_string); 87 | } 88 | } else { 89 | debug_struct.field(&dw_at_to_string(name), &value_as_string); 90 | } 91 | continue; 92 | } 93 | let value = attr.value(); 94 | if let Some(expression) = attr.exprloc_value() { 95 | debug_struct.field( 96 | &dw_at_to_string(name), 97 | &DebugExpression::new(self.unit, expression), 98 | ); 99 | } 100 | /*if let Some(value) = attr.udata_value() { 101 | debug_struct.field(&dw_at_to_string(name), &value); 102 | } else*/ 103 | if let crate::gimli::AttributeValue::FileIndex(file_index) = value { 104 | if let Ok(value_as_string) = crate::fi_to_string(self.dwarf, self.unit, file_index) 105 | { 106 | debug_struct.field(&dw_at_to_string(name), &value_as_string); 107 | } else { 108 | debug_struct.field(&dw_at_to_string(name), &file_index); 109 | } 110 | } else if let crate::gimli::AttributeValue::Encoding(encoding) = value { 111 | debug_struct.field(&dw_at_to_string(name), &dw_ate_to_string(encoding)); 112 | } else { 113 | debug_struct.field(&dw_at_to_string(name), &value); 114 | } 115 | } 116 | if self.entry.has_children() { 117 | let mut tree = self 118 | .unit 119 | .entries_tree(Some(self.entry.offset())) 120 | .map_err(crate::fmt_err)?; 121 | let root = tree.root().map_err(crate::fmt_err)?; 122 | let children = RefCell::new(root.children()); 123 | debug_struct.field( 124 | "children", 125 | &DebugEntriesTreeIter { 126 | dwarf: self.dwarf, 127 | unit: self.unit, 128 | iter: children, 129 | }, 130 | ); 131 | } 132 | debug_struct.finish() 133 | } 134 | } 135 | 136 | fn dw_tag_to_string(at: crate::gimli::DwTag) -> Cow<'static, str> { 137 | if let Some(name) = at.static_string() { 138 | Cow::Borrowed(name) 139 | } else { 140 | Cow::Owned(format!("{}", at.0)) 141 | } 142 | } 143 | 144 | fn dw_at_to_string(at: crate::gimli::DwAt) -> Cow<'static, str> { 145 | if let Some(name) = at.static_string() { 146 | Cow::Borrowed(name) 147 | } else { 148 | Cow::Owned(format!("{}", at.0)) 149 | } 150 | } 151 | 152 | fn dw_ate_to_string(at: crate::gimli::DwAte) -> Cow<'static, str> { 153 | if let Some(name) = at.static_string() { 154 | Cow::Borrowed(name) 155 | } else { 156 | Cow::Owned(format!("{}", at.0)) 157 | } 158 | } 159 | 160 | struct DebugEntriesTreeIter<'tree, 'dwarf, R> 161 | where 162 | R: crate::gimli::Reader, 163 | { 164 | dwarf: &'dwarf crate::gimli::Dwarf, 165 | unit: &'dwarf crate::gimli::Unit, 166 | iter: RefCell>, 167 | } 168 | 169 | impl<'tree, 'dwarf, R> fmt::Debug for DebugEntriesTreeIter<'tree, 'dwarf, R> 170 | where 171 | R: crate::gimli::Reader, 172 | { 173 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 174 | let mut debug_list = f.debug_list(); 175 | let mut iter = self.iter.borrow_mut(); 176 | while let Some(child) = iter.next().map_err(crate::fmt_err)? { 177 | let entry = child.entry(); 178 | debug_list.entry(&DebugEntry { 179 | dwarf: self.dwarf, 180 | unit: self.unit, 181 | entry, 182 | }); 183 | } 184 | debug_list.finish() 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Error kinds. 2 | 3 | pub(crate) fn tag_mismatch( 4 | expected: crate::gimli::DwTag, 5 | actual: crate::gimli::DwTag, 6 | ) -> crate::Error { 7 | anyhow!( 8 | "tag mismatch; expected {:?}, received {:?}", 9 | expected.static_string(), 10 | actual.static_string() 11 | ) 12 | } 13 | 14 | pub(crate) fn missing_attr(attr: crate::gimli::DwAt) -> crate::Error { 15 | anyhow!("DIE did not have attribute attr {:?}", attr.static_string()) 16 | } 17 | 18 | pub(crate) fn invalid_attr(attr: crate::gimli::DwAt) -> crate::Error { 19 | anyhow!( 20 | "attribute {:?} had an unexpected form", 21 | attr.static_string() 22 | ) 23 | } 24 | 25 | pub(crate) fn missing_child(tag: crate::gimli::DwTag) -> crate::Error { 26 | anyhow!( 27 | "DIE did not have expected child of tag {:?}", 28 | tag.static_string() 29 | ) 30 | } 31 | 32 | pub(crate) fn size_mismatch(expected: usize, actual: usize) -> crate::Error { 33 | anyhow!("size mismatch; expected {expected} bytes, found {actual}.") 34 | } 35 | 36 | pub(crate) fn name_mismatch(expected: &'static str, actual: String) -> crate::Error { 37 | anyhow!("name mismatch; expected {expected} bytes, found {actual}.") 38 | } 39 | 40 | pub(crate) fn file_indexing() -> crate::Error { 41 | anyhow!("could not map file index to a file name") 42 | } 43 | 44 | pub(crate) fn arithmetic_overflow() -> crate::Error { 45 | anyhow!("arithmetic operation overflowed") 46 | } 47 | 48 | pub(crate) fn enum_destructure() -> crate::Error { 49 | anyhow!("could not destructure enum into variant") 50 | } 51 | 52 | /// Could not downcast the value into the given type. 53 | #[derive(thiserror::Error, Debug)] 54 | #[error("Could not downcast into {src}, received {dst}")] 55 | pub struct DowncastErr { 56 | src: &'static str, 57 | dst: &'static str, 58 | } 59 | 60 | impl DowncastErr { 61 | pub(crate) fn new() -> Self { 62 | let src = std::any::type_name::(); 63 | let dst = std::any::type_name::(); 64 | Self { src, dst } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! **[EXPERIMENTAL]** 2 | //! Deflect brings reflection to Rust using [DWARF] debug info. 3 | //! 4 | //! Deflect can be used to recover the concrete types of trait objects, inspect 5 | //! the internal state of `async` generators, and pretty-print arbitrary data. 6 | //! 7 | //! [DWARF]: https://en.wikipedia.org/wiki/DWARF 8 | //! 9 | //! ## Example 10 | //! Use the [`Reflect`] trait to debug or recursively destructure any value. 11 | //! 12 | //! ``` 13 | //! # use std::any::Any; 14 | //! # #[allow(dead_code)] 15 | //! pub struct Foo { 16 | //! a: u8 17 | //! } 18 | //! 19 | //! // initialize the debuginfo provider 20 | //! let context = deflect::default_provider()?; 21 | //! 22 | //! // create some type-erased data 23 | //! let erased: Box = Box::new(Foo { a: 42 }); 24 | //! 25 | //! // cast it to `&dyn Reflect` 26 | //! let reflectable: &dyn deflect::Reflect = &erased; 27 | //! 28 | //! // reflect it! 29 | //! let value: deflect::Value = reflectable.reflect(&context)?; 30 | //! 31 | //! // pretty-print the reflected value 32 | //! assert_eq!(value.to_string(), "box Foo { a: 42 }"); 33 | //! 34 | //! // downcast into a `BoxedDyn` value 35 | //! let value: deflect::value::BoxedDyn = value.try_into()?; 36 | //! 37 | //! // dereference the boxed value 38 | //! let value: deflect::Value = value.deref()?; 39 | //! 40 | //! // downcast into a `Struct` value 41 | //! let value: deflect::value::Struct = value.try_into()?; 42 | //! 43 | //! // get the field `a` by name 44 | //! let Some(field) = value.field("a")? else { 45 | //! panic!("no field named `a`!") 46 | //! }; 47 | //! 48 | //! // get the value of the field 49 | //! let value = field.value()?; 50 | //! 51 | //! // downcast into a `u8` 52 | //! let value: u8 = value.try_into()?; 53 | //! 54 | //! // check that it's equal to `42`! 55 | //! assert_eq!(value, 42); 56 | //! # Ok::<_, Box>(()) 57 | //! ``` 58 | //! See the `examples` directory of this crate's source for additional examples. 59 | //! 60 | //! ## Limitations 61 | //! The current implementation of [`default_provider`] only works when DWARF 62 | //! debuginfo is stored in the program's binary. It will not work if DWARF 63 | //! debug info is split into other files. Pull requests are welcome. 64 | //! 65 | //! This crate is highly experimental. It is not suitable as a critical 66 | //! component of any system. The initial releases of this crate require 67 | //! significant polish. Pull requests are welcome. Its known soundness holes 68 | //! include ignorance of `UnsafeCell`. Don't reflect into types containing 69 | //! `UnsafeCell`. 70 | //! 71 | //! Additionally, the particulars of how Rust encodes DWARF debug info my change 72 | //! over time. This crate will do its best to keep up with those changes. Again, 73 | //! pull requests are welcome. 74 | 75 | #![allow(clippy::len_without_is_empty, clippy::needless_lifetimes)] 76 | #![deny(missing_docs)] 77 | 78 | #[macro_use] 79 | pub extern crate anyhow; 80 | use anyhow::Error; 81 | 82 | pub use addr2line::{self, gimli, object}; 83 | 84 | use dashmap::DashMap; 85 | use gimli::{AttributeValue, EndianReader, RunTimeEndian, UnitOffset}; 86 | use once_cell::sync::Lazy; 87 | use std::{ 88 | borrow::{Borrow, Cow}, 89 | cell::RefCell, 90 | collections::HashMap, 91 | fmt, 92 | mem::{self, MaybeUninit}, 93 | path::Path, 94 | ptr::slice_from_raw_parts, 95 | rc::Rc, 96 | }; 97 | 98 | mod debug; 99 | mod error; 100 | pub use error::DowncastErr; 101 | 102 | pub mod schema; 103 | pub mod value; 104 | 105 | type Byte = MaybeUninit; 106 | type Bytes<'value> = &'value [Byte]; 107 | 108 | type Addr2LineReader = EndianReader>; 109 | type Context = addr2line::Context; 110 | 111 | /// Raw debug info for a function. 112 | pub struct DebugInfo<'d, R> 113 | where 114 | R: gimli::Reader, 115 | { 116 | context: &'d addr2line::Context, 117 | unit: &'d gimli::Unit, 118 | entry: gimli::UnitOffset, 119 | } 120 | 121 | impl<'d, R> DebugInfo<'d, R> 122 | where 123 | R: gimli::Reader, 124 | { 125 | /// Constructs a new `DebugInfo`. 126 | pub fn new( 127 | context: &'d addr2line::Context, 128 | unit: &'d gimli::Unit, 129 | entry: gimli::UnitOffset, 130 | ) -> Self { 131 | Self { 132 | context, 133 | unit, 134 | entry, 135 | } 136 | } 137 | } 138 | 139 | /// A source of debug info that can be trusted to correspond to the current 140 | /// executable. 141 | /// 142 | /// ## Safety 143 | /// Implementers of this trait must provide accurate debug info for this 144 | /// program. 145 | pub unsafe trait DebugInfoProvider: Clone { 146 | /// The type of the DWARF reader. 147 | type Reader: gimli::Reader; 148 | 149 | /// Produces debug info for a given function. 150 | fn info_for(&self, fn_addr: u64) -> Result, crate::Error>; 151 | } 152 | 153 | mod dbginfo_provider { 154 | use super::*; 155 | 156 | struct Map { 157 | path: std::path::PathBuf, 158 | static_addr: usize, 159 | } 160 | 161 | fn map_of(dynamic_addr: usize) -> Result { 162 | let pid = std::process::id(); 163 | let mappings = procmaps::Mappings::from_pid(pid as _)?; 164 | 165 | for map in mappings.iter() { 166 | if (map.base..=map.ceiling).contains(&dynamic_addr) { 167 | if let procmaps::Path::MappedFile(file) = &map.pathname { 168 | return Ok(Map { 169 | static_addr: (dynamic_addr - map.base) + map.offset, 170 | path: file.into(), 171 | }); 172 | } 173 | } 174 | } 175 | bail!("could not map the dynamic address 0x{dynamic_addr:x} to a static address in the binary"); 176 | } 177 | 178 | pub fn context_of(dynamic_addr: usize) -> Result<(&'static Context, usize), crate::Error> { 179 | let Map { path, static_addr } = map_of(dynamic_addr)?; 180 | let context = read_context(path)?; 181 | Ok((context, static_addr)) 182 | } 183 | 184 | pub fn read_context

(path: P) -> Result<&'static Context, crate::Error> 185 | where 186 | P: Borrow, 187 | { 188 | static OBJECT_CACHE: Lazy< 189 | DashMap>, 190 | > = Lazy::new(DashMap::new); 191 | 192 | let path = path.borrow().to_owned(); 193 | 194 | let object = OBJECT_CACHE.entry(path.clone()).or_try_insert_with(|| { 195 | let file = std::fs::File::open(&path)?; 196 | let mmap = Box::new(unsafe { memmap2::Mmap::map(&file)? }); 197 | let mmap: &'static memmap2::Mmap = Box::leak::<'static>(mmap); 198 | let mmap: &'static [u8] = mmap; 199 | let object = object::File::parse(mmap)?; 200 | let object = Box::leak(Box::new(object)); 201 | Ok::<_, crate::Error>(object) 202 | })?; 203 | 204 | thread_local! { 205 | pub static CONTEXT_CACHE: RefCell> = 206 | RefCell::new(HashMap::new()); 207 | } 208 | 209 | CONTEXT_CACHE.with(move |context_cache| { 210 | let mut context_cache = context_cache.borrow_mut(); 211 | if let Some(context) = context_cache.get(&path) { 212 | Ok(*context) 213 | } else { 214 | let context = addr2line::Context::new(*object)?; 215 | let context: &'static _ = Box::leak(Box::new(context)); 216 | context_cache.insert(path, context); 217 | Ok(context) 218 | } 219 | }) 220 | } 221 | } 222 | 223 | pub(crate) mod private { 224 | #[derive(Copy, Clone, Debug)] 225 | pub struct DefaultProvider {} 226 | } 227 | 228 | pub(crate) use private::DefaultProvider; 229 | 230 | /// The default provider of DWARF debug info. 231 | pub fn default_provider() -> Result { 232 | unsafe impl DebugInfoProvider for DefaultProvider { 233 | type Reader = Addr2LineReader; 234 | 235 | fn info_for(&self, fn_addr: u64) -> Result, crate::Error> { 236 | let (context, static_addr) = crate::dbginfo_provider::context_of(fn_addr as _)?; 237 | let (unit, entry) = crate::dw_unit_and_die_of_addr(context, static_addr)?; 238 | Ok(DebugInfo { 239 | context, 240 | unit, 241 | entry, 242 | }) 243 | } 244 | } 245 | 246 | Ok(DefaultProvider {}) 247 | } 248 | 249 | /// A reflectable type. 250 | pub trait Reflect { 251 | /// Produces an ID that uniquely identifies the type within its compilation 252 | /// unit. 253 | #[inline(never)] 254 | fn local_type_id(&self) -> usize { 255 | ::local_type_id as usize 256 | } 257 | } 258 | 259 | impl Reflect for T {} 260 | 261 | impl dyn Reflect + '_ { 262 | /// Produces a reflected `Value` of `&self`. 263 | pub fn reflect<'value, 'dwarf, P: DebugInfoProvider>( 264 | &'value self, 265 | provider: &'dwarf P, 266 | ) -> Result, crate::Error> { 267 | let DebugInfo { 268 | context, 269 | unit, 270 | entry, 271 | } = provider.info_for(self.local_type_id() as _)?; 272 | let entry = unit.entry(entry)?; 273 | let r#type = schema::Type::from_die(context.dwarf(), unit, entry)?; 274 | let value = 275 | slice_from_raw_parts(self as *const Self as *const Byte, mem::size_of_val(self)); 276 | unsafe { value::Value::with_type(r#type, &*value, provider) } 277 | } 278 | } 279 | 280 | /// Produces the DWARF unit and entry offset for the DIE of `T`. 281 | fn dw_unit_and_die_of_addr<'ctx, R>( 282 | ctx: &'ctx addr2line::Context, 283 | static_addr: usize, 284 | ) -> Result<(&'ctx crate::gimli::Unit, crate::gimli::UnitOffset), crate::Error> 285 | where 286 | R: crate::gimli::Reader, 287 | { 288 | let Some(frame) = ctx 289 | .find_frames(static_addr as u64)? 290 | .next()? else { 291 | bail!("could not find a DWARF frame for the address 0x{static_addr:x}") 292 | }; 293 | 294 | let Some(dw_die_offset) = frame.dw_die_offset else { 295 | bail!("could not find the DWARF unit offset corresponding to the DIE of the function at static address 0x{static_addr:x}") 296 | }; 297 | 298 | let Some(unit) = ctx.find_dwarf_unit(static_addr as u64) else { 299 | bail!("could not find the DWARF unit containing debug info for the function at static address 0x{static_addr:x}") 300 | }; 301 | 302 | let mut ty = None; 303 | let mut tree = unit.entries_tree(Some(dw_die_offset))?; 304 | let mut children = tree.root()?.children(); 305 | 306 | while let Some(child) = children.next()? { 307 | if ty.is_none() && child.entry().tag() == crate::gimli::DW_TAG_template_type_parameter { 308 | ty = Some(get_type(child.entry())?); 309 | break; 310 | } 311 | } 312 | 313 | let Some(ty) = ty else { 314 | return Err(error::missing_child(crate::gimli::DW_TAG_template_type_parameter)) 315 | }; 316 | 317 | Ok((unit, ty)) 318 | } 319 | 320 | impl fmt::Debug for dyn Reflect { 321 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 322 | let context = default_provider().map_err(crate::fmt_err)?; 323 | let value = self.reflect(&context).map_err(crate::fmt_err)?; 324 | fmt::Display::fmt(&value, f) 325 | } 326 | } 327 | 328 | macro_rules! generate_type_and_value { 329 | ($($(#[$attr:meta])* $t:ident,)*) => { 330 | /// A reflected type. 331 | #[allow(non_camel_case_types)] 332 | #[derive(Debug, Clone)] 333 | #[non_exhaustive] 334 | pub enum Type<'dwarf, R> 335 | where 336 | R: crate::gimli::Reader, 337 | { 338 | $( 339 | $(#[$attr])* 340 | $t(schema::$t::<'dwarf, R>), 341 | )* 342 | } 343 | 344 | impl<'dwarf, R> fmt::Display for Type<'dwarf, R> 345 | where 346 | R: crate::gimli::Reader, 347 | { 348 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 349 | match self { 350 | $(Self::$t(v) => v.fmt(f),)* 351 | } 352 | } 353 | } 354 | 355 | $( 356 | #[doc = concat!( 357 | "Upcast a [`", 358 | stringify!($t), 359 | "<'dwarf, R>`][crate::schema::", 360 | stringify!($t), 361 | "] into a [`Type<'dwarf, R>`][Type].", 362 | )] 363 | impl<'dwarf, R> From> 364 | for Type<'dwarf, R> 365 | where 366 | R: crate::gimli::Reader, 367 | { 368 | fn from(atom: crate::schema::$t<'dwarf, R>) -> Self { 369 | Type::$t(atom) 370 | } 371 | } 372 | 373 | #[doc = concat!( 374 | "Attempt to downcast a [`Type<'dwarf, R>`][Type] into a [`", 375 | stringify!($t), 376 | "<'dwarf, R>`][crate::schema::", 377 | stringify!($t), 378 | "].", 379 | )] 380 | impl<'value, 'dwarf, R> TryFrom> for crate::schema::$t<'dwarf, R> 381 | where 382 | R: crate::gimli::Reader, 383 | { 384 | type Error = crate::DowncastErr; 385 | 386 | fn try_from(value: Type<'dwarf, R>) -> Result { 387 | if let Type::$t(value) = value { 388 | Ok(value) 389 | } else { 390 | Err(crate::DowncastErr::new::, Self>()) 391 | } 392 | } 393 | } 394 | 395 | #[doc = concat!( 396 | "Attempt to downcast a [`&Type<'dwarf, R>`][Type] into a [`&", 397 | stringify!($t), 398 | "<'dwarf, R>`][crate::schema::", 399 | stringify!($t), 400 | "].", 401 | )] 402 | impl<'a, 'value, 'dwarf, R> TryFrom<&'a Type<'dwarf, R>> 403 | for &'a crate::schema::$t<'dwarf, R> 404 | where 405 | R: crate::gimli::Reader, 406 | { 407 | type Error = crate::DowncastErr; 408 | 409 | fn try_from(value: &'a Type<'dwarf, R>) -> Result { 410 | if let Type::$t(value) = value { 411 | Ok(value) 412 | } else { 413 | Err(crate::DowncastErr::new::< 414 | &'a Type<'dwarf, R>, 415 | Self, 416 | >()) 417 | } 418 | } 419 | } 420 | )* 421 | 422 | /// A reflected value. 423 | #[allow(non_camel_case_types)] 424 | #[derive(Debug)] 425 | #[non_exhaustive] 426 | pub enum Value<'value, 'dwarf, P = crate::DefaultProvider> 427 | where 428 | P: crate::DebugInfoProvider, 429 | { 430 | $( 431 | $(#[$attr])* 432 | $t(value::$t::<'value, 'dwarf, P>), 433 | )* 434 | } 435 | 436 | impl<'value, 'dwarf, P> Value<'value, 'dwarf, P> 437 | where 438 | P: crate::DebugInfoProvider, 439 | { 440 | /// Safety: `value` absolutely must have the correct `type`. 441 | pub(crate) unsafe fn with_type( 442 | r#type: crate::schema::Type<'dwarf, P::Reader>, 443 | value: crate::Bytes<'value>, 444 | provider: &'dwarf P, 445 | ) -> Result { 446 | match r#type { 447 | $(crate::schema::Type::$t(schema) => schema.with_bytes(provider, value).map(Self::$t),)* 448 | } 449 | } 450 | } 451 | 452 | impl<'value, 'dwarf, P> fmt::Display for Value<'value, 'dwarf, P> 453 | where 454 | P: crate::DebugInfoProvider, 455 | { 456 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 457 | match self { 458 | $(Self::$t(v) => v.fmt(f),)* 459 | } 460 | } 461 | } 462 | 463 | $( 464 | #[doc = concat!( 465 | "Upcast a [`", 466 | stringify!($t), 467 | "<'value, 'dwarf, P>`][value::", 468 | stringify!($t), 469 | "] into a [`Value<'value, 'dwarf, P>`][Value].", 470 | )] 471 | impl<'value, 'dwarf, P> From> 472 | for Value<'value, 'dwarf, P> 473 | where 474 | P: crate::DebugInfoProvider, 475 | { 476 | fn from(atom: value::$t<'value, 'dwarf, P>) -> Self { 477 | Value::$t(atom) 478 | } 479 | } 480 | 481 | #[doc = concat!( 482 | "Attempt to downcast a [`Value<'value, 'dwarf, P>`][Value] into a [`", 483 | stringify!($t), 484 | "<'value, 'dwarf, P>`][value::", 485 | stringify!($t), 486 | "].", 487 | )] 488 | impl<'value, 'dwarf, P> TryFrom> for value::$t<'value, 'dwarf, P> 489 | where 490 | P: crate::DebugInfoProvider, 491 | { 492 | type Error = crate::DowncastErr; 493 | 494 | fn try_from(value: Value<'value, 'dwarf, P>) -> Result { 495 | if let Value::$t(value) = value { 496 | Ok(value) 497 | } else { 498 | Err(crate::DowncastErr::new::, Self>()) 499 | } 500 | } 501 | } 502 | 503 | #[doc = concat!( 504 | "Attempt to downcast a [`&Value<'value, 'dwarf, P>`][Value] into a [`&", 505 | stringify!($t), 506 | "<'value, 'dwarf, P>`][value::", 507 | stringify!($t), 508 | "].", 509 | )] 510 | impl<'a, 'value, 'dwarf, P> TryFrom<&'a Value<'value, 'dwarf, P>> 511 | for &'a value::$t<'value, 'dwarf, P> 512 | where 513 | P: crate::DebugInfoProvider, 514 | { 515 | type Error = crate::DowncastErr; 516 | 517 | fn try_from(value: &'a Value<'value, 'dwarf, P>) -> Result { 518 | if let Value::$t(value) = value { 519 | Ok(value) 520 | } else { 521 | Err(crate::DowncastErr::new::< 522 | &'a Value<'value, 'dwarf, P>, 523 | Self, 524 | >()) 525 | } 526 | } 527 | } 528 | )* 529 | } 530 | } 531 | 532 | generate_type_and_value! { 533 | /// A reflected [`prim@bool`]. 534 | bool, 535 | 536 | /// A reflected [`prim@char`]. 537 | char, 538 | 539 | /// A reflected [`prim@f32`]. 540 | f32, 541 | 542 | /// A reflected [`prim@f64`]. 543 | f64, 544 | 545 | /// A reflected [`prim@i8`]. 546 | i8, 547 | 548 | /// A reflected [`prim@i16`]. 549 | i16, 550 | 551 | /// A reflected [`prim@i32`]. 552 | i32, 553 | 554 | /// A reflected [`prim@i64`]. 555 | i64, 556 | 557 | /// A reflected [`prim@i128`]. 558 | i128, 559 | 560 | /// A reflected [`prim@isize`]. 561 | isize, 562 | 563 | /// A reflected [`prim@u8`]. 564 | u8, 565 | 566 | /// A reflected [`prim@u16`]. 567 | u16, 568 | 569 | /// A reflected [`prim@u32`]. 570 | u32, 571 | 572 | /// A reflected [`prim@u64`]. 573 | u64, 574 | 575 | /// A reflected [`prim@u128`]. 576 | u128, 577 | 578 | /// A reflected [`prim@usize`]. 579 | usize, 580 | 581 | /// A reflected [`()`][prim@unit]. 582 | unit, 583 | 584 | /// A reflected [`str`][prim@str]. 585 | str, 586 | 587 | /// A reflected [`array`][prim@array]. 588 | Array, 589 | 590 | /// A reflected [`Box`]. 591 | Box, 592 | 593 | /// A reflected [`Box`]'d slice. 594 | BoxedSlice, 595 | 596 | /// A reflected [`Box`]'d dyn. 597 | BoxedDyn, 598 | 599 | /// A reflected slice. 600 | Slice, 601 | 602 | /// A reflected struct. 603 | Struct, 604 | 605 | /// A reflected enum. 606 | Enum, 607 | 608 | /// A reflected function. 609 | Function, 610 | 611 | /// A reflected shared reference. 612 | SharedRef, 613 | 614 | /// A reflected unique reference. 615 | UniqueRef, 616 | 617 | /// A reflected `const` pointer. 618 | ConstPtr, 619 | 620 | /// A reflected `mut` pointer. 621 | MutPtr, 622 | } 623 | 624 | #[track_caller] 625 | fn check_tag>( 626 | entry: &crate::gimli::DebuggingInformationEntry, 627 | expected: crate::gimli::DwTag, 628 | ) -> Result<(), crate::Error> { 629 | let actual = entry.tag(); 630 | if actual != expected { 631 | Err(crate::error::tag_mismatch(expected, actual)) 632 | } else { 633 | Ok(()) 634 | } 635 | } 636 | 637 | fn get>( 638 | entry: &crate::gimli::DebuggingInformationEntry, 639 | attr: crate::gimli::DwAt, 640 | ) -> Result, crate::Error> { 641 | entry 642 | .attr_value(attr)? 643 | .ok_or_else(|| crate::error::missing_attr(attr)) 644 | } 645 | 646 | fn get_opt>( 647 | entry: &crate::gimli::DebuggingInformationEntry, 648 | attr: crate::gimli::DwAt, 649 | ) -> Result>, crate::Error> { 650 | Ok(entry.attr_value(attr)?) 651 | } 652 | 653 | fn get_size>( 654 | entry: &crate::gimli::DebuggingInformationEntry, 655 | ) -> Result { 656 | let size = get(entry, crate::gimli::DW_AT_byte_size)?; 657 | size.udata_value() 658 | .ok_or_else(|| crate::error::invalid_attr(crate::gimli::DW_AT_byte_size)) 659 | } 660 | 661 | fn get_size_opt>( 662 | entry: &crate::gimli::DebuggingInformationEntry, 663 | ) -> Result, crate::Error> { 664 | let maybe_size = entry.attr_value(crate::gimli::DW_AT_byte_size)?; 665 | if let Some(size) = maybe_size { 666 | Ok(Some(size.udata_value().ok_or_else(|| { 667 | crate::error::invalid_attr(crate::gimli::DW_AT_byte_size) 668 | })?)) 669 | } else { 670 | Ok(None) 671 | } 672 | } 673 | 674 | fn get_align>( 675 | entry: &crate::gimli::DebuggingInformationEntry, 676 | ) -> Result, crate::Error> { 677 | let maybe_size = entry.attr_value(crate::gimli::DW_AT_alignment)?; 678 | if let Some(size) = maybe_size { 679 | size.udata_value() 680 | .ok_or_else(|| crate::error::invalid_attr(crate::gimli::DW_AT_alignment)) 681 | .map(Some) 682 | } else { 683 | Ok(None) 684 | } 685 | } 686 | 687 | fn get_type_ref<'entry, 'dwarf, R: crate::gimli::Reader>( 688 | entry: &'entry crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 689 | ) -> Result { 690 | if let AttributeValue::UnitRef(offset) = get(entry, crate::gimli::DW_AT_type)? { 691 | Ok(offset) 692 | } else { 693 | Err(crate::error::invalid_attr(crate::gimli::DW_AT_type)) 694 | } 695 | } 696 | 697 | fn get_type_res<'entry, 'dwarf, R: crate::gimli::Reader>( 698 | unit: &'dwarf crate::gimli::Unit, 699 | entry: &'entry crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 700 | ) -> Result, crate::Error> { 701 | if let AttributeValue::UnitRef(offset) = get(entry, crate::gimli::DW_AT_type)? { 702 | Ok(unit.entry(offset)?) 703 | } else { 704 | Err(crate::error::invalid_attr(crate::gimli::DW_AT_type)) 705 | } 706 | } 707 | 708 | fn get_type<'dwarf, R: crate::gimli::Reader>( 709 | entry: &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 710 | ) -> Result { 711 | let attr = crate::gimli::DW_AT_type; 712 | let value = get(entry, attr)?; 713 | if let AttributeValue::UnitRef(offset) = value { 714 | Ok(offset) 715 | } else { 716 | Err(error::invalid_attr(attr)) 717 | } 718 | } 719 | 720 | fn get_file<'a, R: crate::gimli::Reader + 'a>( 721 | dwarf: &'a crate::gimli::Dwarf, 722 | unit: &'a crate::gimli::Unit, 723 | entry: &crate::gimli::DebuggingInformationEntry, 724 | ) -> Result>, crate::Error> { 725 | let file = get(entry, crate::gimli::DW_AT_decl_file)?; 726 | let AttributeValue::FileIndex(index) = file else { 727 | return Ok(None); // error? 728 | }; 729 | let Some(prog) = &unit.line_program else { return Ok(None) }; 730 | let Some(file) = prog.header().file(index) else { return Ok(None) }; 731 | let filename = dwarf.attr_string(unit, file.path_name())?; 732 | let filename = filename.to_string_lossy()?; 733 | if let Some(dir) = file.directory(prog.header()) { 734 | let dirname = dwarf.attr_string(unit, dir)?; 735 | let dirname = dirname.to_string_lossy()?; 736 | if !dirname.is_empty() { 737 | return Ok(Some(format!("{dirname}/{filename}").into())); 738 | } 739 | } 740 | // TODO: Any other way around this lifetime error? 741 | Ok(Some(filename.into_owned().into())) 742 | } 743 | 744 | fn get_attr_ref>( 745 | entry: &crate::gimli::DebuggingInformationEntry, 746 | name: crate::gimli::DwAt, 747 | ) -> Result, crate::Error> { 748 | if let Some(attr) = entry.attr(name)? { 749 | if let AttributeValue::UnitRef(offset) = attr.value() { 750 | return Ok(Some(offset)); 751 | } 752 | } 753 | Ok(None) 754 | } 755 | 756 | fn fi_to_string<'a, R: crate::gimli::Reader + 'a>( 757 | dwarf: &'a crate::gimli::Dwarf, 758 | unit: &'a crate::gimli::Unit, 759 | file_index: u64, 760 | ) -> Result { 761 | let line_program = unit 762 | .line_program 763 | .as_ref() 764 | .ok_or_else(error::file_indexing)?; 765 | 766 | let file = line_program 767 | .header() 768 | .file(file_index) 769 | .ok_or_else(error::file_indexing)?; 770 | 771 | let filename = dwarf.attr_string(unit, file.path_name())?; 772 | let filename = filename.to_string_lossy()?; 773 | if let Some(dir) = file.directory(line_program.header()) { 774 | let dirname = dwarf.attr_string(unit, dir)?; 775 | let dirname = dirname.to_string_lossy()?; 776 | if !dirname.is_empty() { 777 | return Ok(format!("{dirname}/{filename}")); 778 | } 779 | } 780 | Ok(filename.into_owned()) 781 | } 782 | 783 | struct DebugDisplay(T); 784 | 785 | impl fmt::Debug for DebugDisplay 786 | where 787 | T: fmt::Display, 788 | { 789 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 790 | self.0.fmt(f) 791 | } 792 | } 793 | 794 | fn fmt_err(err: E) -> fmt::Error { 795 | eprintln!("ERROR: {err}"); 796 | fmt::Error 797 | } 798 | -------------------------------------------------------------------------------- /src/schema/array.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A schema for [`[T; N]`][prim@array]. 4 | #[derive(Clone)] 5 | #[allow(non_camel_case_types)] 6 | pub struct Array<'dwarf, R: crate::gimli::Reader> 7 | where 8 | R: crate::gimli::Reader, 9 | { 10 | dwarf: &'dwarf crate::gimli::Dwarf, 11 | unit: &'dwarf crate::gimli::Unit, 12 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 13 | } 14 | 15 | impl<'dwarf, R> Array<'dwarf, R> 16 | where 17 | R: crate::gimli::Reader, 18 | { 19 | /// Construct a new `Function` from a 20 | /// [`DW_TAG_array_type`][crate::gimli::DW_TAG_array_type]. 21 | pub(crate) fn from_dw_tag_array_type( 22 | dwarf: &'dwarf crate::gimli::Dwarf, 23 | unit: &'dwarf crate::gimli::Unit, 24 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 25 | ) -> Result { 26 | crate::check_tag(&entry, crate::gimli::DW_TAG_array_type)?; 27 | Ok(Self { dwarf, unit, entry }) 28 | } 29 | 30 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Function`'s 31 | /// debuginfo belongs to. 32 | #[allow(dead_code)] 33 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 34 | self.dwarf 35 | } 36 | 37 | /// The DWARF [unit][crate::gimli::Unit] that this `Function`'s debuginfo 38 | /// belongs to. 39 | #[allow(dead_code)] 40 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 41 | self.unit 42 | } 43 | 44 | /// The [debugging information 45 | /// entry][crate::gimli::DebuggingInformationEntry] this `Function` 46 | /// abstracts over. 47 | #[allow(dead_code)] 48 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 49 | &self.entry 50 | } 51 | 52 | /// The element type, `T`, of this [`[T; N]`][prim@array] array. 53 | pub fn elt_type(&self) -> Result, crate::Error> { 54 | super::Type::from_die( 55 | self.dwarf, 56 | self.unit, 57 | self.unit.entry(crate::get_type(&self.entry)?)?, 58 | ) 59 | } 60 | 61 | /// The length, `N`, of this [`[T; N]`][prim@array] array. 62 | pub fn len(&self) -> Result { 63 | let mut tree = self.unit.entries_tree(Some(self.entry.offset()))?; 64 | let root = tree.root()?; 65 | let mut children = root.children(); 66 | let dw_tag_subrange_type = children 67 | .next()? 68 | .ok_or_else(|| crate::error::missing_child(crate::gimli::DW_TAG_subrange_type))?; 69 | let dw_tag_subrange_type = dw_tag_subrange_type.entry(); 70 | crate::check_tag(dw_tag_subrange_type, crate::gimli::DW_TAG_subrange_type)?; 71 | let dw_at_count = crate::get(dw_tag_subrange_type, crate::gimli::DW_AT_count)?; 72 | let count = dw_at_count 73 | .udata_value() 74 | .ok_or_else(|| crate::error::invalid_attr(crate::gimli::DW_AT_count))?; 75 | Ok(count) 76 | } 77 | 78 | /// The size of this array, in bytes. 79 | pub fn bytes(&self) -> Result { 80 | let len = self.len()?; 81 | let elt_type = self.elt_type()?; 82 | let elt_size = elt_type.size()?; 83 | len.checked_mul(elt_size) 84 | .ok_or_else(crate::error::arithmetic_overflow) 85 | } 86 | } 87 | 88 | impl<'dwarf, R> fmt::Debug for Array<'dwarf, R> 89 | where 90 | R: crate::gimli::Reader, 91 | { 92 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 93 | let mut debug_tuple = f.debug_tuple("deflect::schema::array"); 94 | debug_tuple.field(&crate::debug::DebugEntry::new( 95 | self.dwarf, 96 | self.unit, 97 | &self.entry, 98 | )); 99 | debug_tuple.finish() 100 | } 101 | } 102 | 103 | impl<'dwarf, R> fmt::Display for Array<'dwarf, R> 104 | where 105 | R: crate::gimli::Reader, 106 | { 107 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 108 | super::Name::from_die(self.dwarf(), self.unit(), self.entry()) 109 | .map_err(crate::fmt_err)? 110 | .fmt(f) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/schema/box.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A schema for a shared reference (i.e., `&T`). 4 | #[derive(Clone)] 5 | pub struct Box<'dwarf, R> 6 | where 7 | R: crate::gimli::Reader, 8 | { 9 | dwarf: &'dwarf crate::gimli::Dwarf, 10 | unit: &'dwarf crate::gimli::Unit, 11 | entry: crate::gimli::UnitOffset, 12 | name: Option>, 13 | target: crate::gimli::UnitOffset, 14 | } 15 | 16 | impl<'dwarf, R> Box<'dwarf, R> 17 | where 18 | R: crate::gimli::Reader, 19 | { 20 | /// Construct a new `Shared`. 21 | pub(super) fn new( 22 | dwarf: &'dwarf crate::gimli::Dwarf, 23 | unit: &'dwarf crate::gimli::Unit, 24 | entry: crate::gimli::UnitOffset, 25 | name: Option>, 26 | target: crate::gimli::UnitOffset, 27 | ) -> Self { 28 | Self { 29 | dwarf, 30 | unit, 31 | entry, 32 | name, 33 | target, 34 | } 35 | } 36 | 37 | /// The name of this reference type. 38 | pub fn name(&self) -> Option<&super::Name> { 39 | self.name.as_ref() 40 | } 41 | 42 | /// The size of this type, in bytes. 43 | pub fn size(&self) -> u64 { 44 | core::mem::size_of::() as _ 45 | } 46 | 47 | /// The type of the referent. 48 | pub fn r#type(&self) -> Result, crate::Error> { 49 | let entry = self.unit.entry(self.target)?; 50 | super::Type::from_die(self.dwarf, self.unit, entry) 51 | } 52 | } 53 | 54 | impl<'dwarf, R> fmt::Debug for Box<'dwarf, R> 55 | where 56 | R: crate::gimli::Reader, 57 | { 58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 59 | let entry = self.unit.entry(self.entry).map_err(crate::fmt_err)?; 60 | let mut debug_tuple = f.debug_tuple("deflect::schema::Shared"); 61 | debug_tuple.field(&crate::debug::DebugEntry::new( 62 | self.dwarf, self.unit, &entry, 63 | )); 64 | debug_tuple.finish() 65 | } 66 | } 67 | 68 | impl<'dwarf, R> fmt::Display for Box<'dwarf, R> 69 | where 70 | R: crate::gimli::Reader, 71 | { 72 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 73 | if let Some(name) = self.name() { 74 | name.fmt(f) 75 | } else { 76 | f.write_str("*? ")?; 77 | let target = self.r#type().map_err(crate::fmt_err)?; 78 | target.fmt(f) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/schema/boxed_dyn.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A schema for `Box`. 4 | #[derive(Clone)] 5 | #[allow(non_camel_case_types)] 6 | pub struct BoxedDyn<'dwarf, R: crate::gimli::Reader> 7 | where 8 | R: crate::gimli::Reader, 9 | { 10 | schema: super::Struct<'dwarf, R>, 11 | pointer: super::Field<'dwarf, R>, 12 | vtable: super::Field<'dwarf, R>, 13 | } 14 | 15 | impl<'dwarf, R> BoxedDyn<'dwarf, R> 16 | where 17 | R: crate::gimli::Reader, 18 | { 19 | /// Construct a new `BoxedSlice`. 20 | pub(crate) fn new( 21 | schema: super::Struct<'dwarf, R>, 22 | pointer: super::Field<'dwarf, R>, 23 | vtable: super::Field<'dwarf, R>, 24 | ) -> Result { 25 | Ok(Self { 26 | schema, 27 | pointer, 28 | vtable, 29 | }) 30 | } 31 | 32 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Struct`'s debuginfo 33 | /// belongs to. 34 | #[allow(dead_code)] 35 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 36 | self.schema.dwarf() 37 | } 38 | 39 | /// The DWARF [unit][crate::gimli::Unit] that this `Struct`'s debuginfo 40 | /// belongs to. 41 | #[allow(dead_code)] 42 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 43 | self.schema.unit() 44 | } 45 | 46 | /// The [debugging information 47 | /// entry][crate::gimli::DebuggingInformationEntry] this `Struct` abstracts 48 | /// over. 49 | #[allow(dead_code)] 50 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 51 | self.schema.entry() 52 | } 53 | 54 | /// This fat pointer, interpreted as a struct. 55 | pub fn as_struct(&self) -> &super::Struct<'dwarf, R> { 56 | &self.schema 57 | } 58 | 59 | /// The `pointer` field of this slice. 60 | pub fn pointer(&self) -> &super::Field<'dwarf, R> { 61 | &self.pointer 62 | } 63 | 64 | /// The `vtable` field of this slice. 65 | pub fn vtable(&self) -> &super::Field<'dwarf, R> { 66 | &self.vtable 67 | } 68 | 69 | /// The size of this fat pointer, in bytes. 70 | pub fn size(&self) -> Result { 71 | crate::get_size(self.entry()) 72 | } 73 | 74 | /// The alignment of fat pointer, in bytes. 75 | pub fn align(&self) -> Result, crate::Error> { 76 | crate::get_align(self.entry()) 77 | } 78 | } 79 | 80 | impl<'dwarf, R> fmt::Debug for BoxedDyn<'dwarf, R> 81 | where 82 | R: crate::gimli::Reader, 83 | { 84 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 85 | let mut debug_tuple = f.debug_tuple("deflect::schema::Slice"); 86 | debug_tuple.field(&crate::debug::DebugEntry::new( 87 | self.dwarf(), 88 | self.unit(), 89 | self.entry(), 90 | )); 91 | debug_tuple.finish() 92 | } 93 | } 94 | 95 | impl<'dwarf, R> fmt::Display for BoxedDyn<'dwarf, R> 96 | where 97 | R: crate::gimli::Reader, 98 | { 99 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { 100 | todo!() 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/schema/boxed_slice.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A schema for `Box<[T]>`. 4 | #[derive(Clone)] 5 | #[allow(non_camel_case_types)] 6 | pub struct BoxedSlice<'dwarf, R: crate::gimli::Reader> 7 | where 8 | R: crate::gimli::Reader, 9 | { 10 | schema: super::Struct<'dwarf, R>, 11 | data_ptr: super::Field<'dwarf, R>, 12 | length: super::Field<'dwarf, R>, 13 | } 14 | 15 | impl<'dwarf, R> BoxedSlice<'dwarf, R> 16 | where 17 | R: crate::gimli::Reader, 18 | { 19 | /// Construct a new `BoxedSlice`. 20 | pub(crate) fn new( 21 | schema: super::Struct<'dwarf, R>, 22 | data_ptr: super::Field<'dwarf, R>, 23 | length: super::Field<'dwarf, R>, 24 | ) -> Result { 25 | Ok(Self { 26 | schema, 27 | data_ptr, 28 | length, 29 | }) 30 | } 31 | 32 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Struct`'s debuginfo 33 | /// belongs to. 34 | #[allow(dead_code)] 35 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 36 | self.schema.dwarf() 37 | } 38 | 39 | /// The DWARF [unit][crate::gimli::Unit] that this `Struct`'s debuginfo 40 | /// belongs to. 41 | #[allow(dead_code)] 42 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 43 | self.schema.unit() 44 | } 45 | 46 | /// The [debugging information 47 | /// entry][crate::gimli::DebuggingInformationEntry] this `Struct` abstracts 48 | /// over. 49 | #[allow(dead_code)] 50 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 51 | self.schema.entry() 52 | } 53 | 54 | /// The `data_ptr` field of this slice. 55 | pub fn data_ptr(&self) -> super::Field<'dwarf, R> { 56 | self.data_ptr.clone() 57 | } 58 | 59 | /// The `length` field of this slice. 60 | pub fn length(&self) -> &super::Field<'dwarf, R> { 61 | &self.length 62 | } 63 | 64 | /// The element type of this slice. 65 | pub fn elt(&self) -> Result, crate::Error> { 66 | if let super::Type::MutPtr(r#ref) = self.data_ptr().r#type()? { 67 | return r#ref.r#type(); 68 | } else { 69 | unreachable!() 70 | } 71 | } 72 | 73 | /// The size of this slice, in bytes. 74 | pub fn size(&self) -> Result { 75 | crate::get_size(self.entry()) 76 | } 77 | 78 | /// The alignment of this slice, in bytes. 79 | pub fn align(&self) -> Result, crate::Error> { 80 | crate::get_align(self.entry()) 81 | } 82 | } 83 | 84 | impl<'dwarf, R> fmt::Debug for BoxedSlice<'dwarf, R> 85 | where 86 | R: crate::gimli::Reader, 87 | { 88 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 89 | let mut debug_tuple = f.debug_tuple("deflect::schema::Slice"); 90 | debug_tuple.field(&crate::debug::DebugEntry::new( 91 | self.dwarf(), 92 | self.unit(), 93 | self.entry(), 94 | )); 95 | debug_tuple.finish() 96 | } 97 | } 98 | 99 | impl<'dwarf, R> fmt::Display for BoxedSlice<'dwarf, R> 100 | where 101 | R: crate::gimli::Reader, 102 | { 103 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { 104 | todo!() 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/schema/data.rs: -------------------------------------------------------------------------------- 1 | /// A static value (e.g., enum discriminant). 2 | #[allow(non_camel_case_types)] 3 | #[derive(Debug, Copy, Clone)] 4 | pub enum Data { 5 | /// A byte of data. 6 | u8(u8), 7 | /// Two bytes of data. 8 | u16(u16), 9 | /// Four bytes of data. 10 | u32(u32), 11 | /// Eight bytes of data. 12 | u64(u64), 13 | } 14 | -------------------------------------------------------------------------------- /src/schema/enum.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use itertools::Itertools; 4 | 5 | /// A schema for an [`enum`](https://doc.rust-lang.org/std/keyword.struct.html). 6 | #[derive(Clone)] 7 | pub struct Enum<'dwarf, R: crate::gimli::Reader> 8 | where 9 | R: crate::gimli::Reader, 10 | { 11 | dwarf: &'dwarf crate::gimli::Dwarf, 12 | unit: &'dwarf crate::gimli::Unit, 13 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 14 | discr_type_offset: crate::gimli::UnitOffset, 15 | name: super::Name, 16 | location: super::Offset<'dwarf, R>, 17 | } 18 | 19 | impl<'dwarf, R> Enum<'dwarf, R> 20 | where 21 | R: crate::gimli::Reader, 22 | { 23 | /// Construct an `Enum` from a 24 | /// [`DW_TAG_enumeration_type`][crate::gimli::DW_TAG_enumeration_type]. 25 | pub(crate) fn from_dw_tag_enumeration_type( 26 | dwarf: &'dwarf crate::gimli::Dwarf, 27 | unit: &'dwarf crate::gimli::Unit, 28 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 29 | ) -> Result { 30 | crate::check_tag(&entry, crate::gimli::DW_TAG_enumeration_type)?; 31 | let name = super::Name::from_die(dwarf, unit, &entry)?; 32 | let discr_type_offset = crate::get_type(&entry)?; 33 | let location = super::Offset::zero(unit); 34 | 35 | Ok(Self { 36 | dwarf, 37 | unit, 38 | entry, 39 | discr_type_offset, 40 | name, 41 | location, 42 | }) 43 | } 44 | 45 | /// Construct an `Enum` from a 46 | /// [`DW_TAG_structure_type`][crate::gimli::DW_TAG_structure_type]. 47 | pub(crate) fn from_dw_tag_structure_type( 48 | dwarf: &'dwarf crate::gimli::Dwarf, 49 | unit: &'dwarf crate::gimli::Unit, 50 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 51 | ) -> Result { 52 | crate::check_tag(&entry, crate::gimli::DW_TAG_structure_type)?; 53 | let name = super::Name::from_die(dwarf, unit, &entry)?; 54 | 55 | let mut tree = unit.entries_tree(Some(entry.offset()))?; 56 | let root = tree.root()?; 57 | let mut variant_part = None; 58 | 59 | { 60 | let mut children = root.children(); 61 | while let Some(child) = children.next()? { 62 | if child.entry().tag() == crate::gimli::DW_TAG_variant_part { 63 | variant_part = Some(child.entry().clone()); 64 | } 65 | } 66 | } 67 | 68 | let dw_tag_variant_part = variant_part 69 | .ok_or_else(|| crate::error::missing_child(crate::gimli::DW_TAG_variant_part))?; 70 | 71 | let dw_at_discr = crate::get_attr_ref(&dw_tag_variant_part, crate::gimli::DW_AT_discr)? 72 | .ok_or_else(|| crate::error::missing_attr(crate::gimli::DW_AT_discr))?; 73 | 74 | let dw_tag_member = unit.entry(dw_at_discr)?; 75 | 76 | let discr_type_offset = crate::get_type(&dw_tag_member)?; 77 | 78 | let location = super::Offset::from_die(unit, &dw_tag_member)?; 79 | 80 | Ok(Self { 81 | dwarf, 82 | unit, 83 | entry, 84 | discr_type_offset, 85 | name, 86 | location, 87 | }) 88 | } 89 | 90 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Enum`'s debuginfo 91 | /// belongs to. 92 | #[allow(dead_code)] 93 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 94 | self.dwarf 95 | } 96 | 97 | /// The DWARF [unit][crate::gimli::Unit] that this `Enum`'s debuginfo 98 | /// belongs to. 99 | #[allow(dead_code)] 100 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 101 | self.unit 102 | } 103 | 104 | /// The [debugging information 105 | /// entry][crate::gimli::DebuggingInformationEntry] this `Enum` abstracts 106 | /// over. 107 | #[allow(dead_code)] 108 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 109 | &self.entry 110 | } 111 | 112 | /// The name of this type. 113 | pub fn name(&self) -> &super::Name { 114 | &self.name 115 | } 116 | 117 | /// The discriminant of this type. 118 | pub fn discriminant_type(&self) -> Result, crate::Error> { 119 | let entry = self.unit.entry(self.discr_type_offset)?; 120 | super::Type::from_die(self.dwarf, self.unit, entry) 121 | } 122 | 123 | /// The discriminant of this type. 124 | pub fn discriminant_location(&self) -> &super::Offset { 125 | &self.location 126 | } 127 | 128 | /// Variants of this type. 129 | pub fn variants(&self) -> Result, crate::Error> { 130 | let discriminant_type = self.discriminant_type()?; 131 | let mut tree = self.unit.entries_tree(Some(self.entry.offset()))?; 132 | let root = tree.root()?; 133 | let tree = match self.entry.tag() { 134 | crate::gimli::DW_TAG_enumeration_type => tree, 135 | crate::gimli::DW_TAG_structure_type => { 136 | let mut children = root.children(); 137 | let mut variant_part = None; 138 | while let Some(child) = children.next()? { 139 | if child.entry().tag() == crate::gimli::DW_TAG_variant_part { 140 | variant_part = Some(child.entry().offset()); 141 | } 142 | } 143 | self.unit.entries_tree(variant_part)? 144 | } 145 | _ => unimplemented!( 146 | "unhandled enum representation: {:#?}", 147 | crate::debug::DebugEntry::new(self.dwarf, self.unit, &self.entry) 148 | ), 149 | }; 150 | Ok(super::Variants::from_tree( 151 | self.dwarf, 152 | self.unit, 153 | tree, 154 | discriminant_type, 155 | )) 156 | } 157 | 158 | /// The size of this type, in bytes. 159 | pub fn size(&self) -> Result { 160 | crate::get_size(self.entry()) 161 | } 162 | 163 | /// The alignment of this type, in bytes. 164 | pub fn align(&self) -> Result, crate::Error> { 165 | crate::get_align(self.entry()) 166 | } 167 | } 168 | 169 | impl<'dwarf, R> fmt::Debug for Enum<'dwarf, R> 170 | where 171 | R: crate::gimli::Reader, 172 | { 173 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 174 | let mut debug_tuple = f.debug_tuple("deflect::schema::Enum"); 175 | debug_tuple.field(&crate::debug::DebugEntry::new( 176 | self.dwarf, 177 | self.unit, 178 | &self.entry, 179 | )); 180 | debug_tuple.finish() 181 | } 182 | } 183 | 184 | impl<'dwarf, R> fmt::Display for Enum<'dwarf, R> 185 | where 186 | R: crate::gimli::Reader, 187 | { 188 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 189 | f.write_str("enum ")?; 190 | 191 | self.name().fmt(f)?; 192 | 193 | f.write_str(" {")?; 194 | 195 | if f.alternate() { 196 | f.write_str("\n ")?; 197 | } else { 198 | f.write_str(" ")?; 199 | } 200 | 201 | let mut variants = self.variants().map_err(crate::fmt_err)?; 202 | let variants = variants.iter().map_err(crate::fmt_err)?; 203 | 204 | variants 205 | .format(if f.alternate() { ",\n " } else { ", " }) 206 | .fmt(f)?; 207 | 208 | if f.alternate() { 209 | f.write_str(",\n}") 210 | } else { 211 | f.write_str(" }") 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/schema/field.rs: -------------------------------------------------------------------------------- 1 | use super::{Name, Offset, Type}; 2 | use std::fmt; 3 | 4 | /// A field of a [struct][super::Struct] or [variant][super::Variant]. 5 | #[derive(Clone)] 6 | pub struct Field<'dwarf, R> 7 | where 8 | R: crate::gimli::Reader, 9 | { 10 | dwarf: &'dwarf crate::gimli::Dwarf, 11 | unit: &'dwarf crate::gimli::Unit, 12 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 13 | } 14 | 15 | impl<'dwarf, R> Field<'dwarf, R> 16 | where 17 | R: crate::gimli::Reader, 18 | { 19 | /// Construct a new `Field` from a 20 | /// [`DW_TAG_member`][crate::gimli::DW_TAG_member]. 21 | pub(crate) fn from_dw_tag_member( 22 | dwarf: &'dwarf crate::gimli::Dwarf, 23 | unit: &'dwarf crate::gimli::Unit, 24 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 25 | ) -> Result { 26 | let _tree = unit.entries_tree(Some(entry.offset()))?; 27 | //crate::debug::inspect_tree(&mut tree, dwarf, unit); 28 | crate::check_tag(&entry, crate::gimli::DW_TAG_member)?; 29 | Ok(Self { dwarf, unit, entry }) 30 | } 31 | 32 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Field`'s debuginfo 33 | /// belongs to. 34 | #[allow(dead_code)] 35 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 36 | self.dwarf 37 | } 38 | 39 | /// The DWARF [unit][crate::gimli::Unit] that this `Field`'s debuginfo 40 | /// belongs to. 41 | #[allow(dead_code)] 42 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 43 | self.unit 44 | } 45 | 46 | /// The [debugging information 47 | /// entry][crate::gimli::DebuggingInformationEntry] this `Field` abstracts 48 | /// over. 49 | #[allow(dead_code)] 50 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 51 | &self.entry 52 | } 53 | 54 | /// The name of this primitive type. 55 | pub fn name(&self) -> Result, crate::Error> { 56 | Name::from_die(self.dwarf(), self.unit(), self.entry()) 57 | } 58 | 59 | /// The size of this field, in bytes. 60 | pub fn size(&self) -> Result, crate::Error> { 61 | crate::get_size_opt(self.entry()) 62 | } 63 | 64 | /// The alignment of this field, in bytes. 65 | pub fn align(&self) -> Result, crate::Error> { 66 | crate::get_align(self.entry()) 67 | } 68 | 69 | /// The offset at which this field occurs. 70 | pub fn offset(&'dwarf self) -> Result, crate::Error> { 71 | Offset::from_die(self.unit(), self.entry()) 72 | } 73 | 74 | /// The type of the field. 75 | pub fn r#type(&self) -> Result, crate::Error> { 76 | let r#type = crate::get_type_res(self.unit, &self.entry)?; 77 | super::Type::from_die(self.dwarf, self.unit, r#type) 78 | } 79 | } 80 | 81 | impl<'dwarf, R> fmt::Display for Field<'dwarf, R> 82 | where 83 | R: crate::gimli::Reader, 84 | { 85 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 86 | self.name().map_err(crate::fmt_err)?.fmt(f) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/schema/fields.rs: -------------------------------------------------------------------------------- 1 | /// Fields of a [struct][super::Struct] or an [enum variant][super::Variant]. 2 | /// 3 | /// Call [`iter`][Self::iter] to iterate over fields. 4 | pub struct Fields<'dwarf, R: crate::gimli::Reader> 5 | where 6 | R: crate::gimli::Reader, 7 | { 8 | dwarf: &'dwarf crate::gimli::Dwarf, 9 | unit: &'dwarf crate::gimli::Unit, 10 | tree: crate::gimli::EntriesTree<'dwarf, 'dwarf, R>, 11 | } 12 | 13 | impl<'dwarf, R> Fields<'dwarf, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) fn from_tree( 18 | dwarf: &'dwarf crate::gimli::Dwarf, 19 | unit: &'dwarf crate::gimli::Unit, 20 | tree: crate::gimli::EntriesTree<'dwarf, 'dwarf, R>, 21 | ) -> Self { 22 | Self { dwarf, unit, tree } 23 | } 24 | 25 | /// Produces an iterator over fields. 26 | pub fn iter(&mut self) -> Result, crate::Error> { 27 | Ok(FieldsIter { 28 | dwarf: self.dwarf, 29 | unit: self.unit, 30 | iter: self.tree.root()?.children(), 31 | }) 32 | } 33 | } 34 | 35 | /// An iterator over fields. 36 | pub struct FieldsIter<'dwarf, 'tree, R: crate::gimli::Reader> 37 | where 38 | R: crate::gimli::Reader, 39 | { 40 | dwarf: &'dwarf crate::gimli::Dwarf, 41 | unit: &'dwarf crate::gimli::Unit, 42 | iter: crate::gimli::EntriesTreeIter<'dwarf, 'dwarf, 'tree, R>, 43 | } 44 | 45 | impl<'dwarf, 'tree, R: crate::gimli::Reader> FieldsIter<'dwarf, 'tree, R> 46 | where 47 | R: crate::gimli::Reader, 48 | { 49 | /// Produces the next field, if any. 50 | pub fn try_next(&mut self) -> Result>, crate::Error> { 51 | loop { 52 | let Some(next) = self.iter.next()? else { return Ok(None) }; 53 | let entry = next.entry(); 54 | if entry.tag() != crate::gimli::DW_TAG_member { 55 | continue; 56 | } 57 | return Ok(Some(super::Field::from_dw_tag_member( 58 | self.dwarf, 59 | self.unit, 60 | entry.clone(), 61 | )?)); 62 | } 63 | } 64 | } 65 | 66 | impl<'dwarf, 'tree, R: crate::gimli::Reader> Iterator 67 | for FieldsIter<'dwarf, 'tree, R> 68 | { 69 | type Item = super::Field<'dwarf, R>; 70 | 71 | fn next(&mut self) -> Option { 72 | match self.try_next() { 73 | Ok(next) => next, 74 | Err(err) => panic!("could not read next field: {err}"), 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/schema/function.rs: -------------------------------------------------------------------------------- 1 | use super::Name; 2 | use std::fmt; 3 | 4 | /// A schema for [`fn`][prim@fn]. 5 | #[derive(Clone)] 6 | pub struct Function<'dwarf, R: crate::gimli::Reader> 7 | where 8 | R: crate::gimli::Reader, 9 | { 10 | dwarf: &'dwarf crate::gimli::Dwarf, 11 | unit: &'dwarf crate::gimli::Unit, 12 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 13 | } 14 | 15 | impl<'dwarf, R> Function<'dwarf, R> 16 | where 17 | R: crate::gimli::Reader, 18 | { 19 | /// Construct a new `Function` from a 20 | /// [`DW_TAG_subroutine_type`][crate::gimli::DW_TAG_subroutine_type]. 21 | pub(crate) fn from_dw_tag_subroutine_type( 22 | dwarf: &'dwarf crate::gimli::Dwarf, 23 | unit: &'dwarf crate::gimli::Unit, 24 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 25 | ) -> Result { 26 | crate::check_tag(&entry, crate::gimli::DW_TAG_subroutine_type)?; 27 | Ok(Self { dwarf, unit, entry }) 28 | } 29 | 30 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Function`'s 31 | /// debuginfo belongs to. 32 | #[allow(dead_code)] 33 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 34 | self.dwarf 35 | } 36 | 37 | /// The DWARF [unit][crate::gimli::Unit] that this `Function`'s debuginfo 38 | /// belongs to. 39 | #[allow(dead_code)] 40 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 41 | self.unit 42 | } 43 | 44 | /// The [debugging information 45 | /// entry][crate::gimli::DebuggingInformationEntry] this `Function` 46 | /// abstracts over. 47 | #[allow(dead_code)] 48 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 49 | &self.entry 50 | } 51 | 52 | /// The name of this type. 53 | pub fn name(&self) -> Result, crate::Error> { 54 | println!( 55 | "{:?}", 56 | crate::debug::DebugEntry::new(self.dwarf, self.unit, &self.entry) 57 | ); 58 | Name::from_die(self.dwarf(), self.unit(), self.entry()) 59 | } 60 | } 61 | 62 | impl<'dwarf, R> fmt::Debug for Function<'dwarf, R> 63 | where 64 | R: crate::gimli::Reader, 65 | { 66 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 67 | let mut debug_tuple = f.debug_tuple("deflect::schema::Function"); 68 | debug_tuple.field(&crate::debug::DebugEntry::new( 69 | self.dwarf, 70 | self.unit, 71 | &self.entry, 72 | )); 73 | debug_tuple.finish() 74 | } 75 | } 76 | 77 | impl<'dwarf, R> fmt::Display for Function<'dwarf, R> 78 | where 79 | R: crate::gimli::Reader, 80 | { 81 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 82 | f.write_str("fn()") 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/schema/mod.rs: -------------------------------------------------------------------------------- 1 | //! Reflections of Rust types. 2 | 3 | mod array; 4 | mod r#box; 5 | mod boxed_dyn; 6 | mod boxed_slice; 7 | mod data; 8 | mod r#enum; 9 | mod field; 10 | mod fields; 11 | mod function; 12 | mod name; 13 | mod offset; 14 | mod pointer; 15 | mod slice; 16 | mod str_impl; 17 | mod r#struct; 18 | mod variant; 19 | mod variants; 20 | 21 | pub use array::Array; 22 | pub use boxed_dyn::BoxedDyn; 23 | pub use boxed_slice::BoxedSlice; 24 | pub use data::Data; 25 | pub use fields::{Fields, FieldsIter}; 26 | pub use function::Function; 27 | pub use name::Name; 28 | pub use offset::Offset; 29 | pub use pointer::{Const, Mut, Pointer, Reference, Shared, Unique}; 30 | pub use r#box::Box; 31 | pub use r#enum::Enum; 32 | pub use r#field::Field; 33 | pub use r#struct::Struct; 34 | pub use r#variant::Variant; 35 | pub use slice::Slice; 36 | pub use str_impl::str; 37 | pub use variants::{Variants, VariantsIter}; 38 | 39 | /// A reflected shared reference type. 40 | pub type SharedRef<'dwarf, R> = crate::schema::Pointer<'dwarf, crate::schema::Shared, R>; 41 | 42 | /// A reflected unique reference type. 43 | pub type UniqueRef<'dwarf, R> = crate::schema::Pointer<'dwarf, crate::schema::Unique, R>; 44 | 45 | /// A reflected `const` pointer type. 46 | pub type ConstPtr<'dwarf, R> = crate::schema::Pointer<'dwarf, crate::schema::Const, R>; 47 | 48 | /// A reflected `mut` pointer type. 49 | pub type MutPtr<'dwarf, R> = crate::schema::Pointer<'dwarf, crate::schema::Mut, R>; 50 | 51 | impl<'dwarf, R> Type<'dwarf, R> 52 | where 53 | R: crate::gimli::Reader, 54 | { 55 | pub(crate) fn from_die( 56 | dwarf: &'dwarf crate::gimli::Dwarf, 57 | unit: &'dwarf crate::gimli::Unit, 58 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 59 | ) -> Result { 60 | Ok(match entry.tag() { 61 | crate::gimli::DW_TAG_base_type => { 62 | let name = Name::from_die(dwarf, unit, &entry)?; 63 | let name = name.to_slice()?; 64 | return match name.as_ref() { 65 | b"bool" => bool::from_dw_tag_base_type(dwarf, unit, entry).map(Self::bool), 66 | b"char" => char::from_dw_tag_base_type(dwarf, unit, entry).map(Self::char), 67 | b"f32" => f32::from_dw_tag_base_type(dwarf, unit, entry).map(Self::f32), 68 | b"f64" => f64::from_dw_tag_base_type(dwarf, unit, entry).map(Self::f64), 69 | b"i8" => i8::from_dw_tag_base_type(dwarf, unit, entry).map(Self::i8), 70 | b"i16" => i16::from_dw_tag_base_type(dwarf, unit, entry).map(Self::i16), 71 | b"i32" => i32::from_dw_tag_base_type(dwarf, unit, entry).map(Self::i32), 72 | b"i64" => i64::from_dw_tag_base_type(dwarf, unit, entry).map(Self::i64), 73 | b"i128" => i128::from_dw_tag_base_type(dwarf, unit, entry).map(Self::i128), 74 | b"isize" => isize::from_dw_tag_base_type(dwarf, unit, entry).map(Self::isize), 75 | b"u8" => u8::from_dw_tag_base_type(dwarf, unit, entry).map(Self::u8), 76 | b"u16" => u16::from_dw_tag_base_type(dwarf, unit, entry).map(Self::u16), 77 | b"u32" => u32::from_dw_tag_base_type(dwarf, unit, entry).map(Self::u32), 78 | b"u64" => u64::from_dw_tag_base_type(dwarf, unit, entry).map(Self::u64), 79 | b"u128" => u128::from_dw_tag_base_type(dwarf, unit, entry).map(Self::u128), 80 | b"usize" => usize::from_dw_tag_base_type(dwarf, unit, entry).map(Self::usize), 81 | b"()" => unit::from_dw_tag_base_type(dwarf, unit, entry).map(Self::unit), 82 | _ => unimplemented!( 83 | "unhandled primitive: {:#?}", 84 | crate::debug::DebugEntry::new(dwarf, unit, &entry) 85 | ), 86 | }; 87 | } 88 | crate::gimli::DW_TAG_structure_type => { 89 | let name = Name::from_die(dwarf, unit, &entry)?; 90 | let name_slice = name.to_slice()?; 91 | if name_slice.starts_with(b"&[") { 92 | return Ok(Self::Slice(Slice::from_dw_tag_structure_type( 93 | dwarf, unit, entry, 94 | )?)); 95 | } else if &*name_slice == b"&str" { 96 | return Ok(Self::str(str::from_dw_tag_structure_type( 97 | dwarf, unit, entry, 98 | )?)); 99 | } else if name_slice.starts_with(b"alloc::boxed::Box<") { 100 | // boxedslice: data_ptr + length 101 | let schema = Struct::from_dw_tag_structure_type(dwarf, unit, entry)?; 102 | let mut fields = schema.fields()?; 103 | let mut fields = fields.iter()?; 104 | let pointer = fields.try_next()?; 105 | let pointer = pointer 106 | .ok_or_else(|| crate::error::missing_child(crate::gimli::DW_TAG_member))?; 107 | let metadata = fields.try_next()?; 108 | let metadata = metadata 109 | .ok_or_else(|| crate::error::missing_child(crate::gimli::DW_TAG_member))?; 110 | let metadata_name = metadata.name()?; 111 | let metadata_name_slice = metadata_name.to_slice()?; 112 | return match metadata_name_slice.as_ref() { 113 | b"length" => { 114 | BoxedSlice::new(schema, pointer, metadata).map(Self::BoxedSlice) 115 | } 116 | b"vtable" => BoxedDyn::new(schema, pointer, metadata).map(Self::BoxedDyn), 117 | _ => Err(crate::error::name_mismatch( 118 | "`length` or `vtable`", 119 | metadata_name.to_string_lossy()?.into_owned(), 120 | ))?, 121 | }; 122 | } else { 123 | let mut tree = unit.entries_tree(Some(entry.offset()))?; 124 | let root = tree.root()?; 125 | let mut children = root.children(); 126 | let mut variants = None; 127 | 128 | while let Some(child) = children.next()? { 129 | if child.entry().tag() == crate::gimli::DW_TAG_variant_part { 130 | variants = Some(child.entry().clone()); 131 | break; 132 | } 133 | } 134 | 135 | if let Some(_variants) = variants { 136 | Self::Enum(Enum::from_dw_tag_structure_type(dwarf, unit, entry)?) 137 | } else { 138 | Self::Struct(Struct::from_dw_tag_structure_type(dwarf, unit, entry)?) 139 | } 140 | } 141 | } 142 | crate::gimli::DW_TAG_enumeration_type => { 143 | Self::Enum(Enum::from_dw_tag_enumeration_type(dwarf, unit, entry)?) 144 | } 145 | crate::gimli::DW_TAG_pointer_type => { 146 | let name = Name::from_die_opt(dwarf, unit, &entry)?; 147 | let target = crate::get_type_ref(&entry)?; 148 | if let Some(name) = name { 149 | let name_as_slice = name.to_slice()?; 150 | if name_as_slice.starts_with(b"*mut ") { 151 | Self::MutPtr(Pointer::new( 152 | dwarf, 153 | unit, 154 | entry.offset(), 155 | Some(name), 156 | target, 157 | )) 158 | } else if name_as_slice.starts_with(b"*const ") { 159 | Self::ConstPtr(Pointer::new( 160 | dwarf, 161 | unit, 162 | entry.offset(), 163 | Some(name), 164 | target, 165 | )) 166 | } else if name_as_slice.starts_with(b"&mut ") { 167 | Self::UniqueRef(Pointer::new( 168 | dwarf, 169 | unit, 170 | entry.offset(), 171 | Some(name), 172 | target, 173 | )) 174 | } else if name_as_slice.starts_with(b"&") || name_as_slice.starts_with(b"fn") { 175 | Self::SharedRef(Pointer::new( 176 | dwarf, 177 | unit, 178 | entry.offset(), 179 | Some(name), 180 | target, 181 | )) 182 | } else if name_as_slice.starts_with(b"alloc::boxed::Box<") { 183 | Self::Box(Box::new(dwarf, unit, entry.offset(), Some(name), target)) 184 | } else { 185 | eprintln!( 186 | "{:#?}", 187 | &crate::debug::DebugEntry::new(dwarf, unit, &entry,) 188 | ); 189 | 190 | return Err(crate::error::invalid_attr(crate::gimli::DW_AT_name)); 191 | } 192 | } else { 193 | // the `data_ptr` field of slices points to a pointer type that doesn't have a 194 | // name. 195 | Self::MutPtr(Pointer::new(dwarf, unit, entry.offset(), None, target)) 196 | } 197 | } 198 | crate::gimli::DW_TAG_subroutine_type => { 199 | Self::Function(Function::from_dw_tag_subroutine_type(dwarf, unit, entry)?) 200 | } 201 | crate::gimli::DW_TAG_array_type => { 202 | Self::Array(Array::from_dw_tag_array_type(dwarf, unit, entry)?) 203 | } 204 | _otherwise => { 205 | eprintln!( 206 | "UNHANDLED DEBUG ENTRY:\n{:#?}", 207 | &crate::debug::DebugEntry::new(dwarf, unit, &entry,) 208 | ); 209 | anyhow::bail!( 210 | "Unhandled debug info kind:\n{:#?}", 211 | crate::debug::DebugEntry::new(dwarf, unit, &entry,) 212 | ) 213 | } 214 | }) 215 | } 216 | 217 | /// The size of the type. 218 | pub fn size(&self) -> Result { 219 | match self { 220 | Self::bool(v) => Ok(v.size()), 221 | Self::char(v) => Ok(v.size()), 222 | Self::f32(v) => Ok(v.size()), 223 | Self::f64(v) => Ok(v.size()), 224 | Self::i8(v) => Ok(v.size()), 225 | Self::i16(v) => Ok(v.size()), 226 | Self::i32(v) => Ok(v.size()), 227 | Self::i64(v) => Ok(v.size()), 228 | Self::i128(v) => Ok(v.size()), 229 | Self::isize(v) => Ok(v.size()), 230 | Self::u8(v) => Ok(v.size()), 231 | Self::u16(v) => Ok(v.size()), 232 | Self::u32(v) => Ok(v.size()), 233 | Self::u64(v) => Ok(v.size()), 234 | Self::u128(v) => Ok(v.size()), 235 | Self::usize(v) => Ok(v.size()), 236 | Self::unit(v) => Ok(v.size()), 237 | Self::Box(v) => Ok(v.size()), 238 | Self::BoxedSlice(v) => v.size(), 239 | Self::BoxedDyn(v) => v.size(), 240 | Self::Array(v) => v.bytes(), 241 | Self::Slice(v) => v.size(), 242 | Self::str(v) => v.size(), 243 | Self::Struct(v) => v.size(), 244 | Self::Enum(v) => v.size(), 245 | Self::Function(_) => Ok(0), 246 | Self::SharedRef(_) => Ok(std::mem::size_of::() as _), 247 | Self::UniqueRef(_) => Ok(std::mem::size_of::() as _), 248 | Self::ConstPtr(_) => Ok(std::mem::size_of::() as _), 249 | Self::MutPtr(_) => Ok(std::mem::size_of::() as _), 250 | } 251 | } 252 | } 253 | 254 | pub use super::Type; 255 | 256 | macro_rules! generate_primitive { 257 | ($($t:ident,)*) => { 258 | $( 259 | generate_primitive!(@ 260 | $t, 261 | concat!( 262 | "A schema for [`", 263 | stringify!($t), 264 | "`][std::primitive::", 265 | stringify!($t), 266 | "]." 267 | ) 268 | ); 269 | )* 270 | }; 271 | (@ $t:ident, $doc:expr) => { 272 | #[doc = $doc] 273 | #[allow(non_camel_case_types)] 274 | #[derive(Clone)] 275 | pub struct $t<'dwarf, R> 276 | where 277 | R: crate::gimli::Reader, 278 | { 279 | dwarf: &'dwarf crate::gimli::Dwarf, 280 | unit: &'dwarf crate::gimli::Unit, 281 | entry: crate::gimli::UnitOffset, 282 | } 283 | 284 | impl<'dwarf, R> $t<'dwarf, R> 285 | where 286 | R: crate::gimli::Reader, 287 | { 288 | pub(crate) fn from_dw_tag_base_type( 289 | dwarf: &'dwarf crate::gimli::Dwarf, 290 | unit: &'dwarf crate::gimli::Unit, 291 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 292 | ) -> Result { 293 | crate::check_tag(&entry, crate::gimli::DW_TAG_base_type)?; 294 | 295 | let name = Name::from_die(dwarf, unit, &entry)?; 296 | let expected = std::any::type_name::(); 297 | if name.to_slice()? != expected.as_bytes() { 298 | let actual = name.to_string_lossy()?.to_string(); 299 | Err(crate::error::name_mismatch(expected, actual))?; 300 | } 301 | 302 | let size: std::primitive::usize = crate::get_size(&entry)? 303 | .try_into()?; 304 | let expected = core::mem::size_of::(); 305 | if size != expected { 306 | Err(crate::error::size_mismatch(expected, size))?; 307 | } 308 | 309 | Ok(Self { 310 | dwarf, 311 | unit, 312 | entry: entry.offset() 313 | }) 314 | } 315 | 316 | 317 | /// The size of this type. 318 | pub fn name(&self) -> &'static std::primitive::str { 319 | std::any::type_name::() 320 | } 321 | 322 | /// The size of this type. 323 | pub fn size(&self) -> std::primitive::u64 { 324 | std::mem::size_of::() as _ 325 | } 326 | 327 | /// The minimum alignment of this type. 328 | pub fn align(&self) -> std::primitive::u64 { 329 | std::mem::align_of::() as _ 330 | } 331 | } 332 | 333 | impl<'dwarf, R> std::fmt::Debug for $t<'dwarf, R> 334 | where 335 | R: crate::gimli::Reader, 336 | { 337 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 338 | let entry = self.unit.entry(self.entry).map_err(crate::fmt_err)?; 339 | let mut debug_tuple = f.debug_tuple(stringify!($t)); 340 | debug_tuple.field(&crate::debug::DebugEntry::new( 341 | self.dwarf, 342 | self.unit, 343 | &entry, 344 | )); 345 | debug_tuple.finish() 346 | } 347 | } 348 | 349 | impl<'dwarf, R> std::fmt::Display for $t<'dwarf, R> 350 | where 351 | R: crate::gimli::Reader, 352 | { 353 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 354 | self.name().fmt(f) 355 | } 356 | } 357 | }; 358 | } 359 | 360 | generate_primitive! { 361 | bool, 362 | char, 363 | f32, 364 | f64, 365 | i8, 366 | i16, 367 | i32, 368 | i64, 369 | i128, 370 | isize, 371 | u8, 372 | u16, 373 | u32, 374 | u64, 375 | u128, 376 | usize, 377 | } 378 | 379 | /// A schema for [`()`][prim@unit]. 380 | #[allow(non_camel_case_types)] 381 | #[derive(Clone)] 382 | pub struct unit<'dwarf, R> 383 | where 384 | R: crate::gimli::Reader, 385 | { 386 | dwarf: &'dwarf crate::gimli::Dwarf, 387 | unit: &'dwarf crate::gimli::Unit, 388 | entry: crate::gimli::UnitOffset, 389 | } 390 | 391 | impl<'dwarf, R> unit<'dwarf, R> 392 | where 393 | R: crate::gimli::Reader, 394 | { 395 | pub(crate) fn from_dw_tag_base_type( 396 | dwarf: &'dwarf crate::gimli::Dwarf, 397 | unit: &'dwarf crate::gimli::Unit, 398 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 399 | ) -> Result { 400 | crate::check_tag(&entry, crate::gimli::DW_TAG_base_type)?; 401 | 402 | let name = Name::from_die(dwarf, unit, &entry)?; 403 | let expected = std::any::type_name::<()>(); 404 | if name.to_slice()? != expected.as_bytes() { 405 | let actual = name.to_string_lossy()?.to_string(); 406 | Err(crate::error::name_mismatch(expected, actual))?; 407 | } 408 | 409 | let size: std::primitive::usize = crate::get_size(&entry)?.try_into()?; 410 | let expected = core::mem::size_of::<()>(); 411 | if size != expected { 412 | Err(crate::error::size_mismatch(expected, size))?; 413 | } 414 | 415 | Ok(Self { 416 | dwarf, 417 | unit, 418 | entry: entry.offset(), 419 | }) 420 | } 421 | 422 | /// The size of this type. 423 | pub fn name(&self) -> &'static std::primitive::str { 424 | std::any::type_name::<()>() 425 | } 426 | 427 | /// The size of this type. 428 | pub fn size(&self) -> std::primitive::u64 { 429 | std::mem::size_of::<()>() as _ 430 | } 431 | 432 | /// The minimum alignment of this type. 433 | pub fn align(&self) -> std::primitive::u64 { 434 | std::mem::align_of::<()>() as _ 435 | } 436 | } 437 | 438 | impl<'dwarf, R> std::fmt::Debug for unit<'dwarf, R> 439 | where 440 | R: crate::gimli::Reader, 441 | { 442 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 443 | let entry = self.unit.entry(self.entry).map_err(crate::fmt_err)?; 444 | let mut debug_tuple = f.debug_tuple(stringify!($t)); 445 | debug_tuple.field(&crate::debug::DebugEntry::new( 446 | self.dwarf, self.unit, &entry, 447 | )); 448 | debug_tuple.finish() 449 | } 450 | } 451 | 452 | impl<'dwarf, R> std::fmt::Display for unit<'dwarf, R> 453 | where 454 | R: crate::gimli::Reader, 455 | { 456 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 457 | self.name().fmt(f) 458 | } 459 | } 460 | -------------------------------------------------------------------------------- /src/schema/name.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, fmt}; 2 | 3 | /// The name associated with a debuginfo entry. 4 | #[derive(Clone)] 5 | pub struct Name 6 | where 7 | R: crate::gimli::Reader, 8 | { 9 | name: R, 10 | } 11 | 12 | impl Name 13 | where 14 | R: crate::gimli::Reader, 15 | { 16 | /// Constructs a `Name` from the [`DW_AT_name`][crate::gimli::DW_AT_name] 17 | /// attribute of the given `entry`. 18 | pub(crate) fn from_die( 19 | dwarf: &crate::gimli::Dwarf, 20 | unit: &crate::gimli::Unit, 21 | entry: &crate::gimli::DebuggingInformationEntry<'_, '_, R>, 22 | ) -> Result { 23 | let name = crate::get(entry, crate::gimli::DW_AT_name)?; 24 | let name = dwarf.attr_string(unit, name)?; 25 | Ok(Self { name }) 26 | } 27 | 28 | /// Constructs a `Name` from the [`DW_AT_name`][crate::gimli::DW_AT_name] 29 | /// attribute of the given `entry`. 30 | pub(crate) fn from_die_opt( 31 | dwarf: &crate::gimli::Dwarf, 32 | unit: &crate::gimli::Unit, 33 | entry: &crate::gimli::DebuggingInformationEntry<'_, '_, R>, 34 | ) -> Result, crate::Error> { 35 | let name = crate::get_opt(entry, crate::gimli::DW_AT_name)?; 36 | Ok(if let Some(name) = name { 37 | let name = dwarf.attr_string(unit, name)?; 38 | Some(Self { name }) 39 | } else { 40 | None 41 | }) 42 | } 43 | 44 | /// Convert all remaining data to a clone-on-write string. 45 | /// 46 | /// The string will be borrowed where possible, but some readers may always 47 | /// return an owned string. 48 | /// 49 | /// Returns an error if the data contains invalid characters. 50 | pub fn to_string(&self) -> Result, crate::Error> { 51 | Ok(self.name.to_string()?) 52 | } 53 | 54 | /// Convert all remaining data to a clone-on-write string, including invalid 55 | /// characters. 56 | /// 57 | /// The string will be borrowed where possible, but some readers may always 58 | /// return an owned string. 59 | pub fn to_string_lossy(&self) -> Result, crate::Error> { 60 | Ok(self.name.to_string_lossy()?) 61 | } 62 | 63 | /// Return all remaining data as a clone-on-write slice of bytes. 64 | /// 65 | /// The slice will be borrowed where possible, but some readers may always 66 | /// return an owned vector. 67 | pub fn to_slice(&self) -> Result, crate::Error> { 68 | Ok(self.name.to_slice()?) 69 | } 70 | } 71 | 72 | impl fmt::Debug for Name 73 | where 74 | R: crate::gimli::Reader, 75 | { 76 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 77 | self.to_string_lossy().map_err(crate::fmt_err)?.fmt(f) 78 | } 79 | } 80 | 81 | impl fmt::Display for Name 82 | where 83 | R: crate::gimli::Reader, 84 | { 85 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 86 | self.to_string_lossy().map_err(crate::fmt_err)?.fmt(f) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/schema/offset.rs: -------------------------------------------------------------------------------- 1 | /// The offset of a field or data member. 2 | #[derive(Copy)] 3 | pub struct Offset<'dwarf, R> 4 | where 5 | R: crate::gimli::Reader, 6 | { 7 | unit: &'dwarf crate::gimli::Unit, 8 | inner: OffsetInner, 9 | } 10 | 11 | #[derive(Copy)] 12 | enum OffsetInner 13 | where 14 | R: crate::gimli::Reader, 15 | { 16 | Udata(u64), 17 | Expression(crate::gimli::read::Expression), 18 | } 19 | 20 | impl<'dwarf, R> Offset<'dwarf, R> 21 | where 22 | R: crate::gimli::Reader, 23 | { 24 | pub(crate) fn zero(unit: &'dwarf crate::gimli::Unit) -> Self { 25 | Self { 26 | unit, 27 | inner: OffsetInner::Udata(0), 28 | } 29 | } 30 | 31 | /// Construct a new `Offset` from a given `entry`'s 32 | /// `DW_AT_data_member_location` attribute. 33 | pub(crate) fn from_die<'entry>( 34 | unit: &'dwarf crate::gimli::Unit, 35 | entry: &'entry crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 36 | ) -> Result { 37 | let location = crate::get(entry, crate::gimli::DW_AT_data_member_location)?; 38 | let inner = if let Some(offset) = location.udata_value() { 39 | OffsetInner::Udata(offset) 40 | } else if let Some(expression) = location.exprloc_value() { 41 | OffsetInner::Expression(expression) 42 | } else { 43 | return Err(crate::error::invalid_attr( 44 | crate::gimli::DW_AT_data_member_location, 45 | )); 46 | }; 47 | Ok(Self { unit, inner }) 48 | } 49 | 50 | /// Produces the actual memory address referred to by this offset. 51 | pub fn address(self, start: u64) -> Result { 52 | match self.inner { 53 | OffsetInner::Udata(offset) => Ok(start + offset), 54 | OffsetInner::Expression(expression) => { 55 | let mut eval = expression.evaluation(self.unit.encoding()); 56 | eval.set_initial_value(start); 57 | if let crate::gimli::EvaluationResult::Complete = eval.evaluate()? { 58 | let result = eval.result(); 59 | match result[..] { 60 | [crate::gimli::Piece { 61 | size_in_bits: None, 62 | bit_offset: None, 63 | location: crate::gimli::Location::Address { address }, 64 | }] => Ok(address), 65 | _ => unimplemented!("unsupported evaluation result {:?}", result,), 66 | } 67 | } else { 68 | unimplemented!("unsupported evaluation result"); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | impl<'dwarf, R> Clone for Offset<'dwarf, R> 76 | where 77 | R: crate::gimli::Reader, 78 | { 79 | fn clone(&self) -> Self { 80 | Self { 81 | unit: self.unit, 82 | inner: self.inner.clone(), 83 | } 84 | } 85 | } 86 | 87 | impl Clone for OffsetInner 88 | where 89 | R: crate::gimli::Reader, 90 | { 91 | fn clone(&self) -> Self { 92 | match self { 93 | Self::Udata(offset) => Self::Udata(*offset), 94 | Self::Expression(expr) => Self::Expression(expr.clone()), 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/schema/pointer.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, marker::PhantomData}; 2 | 3 | use addr2line::gimli::UnitOffset; 4 | 5 | /// The kind of a unique pointer. 6 | #[derive(Clone, Copy)] 7 | pub enum Unique {} 8 | 9 | /// The kind of a shared pointer. 10 | #[derive(Clone, Copy)] 11 | pub enum Shared {} 12 | 13 | /// The kind of a raw `mut` pointer. 14 | #[derive(Clone, Copy)] 15 | pub enum Mut {} 16 | 17 | /// The kind of a raw `const` pointer. 18 | #[derive(Clone, Copy)] 19 | pub enum Const {} 20 | 21 | mod reference_seal { 22 | pub trait Sealed {} 23 | impl Sealed for super::Unique {} 24 | impl Sealed for super::Shared {} 25 | } 26 | 27 | /// A trait implemented by reference kinds. 28 | pub trait Reference: reference_seal::Sealed {} 29 | impl Reference for Unique {} 30 | impl Reference for Shared {} 31 | 32 | /// A schema for a shared reference (i.e., `&T`). 33 | #[derive(Clone)] 34 | pub struct Pointer<'dwarf, K, R> 35 | where 36 | R: crate::gimli::Reader, 37 | { 38 | dwarf: &'dwarf crate::gimli::Dwarf, 39 | unit: &'dwarf crate::gimli::Unit, 40 | entry: UnitOffset, 41 | name: Option>, 42 | target: UnitOffset, 43 | kind: PhantomData, 44 | } 45 | 46 | impl<'dwarf, K, R> Pointer<'dwarf, K, R> 47 | where 48 | R: crate::gimli::Reader, 49 | { 50 | /// Construct a new `Shared`. 51 | pub(super) fn new( 52 | dwarf: &'dwarf crate::gimli::Dwarf, 53 | unit: &'dwarf crate::gimli::Unit, 54 | entry: UnitOffset, 55 | name: Option>, 56 | target: UnitOffset, 57 | ) -> Self { 58 | Self { 59 | dwarf, 60 | unit, 61 | entry, 62 | name, 63 | target, 64 | kind: PhantomData, 65 | } 66 | } 67 | 68 | /// The name of this reference type. 69 | pub fn name(&self) -> Option<&super::Name> { 70 | self.name.as_ref() 71 | } 72 | 73 | /// The size of this type, in bytes. 74 | pub fn size(&self) -> u64 { 75 | core::mem::size_of::() as _ 76 | } 77 | 78 | /// The type of the referent. 79 | pub fn r#type(&self) -> Result, crate::Error> { 80 | let entry = self.unit.entry(self.target)?; 81 | super::Type::from_die(self.dwarf, self.unit, entry) 82 | } 83 | } 84 | 85 | impl<'dwarf, K, R> fmt::Debug for Pointer<'dwarf, K, R> 86 | where 87 | R: crate::gimli::Reader, 88 | { 89 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 90 | let entry = self.unit.entry(self.entry).map_err(crate::fmt_err)?; 91 | let mut debug_tuple = f.debug_tuple("deflect::schema::Shared"); 92 | debug_tuple.field(&crate::debug::DebugEntry::new( 93 | self.dwarf, self.unit, &entry, 94 | )); 95 | debug_tuple.finish() 96 | } 97 | } 98 | 99 | impl<'dwarf, K, R> fmt::Display for Pointer<'dwarf, K, R> 100 | where 101 | R: crate::gimli::Reader, 102 | { 103 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 104 | if let Some(name) = self.name() { 105 | name.fmt(f) 106 | } else { 107 | f.write_str("*? ")?; 108 | let target = self.r#type().map_err(crate::fmt_err)?; 109 | target.fmt(f) 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/schema/slice.rs: -------------------------------------------------------------------------------- 1 | use super::Name; 2 | use std::fmt; 3 | 4 | /// A schema for [`&[T]`][prim@slice]. 5 | #[derive(Clone)] 6 | #[allow(non_camel_case_types)] 7 | pub struct Slice<'dwarf, R: crate::gimli::Reader> 8 | where 9 | R: crate::gimli::Reader, 10 | { 11 | schema: super::Struct<'dwarf, R>, 12 | data_ptr: super::Field<'dwarf, R>, 13 | length: super::Field<'dwarf, R>, 14 | } 15 | 16 | impl<'dwarf, R> Slice<'dwarf, R> 17 | where 18 | R: crate::gimli::Reader, 19 | { 20 | /// Construct a new `Struct` from a 21 | /// [`DW_TAG_structure_type`][crate::gimli::DW_TAG_structure_type]. 22 | pub(crate) fn from_dw_tag_structure_type( 23 | dwarf: &'dwarf crate::gimli::Dwarf, 24 | unit: &'dwarf crate::gimli::Unit, 25 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 26 | ) -> Result { 27 | crate::check_tag(&entry, crate::gimli::DW_TAG_structure_type)?; 28 | 29 | let name = Name::from_die(dwarf, unit, &entry)?; 30 | 31 | let name = name.to_slice()?; 32 | if !(name.starts_with(b"&[") && name.ends_with(b"]")) { 33 | let actual = String::from_utf8_lossy(name.as_ref()).to_string(); 34 | Err(crate::error::name_mismatch("&[T]", actual))?; 35 | }; 36 | 37 | let schema = super::Struct::from_dw_tag_structure_type(dwarf, unit, entry)?; 38 | let mut fields = schema.fields()?; 39 | let mut fields = fields.iter()?; 40 | 41 | let data_ptr = fields.try_next()?; 42 | let data_ptr = data_ptr.ok_or_else(|| anyhow!("expected `data_ptr` field"))?; 43 | 44 | let length = fields.try_next()?; 45 | let length = length.ok_or_else(|| anyhow!("expected `length` field"))?; 46 | 47 | Ok(Self { 48 | schema, 49 | data_ptr, 50 | length, 51 | }) 52 | } 53 | 54 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Struct`'s debuginfo 55 | /// belongs to. 56 | #[allow(dead_code)] 57 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 58 | self.schema.dwarf() 59 | } 60 | 61 | /// The DWARF [unit][crate::gimli::Unit] that this `Struct`'s debuginfo 62 | /// belongs to. 63 | #[allow(dead_code)] 64 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 65 | self.schema.unit() 66 | } 67 | 68 | /// The [debugging information 69 | /// entry][crate::gimli::DebuggingInformationEntry] this `Struct` abstracts 70 | /// over. 71 | #[allow(dead_code)] 72 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 73 | self.schema.entry() 74 | } 75 | 76 | /// The `data_ptr` field of this slice. 77 | pub fn data_ptr(&self) -> super::Field<'dwarf, R> { 78 | self.data_ptr.clone() 79 | } 80 | 81 | /// The `length` field of this slice. 82 | pub fn length(&self) -> &super::Field<'dwarf, R> { 83 | &self.length 84 | } 85 | 86 | /// The element type of this slice. 87 | pub fn elt(&self) -> Result, crate::Error> { 88 | if let super::Type::MutPtr(r#ref) = self.data_ptr().r#type()? { 89 | return r#ref.r#type(); 90 | } else { 91 | unreachable!() 92 | } 93 | } 94 | 95 | /// The size of this slice, in bytes. 96 | pub fn size(&self) -> Result { 97 | crate::get_size(self.entry()) 98 | } 99 | 100 | /// The alignment of this slice, in bytes. 101 | pub fn align(&self) -> Result, crate::Error> { 102 | crate::get_align(self.entry()) 103 | } 104 | } 105 | 106 | impl<'dwarf, R> fmt::Debug for Slice<'dwarf, R> 107 | where 108 | R: crate::gimli::Reader, 109 | { 110 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 111 | let mut debug_tuple = f.debug_tuple("deflect::schema::Slice"); 112 | debug_tuple.field(&crate::debug::DebugEntry::new( 113 | self.dwarf(), 114 | self.unit(), 115 | self.entry(), 116 | )); 117 | debug_tuple.finish() 118 | } 119 | } 120 | 121 | impl<'dwarf, R> fmt::Display for Slice<'dwarf, R> 122 | where 123 | R: crate::gimli::Reader, 124 | { 125 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { 126 | todo!() 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/schema/str_impl.rs: -------------------------------------------------------------------------------- 1 | use super::Name; 2 | use std::fmt; 3 | 4 | /// A schema for [`&[T]`][prim@slice]. 5 | #[derive(Clone)] 6 | #[allow(non_camel_case_types)] 7 | pub struct str<'dwarf, R: crate::gimli::Reader> 8 | where 9 | R: crate::gimli::Reader, 10 | { 11 | schema: super::Struct<'dwarf, R>, 12 | data_ptr: super::Field<'dwarf, R>, 13 | length: super::Field<'dwarf, R>, 14 | } 15 | 16 | impl<'dwarf, R> str<'dwarf, R> 17 | where 18 | R: crate::gimli::Reader, 19 | { 20 | /// Construct a new `Struct` from a 21 | /// [`DW_TAG_structure_type`][crate::gimli::DW_TAG_structure_type]. 22 | pub(crate) fn from_dw_tag_structure_type( 23 | dwarf: &'dwarf crate::gimli::Dwarf, 24 | unit: &'dwarf crate::gimli::Unit, 25 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 26 | ) -> Result { 27 | crate::check_tag(&entry, crate::gimli::DW_TAG_structure_type)?; 28 | 29 | let name = Name::from_die(dwarf, unit, &entry)?; 30 | 31 | let name = name.to_slice()?; 32 | 33 | if &*name != b"&str" { 34 | let actual = String::from_utf8_lossy(name.as_ref()).to_string(); 35 | Err(crate::error::name_mismatch("&str", actual))?; 36 | }; 37 | 38 | let schema = super::Struct::from_dw_tag_structure_type(dwarf, unit, entry)?; 39 | let mut fields = schema.fields()?; 40 | let mut fields = fields.iter()?; 41 | 42 | let data_ptr = fields.try_next()?; 43 | let data_ptr = data_ptr.ok_or_else(|| anyhow!("expected `data_ptr` field"))?; 44 | 45 | let length = fields.try_next()?; 46 | let length = length.ok_or_else(|| anyhow!("expected `length` field"))?; 47 | 48 | Ok(Self { 49 | schema, 50 | data_ptr, 51 | length, 52 | }) 53 | } 54 | 55 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Struct`'s debuginfo 56 | /// belongs to. 57 | #[allow(dead_code)] 58 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 59 | self.schema.dwarf() 60 | } 61 | 62 | /// The DWARF [unit][crate::gimli::Unit] that this `Struct`'s debuginfo 63 | /// belongs to. 64 | #[allow(dead_code)] 65 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 66 | self.schema.unit() 67 | } 68 | 69 | /// The [debugging information 70 | /// entry][crate::gimli::DebuggingInformationEntry] this `Struct` abstracts 71 | /// over. 72 | #[allow(dead_code)] 73 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 74 | self.schema.entry() 75 | } 76 | 77 | /// The `data_ptr` field of this slice. 78 | pub fn data_ptr(&self) -> super::Field<'dwarf, R> { 79 | self.data_ptr.clone() 80 | } 81 | 82 | /// The `length` field of this slice. 83 | pub fn length(&self) -> &super::Field<'dwarf, R> { 84 | &self.length 85 | } 86 | 87 | /// The element type of this slice. 88 | pub fn elt(&self) -> Result, crate::Error> { 89 | if let super::Type::MutPtr(r#ref) = self.data_ptr().r#type()? { 90 | return r#ref.r#type(); 91 | } 92 | panic!() 93 | } 94 | 95 | /// The size of this slice, in bytes. 96 | pub fn size(&self) -> Result { 97 | crate::get_size(self.entry()) 98 | } 99 | 100 | /// The alignment of this slice, in bytes. 101 | pub fn align(&self) -> Result, crate::Error> { 102 | crate::get_align(self.entry()) 103 | } 104 | } 105 | 106 | impl<'dwarf, R> fmt::Debug for str<'dwarf, R> 107 | where 108 | R: crate::gimli::Reader, 109 | { 110 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 111 | let mut debug_tuple = f.debug_tuple("deflect::schema::Slice"); 112 | debug_tuple.field(&crate::debug::DebugEntry::new( 113 | self.dwarf(), 114 | self.unit(), 115 | self.entry(), 116 | )); 117 | debug_tuple.finish() 118 | } 119 | } 120 | 121 | impl<'dwarf, R> fmt::Display for str<'dwarf, R> 122 | where 123 | R: crate::gimli::Reader, 124 | { 125 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { 126 | todo!() 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/schema/struct.rs: -------------------------------------------------------------------------------- 1 | use super::Name; 2 | use std::fmt; 3 | 4 | /// A schema for a [`struct`](https://doc.rust-lang.org/std/keyword.struct.html). 5 | #[allow(non_camel_case_types)] 6 | #[derive(Clone)] 7 | pub struct Struct<'dwarf, R: crate::gimli::Reader> 8 | where 9 | R: crate::gimli::Reader, 10 | { 11 | dwarf: &'dwarf crate::gimli::Dwarf, 12 | unit: &'dwarf crate::gimli::Unit, 13 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 14 | } 15 | 16 | impl<'dwarf, R> Struct<'dwarf, R> 17 | where 18 | R: crate::gimli::Reader, 19 | { 20 | /// Construct a new `Struct` from a 21 | /// [`DW_TAG_structure_type`][crate::gimli::DW_TAG_structure_type]. 22 | pub(crate) fn from_dw_tag_structure_type( 23 | dwarf: &'dwarf crate::gimli::Dwarf, 24 | unit: &'dwarf crate::gimli::Unit, 25 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 26 | ) -> Result { 27 | crate::check_tag(&entry, crate::gimli::DW_TAG_structure_type)?; 28 | Ok(Self { dwarf, unit, entry }) 29 | } 30 | 31 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Struct`'s debuginfo 32 | /// belongs to. 33 | #[allow(dead_code)] 34 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 35 | self.dwarf 36 | } 37 | 38 | /// The DWARF [unit][crate::gimli::Unit] that this `Struct`'s debuginfo 39 | /// belongs to. 40 | #[allow(dead_code)] 41 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 42 | self.unit 43 | } 44 | 45 | /// The [debugging information 46 | /// entry][crate::gimli::DebuggingInformationEntry] this `Struct` abstracts 47 | /// over. 48 | #[allow(dead_code)] 49 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 50 | &self.entry 51 | } 52 | 53 | /// The name of this primitive type. 54 | pub fn name(&self) -> Result, crate::Error> { 55 | Name::from_die(self.dwarf(), self.unit(), self.entry()) 56 | } 57 | 58 | /// The size of this field, in bytes. 59 | pub fn size(&self) -> Result { 60 | crate::get_size(self.entry()) 61 | } 62 | 63 | /// The alignment of this field, in bytes. 64 | pub fn align(&self) -> Result, crate::Error> { 65 | crate::get_align(self.entry()) 66 | } 67 | 68 | /// The fields of this struct. 69 | pub fn fields(&self) -> Result, crate::Error> { 70 | let tree = self.unit.entries_tree(Some(self.entry.offset()))?; 71 | Ok(super::Fields::from_tree(self.dwarf, self.unit, tree)) 72 | } 73 | } 74 | 75 | impl<'dwarf, R> fmt::Debug for Struct<'dwarf, R> 76 | where 77 | R: crate::gimli::Reader, 78 | { 79 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 80 | let mut debug_tuple = f.debug_tuple("deflect::schema::Struct"); 81 | debug_tuple.field(&crate::debug::DebugEntry::new( 82 | self.dwarf, 83 | self.unit, 84 | &self.entry, 85 | )); 86 | debug_tuple.finish() 87 | } 88 | } 89 | 90 | impl<'dwarf, R> fmt::Display for Struct<'dwarf, R> 91 | where 92 | R: crate::gimli::Reader, 93 | { 94 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 | let type_name = self.name().map_err(crate::fmt_err)?; 96 | let type_name = type_name.to_string_lossy().map_err(crate::fmt_err)?; 97 | let mut debug_struct = f.debug_struct(&type_name); 98 | let mut fields = self.fields().map_err(crate::fmt_err)?; 99 | let mut fields = fields.iter().map_err(crate::fmt_err)?; 100 | while let Some(field) = fields.try_next().map_err(crate::fmt_err)? { 101 | let field_name = field.name().map_err(crate::fmt_err)?; 102 | let field_name = field_name.to_string_lossy().map_err(crate::fmt_err)?; 103 | let field_type = field.r#type().map_err(crate::fmt_err)?; 104 | debug_struct.field(&field_name, &crate::DebugDisplay(field_type)); 105 | } 106 | debug_struct.finish() 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/schema/variant.rs: -------------------------------------------------------------------------------- 1 | use super::Name; 2 | use std::{borrow::Cow, fmt}; 3 | 4 | /// A variant of an [`enum`][super::Enum]. 5 | #[derive(Clone)] 6 | pub struct Variant<'dwarf, R: crate::gimli::Reader> 7 | where 8 | R: crate::gimli::Reader, 9 | { 10 | dwarf: &'dwarf crate::gimli::Dwarf, 11 | unit: &'dwarf crate::gimli::Unit, 12 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 13 | discriminant_val: Option, 14 | } 15 | 16 | impl<'dwarf, R> Variant<'dwarf, R> 17 | where 18 | R: crate::gimli::Reader, 19 | { 20 | pub(crate) fn new( 21 | dwarf: &'dwarf crate::gimli::Dwarf, 22 | unit: &'dwarf crate::gimli::Unit, 23 | entry: crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R>, 24 | discriminant_val: Option, 25 | ) -> Self { 26 | Self { 27 | dwarf, 28 | unit, 29 | entry, 30 | discriminant_val, 31 | } 32 | } 33 | 34 | /// The [DWARF](crate::gimli::Dwarf) sections that this `Variant`'s 35 | /// debuginfo belongs to. 36 | #[allow(dead_code)] 37 | pub(crate) fn dwarf(&self) -> &'dwarf crate::gimli::Dwarf { 38 | self.dwarf 39 | } 40 | 41 | /// The DWARF [unit][crate::gimli::Unit] that this `Variant`'s debuginfo 42 | /// belongs to. 43 | #[allow(dead_code)] 44 | pub(crate) fn unit(&self) -> &crate::gimli::Unit { 45 | self.unit 46 | } 47 | 48 | /// The [debugging information 49 | /// entry][crate::gimli::DebuggingInformationEntry] this `Variant` abstracts 50 | /// over. 51 | #[allow(dead_code)] 52 | pub(crate) fn entry(&self) -> &crate::gimli::DebuggingInformationEntry<'dwarf, 'dwarf, R> { 53 | &self.entry 54 | } 55 | 56 | /// The name of this variant. 57 | pub fn name(&self) -> Result, crate::Error> { 58 | Name::from_die(self.dwarf(), self.unit(), self.entry()) 59 | } 60 | 61 | /// The size of this variant, in bytes. 62 | pub fn size(&self) -> Result, crate::Error> { 63 | crate::get_size_opt(self.entry()) 64 | } 65 | 66 | /// The alignment of this variant, in bytes. 67 | pub fn align(&self) -> Result, crate::Error> { 68 | crate::get_align(self.entry()) 69 | } 70 | 71 | /// The file the variant was defined in (if available). 72 | pub fn file(&self) -> Result>, crate::Error> { 73 | crate::get_file(self.dwarf, self.unit, &self.entry) 74 | } 75 | 76 | /// The discriminant value (if any). 77 | pub fn discriminant_value(&self) -> &Option { 78 | &self.discriminant_val 79 | } 80 | 81 | /// The fields of this variant. 82 | pub fn fields(&self) -> Result, crate::Error> { 83 | let tree = self.unit.entries_tree(Some(self.entry.offset()))?; 84 | Ok(super::Fields::from_tree(self.dwarf, self.unit, tree)) 85 | } 86 | } 87 | 88 | impl<'dwarf, R> fmt::Display for Variant<'dwarf, R> 89 | where 90 | R: crate::gimli::Reader, 91 | { 92 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 93 | let variant_name = self.name().map_err(crate::fmt_err)?; 94 | let variant_name = variant_name.to_string_lossy().map_err(crate::fmt_err)?; 95 | let mut debug_struct = f.debug_struct(&variant_name); 96 | let mut fields = self.fields().map_err(crate::fmt_err)?; 97 | let mut fields = fields.iter().map_err(crate::fmt_err)?; 98 | while let Some(field) = fields.try_next().map_err(crate::fmt_err)? { 99 | let field_name = field.name().map_err(crate::fmt_err)?; 100 | let field_name = field_name.to_string_lossy().map_err(crate::fmt_err)?; 101 | let field_type = field.r#type().map_err(crate::fmt_err)?; 102 | debug_struct.field(&field_name, &crate::DebugDisplay(field_type)); 103 | } 104 | debug_struct.finish() 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/schema/variants.rs: -------------------------------------------------------------------------------- 1 | /// Variants of an [enum][super::Enum]. 2 | /// 3 | /// Call [`iter`][Self::iter] to iterate over variants. 4 | pub struct Variants<'dwarf, R: crate::gimli::Reader> 5 | where 6 | R: crate::gimli::Reader, 7 | { 8 | dwarf: &'dwarf crate::gimli::Dwarf, 9 | unit: &'dwarf crate::gimli::Unit, 10 | tree: crate::gimli::EntriesTree<'dwarf, 'dwarf, R>, 11 | discriminant_type: super::Type<'dwarf, R>, 12 | } 13 | 14 | impl<'dwarf, R> Variants<'dwarf, R> 15 | where 16 | R: crate::gimli::Reader, 17 | { 18 | pub(crate) fn from_tree( 19 | dwarf: &'dwarf crate::gimli::Dwarf, 20 | unit: &'dwarf crate::gimli::Unit, 21 | tree: crate::gimli::EntriesTree<'dwarf, 'dwarf, R>, 22 | discriminant_type: super::Type<'dwarf, R>, 23 | ) -> Self { 24 | Self { 25 | dwarf, 26 | unit, 27 | tree, 28 | discriminant_type, 29 | } 30 | } 31 | 32 | /// Produces an iterator over variants. 33 | pub fn iter(&mut self) -> Result, crate::Error> { 34 | Ok(VariantsIter { 35 | dwarf: self.dwarf, 36 | unit: self.unit, 37 | iter: self.tree.root()?.children(), 38 | discriminant_type: &self.discriminant_type, 39 | }) 40 | } 41 | } 42 | 43 | /// An iterator over variants. 44 | pub struct VariantsIter<'dwarf, 'tree, R: crate::gimli::Reader> 45 | where 46 | R: crate::gimli::Reader, 47 | { 48 | dwarf: &'dwarf crate::gimli::Dwarf, 49 | unit: &'dwarf crate::gimli::Unit, 50 | iter: crate::gimli::EntriesTreeIter<'dwarf, 'dwarf, 'tree, R>, 51 | discriminant_type: &'tree super::Type<'dwarf, R>, 52 | } 53 | 54 | impl<'dwarf, 'tree, R: crate::gimli::Reader> VariantsIter<'dwarf, 'tree, R> 55 | where 56 | R: crate::gimli::Reader, 57 | { 58 | /// Produces the next variant, if any. 59 | pub fn try_next(&mut self) -> Result>, crate::Error> { 60 | loop { 61 | let Some(next) = self.iter.next()? else { return Ok(None) }; 62 | let entry = next.entry(); 63 | match entry.tag() { 64 | crate::gimli::DW_TAG_variant => { 65 | let discriminant_value = entry 66 | .attr_value(crate::gimli::DW_AT_discr_value)? 67 | .and_then(|dw_at_discr_value| dw_at_discr_value.udata_value()) 68 | .map(|dw_at_discr_value| { 69 | discriminant_value(self.discriminant_type, dw_at_discr_value) 70 | }); 71 | 72 | let mut entry = next.children(); 73 | let entry = entry.next()?; 74 | let entry = entry 75 | .ok_or_else(|| crate::error::missing_child(crate::gimli::DW_TAG_member))?; 76 | let entry = crate::get_type(entry.entry())?; 77 | let entry = self.unit.entry(entry)?; 78 | return Ok(Some(super::Variant::new( 79 | self.dwarf, 80 | self.unit, 81 | entry, 82 | discriminant_value, 83 | ))); 84 | } 85 | crate::gimli::DW_TAG_enumerator => { 86 | let discriminant_value = entry 87 | .attr_value(crate::gimli::DW_AT_discr_value)? 88 | .and_then(|dw_at_discr_value| dw_at_discr_value.udata_value()) 89 | .map(|dw_at_discr_value| { 90 | discriminant_value(self.discriminant_type, dw_at_discr_value) 91 | }); 92 | 93 | return Ok(Some(super::Variant::new( 94 | self.dwarf, 95 | self.unit, 96 | entry.clone(), 97 | discriminant_value, 98 | ))); 99 | } 100 | crate::gimli::DW_TAG_member => continue, 101 | other => { 102 | anyhow::bail!( 103 | "Cannot find discriminant value in {:?} at {:x?}", 104 | other.static_string(), 105 | entry.offset() 106 | ); 107 | } 108 | } 109 | } 110 | } 111 | } 112 | 113 | impl<'dwarf, 'tree, R: crate::gimli::Reader> Iterator 114 | for VariantsIter<'dwarf, 'tree, R> 115 | { 116 | type Item = super::Variant<'dwarf, R>; 117 | 118 | fn next(&mut self) -> Option { 119 | match self.try_next() { 120 | Ok(next) => next, 121 | Err(err) => panic!("could not read next variant: {err}"), 122 | } 123 | } 124 | } 125 | 126 | fn discriminant_value<'dwarf, R>(ty: &super::Type<'dwarf, R>, v: u64) -> super::Data 127 | where 128 | R: crate::gimli::Reader, 129 | { 130 | match ty { 131 | super::Type::u8(_) => super::Data::u8(v as _), 132 | super::Type::u16(_) => super::Data::u16(v as _), 133 | super::Type::u32(_) => super::Data::u32(v as _), 134 | super::Type::u64(_) => super::Data::u64(v as _), 135 | _ => unimplemented!(), 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/value/array.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected [`[T; N]`][prim@array] value. 4 | #[derive(Clone)] 5 | #[allow(non_camel_case_types)] 6 | pub struct Array<'value, 'dwarf, P = crate::DefaultProvider> 7 | where 8 | P: crate::DebugInfoProvider, 9 | { 10 | value: crate::Bytes<'value>, 11 | schema: crate::schema::Array<'dwarf, P::Reader>, 12 | provider: &'dwarf P, 13 | } 14 | 15 | impl<'dwarf, R> crate::schema::Array<'dwarf, R> 16 | where 17 | R: crate::gimli::Reader, 18 | { 19 | pub(crate) unsafe fn with_bytes<'value, P>( 20 | self, 21 | provider: &'dwarf P, 22 | value: crate::Bytes<'value>, 23 | ) -> Result, crate::Error> 24 | where 25 | P: crate::DebugInfoProvider, 26 | { 27 | let len = self.len()?; 28 | let elt_size = self.elt_type()?.size()?; 29 | let bytes = len 30 | .checked_mul(elt_size) 31 | .ok_or_else(crate::error::arithmetic_overflow)?; 32 | let bytes = usize::try_from(bytes)?; 33 | let value = &value[..bytes]; 34 | Ok(Array { 35 | value, 36 | schema: self, 37 | provider, 38 | }) 39 | } 40 | } 41 | 42 | impl<'value, 'dwarf, P> Array<'value, 'dwarf, P> 43 | where 44 | P: crate::DebugInfoProvider, 45 | { 46 | /// The schema of this value. 47 | pub fn schema(&self) -> &crate::schema::Array<'dwarf, P::Reader> { 48 | &self.schema 49 | } 50 | 51 | /// An iterator over values in the array. 52 | pub fn iter(&self) -> Result, crate::Error> { 53 | let elt_type = self.schema.elt_type()?; 54 | let elt_size = elt_type.size()?; 55 | let elt_size = usize::try_from(elt_size)?; 56 | let length = self.schema.len()?; 57 | let length: usize = length.try_into()?; 58 | Ok(unsafe { super::Iter::new(self.value, elt_size, elt_type, length, self.provider) }) 59 | } 60 | } 61 | 62 | impl<'value, 'dwarf, P> fmt::Debug for Array<'value, 'dwarf, P> 63 | where 64 | P: crate::DebugInfoProvider, 65 | { 66 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 67 | let mut debug_struct = f.debug_struct("deflect::value::Array"); 68 | debug_struct.field("schema", &self.schema); 69 | debug_struct.field("value", &self.value); 70 | debug_struct.finish() 71 | } 72 | } 73 | 74 | impl<'value, 'dwarf, P> fmt::Display for Array<'value, 'dwarf, P> 75 | where 76 | P: crate::DebugInfoProvider, 77 | { 78 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 79 | let mut debug_list = f.debug_list(); 80 | for maybe_elt in self.iter().map_err(crate::fmt_err)? { 81 | let elt = maybe_elt.map_err(crate::fmt_err)?; 82 | debug_list.entry(&crate::DebugDisplay(elt)); 83 | } 84 | debug_list.finish() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/value/box.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected [`Box`] value. 4 | pub struct Box<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | schema: crate::schema::Box<'dwarf, P::Reader>, 9 | value: crate::Bytes<'value>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'dwarf, R> crate::schema::Box<'dwarf, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) unsafe fn with_bytes<'value, P>( 18 | self, 19 | provider: &'dwarf P, 20 | value: crate::Bytes<'value>, 21 | ) -> Result, crate::Error> 22 | where 23 | P: crate::DebugInfoProvider, 24 | { 25 | Ok(Box { 26 | schema: self, 27 | value, 28 | provider, 29 | }) 30 | } 31 | } 32 | 33 | impl<'value, 'dwarf, P> Box<'value, 'dwarf, P> 34 | where 35 | P: crate::DebugInfoProvider, 36 | { 37 | /// The schema of this value. 38 | pub fn schema(&self) -> &crate::schema::Box<'dwarf, P::Reader> { 39 | &self.schema 40 | } 41 | 42 | /// The reflected value behind this reference. 43 | pub fn deref(&self) -> Result, crate::Error> { 44 | let value = unsafe { *(self.value.as_ptr() as *const *const crate::Byte) }; 45 | let r#type = self.schema.r#type()?; 46 | let size = r#type.size()?; 47 | let size = size.try_into()?; 48 | let value = std::ptr::slice_from_raw_parts(value, size); 49 | let value = unsafe { &*value }; 50 | unsafe { super::Value::with_type(r#type, value, self.provider) } 51 | } 52 | } 53 | 54 | /* 55 | impl<'value, 'dwarf, P> Box<'value, 'dwarf, P> 56 | where 57 | P: crate::DebugInfoProvider, 58 | { 59 | /// The unreflected value behind this reference. 60 | pub(crate) fn deref_raw(&self) -> Result, crate::Error> { 61 | let value = unsafe { *(self.value.as_ptr() as *const *const crate::Byte) }; 62 | let r#type = self.schema.r#type()?; 63 | let size = r#type.size()?; 64 | let size = size.try_into()?; 65 | let value = std::ptr::slice_from_raw_parts(value, size); 66 | let value = unsafe { &*value }; 67 | Ok(value) 68 | } 69 | }*/ 70 | 71 | impl<'value, 'dwarf, P> fmt::Debug for Box<'value, 'dwarf, P> 72 | where 73 | P: crate::DebugInfoProvider, 74 | { 75 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 76 | let mut debug_struct = f.debug_struct("deflect::value::Ref"); 77 | debug_struct.field("schema", &self.schema); 78 | debug_struct.field("value", &self.value); 79 | debug_struct.finish() 80 | } 81 | } 82 | 83 | impl<'value, 'dwarf, P> fmt::Display for Box<'value, 'dwarf, P> 84 | where 85 | P: crate::DebugInfoProvider, 86 | { 87 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 88 | f.write_str("box ")?; 89 | self.deref().map_err(crate::fmt_err)?.fmt(f) 90 | } 91 | } -------------------------------------------------------------------------------- /src/value/boxed_dyn.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected [`Box`]'d `dyn Trait` object value. 4 | pub struct BoxedDyn<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | value: crate::Bytes<'value>, 9 | schema: crate::schema::BoxedDyn<'dwarf, P::Reader>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'dwarf, R> crate::schema::BoxedDyn<'dwarf, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) unsafe fn with_bytes<'value, P>( 18 | self, 19 | provider: &'dwarf P, 20 | value: crate::Bytes<'value>, 21 | ) -> Result, crate::Error> 22 | where 23 | P: crate::DebugInfoProvider, 24 | { 25 | Ok(BoxedDyn { 26 | schema: self, 27 | value, 28 | provider, 29 | }) 30 | } 31 | } 32 | 33 | impl<'value, 'dwarf, P> BoxedDyn<'value, 'dwarf, P> 34 | where 35 | P: crate::DebugInfoProvider, 36 | { 37 | /// The schema of this value. 38 | pub fn schema(&self) -> &crate::schema::BoxedDyn<'dwarf, P::Reader> { 39 | &self.schema 40 | } 41 | 42 | fn data(&self, size: usize) -> Result, crate::Error> { 43 | let field = 44 | unsafe { super::Field::new(self.schema.pointer().clone(), self.value, self.provider) }; 45 | let value = field.value()?; 46 | let value: super::Pointer = value.try_into()?; 47 | let ptr = value.deref_raw_dyn(size)?; 48 | Ok(ptr) 49 | } 50 | 51 | fn vtable(&self) -> Result, crate::Error> { 52 | let vtable = self.schema.vtable().clone(); 53 | let field = unsafe { super::Field::new(vtable, self.value, self.provider) }; 54 | let value = field.value()?; 55 | Ok(value) 56 | } 57 | 58 | /// [`Box`]'d `dyn Trait` object value. 59 | pub fn deref(&self) -> Result, crate::Error> { 60 | let vtable = self.vtable()?; 61 | let vtable: super::Pointer = vtable.try_into()?; 62 | let vtable = vtable.deref()?; 63 | let vtable: super::Array<_> = vtable.try_into()?; 64 | let mut vtable = vtable.iter()?; 65 | let drop_glue = vtable.next(); 66 | let drop_glue = drop_glue.unwrap(); 67 | let drop_glue = drop_glue?; 68 | let drop_glue: usize = drop_glue.try_into()?; 69 | 70 | let size = vtable.next(); 71 | let size = size.unwrap(); 72 | let size = size?; 73 | let size: usize = size.try_into()?; 74 | 75 | let data = self.data(size)?; 76 | 77 | let crate::DebugInfo { 78 | context, 79 | unit, 80 | entry, 81 | } = self.provider.info_for(drop_glue as _)?; 82 | let entry = unit.entry(entry)?; 83 | let schema = crate::schema::Type::from_die(context.dwarf(), unit, entry)?; 84 | 85 | unsafe { crate::Value::with_type(schema, data, self.provider) } 86 | } 87 | } 88 | 89 | impl<'value, 'dwarf, P> fmt::Debug for BoxedDyn<'value, 'dwarf, P> 90 | where 91 | P: crate::DebugInfoProvider, 92 | { 93 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 94 | let mut debug_struct = f.debug_struct("deflect::value::BoxedDyn"); 95 | debug_struct.field("schema", &self.schema); 96 | debug_struct.field("value", &self.value); 97 | debug_struct.finish() 98 | } 99 | } 100 | 101 | impl<'value, 'dwarf, P> fmt::Display for BoxedDyn<'value, 'dwarf, P> 102 | where 103 | P: crate::DebugInfoProvider, 104 | { 105 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 106 | let value = self.deref().map_err(crate::fmt_err)?; 107 | f.write_str("box ")?; 108 | value.fmt(f) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/value/boxed_slice.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected [`Box`]'d slice. 4 | pub struct BoxedSlice<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | value: crate::Bytes<'value>, 9 | schema: crate::schema::BoxedSlice<'dwarf, P::Reader>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'dwarf, R> crate::schema::BoxedSlice<'dwarf, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) unsafe fn with_bytes<'value, P>( 18 | self, 19 | provider: &'dwarf P, 20 | value: crate::Bytes<'value>, 21 | ) -> Result, crate::Error> 22 | where 23 | P: crate::DebugInfoProvider, 24 | { 25 | Ok(BoxedSlice { 26 | schema: self, 27 | value, 28 | provider, 29 | }) 30 | } 31 | } 32 | 33 | impl<'value, 'dwarf, P> BoxedSlice<'value, 'dwarf, P> 34 | where 35 | P: crate::DebugInfoProvider, 36 | { 37 | /// The schema of this value. 38 | pub fn schema(&self) -> &crate::schema::BoxedSlice<'dwarf, P::Reader> { 39 | &self.schema 40 | } 41 | 42 | /// The `data_ptr` field of this boxed slice. 43 | pub fn data_ptr(&self) -> Result, crate::Error> { 44 | let field = 45 | unsafe { super::Field::new(self.schema.data_ptr().clone(), self.value, self.provider) }; 46 | let value = field.value()?; 47 | let value: super::Pointer = value.try_into()?; 48 | let ptr = value.deref_raw()?; 49 | Ok(ptr) 50 | } 51 | 52 | /// The `length` field of this boxed slice. 53 | pub fn length(&self) -> Result { 54 | let field = 55 | unsafe { super::Field::new(self.schema.length().clone(), self.value, self.provider) }; 56 | let value = field.value()?; 57 | let len: usize = value.try_into()?; 58 | Ok(len) 59 | } 60 | 61 | /// An iterator over the values in this slice. 62 | pub fn iter(&self) -> Result, crate::Error> { 63 | let elt_type = self.schema.elt()?; 64 | let elt_size = elt_type.size()?; 65 | let elt_size = usize::try_from(elt_size)?; 66 | 67 | let length = self.length()?; 68 | let bytes = elt_size * length; 69 | 70 | let value = self.data_ptr()?.as_ptr(); 71 | let value = std::ptr::slice_from_raw_parts(value, bytes); 72 | let value = unsafe { &*value }; 73 | 74 | Ok(unsafe { super::Iter::new(value, elt_size, elt_type, length, self.provider) }) 75 | } 76 | } 77 | 78 | impl<'value, 'dwarf, P> fmt::Debug for BoxedSlice<'value, 'dwarf, P> 79 | where 80 | P: crate::DebugInfoProvider, 81 | { 82 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 83 | let mut debug_struct = f.debug_struct("deflect::value::BoxedSlice"); 84 | debug_struct.field("schema", &self.schema); 85 | debug_struct.field("value", &self.value); 86 | debug_struct.finish() 87 | } 88 | } 89 | 90 | impl<'value, 'dwarf, P> fmt::Display for BoxedSlice<'value, 'dwarf, P> 91 | where 92 | P: crate::DebugInfoProvider, 93 | { 94 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 | f.write_str("box ")?; 96 | let mut debug_list = f.debug_list(); 97 | for maybe_elt in self.iter().map_err(crate::fmt_err)? { 98 | let elt = maybe_elt.map_err(crate::fmt_err)?; 99 | debug_list.entry(&crate::DebugDisplay(elt)); 100 | } 101 | debug_list.finish()?; 102 | f.write_str("[..]") 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/value/enum.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A value of a sum type; e.g., a Rust-style enum. 4 | pub struct Enum<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | schema: crate::schema::Enum<'dwarf, P::Reader>, 9 | value: crate::Bytes<'value>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'dwarf, R> crate::schema::Enum<'dwarf, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) unsafe fn with_bytes<'value, P>( 18 | self, 19 | provider: &'dwarf P, 20 | value: crate::Bytes<'value>, 21 | ) -> Result, crate::Error> 22 | where 23 | P: crate::DebugInfoProvider, 24 | { 25 | let size = self.size()?.try_into()?; 26 | let value = &value[..size]; 27 | Ok(Enum { 28 | schema: self, 29 | value, 30 | provider, 31 | }) 32 | } 33 | } 34 | 35 | impl<'value, 'dwarf, P> Enum<'value, 'dwarf, P> 36 | where 37 | P: crate::DebugInfoProvider, 38 | { 39 | /// The schema of this value. 40 | pub fn schema(&self) -> &crate::schema::Enum<'dwarf, P::Reader> { 41 | &self.schema 42 | } 43 | 44 | /// The variant of this enum. 45 | pub fn variant(&self) -> Result, crate::Error> { 46 | let mut default = None; 47 | let mut matched = None; 48 | 49 | let schema = self.schema(); 50 | let discr_loc = schema.discriminant_location().clone(); 51 | let enum_addr = self.value.as_ptr() as *const () as u64; 52 | let discr_addr = discr_loc.address(enum_addr)?; 53 | 54 | let mut variants = schema.variants()?; 55 | let mut variants = variants.iter()?; 56 | 57 | while let Some(variant) = variants.try_next()? { 58 | if let Some(discriminant) = variant.discriminant_value() { 59 | use crate::schema::Data; 60 | let matches = match discriminant { 61 | Data::u8(v) => (unsafe { *(discr_addr as *const u8) } == *v), 62 | Data::u16(v) => (unsafe { *(discr_addr as *const u16) } == *v), 63 | Data::u32(v) => (unsafe { *(discr_addr as *const u32) } == *v), 64 | Data::u64(v) => (unsafe { *(discr_addr as *const u64) } == *v), 65 | }; 66 | if matches { 67 | matched = Some(variant.clone()); 68 | } 69 | } else { 70 | default = Some(variant.clone()); 71 | } 72 | } 73 | 74 | let schema = matched 75 | .or(default) 76 | .ok_or_else(crate::error::enum_destructure)?; 77 | Ok(unsafe { super::Variant::new(schema, self.value, self.provider) }) 78 | } 79 | } 80 | 81 | impl<'value, 'dwarf, P> fmt::Debug for Enum<'value, 'dwarf, P> 82 | where 83 | P: crate::DebugInfoProvider, 84 | { 85 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 86 | let mut debug_struct = f.debug_struct("deflect::value::Enum"); 87 | debug_struct.field("schema", &self.schema); 88 | debug_struct.field("value", &self.value); 89 | debug_struct.finish() 90 | } 91 | } 92 | 93 | impl<'value, 'dwarf, P> fmt::Display for Enum<'value, 'dwarf, P> 94 | where 95 | P: crate::DebugInfoProvider, 96 | { 97 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 98 | self.schema().name().fmt(f)?; 99 | f.write_str("::")?; 100 | self.variant().map_err(crate::fmt_err)?.fmt(f) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/value/field.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A field of a [struct][super::Struct] or [variant][super::Variant]. 4 | pub struct Field<'value, 'dwarf, P: crate::DebugInfoProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | schema: crate::schema::Field<'dwarf, P::Reader>, 9 | value: crate::Bytes<'value>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'value, 'dwarf, P> Field<'value, 'dwarf, P> 14 | where 15 | P: crate::DebugInfoProvider, 16 | { 17 | pub(crate) unsafe fn new( 18 | schema: crate::schema::Field<'dwarf, P::Reader>, 19 | value: crate::Bytes<'value>, 20 | provider: &'dwarf P, 21 | ) -> Self { 22 | Self { 23 | schema, 24 | value, 25 | provider, 26 | } 27 | } 28 | 29 | /// The schema of this field. 30 | pub fn schema(&self) -> &crate::schema::Field<'dwarf, P::Reader> { 31 | &self.schema 32 | } 33 | 34 | /// The value of this field. 35 | pub fn value(&self) -> Result, crate::Error> { 36 | let schema = self.schema(); 37 | let r#type = schema.r#type()?; 38 | let offset = schema.offset()?.address(0)? as usize; 39 | let value = &self.value[offset..]; 40 | unsafe { super::Value::with_type(r#type, value, self.provider) } 41 | } 42 | } 43 | 44 | impl<'value, 'dwarf, P> fmt::Display for Field<'value, 'dwarf, P> 45 | where 46 | P: crate::DebugInfoProvider, 47 | { 48 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 49 | self.schema().name().map_err(crate::fmt_err)?.fmt(f)?; 50 | f.write_str(" : ")?; 51 | self.value().map_err(crate::fmt_err)?.fmt(f) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/value/fields.rs: -------------------------------------------------------------------------------- 1 | /// Fields of a [struct][super::Struct] or an [enum variant][super::Variant]. 2 | /// 3 | /// Call [`iter`][Self::iter] to iterate over variants. 4 | pub struct Fields<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | schema: crate::schema::Fields<'dwarf, P::Reader>, 9 | value: crate::Bytes<'value>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'value, 'dwarf, P> Fields<'value, 'dwarf, P> 14 | where 15 | P: crate::DebugInfoProvider, 16 | { 17 | pub(crate) fn new( 18 | schema: crate::schema::Fields<'dwarf, P::Reader>, 19 | value: crate::Bytes<'value>, 20 | provider: &'dwarf P, 21 | ) -> Self { 22 | Self { 23 | schema, 24 | value, 25 | provider, 26 | } 27 | } 28 | 29 | /// Produces an iterator over variants. 30 | pub fn iter<'tree>( 31 | &'tree mut self, 32 | ) -> Result, crate::Error> { 33 | Ok(FieldsIter { 34 | schema: self.schema.iter()?, 35 | value: self.value, 36 | provider: self.provider, 37 | }) 38 | } 39 | } 40 | 41 | /// An iterator over variants. 42 | pub struct FieldsIter<'value, 'tree, 'dwarf, P = crate::DefaultProvider> 43 | where 44 | P: crate::DebugInfoProvider, 45 | { 46 | schema: crate::schema::FieldsIter<'dwarf, 'tree, P::Reader>, 47 | value: crate::Bytes<'value>, 48 | provider: &'dwarf P, 49 | } 50 | 51 | impl<'value, 'tree, 'dwarf, P> FieldsIter<'value, 'tree, 'dwarf, P> 52 | where 53 | P: crate::DebugInfoProvider, 54 | { 55 | /// Produces the next field, if any. 56 | pub fn try_next(&mut self) -> Result>, crate::Error> { 57 | let Some(next) = self.schema.try_next()? else { return Ok(None) }; 58 | Ok(Some(unsafe { 59 | super::field::Field::new(next, self.value, self.provider) 60 | })) 61 | } 62 | } 63 | 64 | impl<'value, 'tree, 'dwarf, P> Iterator for FieldsIter<'value, 'tree, 'dwarf, P> 65 | where 66 | P: crate::DebugInfoProvider, 67 | { 68 | type Item = super::Field<'value, 'dwarf, P>; 69 | 70 | fn next(&mut self) -> Option { 71 | match self.try_next() { 72 | Ok(next) => next, 73 | Err(err) => panic!("could not read next field: {err}"), 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/value/function.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A function value. 4 | pub struct Function<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | _value: crate::Bytes<'value>, 9 | schema: crate::schema::Function<'dwarf, P::Reader>, 10 | _provider: &'dwarf P, 11 | } 12 | 13 | impl<'dwarf, R> crate::schema::Function<'dwarf, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) unsafe fn with_bytes<'value, P>( 18 | self, 19 | provider: &'dwarf P, 20 | value: crate::Bytes<'value>, 21 | ) -> Result, crate::Error> 22 | where 23 | P: crate::DebugInfoProvider, 24 | { 25 | Ok(Function { 26 | schema: self, 27 | _value: value, 28 | _provider: provider, 29 | }) 30 | } 31 | } 32 | 33 | impl<'value, 'dwarf, P> Function<'value, 'dwarf, P> 34 | where 35 | P: crate::DebugInfoProvider, 36 | { 37 | /// The schema of this value. 38 | pub fn schema(&self) -> &crate::schema::Function<'dwarf, P::Reader> { 39 | &self.schema 40 | } 41 | } 42 | 43 | impl<'value, 'dwarf, P> fmt::Debug for Function<'value, 'dwarf, P> 44 | where 45 | P: crate::DebugInfoProvider, 46 | { 47 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 48 | f.write_str("fn(){}") 49 | } 50 | } 51 | 52 | impl<'value, 'dwarf, P> fmt::Display for Function<'value, 'dwarf, P> 53 | where 54 | P: crate::DebugInfoProvider, 55 | { 56 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 57 | self.schema.fmt(f) 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/value/iter.rs: -------------------------------------------------------------------------------- 1 | /// An iterator over items in an [array][super::Array] or [slice][super::Slice]. 2 | pub struct Iter<'value, 'dwarf, P = crate::DefaultProvider> 3 | where 4 | P: crate::DebugInfoProvider, 5 | { 6 | value: crate::Bytes<'value>, 7 | elt_size: usize, 8 | elt_type: crate::schema::Type<'dwarf, P::Reader>, 9 | length: usize, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'value, 'dwarf, P> Iter<'value, 'dwarf, P> 14 | where 15 | P: crate::DebugInfoProvider, 16 | { 17 | pub(crate) unsafe fn new( 18 | value: crate::Bytes<'value>, 19 | elt_size: usize, 20 | elt_type: crate::schema::Type<'dwarf, P::Reader>, 21 | length: usize, 22 | provider: &'dwarf P, 23 | ) -> Self { 24 | Self { 25 | value, 26 | elt_size, 27 | elt_type, 28 | length, 29 | provider, 30 | } 31 | } 32 | } 33 | 34 | impl<'value, 'dwarf, P> Iterator for Iter<'value, 'dwarf, P> 35 | where 36 | P: crate::DebugInfoProvider, 37 | { 38 | type Item = Result, crate::Error>; 39 | 40 | fn next(&mut self) -> Option { 41 | if self.length == 0 { 42 | return None; 43 | } 44 | 45 | let (elt, rest) = self.value.split_at(self.elt_size); 46 | self.value = rest; 47 | self.length -= 1; 48 | 49 | Some(unsafe { super::Value::with_type(self.elt_type.clone(), elt, self.provider) }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/value/mod.rs: -------------------------------------------------------------------------------- 1 | //! Reflections of Rust values. 2 | 3 | mod array; 4 | mod r#box; 5 | mod boxed_dyn; 6 | mod boxed_slice; 7 | mod r#enum; 8 | mod field; 9 | mod fields; 10 | mod function; 11 | mod iter; 12 | mod pointer; 13 | mod slice_impl; 14 | mod str_impl; 15 | mod r#struct; 16 | mod variant; 17 | 18 | pub use array::Array; 19 | pub use boxed_dyn::BoxedDyn; 20 | pub use boxed_slice::BoxedSlice; 21 | pub use field::Field; 22 | pub use fields::{Fields, FieldsIter}; 23 | pub use function::Function; 24 | pub use iter::Iter; 25 | pub use pointer::Pointer; 26 | pub use r#box::Box; 27 | pub use r#enum::Enum; 28 | pub use r#struct::Struct; 29 | pub use slice_impl::Slice; 30 | pub use str_impl::str; 31 | pub use variant::Variant; 32 | 33 | /// A reflected shared reference value. 34 | pub type SharedRef<'value, 'dwarf, P = crate::DefaultProvider> = 35 | crate::value::Pointer<'value, 'dwarf, crate::schema::Shared, P>; 36 | 37 | /// A reflected unique reference value. 38 | pub type UniqueRef<'value, 'dwarf, P = crate::DefaultProvider> = 39 | crate::value::Pointer<'value, 'dwarf, crate::schema::Unique, P>; 40 | 41 | /// A reflected `const` pointer value. 42 | pub type ConstPtr<'value, 'dwarf, P = crate::DefaultProvider> = 43 | crate::value::Pointer<'value, 'dwarf, crate::schema::Const, P>; 44 | 45 | /// A reflected `mut` pointer value. 46 | pub type MutPtr<'value, 'dwarf, P = crate::DefaultProvider> = 47 | crate::value::Pointer<'value, 'dwarf, crate::schema::Mut, P>; 48 | 49 | pub use super::Value; 50 | 51 | macro_rules! generate_primitive_conversions { 52 | ($t:ident) => { 53 | impl<'value, 'dwarf, P> From<$t<'value, 'dwarf, P>> for &'value std::primitive::$t 54 | where 55 | P: crate::DebugInfoProvider, 56 | { 57 | fn from(atom: $t<'value, 'dwarf, P>) -> Self { 58 | atom.value() 59 | } 60 | } 61 | 62 | impl<'a, 'value, 'dwarf, P> TryFrom<&'a Value<'value, 'dwarf, P>> 63 | for &'value std::primitive::$t 64 | where 65 | P: crate::DebugInfoProvider, 66 | { 67 | type Error = crate::DowncastErr; 68 | 69 | fn try_from(value: &'a Value<'value, 'dwarf, P>) -> Result { 70 | if let Value::$t(value) = value { 71 | Ok(value.value()) 72 | } else { 73 | Err(crate::DowncastErr::new::<&'a Value<'value, 'dwarf, P>, Self>()) 74 | } 75 | } 76 | } 77 | 78 | impl<'a, 'value, 'dwarf, P> TryFrom<&'a Value<'value, 'dwarf, P>> for std::primitive::$t 79 | where 80 | P: crate::DebugInfoProvider, 81 | { 82 | type Error = crate::DowncastErr; 83 | 84 | fn try_from(value: &'a Value<'value, 'dwarf, P>) -> Result { 85 | if let Value::$t(value) = value { 86 | Ok(*value.value()) 87 | } else { 88 | Err(crate::DowncastErr::new::<&'a Value<'value, 'dwarf, P>, Self>()) 89 | } 90 | } 91 | } 92 | 93 | impl<'value, 'dwarf, P> TryFrom> for &'value std::primitive::$t 94 | where 95 | P: crate::DebugInfoProvider, 96 | { 97 | type Error = crate::DowncastErr; 98 | 99 | fn try_from(value: Value<'value, 'dwarf, P>) -> Result { 100 | if let Value::$t(value) = value { 101 | Ok(value.value()) 102 | } else { 103 | Err(crate::DowncastErr::new::, Self>()) 104 | } 105 | } 106 | } 107 | 108 | impl<'value, 'dwarf, P> TryFrom> for std::primitive::$t 109 | where 110 | P: crate::DebugInfoProvider, 111 | { 112 | type Error = crate::DowncastErr; 113 | 114 | fn try_from(value: Value<'value, 'dwarf, P>) -> Result { 115 | if let Value::$t(value) = value { 116 | Ok(*value.value()) 117 | } else { 118 | Err(crate::DowncastErr::new::, Self>()) 119 | } 120 | } 121 | } 122 | }; 123 | } 124 | 125 | macro_rules! generate_primitive { 126 | ($($t:ident,)*) => { 127 | $( 128 | generate_primitive!(@ 129 | $t, 130 | concat!( 131 | "A reflected [`", 132 | stringify!($t), 133 | "`][std::primitive::", 134 | stringify!($t), 135 | "] value." 136 | ) 137 | ); 138 | )* 139 | }; 140 | (@ $t:ident, $doc:expr) => { 141 | #[doc = $doc] 142 | #[allow(non_camel_case_types)] 143 | #[derive(Clone)] 144 | pub struct $t<'value, 'dwarf, P = crate::DefaultProvider> 145 | where 146 | P: crate::DebugInfoProvider, 147 | { 148 | value: &'value std::primitive::$t, 149 | schema: crate::schema::$t<'dwarf, P::Reader>, 150 | provider: std::marker::PhantomData

, 151 | } 152 | 153 | impl<'dwarf, R> crate::schema::$t<'dwarf, R> 154 | where 155 | R: crate::gimli::Reader 156 | { 157 | pub(crate) unsafe fn with_bytes<'value, P>(self, _provider: &'dwarf P, bytes: crate::Bytes<'value>) -> Result<$t<'value, 'dwarf, P>, crate::Error> 158 | where 159 | P: crate::DebugInfoProvider, 160 | { 161 | let size = self.size() as std::primitive::usize; 162 | let value = &bytes[..size]; 163 | let (&[], [value], &[]) = value.align_to() else { 164 | bail!("primitive is misaligned") 165 | }; 166 | Ok($t { 167 | value, 168 | schema: self, 169 | provider: std::marker::PhantomData, 170 | }) 171 | } 172 | } 173 | 174 | impl<'value, 'dwarf, P> $t<'value, 'dwarf, P> 175 | where 176 | P: crate::DebugInfoProvider 177 | { 178 | /// The schema of this value. 179 | pub fn schema(&self) -> &crate::schema::$t<'dwarf, P::Reader> { 180 | &self.schema 181 | } 182 | 183 | /// The rust value of this reflected value. 184 | pub fn value(&self) -> &'value std::primitive::$t { 185 | self.value 186 | } 187 | } 188 | 189 | impl<'value, 'dwarf, P> std::fmt::Debug for $t<'value, 'dwarf, P> 190 | where 191 | P: crate::DebugInfoProvider 192 | { 193 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 194 | let mut debug_struct = f.debug_struct(concat!("deflect::value::", stringify!($t))); 195 | debug_struct.field("schema", &self.schema); 196 | debug_struct.field("value", &self.value); 197 | debug_struct.finish() 198 | } 199 | } 200 | 201 | impl<'value, 'dwarf, P> std::fmt::Display for $t<'value, 'dwarf, P> 202 | where 203 | P: crate::DebugInfoProvider 204 | { 205 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 206 | self.value.fmt(f) 207 | } 208 | } 209 | 210 | generate_primitive_conversions!($t); 211 | }; 212 | } 213 | 214 | generate_primitive! { 215 | bool, 216 | char, 217 | f32, 218 | f64, 219 | i8, 220 | i16, 221 | i32, 222 | i64, 223 | i128, 224 | isize, 225 | u8, 226 | u16, 227 | u32, 228 | u64, 229 | u128, 230 | usize, 231 | } 232 | 233 | /// A reflected [`()`][prim@unit] value. 234 | #[allow(non_camel_case_types)] 235 | #[derive(Clone)] 236 | pub struct unit<'value, 'dwarf, P = crate::DefaultProvider> 237 | where 238 | P: crate::DebugInfoProvider, 239 | { 240 | value: &'value (), 241 | schema: crate::schema::unit<'dwarf, P::Reader>, 242 | } 243 | 244 | impl<'dwarf, R> crate::schema::unit<'dwarf, R> 245 | where 246 | R: crate::gimli::Reader, 247 | { 248 | pub(crate) unsafe fn with_bytes<'value, P>( 249 | self, 250 | _provider: &'dwarf P, 251 | bytes: crate::Bytes<'value>, 252 | ) -> Result, crate::Error> 253 | where 254 | P: crate::DebugInfoProvider, 255 | { 256 | let size = self.size() as std::primitive::usize; 257 | let value = &bytes[..size]; 258 | let value = &*(value.as_ptr() as *const _); 259 | Ok(unit { 260 | value, 261 | schema: self, 262 | }) 263 | } 264 | } 265 | 266 | impl<'value, 'dwarf, P> unit<'value, 'dwarf, P> 267 | where 268 | P: crate::DebugInfoProvider, 269 | { 270 | /// The Rust value of this reflected value. 271 | pub fn value(&self) -> &'value () { 272 | self.value 273 | } 274 | 275 | /// The schema of this reflected value. 276 | pub fn schema(&self) -> &crate::schema::unit<'dwarf, P::Reader> { 277 | &self.schema 278 | } 279 | } 280 | 281 | impl<'value, 'dwarf, P> std::fmt::Debug for unit<'value, 'dwarf, P> 282 | where 283 | P: crate::DebugInfoProvider, 284 | { 285 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 286 | let mut debug_struct = f.debug_struct(concat!("deflect::value::", stringify!($t))); 287 | debug_struct.field("schema", &self.schema); 288 | debug_struct.field("value", &self.value); 289 | debug_struct.finish() 290 | } 291 | } 292 | 293 | impl<'value, 'dwarf, P> std::fmt::Display for unit<'value, 'dwarf, P> 294 | where 295 | P: crate::DebugInfoProvider, 296 | { 297 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 298 | f.write_str("()") 299 | } 300 | } 301 | 302 | impl<'a, 'value, 'dwarf, P> TryFrom<&'a Value<'value, 'dwarf, P>> for &'value () 303 | where 304 | P: crate::DebugInfoProvider, 305 | { 306 | type Error = crate::DowncastErr; 307 | 308 | fn try_from(value: &'a Value<'value, 'dwarf, P>) -> Result { 309 | if let Value::unit(value) = value { 310 | Ok(value.value()) 311 | } else { 312 | Err(crate::DowncastErr::new::<&'a Value<'value, 'dwarf, P>, Self>()) 313 | } 314 | } 315 | } 316 | 317 | impl<'a, 'value, 'dwarf, P> TryFrom<&'a Value<'value, 'dwarf, P>> for () 318 | where 319 | P: crate::DebugInfoProvider, 320 | { 321 | type Error = crate::DowncastErr; 322 | 323 | fn try_from(value: &'a Value<'value, 'dwarf, P>) -> Result { 324 | if let Value::unit(value) = value { 325 | value.value(); 326 | Ok(()) 327 | } else { 328 | Err(crate::DowncastErr::new::<&'a Value<'value, 'dwarf, P>, Self>()) 329 | } 330 | } 331 | } 332 | 333 | impl<'value, 'dwarf, P> TryFrom> for &'value () 334 | where 335 | P: crate::DebugInfoProvider, 336 | { 337 | type Error = crate::DowncastErr; 338 | 339 | fn try_from(value: Value<'value, 'dwarf, P>) -> Result { 340 | if let Value::unit(value) = value { 341 | Ok(value.value()) 342 | } else { 343 | Err(crate::DowncastErr::new::, Self>()) 344 | } 345 | } 346 | } 347 | 348 | impl<'value, 'dwarf, P> TryFrom> for () 349 | where 350 | P: crate::DebugInfoProvider, 351 | { 352 | type Error = crate::DowncastErr; 353 | 354 | fn try_from(value: Value<'value, 'dwarf, P>) -> Result { 355 | if let Value::unit(value) = value { 356 | value.value(); 357 | Ok(()) 358 | } else { 359 | Err(crate::DowncastErr::new::, Self>()) 360 | } 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /src/value/pointer.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected pointer or reference. 4 | pub struct Pointer<'value, 'dwarf, K, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | schema: crate::schema::Pointer<'dwarf, K, P::Reader>, 9 | value: crate::Bytes<'value>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'dwarf, K, R> crate::schema::Pointer<'dwarf, K, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) unsafe fn with_bytes<'value, P>( 18 | self, 19 | provider: &'dwarf P, 20 | value: crate::Bytes<'value>, 21 | ) -> Result, crate::Error> 22 | where 23 | P: crate::DebugInfoProvider, 24 | { 25 | Ok(Pointer { 26 | schema: self, 27 | value, 28 | provider, 29 | }) 30 | } 31 | } 32 | 33 | impl<'value, 'dwarf, K, P> Pointer<'value, 'dwarf, K, P> 34 | where 35 | K: crate::schema::Reference, 36 | P: crate::DebugInfoProvider, 37 | { 38 | /// The reflected value behind this reference. 39 | pub fn deref(&self) -> Result, crate::Error> 40 | where 41 | K: crate::schema::Reference, 42 | { 43 | let value = unsafe { *(self.value.as_ptr() as *const *const crate::Byte) }; 44 | let r#type = self.schema.r#type()?; 45 | let size = r#type.size()?; 46 | let size = size.try_into()?; 47 | let value = std::ptr::slice_from_raw_parts(value, size); 48 | let value = unsafe { &*value }; 49 | unsafe { super::Value::with_type(r#type, value, self.provider) } 50 | } 51 | } 52 | 53 | impl<'value, 'dwarf, K, P> Pointer<'value, 'dwarf, K, P> 54 | where 55 | P: crate::DebugInfoProvider, 56 | { 57 | /// The schema of this value. 58 | pub fn schema(&self) -> &crate::schema::Pointer<'dwarf, K, P::Reader> { 59 | &self.schema 60 | } 61 | 62 | /// The unreflected value behind this reference. 63 | pub(crate) fn deref_raw(&self) -> Result, crate::Error> { 64 | let value = unsafe { *(self.value.as_ptr() as *const *const crate::Byte) }; 65 | let r#type = self.schema.r#type()?; 66 | let size = r#type.size()?; 67 | let size = size.try_into()?; 68 | let value = std::ptr::slice_from_raw_parts(value, size); 69 | let value = unsafe { &*value }; 70 | Ok(value) 71 | } 72 | 73 | /// The unreflected value behind this reference. 74 | pub(crate) fn deref_raw_dyn(&self, size: usize) -> Result, crate::Error> { 75 | let value = unsafe { *(self.value.as_ptr() as *const *const crate::Byte) }; 76 | let value = std::ptr::slice_from_raw_parts(value, size); 77 | let value = unsafe { &*value }; 78 | Ok(value) 79 | } 80 | } 81 | 82 | impl<'value, 'dwarf, K, P> fmt::Debug for Pointer<'value, 'dwarf, K, P> 83 | where 84 | P: crate::DebugInfoProvider, 85 | { 86 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 87 | let mut debug_struct = f.debug_struct("deflect::value::Ref"); 88 | debug_struct.field("schema", &self.schema); 89 | debug_struct.field("value", &self.value); 90 | debug_struct.finish() 91 | } 92 | } 93 | 94 | impl<'value, 'dwarf, P> fmt::Display for Pointer<'value, 'dwarf, crate::schema::Shared, P> 95 | where 96 | P: crate::DebugInfoProvider, 97 | { 98 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 99 | f.write_str("&")?; 100 | self.deref().map_err(crate::fmt_err)?.fmt(f) 101 | } 102 | } 103 | 104 | impl<'value, 'dwarf, P> fmt::Display for Pointer<'value, 'dwarf, crate::schema::Unique, P> 105 | where 106 | P: crate::DebugInfoProvider, 107 | { 108 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 109 | f.write_str("&mut ")?; 110 | self.deref().map_err(crate::fmt_err)?.fmt(f) 111 | } 112 | } 113 | 114 | impl<'value, 'dwarf, P> fmt::Display for Pointer<'value, 'dwarf, crate::schema::Const, P> 115 | where 116 | P: crate::DebugInfoProvider, 117 | { 118 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 119 | let value = self.deref_raw().map_err(crate::fmt_err)?; 120 | let addr = value.as_ptr() as usize; 121 | addr.fmt(f)?; 122 | f.write_str(" as *const _") 123 | } 124 | } 125 | 126 | impl<'value, 'dwarf, P> fmt::Display for Pointer<'value, 'dwarf, crate::schema::Mut, P> 127 | where 128 | P: crate::DebugInfoProvider, 129 | { 130 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 131 | let value = self.deref_raw().map_err(crate::fmt_err)?; 132 | let addr = value.as_ptr() as usize; 133 | addr.fmt(f)?; 134 | f.write_str(" as *mut _") 135 | } 136 | } 137 | 138 | -------------------------------------------------------------------------------- /src/value/slice_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected slice value. 4 | pub struct Slice<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | value: crate::Bytes<'value>, 9 | schema: crate::schema::Slice<'dwarf, P::Reader>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'dwarf, R> crate::schema::Slice<'dwarf, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) unsafe fn with_bytes<'value, P>( 18 | self, 19 | provider: &'dwarf P, 20 | bytes: crate::Bytes<'value>, 21 | ) -> Result, crate::Error> 22 | where 23 | P: crate::DebugInfoProvider, 24 | { 25 | Ok(Slice { 26 | value: bytes, 27 | schema: self, 28 | provider, 29 | }) 30 | } 31 | } 32 | 33 | impl<'value, 'dwarf, P> Slice<'value, 'dwarf, P> 34 | where 35 | P: crate::DebugInfoProvider, 36 | { 37 | /// The schema of this value. 38 | pub fn schema(&self) -> &crate::schema::Slice<'dwarf, P::Reader> { 39 | &self.schema 40 | } 41 | 42 | /// The value of the `data_ptr` field of this slice. 43 | pub fn data_ptr(&self) -> Result, crate::Error> { 44 | let field = 45 | unsafe { super::Field::new(self.schema.data_ptr().clone(), self.value, self.provider) }; 46 | let value = field.value()?; 47 | let value: super::Pointer = value.try_into()?; 48 | let ptr = value.deref_raw()?; 49 | Ok(ptr) 50 | } 51 | 52 | /// The value of the `length` field of this slice. 53 | pub fn length(&self) -> Result { 54 | let field = 55 | unsafe { super::Field::new(self.schema.length().clone(), self.value, self.provider) }; 56 | let value = field.value()?; 57 | let len: usize = value.try_into()?; 58 | Ok(len) 59 | } 60 | 61 | /// An iterator over values of this slice. 62 | pub fn iter(&self) -> Result, crate::Error> { 63 | let elt_type = self.schema.elt()?; 64 | let elt_size = elt_type.size()?; 65 | let elt_size = usize::try_from(elt_size)?; 66 | 67 | let length = self.length()?; 68 | let bytes = elt_size * length; 69 | 70 | let value = self.data_ptr()?.as_ptr(); 71 | let value = std::ptr::slice_from_raw_parts(value, bytes); 72 | let value = unsafe { &*value }; 73 | 74 | Ok(unsafe { super::Iter::new(value, elt_size, elt_type, length, self.provider) }) 75 | } 76 | } 77 | 78 | impl<'value, 'dwarf, P> fmt::Debug for Slice<'value, 'dwarf, P> 79 | where 80 | P: crate::DebugInfoProvider, 81 | { 82 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 83 | let mut debug_struct = f.debug_struct("deflect::value::Slice"); 84 | debug_struct.field("schema", &self.schema); 85 | debug_struct.field("value", &self.value); 86 | debug_struct.finish() 87 | } 88 | } 89 | 90 | impl<'value, 'dwarf, P> fmt::Display for Slice<'value, 'dwarf, P> 91 | where 92 | P: crate::DebugInfoProvider, 93 | { 94 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 | f.write_str("&")?; 96 | let mut debug_list = f.debug_list(); 97 | for maybe_elt in self.iter().map_err(crate::fmt_err)? { 98 | let elt = maybe_elt.map_err(crate::fmt_err)?; 99 | debug_list.entry(&crate::DebugDisplay(elt)); 100 | } 101 | debug_list.finish() 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/value/str_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected `&str` value. 4 | #[allow(non_camel_case_types)] 5 | pub struct str<'value, 'dwarf, P = crate::DefaultProvider> 6 | where 7 | P: crate::DebugInfoProvider, 8 | { 9 | value: &'value std::primitive::str, 10 | schema: crate::schema::str<'dwarf, P::Reader>, 11 | _provider: &'dwarf P, 12 | } 13 | 14 | impl<'dwarf, R> crate::schema::str<'dwarf, R> 15 | where 16 | R: crate::gimli::Reader, 17 | { 18 | pub(crate) unsafe fn with_bytes<'value, P>( 19 | self, 20 | provider: &'dwarf P, 21 | value: crate::Bytes<'value>, 22 | ) -> Result, crate::Error> 23 | where 24 | P: crate::DebugInfoProvider, 25 | { 26 | let data_ptr = unsafe { super::Field::new(self.data_ptr().clone(), value, provider) }; 27 | let data_ptr = data_ptr.value()?; 28 | let data_ptr: super::Pointer = data_ptr.try_into()?; 29 | let data = data_ptr.deref_raw()?.as_ptr(); 30 | 31 | let length = unsafe { super::Field::new(self.length().clone(), value, provider) }; 32 | let length = length.value()?; 33 | let length = length.try_into()?; 34 | 35 | let value = std::ptr::slice_from_raw_parts(data, length); 36 | let value = unsafe { &*(value as *const std::primitive::str) }; 37 | 38 | Ok(str { 39 | value, 40 | schema: self, 41 | _provider: provider, 42 | }) 43 | } 44 | } 45 | 46 | impl<'value, 'dwarf, P> str<'value, 'dwarf, P> 47 | where 48 | P: crate::DebugInfoProvider, 49 | { 50 | /// The schema of this value. 51 | pub fn schema(&self) -> &crate::schema::str<'dwarf, P::Reader> { 52 | &self.schema 53 | } 54 | 55 | /// The Rust value corresponding to this reflected value. 56 | pub fn value(&self) -> &'value std::primitive::str { 57 | self.value 58 | } 59 | } 60 | 61 | impl<'value, 'dwarf, P> fmt::Debug for str<'value, 'dwarf, P> 62 | where 63 | P: crate::DebugInfoProvider, 64 | { 65 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 66 | let mut debug_struct = f.debug_struct("deflect::value::Slice"); 67 | debug_struct.field("schema", &self.schema); 68 | debug_struct.field("value", &self.value); 69 | debug_struct.finish() 70 | } 71 | } 72 | 73 | impl<'value, 'dwarf, P> fmt::Display for str<'value, 'dwarf, P> 74 | where 75 | P: crate::DebugInfoProvider, 76 | { 77 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 78 | fmt::Debug::fmt(self.value(), f) 79 | } 80 | } 81 | 82 | impl<'value, 'dwarf, P> From> for &'value std::primitive::str 83 | where 84 | P: crate::DebugInfoProvider, 85 | { 86 | fn from(atom: str<'value, 'dwarf, P>) -> Self { 87 | atom.value() 88 | } 89 | } 90 | 91 | impl<'a, 'value, 'dwarf, P> TryFrom<&'a super::Value<'value, 'dwarf, P>> 92 | for &'value std::primitive::str 93 | where 94 | P: crate::DebugInfoProvider, 95 | { 96 | type Error = crate::error::DowncastErr; 97 | 98 | fn try_from(value: &'a super::Value<'value, 'dwarf, P>) -> Result { 99 | if let super::Value::str(value) = value { 100 | Ok(value.value()) 101 | } else { 102 | Err(crate::error::DowncastErr::new::< 103 | &'a super::Value<'value, 'dwarf, P>, 104 | Self, 105 | >()) 106 | } 107 | } 108 | } 109 | 110 | impl<'value, 'dwarf, P> TryFrom> for &'value std::primitive::str 111 | where 112 | P: crate::DebugInfoProvider, 113 | { 114 | type Error = crate::error::DowncastErr; 115 | 116 | fn try_from(value: super::Value<'value, 'dwarf, P>) -> Result { 117 | if let super::Value::str(value) = value { 118 | Ok(value.value()) 119 | } else { 120 | Err(crate::error::DowncastErr::new::< 121 | super::Value<'value, 'dwarf, P>, 122 | Self, 123 | >()) 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/value/struct.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected struct value. 4 | pub struct Struct<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | schema: crate::schema::Struct<'dwarf, P::Reader>, 9 | value: crate::Bytes<'value>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'dwarf, R> crate::schema::Struct<'dwarf, R> 14 | where 15 | R: crate::gimli::Reader, 16 | { 17 | pub(crate) unsafe fn with_bytes<'value, P>( 18 | self, 19 | provider: &'dwarf P, 20 | value: crate::Bytes<'value>, 21 | ) -> Result, crate::Error> 22 | where 23 | P: crate::DebugInfoProvider, 24 | { 25 | Ok(Struct { 26 | schema: self, 27 | value, 28 | provider, 29 | }) 30 | } 31 | } 32 | 33 | impl<'value, 'dwarf, P> Struct<'value, 'dwarf, P> 34 | where 35 | P: crate::DebugInfoProvider, 36 | { 37 | /// The schema of this value. 38 | pub fn schema(&self) -> &crate::schema::Struct<'dwarf, P::Reader> { 39 | &self.schema 40 | } 41 | 42 | /// Get a field of this struct by name. 43 | pub fn field(&self, field_name: N) -> Result>, crate::Error> 44 | where 45 | N: AsRef<[u8]>, 46 | { 47 | let target_name = field_name.as_ref(); 48 | let mut fields = self.fields()?; 49 | let mut fields = fields.iter()?; 50 | while let Some(field) = fields.try_next()? { 51 | let field_name = field.schema().name()?; 52 | let field_name = field_name.to_slice()?; 53 | if target_name == field_name.as_ref() { 54 | return Ok(Some(field)); 55 | } 56 | } 57 | Ok(None) 58 | } 59 | 60 | /// The fields of this struct. 61 | pub fn fields(&self) -> Result, crate::Error> { 62 | let fields = self.schema.fields()?; 63 | Ok(super::Fields::new(fields, self.value, self.provider)) 64 | } 65 | } 66 | 67 | impl<'value, 'dwarf, P> fmt::Debug for Struct<'value, 'dwarf, P> 68 | where 69 | P: crate::DebugInfoProvider, 70 | { 71 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 72 | let mut debug_struct = f.debug_struct("deflect::value::Struct"); 73 | debug_struct.field("schema", &self.schema); 74 | debug_struct.field("value", &self.value); 75 | debug_struct.finish() 76 | } 77 | } 78 | 79 | impl<'value, 'dwarf, P> fmt::Display for Struct<'value, 'dwarf, P> 80 | where 81 | P: crate::DebugInfoProvider, 82 | { 83 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 84 | let schema = self.schema(); 85 | let type_name = schema.name().map_err(crate::fmt_err)?; 86 | let type_name = type_name.to_string_lossy().map_err(crate::fmt_err)?; 87 | let mut debug_struct = f.debug_struct(&type_name); 88 | let mut fields = self.fields().map_err(crate::fmt_err)?; 89 | let mut fields = fields.iter().map_err(crate::fmt_err)?; 90 | while let Some(field) = fields.try_next().map_err(crate::fmt_err)? { 91 | let field_name = field.schema().name().map_err(crate::fmt_err)?; 92 | let field_name = field_name.to_string_lossy().map_err(crate::fmt_err)?; 93 | let field_value = field.value().map_err(crate::fmt_err)?; 94 | debug_struct.field(&field_name, &crate::DebugDisplay(field_value)); 95 | } 96 | debug_struct.finish() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/value/variant.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// A reflected enum variant value. 4 | pub struct Variant<'value, 'dwarf, P = crate::DefaultProvider> 5 | where 6 | P: crate::DebugInfoProvider, 7 | { 8 | schema: crate::schema::Variant<'dwarf, P::Reader>, 9 | value: crate::Bytes<'value>, 10 | provider: &'dwarf P, 11 | } 12 | 13 | impl<'value, 'dwarf, P> Variant<'value, 'dwarf, P> 14 | where 15 | P: crate::DebugInfoProvider, 16 | { 17 | pub(crate) unsafe fn new( 18 | schema: crate::schema::Variant<'dwarf, P::Reader>, 19 | value: crate::Bytes<'value>, 20 | provider: &'dwarf P, 21 | ) -> Self { 22 | Self { 23 | schema, 24 | value, 25 | provider, 26 | } 27 | } 28 | 29 | /// The schema of this value. 30 | pub fn schema(&self) -> &crate::schema::Variant<'dwarf, P::Reader> { 31 | &self.schema 32 | } 33 | 34 | /// The fields of this variant. 35 | pub fn fields(&self) -> Result, crate::Error> { 36 | let fields = self.schema.fields()?; 37 | Ok(super::Fields::new(fields, self.value, self.provider)) 38 | } 39 | } 40 | 41 | impl<'value, 'dwarf, P> fmt::Display for Variant<'value, 'dwarf, P> 42 | where 43 | P: crate::DebugInfoProvider, 44 | { 45 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 46 | let variant_name = self.schema().name().map_err(crate::fmt_err)?; 47 | let variant_name = variant_name.to_string_lossy().map_err(crate::fmt_err)?; 48 | let mut debug_struct = f.debug_struct(&variant_name); 49 | let mut fields = self.fields().map_err(crate::fmt_err)?; 50 | let mut fields = fields.iter().map_err(crate::fmt_err)?; 51 | while let Some(field) = fields.try_next().map_err(crate::fmt_err)? { 52 | let field_name = field.schema().name().map_err(crate::fmt_err)?; 53 | let field_name = field_name.to_string_lossy().map_err(crate::fmt_err)?; 54 | let field_value = field.value().map_err(crate::fmt_err)?; 55 | debug_struct.field(&field_name, &crate::DebugDisplay(field_value)); 56 | } 57 | debug_struct.finish() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/reflect.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | struct DisplayDebug(T); 3 | 4 | impl fmt::Display for DisplayDebug 5 | where 6 | T: fmt::Debug, 7 | { 8 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 9 | self.0.fmt(f) 10 | } 11 | } 12 | 13 | #[test] 14 | fn phantom_data() -> Result<(), Box> { 15 | use std::marker::PhantomData; 16 | let erased: &dyn deflect::Reflect = &PhantomData::; 17 | let context = deflect::default_provider()?; 18 | let value = erased.reflect(&context)?; 19 | let value: deflect::value::Struct<_> = value.try_into()?; 20 | assert_eq!(value.to_string(), "PhantomData"); 21 | Ok(()) 22 | } 23 | 24 | #[test] 25 | fn unit_struct() -> Result<(), Box> { 26 | struct UnitStruct; 27 | let erased: &dyn deflect::Reflect = &UnitStruct; 28 | let context = deflect::default_provider()?; 29 | let value = erased.reflect(&context)?; 30 | assert_eq!(value.to_string(), "UnitStruct"); 31 | Ok(()) 32 | } 33 | 34 | #[test] 35 | fn tuple_struct() -> Result<(), Box> { 36 | struct TupleStruct(u8); 37 | let erased: &dyn deflect::Reflect = &TupleStruct(42); 38 | let context = deflect::default_provider()?; 39 | let value = erased.reflect(&context)?; 40 | assert_eq!(value.to_string(), "TupleStruct { __0: 42 }"); 41 | Ok(()) 42 | } 43 | 44 | #[test] 45 | fn braced_struct() -> Result<(), Box> { 46 | struct BracedStruct { 47 | #[allow(dead_code)] 48 | foo: u8, 49 | } 50 | let erased: &dyn deflect::Reflect = &BracedStruct { foo: 42 }; 51 | let context = deflect::default_provider()?; 52 | let value = erased.reflect(&context)?; 53 | assert_eq!(value.to_string(), "BracedStruct { foo: 42 }"); 54 | Ok(()) 55 | } 56 | 57 | mod r#ref { 58 | #[test] 59 | fn unit_struct() -> Result<(), Box> { 60 | struct UnitStruct; 61 | let erased: &dyn deflect::Reflect = &UnitStruct; 62 | let context = deflect::default_provider()?; 63 | let value = erased.reflect(&context)?; 64 | assert_eq!(value.to_string(), "UnitStruct"); 65 | Ok(()) 66 | } 67 | } 68 | 69 | mod slice { 70 | use itertools::Itertools; 71 | 72 | #[quickcheck_macros::quickcheck] 73 | fn of_units(data: Vec<()>) -> Result<(), Box> { 74 | let slice = data.as_slice(); 75 | let erased: &dyn deflect::Reflect = &slice; 76 | let context = deflect::default_provider()?; 77 | let value = erased.reflect(&context)?; 78 | let value: deflect::value::Slice<_> = value.try_into()?; 79 | 80 | assert_eq!(data.len(), value.length()?); 81 | assert_eq!(data.len(), value.iter()?.count()); 82 | 83 | let collected: Vec<_> = value 84 | .iter()? 85 | .map_ok(<()>::try_from) 86 | .flatten_ok() 87 | .try_collect()?; 88 | 89 | assert_eq!(data, collected); 90 | 91 | Ok(()) 92 | } 93 | 94 | #[quickcheck_macros::quickcheck] 95 | fn of_chars(data: Vec) -> Result<(), Box> { 96 | let slice = data.as_slice(); 97 | let erased: &dyn deflect::Reflect = &slice; 98 | let context = deflect::default_provider()?; 99 | let value = erased.reflect(&context)?; 100 | let value: deflect::value::Slice<_> = value.try_into()?; 101 | 102 | assert_eq!(data.len(), value.length()?); 103 | assert_eq!(data.len(), value.iter()?.count()); 104 | 105 | let collected: Vec<_> = value 106 | .iter()? 107 | .map_ok(char::try_from) 108 | .flatten_ok() 109 | .try_collect()?; 110 | 111 | assert_eq!(data, collected); 112 | 113 | Ok(()) 114 | } 115 | } 116 | 117 | #[quickcheck_macros::quickcheck] 118 | fn str(data: String) -> Result<(), Box> { 119 | let slice = data.as_str(); 120 | let erased: &dyn deflect::Reflect = &slice; 121 | let context = deflect::default_provider()?; 122 | let value = erased.reflect(&context)?; 123 | let value: &str = value.try_into()?; 124 | assert_eq!(slice, value); 125 | Ok(()) 126 | } 127 | 128 | #[test] 129 | fn boxed_dyn() -> Result<(), Box> { 130 | struct Foo; 131 | 132 | trait Trait {} 133 | 134 | impl Trait for Foo {} 135 | 136 | let context = deflect::default_provider()?; 137 | 138 | let data: Box = Box::new(Foo); 139 | let erased: &dyn deflect::Reflect = &data; 140 | 141 | let value: deflect::Value = erased.reflect(&context)?; 142 | let value: deflect::value::BoxedDyn = value.try_into()?; 143 | 144 | assert_eq!(value.to_string(), "box Foo"); 145 | 146 | let value: deflect::Value = value.deref()?; 147 | let value: deflect::value::Struct = value.try_into()?; 148 | 149 | assert_eq!(value.to_string(), "Foo"); 150 | 151 | Ok(()) 152 | } 153 | 154 | #[test] 155 | fn boxed_slice() -> Result<(), Box> { 156 | let data = vec![1, 2, 3].into_boxed_slice(); 157 | let erased: &dyn deflect::Reflect = &data; 158 | let context = deflect::default_provider()?; 159 | let value: deflect::Value = erased.reflect(&context)?; 160 | let value: deflect::value::BoxedSlice = value.try_into()?; 161 | assert_eq!(value.to_string(), "box [1, 2, 3][..]"); 162 | Ok(()) 163 | } 164 | 165 | mod primitive { 166 | 167 | use std::ptr; 168 | 169 | #[quickcheck_macros::quickcheck] 170 | fn unit(n: ()) -> Result<(), Box> { 171 | let erased: &dyn deflect::Reflect = &n; 172 | let context = deflect::default_provider()?; 173 | let value = erased.reflect(&context)?; 174 | assert_eq!("()", value.to_string()); 175 | assert!(ptr::eq( 176 | &n, 177 | <&_>::try_from(value).expect("failed to downcast") 178 | )); 179 | Ok(()) 180 | } 181 | 182 | #[quickcheck_macros::quickcheck] 183 | fn bool(n: bool) -> Result<(), Box> { 184 | let erased: &dyn deflect::Reflect = &n; 185 | let context = deflect::default_provider()?; 186 | let value = erased.reflect(&context)?; 187 | assert_eq!(n.to_string(), value.to_string()); 188 | assert!(ptr::eq( 189 | &n, 190 | <&_>::try_from(value).expect("failed to downcast") 191 | )); 192 | Ok(()) 193 | } 194 | 195 | #[quickcheck_macros::quickcheck] 196 | fn char(n: char) -> Result<(), Box> { 197 | let erased: &dyn deflect::Reflect = &n; 198 | let context = deflect::default_provider()?; 199 | let value = erased.reflect(&context)?; 200 | assert_eq!(n.to_string(), value.to_string()); 201 | assert!(ptr::eq( 202 | &n, 203 | <&_>::try_from(value).expect("failed to downcast") 204 | )); 205 | Ok(()) 206 | } 207 | 208 | #[quickcheck_macros::quickcheck] 209 | fn f32(n: f32) -> Result<(), Box> { 210 | let erased: &dyn deflect::Reflect = &n; 211 | let context = deflect::default_provider()?; 212 | let value = erased.reflect(&context)?; 213 | assert_eq!(n.to_string(), value.to_string()); 214 | assert!(ptr::eq( 215 | &n, 216 | <&_>::try_from(value).expect("failed to downcast") 217 | )); 218 | Ok(()) 219 | } 220 | 221 | #[quickcheck_macros::quickcheck] 222 | fn f64(n: f64) -> Result<(), Box> { 223 | let erased: &dyn deflect::Reflect = &n; 224 | let context = deflect::default_provider()?; 225 | let value = erased.reflect(&context)?; 226 | assert_eq!(n.to_string(), value.to_string()); 227 | assert!(ptr::eq( 228 | &n, 229 | <&_>::try_from(value).expect("failed to downcast") 230 | )); 231 | Ok(()) 232 | } 233 | 234 | #[quickcheck_macros::quickcheck] 235 | fn i8(n: i8) -> Result<(), Box> { 236 | let erased: &dyn deflect::Reflect = &n; 237 | let context = deflect::default_provider()?; 238 | let value = erased.reflect(&context)?; 239 | assert_eq!(n.to_string(), value.to_string()); 240 | assert!(ptr::eq( 241 | &n, 242 | <&_>::try_from(value).expect("failed to downcast") 243 | )); 244 | Ok(()) 245 | } 246 | 247 | #[quickcheck_macros::quickcheck] 248 | fn i16(n: i16) -> Result<(), Box> { 249 | let erased: &dyn deflect::Reflect = &n; 250 | let context = deflect::default_provider()?; 251 | let value = erased.reflect(&context)?; 252 | assert_eq!(n.to_string(), value.to_string()); 253 | assert!(ptr::eq( 254 | &n, 255 | <&_>::try_from(value).expect("failed to downcast") 256 | )); 257 | Ok(()) 258 | } 259 | 260 | #[quickcheck_macros::quickcheck] 261 | fn i32(n: i32) -> Result<(), Box> { 262 | let erased: &dyn deflect::Reflect = &n; 263 | let context = deflect::default_provider()?; 264 | let value = erased.reflect(&context)?; 265 | assert_eq!(n.to_string(), value.to_string()); 266 | assert!(ptr::eq( 267 | &n, 268 | <&_>::try_from(value).expect("failed to downcast") 269 | )); 270 | Ok(()) 271 | } 272 | 273 | #[quickcheck_macros::quickcheck] 274 | fn i64(n: i64) -> Result<(), Box> { 275 | let erased: &dyn deflect::Reflect = &n; 276 | let context = deflect::default_provider()?; 277 | let value = erased.reflect(&context)?; 278 | assert_eq!(n.to_string(), value.to_string()); 279 | assert!(ptr::eq( 280 | &n, 281 | <&_>::try_from(value).expect("failed to downcast") 282 | )); 283 | Ok(()) 284 | } 285 | 286 | #[quickcheck_macros::quickcheck] 287 | fn i128(n: i128) -> Result<(), Box> { 288 | let erased: &dyn deflect::Reflect = &n; 289 | let context = deflect::default_provider()?; 290 | let value = erased.reflect(&context)?; 291 | assert_eq!(n.to_string(), value.to_string()); 292 | assert!(ptr::eq( 293 | &n, 294 | <&_>::try_from(value).expect("failed to downcast") 295 | )); 296 | Ok(()) 297 | } 298 | 299 | #[quickcheck_macros::quickcheck] 300 | fn isize(n: isize) -> Result<(), Box> { 301 | let erased: &dyn deflect::Reflect = &n; 302 | let context = deflect::default_provider()?; 303 | let value = erased.reflect(&context)?; 304 | assert_eq!(n.to_string(), value.to_string()); 305 | assert!(ptr::eq( 306 | &n, 307 | <&_>::try_from(value).expect("failed to downcast") 308 | )); 309 | Ok(()) 310 | } 311 | 312 | #[quickcheck_macros::quickcheck] 313 | fn u8(n: u8) -> Result<(), Box> { 314 | let erased: &dyn deflect::Reflect = &n; 315 | let context = deflect::default_provider()?; 316 | let value = erased.reflect(&context)?; 317 | assert_eq!(n.to_string(), value.to_string()); 318 | assert!(ptr::eq( 319 | &n, 320 | <&_>::try_from(value).expect("failed to downcast") 321 | )); 322 | Ok(()) 323 | } 324 | 325 | #[quickcheck_macros::quickcheck] 326 | fn u16(n: u16) -> Result<(), Box> { 327 | let erased: &dyn deflect::Reflect = &n; 328 | let context = deflect::default_provider()?; 329 | let value = erased.reflect(&context)?; 330 | assert_eq!(n.to_string(), value.to_string()); 331 | assert!(ptr::eq( 332 | &n, 333 | <&_>::try_from(value).expect("failed to downcast") 334 | )); 335 | Ok(()) 336 | } 337 | 338 | #[quickcheck_macros::quickcheck] 339 | fn u32(n: u32) -> Result<(), Box> { 340 | let erased: &dyn deflect::Reflect = &n; 341 | let context = deflect::default_provider()?; 342 | let value = erased.reflect(&context)?; 343 | assert_eq!(n.to_string(), value.to_string()); 344 | assert!(ptr::eq( 345 | &n, 346 | <&_>::try_from(value).expect("failed to downcast") 347 | )); 348 | Ok(()) 349 | } 350 | 351 | #[quickcheck_macros::quickcheck] 352 | fn u64(n: u64) -> Result<(), Box> { 353 | let erased: &dyn deflect::Reflect = &n; 354 | let context = deflect::default_provider()?; 355 | let value = erased.reflect(&context)?; 356 | assert_eq!(n.to_string(), value.to_string()); 357 | assert!(ptr::eq( 358 | &n, 359 | <&_>::try_from(value).expect("failed to downcast") 360 | )); 361 | Ok(()) 362 | } 363 | 364 | #[quickcheck_macros::quickcheck] 365 | fn u128(n: u128) -> Result<(), Box> { 366 | let erased: &dyn deflect::Reflect = &n; 367 | let context = deflect::default_provider()?; 368 | let value = erased.reflect(&context)?; 369 | assert_eq!(n.to_string(), value.to_string()); 370 | assert!(ptr::eq( 371 | &n, 372 | <&_>::try_from(value).expect("failed to downcast") 373 | )); 374 | Ok(()) 375 | } 376 | 377 | #[quickcheck_macros::quickcheck] 378 | fn usize(n: usize) -> Result<(), Box> { 379 | let erased: &dyn deflect::Reflect = &n; 380 | let context = deflect::default_provider()?; 381 | let value = erased.reflect(&context)?; 382 | assert_eq!(n.to_string(), value.to_string()); 383 | assert!(ptr::eq( 384 | &n, 385 | <&_>::try_from(value).expect("failed to downcast") 386 | )); 387 | Ok(()) 388 | } 389 | } 390 | --------------------------------------------------------------------------------