├── .editorconfig ├── .github ├── CODEOWNERS └── workflows │ └── check.yml ├── .gitignore ├── .gitmodules ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples ├── bench-decoder.rs ├── build.rs ├── data.rs ├── exports.rs ├── info.rs ├── inject.rs ├── roundtrip.rs └── show.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ └── deserialize.rs ├── justfile ├── res └── cases │ └── v1 │ ├── accumulate_u8.wasm │ ├── clang.wasm │ ├── const.wasm │ ├── err-int-too-long.wasm │ ├── err-leb-i32-too-long-2.wasm │ ├── err-leb-i32-too-long.wasm │ ├── err-leb-i64-too-long.wasm │ ├── err-leb-u32-too-long.wasm │ ├── err-return-type.wasm │ ├── err-sections-after-custom.wasm │ ├── global_section.wasm │ ├── hello.wasm │ ├── ifelse.wasm │ ├── inc_i32.wasm │ ├── names.wasm │ ├── names_with_imports.wasm │ ├── offset.wasm │ ├── payload_len.wasm │ ├── peek_sample.wasm │ ├── relocatable.wasm │ ├── start_add.wasm │ ├── start_add_custom.wasm │ ├── start_mut.wasm │ ├── test.wasm │ ├── test2.wasm │ ├── test3.wasm │ ├── test4.wasm │ ├── test5.rs │ ├── test5.wasm │ ├── test6.rs │ ├── test6.wasm │ ├── two-mems.wasm │ ├── varuint1_1.wasm │ └── with_names.wasm ├── rustfmt.toml ├── src ├── builder │ ├── code.rs │ ├── data.rs │ ├── export.rs │ ├── global.rs │ ├── import.rs │ ├── invoke.rs │ ├── memory.rs │ ├── misc.rs │ ├── mod.rs │ ├── module.rs │ └── table.rs ├── elements │ ├── export_entry.rs │ ├── func.rs │ ├── global_entry.rs │ ├── import_entry.rs │ ├── index_map.rs │ ├── mod.rs │ ├── module.rs │ ├── name_section.rs │ ├── ops.rs │ ├── primitives.rs │ ├── reloc_section.rs │ ├── section.rs │ ├── segment.rs │ └── types.rs ├── io.rs └── lib.rs └── testsuite ├── Cargo.toml └── src ├── lib.rs └── run.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | indent_style=tab 4 | indent_size=tab 5 | tab_width=4 6 | end_of_line=lf 7 | charset=utf-8 8 | trim_trailing_whitespace=true 9 | max_line_length=120 10 | insert_final_newline=true 11 | 12 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # For details about syntax, see: 2 | # https://help.github.com/en/articles/about-code-owners 3 | 4 | / @NikVolf @pepyakin 5 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | rustfmt: 14 | runs-on: "ubuntu-latest" 15 | steps: 16 | - name: Install Rust toolchain 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | profile: minimal 20 | toolchain: nightly 21 | components: rustfmt 22 | 23 | - uses: actions/checkout@v2 24 | 25 | - name: Cargo fmt 26 | uses: actions-rs/cargo@v1 27 | with: 28 | toolchain: nightly 29 | command: fmt 30 | args: --all -- --check 31 | 32 | clippy: 33 | runs-on: "ubuntu-latest" 34 | steps: 35 | - name: Install Rust toolchain 36 | uses: actions-rs/toolchain@v1 37 | with: 38 | profile: minimal 39 | toolchain: stable 40 | components: clippy 41 | 42 | - uses: actions/checkout@v2 43 | with: 44 | submodules: true 45 | 46 | - name: Cargo clippy 47 | uses: actions-rs/cargo@v1 48 | with: 49 | toolchain: stable 50 | command: clippy 51 | args: --all-targets --all-features --workspace -- -D warnings 52 | 53 | test: 54 | strategy: 55 | matrix: 56 | os: ["ubuntu-latest", "macos-latest"] 57 | toolchain: ["stable", "nightly"] 58 | runs-on: ${{ matrix.os }} 59 | steps: 60 | - name: Install Rust toolchain 61 | uses: actions-rs/toolchain@v1 62 | with: 63 | profile: minimal 64 | target: wasm32-unknown-unknown 65 | toolchain: ${{ matrix.toolchain }} 66 | 67 | - uses: actions/checkout@v2 68 | with: 69 | submodules: true 70 | 71 | - name: Cargo build 72 | uses: actions-rs/cargo@v1 73 | with: 74 | toolchain: ${{ matrix.toolchain }} 75 | command: build 76 | args: --workspace 77 | 78 | - name: Cargo build (no_std) 79 | uses: actions-rs/cargo@v1 80 | with: 81 | toolchain: ${{ matrix.toolchain }} 82 | command: build 83 | args: --no-default-features 84 | 85 | - name: Cargo build (wasm) 86 | uses: actions-rs/cargo@v1 87 | with: 88 | toolchain: ${{ matrix.toolchain }} 89 | command: build 90 | args: --no-default-features --target wasm32-unknown-unknown 91 | 92 | - name: Cargo test 93 | uses: actions-rs/cargo@v1 94 | with: 95 | toolchain: ${{ matrix.toolchain }} 96 | command: test 97 | args: --all-features --workspace 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .vscode 4 | **/.DS_Store 5 | rls 6 | .idea 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "spec/testsuite"] 2 | path = testsuite/spec 3 | url = https://github.com/WebAssembly/testsuite.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parity-wasm" 3 | version = "0.45.1" 4 | authors = ["Nikolay Volf ", "Svyatoslav Nikolsky ", "Sergey Shulepov "] 5 | license = "MIT/Apache-2.0" 6 | readme = "README.md" 7 | repository = "https://github.com/paritytech/parity-wasm" 8 | homepage = "https://github.com/paritytech/parity-wasm" 9 | documentation = "https://docs.rs/parity-wasm" 10 | description = "WebAssembly low-level format library" 11 | keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"] 12 | categories = ["wasm", "parser-implementations"] 13 | include = ["src/**/*", "LICENSE-*", "README.md"] 14 | edition = "2021" 15 | rust-version = "1.56.1" 16 | 17 | [workspace] 18 | members = ["testsuite"] 19 | 20 | [dev-dependencies] 21 | time = "0.3" 22 | 23 | [features] 24 | default = ["std"] 25 | std = [] 26 | 27 | # Reduce stack usage for buffered read operations. 28 | # This feature is useful when integrating on resource constrained devices such as microcontroler 29 | # where the stack size is fixed (stacks do not grow) and limited to a few (k)bytes. 30 | reduced-stack-buffer = [] 31 | 32 | # 33 | # Features for enabling non-MVP proposals. 34 | # These features should be tested as part of Travis CI build. 35 | # 36 | 37 | # Atomics aka threading. 38 | # https://github.com/webassembly/threads/ 39 | atomics = [] 40 | 41 | # SIMD 42 | # https://github.com/WebAssembly/simd/ 43 | simd = [] 44 | 45 | # Sign-extension operators 46 | # https://github.com/WebAssembly/sign-extension-ops/ 47 | sign_ext = [] 48 | 49 | # Bulk-memory operators 50 | # https://github.com/WebAssembly/bulk-memory-operations/ 51 | bulk = [] 52 | 53 | # Multi-value 54 | # https://github.com/WebAssembly/multi-value/ 55 | multi_value = [] 56 | -------------------------------------------------------------------------------- /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) 2017-2018 Parity Technologies Limited 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. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parity-wasm 2 | 3 | > :warning: **This repository/crate is deprecated and unmaintained**: If you interested in maintaining 4 | this crate consider forking it. We can provide a link to the most promising forks here. 5 | 6 | Low-level WebAssembly format library. 7 | 8 | [![Build Status](https://travis-ci.org/paritytech/parity-wasm.svg?branch=master)](https://travis-ci.org/paritytech/parity-wasm) 9 | [![crates.io link](https://img.shields.io/crates/v/parity-wasm.svg)](https://crates.io/crates/parity-wasm) 10 | 11 | [Documentation](https://docs.rs/parity-wasm) 12 | 13 | ## Rust WebAssembly format serializing/deserializing 14 | 15 | Add to Cargo.toml 16 | 17 | ```toml 18 | [dependencies] 19 | parity-wasm = "0.42" 20 | ``` 21 | 22 | and then 23 | 24 | ```rust 25 | let module = parity_wasm::deserialize_file("./res/cases/v1/hello.wasm").unwrap(); 26 | assert!(module.code_section().is_some()); 27 | 28 | let code_section = module.code_section().unwrap(); // Part of the module with functions code 29 | 30 | println!("Function count in wasm file: {}", code_section.bodies().len()); 31 | ``` 32 | 33 | ## Wabt Test suite 34 | 35 | `parity-wasm` supports full [wasm testsuite](https://github.com/WebAssembly/testsuite), running asserts that involves deserialization. 36 | 37 | To run testsuite: 38 | 39 | - checkout with submodules (`git submodule update --init --recursive`) 40 | - run `cargo test --release --workspace` 41 | 42 | Decoder can be fuzzed with `cargo-fuzz` using [`wasm-opt`](https://github.com/WebAssembly/binaryen): 43 | 44 | - make sure you have all prerequisites to build `binaryen` and `cargo-fuzz` (`cmake` and a C++11 toolchain) 45 | - checkout with submodules (`git submodule update --init --recursive`) 46 | - install `cargo fuzz` subcommand with `cargo install cargo-fuzz` 47 | - set rustup to use a nightly toolchain, because `cargo fuzz` uses a rust compiler plugin: `rustup override set nightly` 48 | - run `cargo fuzz run deserialize` 49 | 50 | ## `no_std` crates 51 | 52 | This crate has a feature, `std`, that is enabled by default. To use this crate 53 | in a `no_std` context, add the following to your `Cargo.toml` (still requires allocator though): 54 | 55 | ```toml 56 | [dependencies] 57 | parity-wasm = { version = "0.41", default-features = false } 58 | ``` 59 | 60 | ## License 61 | 62 | `parity-wasm` is primarily distributed under the terms of both the MIT 63 | license and the Apache License (Version 2.0), at your choice. 64 | 65 | See LICENSE-APACHE, and LICENSE-MIT for details. 66 | 67 | ### Contribution 68 | 69 | Unless you explicitly state otherwise, any contribution intentionally submitted 70 | for inclusion in parity-wasm by you, as defined in the Apache-2.0 license, shall be 71 | dual licensed as above, without any additional terms or conditions. 72 | -------------------------------------------------------------------------------- /examples/bench-decoder.rs: -------------------------------------------------------------------------------- 1 | extern crate parity_wasm; 2 | extern crate time; 3 | 4 | use std::fs; 5 | use time::Instant; 6 | 7 | fn rate(file_name: &'static str, iterations: u64) { 8 | let file_size = fs::metadata(file_name) 9 | .unwrap_or_else(|_| panic!("{} to exist", file_name)) 10 | .len(); 11 | let mut total_ms = 0; 12 | 13 | for _ in 0..iterations { 14 | let start = Instant::now(); 15 | let _module = parity_wasm::deserialize_file(file_name); 16 | let end = Instant::now(); 17 | 18 | total_ms += (end - start).whole_milliseconds(); 19 | } 20 | 21 | println!( 22 | "Rate for {}: {} MB/s", 23 | file_name, 24 | (file_size as f64 * iterations as f64 / (1024*1024) as f64) / // total work megabytes 25 | (total_ms as f64 / 1000f64) // total seconds 26 | ); 27 | } 28 | 29 | fn main() { 30 | rate("./res/cases/v1/clang.wasm", 10); 31 | rate("./res/cases/v1/hello.wasm", 100); 32 | rate("./res/cases/v1/with_names.wasm", 100); 33 | } 34 | -------------------------------------------------------------------------------- /examples/build.rs: -------------------------------------------------------------------------------- 1 | // Simple example of how to use parity-wasm builder api. 2 | // Builder api introduced as a method for fast generation of 3 | // different small wasm modules. 4 | 5 | extern crate parity_wasm; 6 | 7 | use std::env; 8 | 9 | use parity_wasm::{builder, elements}; 10 | 11 | fn main() { 12 | // Example binary accepts one parameter which is the output file 13 | // where generated wasm module will be written at the end of execution 14 | let args = env::args().collect::>(); 15 | if args.len() != 2 { 16 | println!("Usage: {} output_file.wasm", args[0]); 17 | return 18 | } 19 | 20 | // Main entry for the builder api is the module function 21 | // It returns empty module builder structure which can be further 22 | // appended with various wasm artefacts 23 | let module = builder::module() 24 | // Here we append function to the builder 25 | // function() function returns a function builder attached 26 | // to the module builder. 27 | .function() 28 | // We describe signature for the function via signature() 29 | // function. In our simple example it's just one input 30 | // argument of type 'i32' without return value 31 | .signature() 32 | .with_param(elements::ValueType::I32) 33 | .build() 34 | // body() without any further arguments means that the body 35 | // of the function will be empty 36 | .body() 37 | .build() 38 | // This is the end of the function builder. When `build()` is 39 | // invoked, function builder returns original module builder 40 | // from which it was invoked 41 | .build() 42 | // And finally we finish our module builder to produce actual 43 | // wasm module. 44 | .build(); 45 | 46 | // Module structure can be serialzed to produce a valid wasm file 47 | parity_wasm::serialize_to_file(&args[1], module).unwrap(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/data.rs: -------------------------------------------------------------------------------- 1 | // This short example provides the utility to inspect 2 | // wasm file data section. 3 | 4 | extern crate parity_wasm; 5 | 6 | use std::env; 7 | 8 | fn main() { 9 | // Example executable takes one argument which must 10 | // refernce the existing file with a valid wasm module 11 | let args = env::args().collect::>(); 12 | if args.len() != 2 { 13 | println!("Usage: {} somefile.wasm", args[0]); 14 | return 15 | } 16 | 17 | // Here we load module using dedicated for this purpose 18 | // `deserialize_file` function (which works only with modules) 19 | let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); 20 | 21 | // We query module for data section. Note that not every valid 22 | // wasm module must contain a data section. So in case the provided 23 | // module does not contain data section, we panic with an error 24 | let data_section = module.data_section().expect("no data section in module"); 25 | 26 | // Printing the total count of data segments 27 | println!("Data segments: {}", data_section.entries().len()); 28 | 29 | for (index, entry) in data_section.entries().iter().enumerate() { 30 | // Printing the details info of each data segment 31 | // see `elements::DataSegment` for more properties 32 | // you can query 33 | println!(" Entry #{}", index); 34 | 35 | // This shows the initialization member of data segment 36 | // (expression which must resolve in the linear memory location). 37 | if let Some(offset) = entry.offset() { 38 | println!(" init: {}", offset.code()[0]); 39 | } 40 | 41 | // This shows the total length of the data segment in bytes. 42 | println!(" size: {}", entry.value().len()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/exports.rs: -------------------------------------------------------------------------------- 1 | // This examples allow to query all function exports of the 2 | // provided wasm module 3 | 4 | extern crate parity_wasm; 5 | 6 | use std::env::args; 7 | 8 | use parity_wasm::elements::{External, FunctionType, Internal, Module, Type}; 9 | 10 | // Auxillary function to resolve function type (signature) given it's callable index 11 | fn type_by_index(module: &Module, index: usize) -> FunctionType { 12 | // Demand that function and type section exist. Otherwise, fail with a 13 | // corresponding error. 14 | let function_section = module.function_section().expect("No function section found"); 15 | let type_section = module.type_section().expect("No type section found"); 16 | 17 | // This counts the number of _function_ imports listed by the module, excluding 18 | // the globals, since indexing for actual functions for `call` and `export` purposes 19 | // includes both imported and own functions. So we actualy need the imported function count 20 | // to resolve actual index of the given function in own functions list. 21 | let import_section_len: usize = match module.import_section() { 22 | Some(import) => import 23 | .entries() 24 | .iter() 25 | .filter(|entry| matches!(entry.external(), &External::Function(_))) 26 | .count(), 27 | None => 0, 28 | }; 29 | 30 | // Substract the value queried in the previous step from the provided index 31 | // to get own function index from which we can query type next. 32 | let function_index_in_section = index - import_section_len; 33 | 34 | // Query the own function given we have it's index 35 | let func_type_ref: usize = 36 | function_section.entries()[function_index_in_section].type_ref() as usize; 37 | 38 | // Finally, return function type (signature) 39 | match type_section.types()[func_type_ref] { 40 | Type::Function(ref func_type) => func_type.clone(), 41 | } 42 | } 43 | 44 | fn main() { 45 | // Example executable takes one argument which must 46 | // refernce the existing file with a valid wasm module 47 | let args: Vec<_> = args().collect(); 48 | if args.len() < 2 { 49 | println!("Prints export function names with and their types"); 50 | println!("Usage: {} ", args[0]); 51 | return 52 | } 53 | 54 | // Here we load module using dedicated for this purpose 55 | // `deserialize_file` function (which works only with modules) 56 | let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized"); 57 | 58 | // Query the export section from the loaded module. Note that not every 59 | // wasm module obliged to contain export section. So in case there is no 60 | // any export section, we panic with the corresponding error. 61 | let export_section = module.export_section().expect("No export section found"); 62 | 63 | // Process all exports, leaving only those which reference the internal function 64 | // of the wasm module 65 | let exports: Vec = export_section 66 | .entries() 67 | .iter() 68 | .filter_map(|entry| 69 | // This is match on export variant, which can be function, global,table or memory 70 | // We are interested only in functions for an example 71 | match *entry.internal() { 72 | // Return function export name (return by field() function and it's index) 73 | Internal::Function(index) => Some((entry.field(), index as usize)), 74 | _ => None 75 | }) 76 | // Another map to resolve function signature index given it's internal index and return 77 | // the printable string of the export 78 | .map(|(field, index)| format!("{:}: {:?}", field, type_by_index(&module, index).params())) 79 | .collect(); 80 | 81 | // Print the result 82 | for export in exports { 83 | println!("{:}", export); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/info.rs: -------------------------------------------------------------------------------- 1 | extern crate parity_wasm; 2 | 3 | use parity_wasm::elements::Section; 4 | use std::env; 5 | 6 | fn main() { 7 | let args = env::args().collect::>(); 8 | if args.len() != 2 { 9 | println!("Usage: {} somefile.wasm", args[0]); 10 | return 11 | } 12 | 13 | let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); 14 | 15 | println!("Module sections: {}", module.sections().len()); 16 | 17 | for section in module.sections() { 18 | match *section { 19 | Section::Import(ref import_section) => { 20 | println!(" Imports: {}", import_section.entries().len()); 21 | import_section 22 | .entries() 23 | .iter() 24 | .map(|e| println!(" {}.{}", e.module(), e.field())) 25 | .count(); 26 | }, 27 | Section::Export(ref exports_section) => { 28 | println!(" Exports: {}", exports_section.entries().len()); 29 | exports_section.entries().iter().map(|e| println!(" {}", e.field())).count(); 30 | }, 31 | Section::Function(ref function_section) => { 32 | println!(" Functions: {}", function_section.entries().len()); 33 | }, 34 | Section::Type(ref type_section) => { 35 | println!(" Types: {}", type_section.types().len()); 36 | }, 37 | Section::Global(ref globals_section) => { 38 | println!(" Globals: {}", globals_section.entries().len()); 39 | }, 40 | Section::Table(ref table_section) => { 41 | println!(" Tables: {}", table_section.entries().len()); 42 | }, 43 | Section::Memory(ref memory_section) => { 44 | println!(" Memories: {}", memory_section.entries().len()); 45 | }, 46 | Section::Data(ref data_section) if !data_section.entries().is_empty() => { 47 | let data = &data_section.entries()[0]; 48 | println!(" Data size: {}", data.value().len()); 49 | }, 50 | _ => {}, 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/inject.rs: -------------------------------------------------------------------------------- 1 | extern crate parity_wasm; 2 | 3 | use std::env; 4 | 5 | use parity_wasm::{builder, elements}; 6 | 7 | pub fn inject_nop(instructions: &mut elements::Instructions) { 8 | use parity_wasm::elements::Instruction::*; 9 | let instructions = instructions.elements_mut(); 10 | let mut position = 0; 11 | loop { 12 | let need_inject = matches!(&instructions[position], &Block(_) | &If(_)); 13 | if need_inject { 14 | instructions.insert(position + 1, Nop); 15 | } 16 | 17 | position += 1; 18 | if position >= instructions.len() { 19 | break 20 | } 21 | } 22 | } 23 | 24 | fn main() { 25 | let args = env::args().collect::>(); 26 | if args.len() != 3 { 27 | println!("Usage: {} input_file.wasm output_file.wasm", args[0]); 28 | return 29 | } 30 | 31 | let mut module = parity_wasm::deserialize_file(&args[1]).unwrap(); 32 | 33 | for section in module.sections_mut() { 34 | if let elements::Section::Code(ref mut code_section) = *section { 35 | for ref mut func_body in code_section.bodies_mut() { 36 | inject_nop(func_body.code_mut()); 37 | } 38 | } 39 | } 40 | 41 | let mut build = builder::from_module(module); 42 | let import_sig = build.push_signature( 43 | builder::signature().param().i32().param().i32().result().i32().build_sig(), 44 | ); 45 | let build = build.import().module("env").field("log").external().func(import_sig).build(); 46 | 47 | parity_wasm::serialize_to_file(&args[2], build.build()).unwrap(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/roundtrip.rs: -------------------------------------------------------------------------------- 1 | extern crate parity_wasm; 2 | 3 | use std::env; 4 | 5 | fn main() { 6 | let args = env::args().collect::>(); 7 | if args.len() != 3 { 8 | println!("Usage: {} in.wasm out.wasm", args[0]); 9 | return 10 | } 11 | 12 | let module = match parity_wasm::deserialize_file(&args[1]) 13 | .expect("Failed to load module") 14 | .parse_names() 15 | .and_then(|module| module.parse_reloc()) 16 | { 17 | Ok(m) => m, 18 | Err((errors, m)) => { 19 | for (index, error) in errors.into_iter() { 20 | println!("Custom section #{} parse error: {:?}", index, error); 21 | } 22 | m 23 | }, 24 | }; 25 | 26 | parity_wasm::serialize_to_file(&args[2], module).expect("Failed to write module"); 27 | } 28 | -------------------------------------------------------------------------------- /examples/show.rs: -------------------------------------------------------------------------------- 1 | extern crate parity_wasm; 2 | 3 | use std::env; 4 | 5 | fn main() { 6 | let args = env::args().collect::>(); 7 | if args.len() != 3 { 8 | println!("Usage: {} ", args[0]); 9 | return 10 | } 11 | 12 | let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); 13 | let function_index = args[2].parse::().expect("Failed to parse function index"); 14 | 15 | if module.code_section().is_none() { 16 | println!("no code in module!"); 17 | std::process::exit(1); 18 | } 19 | 20 | let sig = match module.function_section().unwrap().entries().get(function_index) { 21 | Some(s) => s, 22 | None => { 23 | println!("no such function in module!"); 24 | std::process::exit(1) 25 | }, 26 | }; 27 | 28 | let sig_type = &module.type_section().expect("No type section: module malformed").types() 29 | [sig.type_ref() as usize]; 30 | let code = 31 | &module.code_section().expect("Already checked, impossible").bodies()[function_index]; 32 | 33 | println!("signature: {:?}", sig_type); 34 | println!("code: "); 35 | for instruction in code.code().elements() { 36 | println!("{}", instruction); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "parity-wasm-fuzz" 4 | version = "0.0.1" 5 | authors = ["Pat Hickey phickey@fastly.com"] 6 | publish = false 7 | edition = "2021" 8 | rust-version = "1.56.1" 9 | 10 | [package.metadata] 11 | cargo-fuzz = true 12 | 13 | [dependencies.binaryen] 14 | version = "0.3.0" 15 | 16 | [dependencies.parity-wasm] 17 | path = ".." 18 | [dependencies.libfuzzer-sys] 19 | git = "https://github.com/rust-fuzz/libfuzzer-sys.git" 20 | 21 | [dependencies.mktemp] 22 | version = "0.3.1" 23 | 24 | # Prevent this from interfering with workspaces 25 | [workspace] 26 | members = ["."] 27 | 28 | [[bin]] 29 | name = "deserialize" 30 | path = "fuzz_targets/deserialize.rs" 31 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/deserialize.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #[macro_use] 3 | extern crate libfuzzer_sys; 4 | extern crate parity_wasm; 5 | extern crate binaryen; 6 | 7 | fuzz_target!(|data: &[u8]| { 8 | let binaryen_module = binaryen::tools::translate_to_fuzz(data); 9 | 10 | // enable binaryen's validation if in doubt. 11 | // assert!(binaryen_module.is_valid()); 12 | 13 | let wasm = binaryen_module.write(); 14 | 15 | let _module: parity_wasm::elements::Module = parity_wasm::deserialize_buffer(&wasm) 16 | .expect( 17 | "deserialize output of wasm-opt, indicating possible bug in deserializer", 18 | ); 19 | }); 20 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | _default: 2 | just --list 3 | 4 | # Run rustfmt and ensure the code meets the expectation of the checks in the CI 5 | format: 6 | cargo +nightly fmt --all 7 | 8 | # Run basic checks similar to what the CI does to ensure your code is fine 9 | check: 10 | cargo +nightly fmt --all -- --check 11 | cargo +stable clippy --all-targets --all-features -- -D warnings 12 | 13 | # Run the tests 14 | test: 15 | cargo test --all-features 16 | 17 | # So you are ready? This runs format, check and test 18 | ready: format check test 19 | -------------------------------------------------------------------------------- /res/cases/v1/accumulate_u8.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/accumulate_u8.wasm -------------------------------------------------------------------------------- /res/cases/v1/clang.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/clang.wasm -------------------------------------------------------------------------------- /res/cases/v1/const.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/const.wasm -------------------------------------------------------------------------------- /res/cases/v1/err-int-too-long.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/err-int-too-long.wasm -------------------------------------------------------------------------------- /res/cases/v1/err-leb-i32-too-long-2.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/err-leb-i32-too-long-2.wasm -------------------------------------------------------------------------------- /res/cases/v1/err-leb-i32-too-long.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/err-leb-i32-too-long.wasm -------------------------------------------------------------------------------- /res/cases/v1/err-leb-i64-too-long.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/err-leb-i64-too-long.wasm -------------------------------------------------------------------------------- /res/cases/v1/err-leb-u32-too-long.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/err-leb-u32-too-long.wasm -------------------------------------------------------------------------------- /res/cases/v1/err-return-type.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/err-return-type.wasm -------------------------------------------------------------------------------- /res/cases/v1/err-sections-after-custom.wasm: -------------------------------------------------------------------------------- 1 | asm`p 2 | A  3 |  p 4 | A  5 |  -------------------------------------------------------------------------------- /res/cases/v1/global_section.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/global_section.wasm -------------------------------------------------------------------------------- /res/cases/v1/hello.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/hello.wasm -------------------------------------------------------------------------------- /res/cases/v1/ifelse.wasm: -------------------------------------------------------------------------------- 1 | asm` 2 | A!AAA !  -------------------------------------------------------------------------------- /res/cases/v1/inc_i32.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/inc_i32.wasm -------------------------------------------------------------------------------- /res/cases/v1/names.wasm: -------------------------------------------------------------------------------- 1 | asm ``p'memorydoublerust_begin_unwind  2 |  At @ 3 | A #namedoublerust_begin_unwind -------------------------------------------------------------------------------- /res/cases/v1/names_with_imports.wasm: -------------------------------------------------------------------------------- 1 | asm` 2 |  nameabcdef -------------------------------------------------------------------------------- /res/cases/v1/offset.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/offset.wasm -------------------------------------------------------------------------------- /res/cases/v1/payload_len.wasm: -------------------------------------------------------------------------------- 1 | asm* 2 |   3 | 4 | -------------------------------------------------------------------------------- /res/cases/v1/peek_sample.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/peek_sample.wasm -------------------------------------------------------------------------------- /res/cases/v1/relocatable.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/relocatable.wasm -------------------------------------------------------------------------------- /res/cases/v1/start_add.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/start_add.wasm -------------------------------------------------------------------------------- /res/cases/v1/start_add_custom.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/start_add_custom.wasm -------------------------------------------------------------------------------- /res/cases/v1/start_mut.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/start_mut.wasm -------------------------------------------------------------------------------- /res/cases/v1/test.wasm: -------------------------------------------------------------------------------- 1 | asm{```~``````````~`````~ -------------------------------------------------------------------------------- /res/cases/v1/test2.wasm: -------------------------------------------------------------------------------- 1 | asm `` envabort -------------------------------------------------------------------------------- /res/cases/v1/test3.wasm: -------------------------------------------------------------------------------- 1 | asm`A 2 | #!# j$#AjApq$  -------------------------------------------------------------------------------- /res/cases/v1/test4.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/test4.wasm -------------------------------------------------------------------------------- /res/cases/v1/test5.rs: -------------------------------------------------------------------------------- 1 | #![feature(link_args)] 2 | #![feature(lang_items)] 3 | #![feature(start)] 4 | #![no_std] 5 | 6 | #[lang="panic_fmt"] 7 | extern fn panic_fmt(_: ::core::fmt::Arguments, _: &'static str, _: u32) -> ! { 8 | } 9 | 10 | #[lang = "eh_personality"] 11 | extern fn eh_personality() { 12 | } 13 | 14 | #[link_args = "-s EXPORTED_FUNCTIONS=['_hello_world']"] 15 | extern {} 16 | 17 | #[no_mangle] 18 | pub fn hello_world() -> isize { 19 | 45 + 99 20 | } 21 | 22 | #[start] 23 | fn main(argc: isize, argv: *const *const u8) -> isize { 24 | /* Intentionally left blank */ 25 | 0 26 | } 27 | -------------------------------------------------------------------------------- /res/cases/v1/test5.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/test5.wasm -------------------------------------------------------------------------------- /res/cases/v1/test6.rs: -------------------------------------------------------------------------------- 1 | #![feature(link_args)] 2 | #![feature(lang_items)] 3 | #![feature(start)] 4 | #![no_std] 5 | #![no_main] 6 | 7 | #[lang="panic_fmt"] 8 | extern fn panic_fmt(_: ::core::fmt::Arguments, _: &'static str, _: u32) { 9 | } 10 | 11 | #[lang = "eh_personality"] 12 | extern fn eh_personality() { 13 | } 14 | 15 | #[link_args = "-s WASM=1 -s NO_EXIT_RUNTIME=1 -s NO_FILESYSTEM=1"] 16 | extern {} 17 | 18 | #[no_mangle] 19 | pub fn hello_world() -> isize { 20 | 45 + 99 21 | } -------------------------------------------------------------------------------- /res/cases/v1/test6.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/test6.wasm -------------------------------------------------------------------------------- /res/cases/v1/two-mems.wasm: -------------------------------------------------------------------------------- 1 | asm -------------------------------------------------------------------------------- /res/cases/v1/varuint1_1.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/varuint1_1.wasm -------------------------------------------------------------------------------- /res/cases/v1/with_names.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/parity-wasm/b760a74d4b533adb01c52fb5a91d59ab87587097/res/cases/v1/with_names.wasm -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Basic 2 | hard_tabs = true 3 | max_width = 100 4 | use_small_heuristics = "Max" 5 | # Imports 6 | imports_granularity = "Crate" 7 | reorder_imports = true 8 | # Consistency 9 | newline_style = "Unix" 10 | # Misc 11 | chain_width = 80 12 | spaces_around_ranges = false 13 | binop_separator = "Back" 14 | reorder_impl_items = false 15 | match_arm_leading_pipes = "Preserve" 16 | match_arm_blocks = false 17 | match_block_trailing_comma = true 18 | trailing_comma = "Vertical" 19 | trailing_semicolon = false 20 | use_field_init_shorthand = true 21 | -------------------------------------------------------------------------------- /src/builder/code.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | invoke::{Identity, Invoke}, 3 | misc::{ValueTypeBuilder, ValueTypesBuilder}, 4 | }; 5 | use crate::elements; 6 | use alloc::vec::Vec; 7 | 8 | /// Signature template description 9 | pub enum Signature { 10 | TypeReference(u32), 11 | Inline(elements::FunctionType), 12 | } 13 | 14 | /// Signature builder 15 | pub struct SignatureBuilder { 16 | callback: F, 17 | signature: elements::FunctionType, 18 | } 19 | 20 | impl SignatureBuilder { 21 | /// New signature builder 22 | pub fn new() -> Self { 23 | SignatureBuilder::with_callback(Identity) 24 | } 25 | } 26 | 27 | impl Default for SignatureBuilder { 28 | fn default() -> Self { 29 | Self::new() 30 | } 31 | } 32 | impl SignatureBuilder 33 | where 34 | F: Invoke, 35 | { 36 | /// New builder with callback function specified 37 | pub fn with_callback(callback: F) -> Self { 38 | SignatureBuilder { callback, signature: elements::FunctionType::default() } 39 | } 40 | 41 | /// Add argument to signature builder 42 | pub fn with_param(mut self, value_type: elements::ValueType) -> Self { 43 | self.signature.params_mut().push(value_type); 44 | self 45 | } 46 | 47 | /// Add multiple arguments to signature builder 48 | pub fn with_params(mut self, value_types: I) -> Self 49 | where 50 | I: IntoIterator, 51 | { 52 | self.signature.params_mut().extend(value_types); 53 | self 54 | } 55 | 56 | /// Start build new argument 57 | pub fn param(self) -> ValueTypeBuilder { 58 | ValueTypeBuilder::with_callback(self) 59 | } 60 | 61 | /// Start build multiple arguments 62 | pub fn params(self) -> ValueTypesBuilder { 63 | ValueTypesBuilder::with_callback(self) 64 | } 65 | 66 | /// Add result to signature builder 67 | pub fn with_result(mut self, value_type: elements::ValueType) -> Self { 68 | self.signature.results_mut().push(value_type); 69 | self 70 | } 71 | 72 | /// Add multiple results to signature builder 73 | pub fn with_results(mut self, value_types: I) -> Self 74 | where 75 | I: IntoIterator, 76 | { 77 | self.signature.results_mut().extend(value_types); 78 | self 79 | } 80 | 81 | /// Start building new result 82 | pub fn result(self) -> ValueTypeBuilder { 83 | ValueTypeBuilder::with_callback(self) 84 | } 85 | 86 | /// Start building multiple results 87 | pub fn results(self) -> ValueTypeBuilder { 88 | ValueTypeBuilder::with_callback(self) 89 | } 90 | 91 | /// Finish current builder 92 | pub fn build(self) -> F::Result { 93 | self.callback.invoke(self.signature) 94 | } 95 | 96 | /// Finish current builder returning intermediate `Signature` struct 97 | pub fn build_sig(self) -> Signature { 98 | Signature::Inline(self.signature) 99 | } 100 | } 101 | 102 | impl Invoke for SignatureBuilder 103 | where 104 | F: Invoke, 105 | I: IntoIterator, 106 | { 107 | type Result = Self; 108 | 109 | fn invoke(self, args: I) -> Self { 110 | self.with_params(args) 111 | } 112 | } 113 | 114 | impl Invoke for SignatureBuilder 115 | where 116 | F: Invoke, 117 | { 118 | type Result = Self; 119 | 120 | fn invoke(self, arg: elements::ValueType) -> Self { 121 | self.with_result(arg) 122 | } 123 | } 124 | 125 | /// Type (signature) reference builder (for function/import/indirect call) 126 | pub struct TypeRefBuilder { 127 | callback: F, 128 | type_ref: u32, 129 | } 130 | 131 | impl TypeRefBuilder 132 | where 133 | F: Invoke, 134 | { 135 | /// New builder chained with specified callback 136 | pub fn with_callback(callback: F) -> Self { 137 | TypeRefBuilder { callback, type_ref: 0 } 138 | } 139 | 140 | /// Set/override of type reference 141 | pub fn val(mut self, val: u32) -> Self { 142 | self.type_ref = val; 143 | self 144 | } 145 | 146 | /// Finish current builder 147 | pub fn build(self) -> F::Result { 148 | self.callback.invoke(self.type_ref) 149 | } 150 | } 151 | 152 | /// Multiple signatures builder 153 | pub struct SignaturesBuilder { 154 | callback: F, 155 | section: Vec, 156 | } 157 | 158 | impl SignaturesBuilder { 159 | /// New empty functions section builder 160 | pub fn new() -> Self { 161 | SignaturesBuilder::with_callback(Identity) 162 | } 163 | } 164 | 165 | impl Default for SignaturesBuilder { 166 | fn default() -> Self { 167 | Self::new() 168 | } 169 | } 170 | 171 | impl SignaturesBuilder { 172 | /// New builder chained with specified callback 173 | pub fn with_callback(callback: F) -> Self { 174 | SignaturesBuilder { callback, section: Vec::new() } 175 | } 176 | 177 | /// Push new signature into the builder output 178 | pub fn with_signature(mut self, signature: Signature) -> Self { 179 | self.section.push(signature); 180 | self 181 | } 182 | 183 | /// Start building new signature with `TypeRefBuilder` 184 | pub fn type_ref(self) -> TypeRefBuilder { 185 | TypeRefBuilder::with_callback(self) 186 | } 187 | } 188 | 189 | impl SignaturesBuilder 190 | where 191 | F: Invoke, 192 | { 193 | /// Start building new signature with dedicated builder 194 | pub fn signature(self) -> SignatureBuilder { 195 | SignatureBuilder::with_callback(self) 196 | } 197 | } 198 | 199 | impl Invoke for SignaturesBuilder { 200 | type Result = Self; 201 | 202 | fn invoke(self, signature: elements::FunctionType) -> Self { 203 | self.with_signature(Signature::Inline(signature)) 204 | } 205 | } 206 | 207 | impl Invoke for SignaturesBuilder { 208 | type Result = Self; 209 | 210 | fn invoke(self, type_ref: u32) -> Self { 211 | self.with_signature(Signature::TypeReference(type_ref)) 212 | } 213 | } 214 | 215 | impl SignaturesBuilder 216 | where 217 | F: Invoke, 218 | { 219 | /// Finalize builder spawning element 220 | pub fn build(self) -> F::Result { 221 | let mut result = elements::FunctionSection::default(); 222 | for f in self.section.into_iter() { 223 | if let Signature::TypeReference(type_ref) = f { 224 | result.entries_mut().push(elements::Func::new(type_ref)); 225 | } else { 226 | unreachable!(); // never possible with current generics impl-s 227 | } 228 | } 229 | self.callback.invoke(result) 230 | } 231 | } 232 | 233 | /// Signature bindings 234 | pub type SignatureBindings = Vec; 235 | 236 | impl SignaturesBuilder 237 | where 238 | F: Invoke, 239 | { 240 | /// Bind signature list 241 | pub fn bind(self) -> F::Result { 242 | self.callback.invoke(self.section) 243 | } 244 | } 245 | 246 | /// Function body (code) builder 247 | pub struct FuncBodyBuilder { 248 | callback: F, 249 | body: elements::FuncBody, 250 | } 251 | 252 | impl FuncBodyBuilder { 253 | /// New body (code) builder given the chain callback 254 | pub fn with_callback(callback: F) -> Self { 255 | FuncBodyBuilder { 256 | callback, 257 | body: elements::FuncBody::new(Vec::new(), elements::Instructions::empty()), 258 | } 259 | } 260 | } 261 | 262 | impl FuncBodyBuilder 263 | where 264 | F: Invoke, 265 | { 266 | /// Set/override entirely with FuncBody struct 267 | pub fn with_func(mut self, func: elements::FuncBody) -> Self { 268 | self.body = func; 269 | self 270 | } 271 | 272 | /// Extend function local list with new entries 273 | pub fn with_locals(mut self, locals: I) -> Self 274 | where 275 | I: IntoIterator, 276 | { 277 | self.body.locals_mut().extend(locals); 278 | self 279 | } 280 | 281 | /// Set code of the function 282 | pub fn with_instructions(mut self, instructions: elements::Instructions) -> Self { 283 | *self.body.code_mut() = instructions; 284 | self 285 | } 286 | 287 | /// Finish current builder spawning resulting struct 288 | pub fn build(self) -> F::Result { 289 | self.callback.invoke(self.body) 290 | } 291 | } 292 | 293 | /// Function definition (extended structure to specify function entirely, incl. signature, mainness and code) 294 | pub struct FunctionDefinition { 295 | /// Is this function is start function 296 | pub is_main: bool, 297 | /// Signature description 298 | pub signature: Signature, 299 | /// Body (code) of the function 300 | pub code: elements::FuncBody, 301 | } 302 | 303 | impl Default for FunctionDefinition { 304 | fn default() -> Self { 305 | FunctionDefinition { 306 | is_main: false, 307 | signature: Signature::TypeReference(0), 308 | code: elements::FuncBody::empty(), 309 | } 310 | } 311 | } 312 | 313 | /// Function definition builder 314 | pub struct FunctionBuilder { 315 | callback: F, 316 | func: FunctionDefinition, 317 | } 318 | 319 | impl FunctionBuilder { 320 | /// New function builder 321 | pub fn new() -> Self { 322 | FunctionBuilder::with_callback(Identity) 323 | } 324 | } 325 | 326 | impl Default for FunctionBuilder { 327 | fn default() -> Self { 328 | Self::new() 329 | } 330 | } 331 | 332 | impl FunctionBuilder 333 | where 334 | F: Invoke, 335 | { 336 | /// New function builder with chained callback 337 | pub fn with_callback(callback: F) -> Self { 338 | FunctionBuilder { callback, func: Default::default() } 339 | } 340 | 341 | /// Set that this function is main entry point 342 | pub fn main(mut self) -> Self { 343 | self.func.is_main = true; 344 | self 345 | } 346 | 347 | /// Start signature builder of the function 348 | pub fn signature(self) -> SignatureBuilder { 349 | SignatureBuilder::with_callback(self) 350 | } 351 | 352 | /// Override current signature entirely with new one from known struct 353 | pub fn with_signature(mut self, signature: Signature) -> Self { 354 | self.func.signature = signature; 355 | self 356 | } 357 | 358 | /// Start code (body) builder 359 | pub fn body(self) -> FuncBodyBuilder { 360 | FuncBodyBuilder::with_callback(self) 361 | } 362 | 363 | /// Set body (code) for this function 364 | pub fn with_body(mut self, body: elements::FuncBody) -> Self { 365 | self.func.code = body; 366 | self 367 | } 368 | 369 | /// Finalize current builder spawning resulting struct in the callback 370 | pub fn build(self) -> F::Result { 371 | self.callback.invoke(self.func) 372 | } 373 | } 374 | 375 | impl Invoke for FunctionBuilder 376 | where 377 | F: Invoke, 378 | { 379 | type Result = Self; 380 | 381 | fn invoke(self, signature: elements::FunctionType) -> Self { 382 | self.with_signature(Signature::Inline(signature)) 383 | } 384 | } 385 | 386 | impl Invoke for FunctionBuilder 387 | where 388 | F: Invoke, 389 | { 390 | type Result = Self; 391 | 392 | fn invoke(self, type_ref: u32) -> Self { 393 | self.with_signature(Signature::TypeReference(type_ref)) 394 | } 395 | } 396 | 397 | impl Invoke for FunctionBuilder 398 | where 399 | F: Invoke, 400 | { 401 | type Result = Self; 402 | 403 | fn invoke(self, body: elements::FuncBody) -> Self::Result { 404 | self.with_body(body) 405 | } 406 | } 407 | 408 | /// New builder of signature list 409 | pub fn signatures() -> SignaturesBuilder { 410 | SignaturesBuilder::new() 411 | } 412 | 413 | /// New signature builder 414 | pub fn signature() -> SignatureBuilder { 415 | SignatureBuilder::new() 416 | } 417 | 418 | /// New builder of function (signature & body) 419 | pub fn function() -> FunctionBuilder { 420 | FunctionBuilder::new() 421 | } 422 | 423 | #[cfg(test)] 424 | mod tests { 425 | 426 | use super::{function, signatures}; 427 | use crate::elements; 428 | 429 | #[test] 430 | fn example() { 431 | let result = signatures().type_ref().val(1).build().build(); 432 | 433 | assert_eq!(result.entries().len(), 1); 434 | 435 | let result = signatures() 436 | .signature() 437 | .param() 438 | .i32() 439 | .param() 440 | .i32() 441 | .result() 442 | .i64() 443 | .build() 444 | .bind(); 445 | 446 | assert_eq!(result.len(), 1); 447 | } 448 | 449 | #[test] 450 | fn func_example() { 451 | let func = function() 452 | .signature() 453 | .param() 454 | .i32() 455 | .result() 456 | .i32() 457 | .build() 458 | .body() 459 | .with_instructions(elements::Instructions::empty()) 460 | .build() 461 | .build(); 462 | 463 | assert_eq!(func.code.locals().len(), 0); 464 | assert_eq!(func.code.code().elements().len(), 1); 465 | } 466 | 467 | #[test] 468 | fn func_example_multi_result() { 469 | let func = function() 470 | .signature() 471 | .param() 472 | .i32() 473 | .result() 474 | .i32() 475 | .result() 476 | .i32() 477 | .build() 478 | .body() 479 | .with_instructions(elements::Instructions::empty()) 480 | .build() 481 | .build(); 482 | 483 | assert_eq!(func.code.locals().len(), 0); 484 | assert_eq!(func.code.code().elements().len(), 1); 485 | } 486 | } 487 | -------------------------------------------------------------------------------- /src/builder/data.rs: -------------------------------------------------------------------------------- 1 | use super::invoke::{Identity, Invoke}; 2 | use crate::elements; 3 | use alloc::vec::Vec; 4 | 5 | /// Data segment builder 6 | pub struct DataSegmentBuilder { 7 | callback: F, 8 | // todo: add mapper once multiple memory refs possible 9 | mem_index: u32, 10 | offset: elements::InitExpr, 11 | value: Vec, 12 | } 13 | 14 | impl DataSegmentBuilder { 15 | /// New data segment builder 16 | pub fn new() -> Self { 17 | DataSegmentBuilder::with_callback(Identity) 18 | } 19 | } 20 | 21 | impl Default for DataSegmentBuilder { 22 | fn default() -> Self { 23 | Self::new() 24 | } 25 | } 26 | 27 | impl DataSegmentBuilder { 28 | /// New data segment builder inside the chain context 29 | pub fn with_callback(callback: F) -> Self { 30 | DataSegmentBuilder { 31 | callback, 32 | mem_index: 0, 33 | offset: elements::InitExpr::empty(), 34 | value: Vec::new(), 35 | } 36 | } 37 | 38 | /// Set offset initialization instruction. `End` instruction will be added automatically. 39 | pub fn offset(mut self, instruction: elements::Instruction) -> Self { 40 | self.offset = elements::InitExpr::new(vec![instruction, elements::Instruction::End]); 41 | self 42 | } 43 | 44 | /// Set the bytes value of the segment 45 | pub fn value(mut self, value: Vec) -> Self { 46 | self.value = value; 47 | self 48 | } 49 | } 50 | 51 | impl DataSegmentBuilder 52 | where 53 | F: Invoke, 54 | { 55 | /// Finish current builder, spawning resulting struct 56 | pub fn build(self) -> F::Result { 57 | self.callback.invoke(elements::DataSegment::new( 58 | self.mem_index, 59 | Some(self.offset), 60 | self.value, 61 | )) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/builder/export.rs: -------------------------------------------------------------------------------- 1 | use super::invoke::{Identity, Invoke}; 2 | use crate::elements; 3 | use alloc::{borrow::ToOwned, string::String}; 4 | 5 | /// Export entry builder 6 | pub struct ExportBuilder { 7 | callback: F, 8 | field: String, 9 | binding: elements::Internal, 10 | } 11 | 12 | impl ExportBuilder { 13 | /// New export builder 14 | pub fn new() -> Self { 15 | ExportBuilder::with_callback(Identity) 16 | } 17 | } 18 | 19 | impl Default for ExportBuilder { 20 | fn default() -> Self { 21 | Self::new() 22 | } 23 | } 24 | 25 | impl ExportBuilder { 26 | /// New export entry builder in the specified chained context 27 | pub fn with_callback(callback: F) -> Self { 28 | ExportBuilder { callback, field: String::new(), binding: elements::Internal::Function(0) } 29 | } 30 | 31 | /// Set the field name of the export entry 32 | pub fn field(mut self, field: &str) -> Self { 33 | self.field = field.to_owned(); 34 | self 35 | } 36 | 37 | /// Specify the internal module mapping for this entry 38 | pub fn with_internal(mut self, external: elements::Internal) -> Self { 39 | self.binding = external; 40 | self 41 | } 42 | 43 | /// Start the internal builder for this export entry 44 | pub fn internal(self) -> ExportInternalBuilder { 45 | ExportInternalBuilder::with_callback(self) 46 | } 47 | } 48 | 49 | impl ExportBuilder 50 | where 51 | F: Invoke, 52 | { 53 | /// Finalize export entry builder spawning the resulting struct 54 | pub fn build(self) -> F::Result { 55 | self.callback.invoke(elements::ExportEntry::new(self.field, self.binding)) 56 | } 57 | } 58 | 59 | impl Invoke for ExportBuilder { 60 | type Result = Self; 61 | fn invoke(self, val: elements::Internal) -> Self { 62 | self.with_internal(val) 63 | } 64 | } 65 | 66 | /// Internal mapping builder for export entry 67 | pub struct ExportInternalBuilder { 68 | callback: F, 69 | binding: elements::Internal, 70 | } 71 | 72 | impl ExportInternalBuilder 73 | where 74 | F: Invoke, 75 | { 76 | /// New export entry internal mapping for the chained context 77 | pub fn with_callback(callback: F) -> Self { 78 | ExportInternalBuilder { callback, binding: elements::Internal::Function(0) } 79 | } 80 | 81 | /// Map to function by index 82 | pub fn func(mut self, index: u32) -> F::Result { 83 | self.binding = elements::Internal::Function(index); 84 | self.callback.invoke(self.binding) 85 | } 86 | 87 | /// Map to memory 88 | pub fn memory(mut self, index: u32) -> F::Result { 89 | self.binding = elements::Internal::Memory(index); 90 | self.callback.invoke(self.binding) 91 | } 92 | 93 | /// Map to table 94 | pub fn table(mut self, index: u32) -> F::Result { 95 | self.binding = elements::Internal::Table(index); 96 | self.callback.invoke(self.binding) 97 | } 98 | 99 | /// Map to global 100 | pub fn global(mut self, index: u32) -> F::Result { 101 | self.binding = elements::Internal::Global(index); 102 | self.callback.invoke(self.binding) 103 | } 104 | } 105 | 106 | /// New builder for export entry 107 | pub fn export() -> ExportBuilder { 108 | ExportBuilder::new() 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use super::export; 114 | 115 | #[test] 116 | fn example() { 117 | let entry = export().field("memory").internal().memory(0).build(); 118 | assert_eq!(entry.field(), "memory"); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/builder/global.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | invoke::{Identity, Invoke}, 3 | misc::ValueTypeBuilder, 4 | }; 5 | 6 | use crate::elements; 7 | 8 | /// Global builder 9 | pub struct GlobalBuilder { 10 | callback: F, 11 | value_type: elements::ValueType, 12 | is_mutable: bool, 13 | init_expr: elements::InitExpr, 14 | } 15 | 16 | impl GlobalBuilder { 17 | /// New global builder 18 | pub fn new() -> Self { 19 | GlobalBuilder::with_callback(Identity) 20 | } 21 | } 22 | 23 | impl Default for GlobalBuilder { 24 | fn default() -> Self { 25 | Self::new() 26 | } 27 | } 28 | 29 | impl GlobalBuilder { 30 | /// New global builder with callback (in chained context) 31 | pub fn with_callback(callback: F) -> Self { 32 | GlobalBuilder { 33 | callback, 34 | value_type: elements::ValueType::I32, 35 | init_expr: elements::InitExpr::empty(), 36 | is_mutable: false, 37 | } 38 | } 39 | 40 | /// Set/override resulting global type 41 | pub fn with_type(mut self, value_type: elements::ValueType) -> Self { 42 | self.value_type = value_type; 43 | self 44 | } 45 | 46 | /// Set mutabilty to true 47 | pub fn mutable(mut self) -> Self { 48 | self.is_mutable = true; 49 | self 50 | } 51 | 52 | /// Set initialization expression instruction for this global (`end` instruction will be added automatically) 53 | pub fn init_expr(mut self, instruction: elements::Instruction) -> Self { 54 | self.init_expr = elements::InitExpr::new(vec![instruction, elements::Instruction::End]); 55 | self 56 | } 57 | 58 | /// Start value type builder 59 | pub fn value_type(self) -> ValueTypeBuilder { 60 | ValueTypeBuilder::with_callback(self) 61 | } 62 | } 63 | 64 | impl GlobalBuilder 65 | where 66 | F: Invoke, 67 | { 68 | /// Finalize current builder spawning resulting struct 69 | pub fn build(self) -> F::Result { 70 | self.callback.invoke(elements::GlobalEntry::new( 71 | elements::GlobalType::new(self.value_type, self.is_mutable), 72 | self.init_expr, 73 | )) 74 | } 75 | } 76 | 77 | impl Invoke for GlobalBuilder { 78 | type Result = Self; 79 | fn invoke(self, the_type: elements::ValueType) -> Self { 80 | self.with_type(the_type) 81 | } 82 | } 83 | 84 | /// New builder for export entry 85 | pub fn global() -> GlobalBuilder { 86 | GlobalBuilder::new() 87 | } 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::global; 92 | use crate::elements; 93 | 94 | #[test] 95 | fn example() { 96 | let entry = global().value_type().i32().build(); 97 | assert_eq!(entry.global_type().content_type(), elements::ValueType::I32); 98 | assert!(!entry.global_type().is_mutable()); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/builder/import.rs: -------------------------------------------------------------------------------- 1 | use super::invoke::{Identity, Invoke}; 2 | use crate::elements; 3 | use alloc::{borrow::ToOwned, string::String}; 4 | 5 | /// Import builder 6 | pub struct ImportBuilder { 7 | callback: F, 8 | module: String, 9 | field: String, 10 | binding: elements::External, 11 | } 12 | 13 | impl ImportBuilder { 14 | /// New import builder 15 | pub fn new() -> Self { 16 | ImportBuilder::with_callback(Identity) 17 | } 18 | } 19 | 20 | impl Default for ImportBuilder { 21 | fn default() -> Self { 22 | Self::new() 23 | } 24 | } 25 | 26 | impl ImportBuilder { 27 | /// New import builder with callback (in chained context) 28 | pub fn with_callback(callback: F) -> Self { 29 | ImportBuilder { 30 | callback, 31 | module: String::new(), 32 | field: String::new(), 33 | binding: elements::External::Function(0), 34 | } 35 | } 36 | 37 | /// Set/override module name 38 | pub fn module(mut self, name: &str) -> Self { 39 | self.module = name.to_owned(); 40 | self 41 | } 42 | 43 | /// Set/override field name 44 | pub fn field(mut self, name: &str) -> Self { 45 | self.field = name.to_owned(); 46 | self 47 | } 48 | 49 | /// Set/override both module name and field name 50 | pub fn path(self, module: &str, field: &str) -> Self { 51 | self.module(module).field(field) 52 | } 53 | 54 | /// Set/override external mapping for this import 55 | pub fn with_external(mut self, external: elements::External) -> Self { 56 | self.binding = external; 57 | self 58 | } 59 | 60 | /// Start new external mapping builder 61 | pub fn external(self) -> ImportExternalBuilder { 62 | ImportExternalBuilder::with_callback(self) 63 | } 64 | } 65 | 66 | impl ImportBuilder 67 | where 68 | F: Invoke, 69 | { 70 | /// Finalize current builder spawning the resulting struct 71 | pub fn build(self) -> F::Result { 72 | self.callback 73 | .invoke(elements::ImportEntry::new(self.module, self.field, self.binding)) 74 | } 75 | } 76 | 77 | impl Invoke for ImportBuilder { 78 | type Result = Self; 79 | fn invoke(self, val: elements::External) -> Self { 80 | self.with_external(val) 81 | } 82 | } 83 | 84 | /// Import to external mapping builder 85 | pub struct ImportExternalBuilder { 86 | callback: F, 87 | binding: elements::External, 88 | } 89 | 90 | impl ImportExternalBuilder 91 | where 92 | F: Invoke, 93 | { 94 | /// New import to external mapping builder with callback (in chained context) 95 | pub fn with_callback(callback: F) -> Self { 96 | ImportExternalBuilder { callback, binding: elements::External::Function(0) } 97 | } 98 | 99 | /// Function mapping with type reference 100 | pub fn func(mut self, index: u32) -> F::Result { 101 | self.binding = elements::External::Function(index); 102 | self.callback.invoke(self.binding) 103 | } 104 | 105 | /// Memory mapping with specified limits 106 | pub fn memory(mut self, min: u32, max: Option) -> F::Result { 107 | self.binding = elements::External::Memory(elements::MemoryType::new(min, max)); 108 | self.callback.invoke(self.binding) 109 | } 110 | 111 | /// Table mapping with specified limits 112 | pub fn table(mut self, min: u32, max: Option) -> F::Result { 113 | self.binding = elements::External::Table(elements::TableType::new(min, max)); 114 | self.callback.invoke(self.binding) 115 | } 116 | 117 | /// Global mapping with speciifed type and mutability 118 | pub fn global(mut self, value_type: elements::ValueType, is_mut: bool) -> F::Result { 119 | self.binding = elements::External::Global(elements::GlobalType::new(value_type, is_mut)); 120 | self.callback.invoke(self.binding) 121 | } 122 | } 123 | 124 | /// New builder for import entry 125 | pub fn import() -> ImportBuilder { 126 | ImportBuilder::new() 127 | } 128 | 129 | #[cfg(test)] 130 | mod tests { 131 | use super::import; 132 | 133 | #[test] 134 | fn example() { 135 | let entry = 136 | import().module("env").field("memory").external().memory(256, Some(256)).build(); 137 | 138 | assert_eq!(entry.module(), "env"); 139 | assert_eq!(entry.field(), "memory"); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/builder/invoke.rs: -------------------------------------------------------------------------------- 1 | //! invoke helper 2 | 3 | /// Helper trait to allow chaining 4 | pub trait Invoke { 5 | type Result; 6 | 7 | fn invoke(self, arg: A) -> Self::Result; 8 | } 9 | 10 | /// Identity chain element 11 | pub struct Identity; 12 | 13 | impl Invoke for Identity { 14 | type Result = A; 15 | 16 | fn invoke(self, arg: A) -> A { 17 | arg 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/builder/memory.rs: -------------------------------------------------------------------------------- 1 | use super::invoke::{Identity, Invoke}; 2 | use crate::elements; 3 | use alloc::vec::Vec; 4 | 5 | /// Memory definition struct 6 | #[derive(Debug, PartialEq)] 7 | pub struct MemoryDefinition { 8 | /// Minimum memory size 9 | pub min: u32, 10 | /// Maximum memory size 11 | pub max: Option, 12 | /// Memory data segments (static regions) 13 | pub data: Vec, 14 | } 15 | 16 | /// Memory static region entry definition 17 | #[derive(Debug, PartialEq)] 18 | pub struct MemoryDataDefinition { 19 | /// Segment initialization expression for offset 20 | pub offset: elements::InitExpr, 21 | /// Raw bytes of static region 22 | pub values: Vec, 23 | } 24 | 25 | /// Memory and static regions builder 26 | pub struct MemoryBuilder { 27 | callback: F, 28 | memory: MemoryDefinition, 29 | } 30 | 31 | impl MemoryBuilder { 32 | /// New memory builder 33 | pub fn new() -> Self { 34 | MemoryBuilder::with_callback(Identity) 35 | } 36 | } 37 | 38 | impl Default for MemoryBuilder { 39 | fn default() -> Self { 40 | Self::new() 41 | } 42 | } 43 | 44 | impl MemoryBuilder 45 | where 46 | F: Invoke, 47 | { 48 | /// New memory builder with callback (in chained context) 49 | pub fn with_callback(callback: F) -> Self { 50 | MemoryBuilder { callback, memory: Default::default() } 51 | } 52 | 53 | /// Set/override minimum size 54 | pub fn with_min(mut self, min: u32) -> Self { 55 | self.memory.min = min; 56 | self 57 | } 58 | 59 | /// Set/override maximum size 60 | pub fn with_max(mut self, max: Option) -> Self { 61 | self.memory.max = max; 62 | self 63 | } 64 | 65 | /// Push new static region with initialized offset expression and raw bytes 66 | pub fn with_data(mut self, index: u32, values: Vec) -> Self { 67 | self.memory.data.push(MemoryDataDefinition { 68 | offset: elements::InitExpr::new(vec![ 69 | elements::Instruction::I32Const(index as i32), 70 | elements::Instruction::End, 71 | ]), 72 | values, 73 | }); 74 | self 75 | } 76 | 77 | /// Finalize current builder, spawning resulting struct 78 | pub fn build(self) -> F::Result { 79 | self.callback.invoke(self.memory) 80 | } 81 | } 82 | 83 | impl Default for MemoryDefinition { 84 | fn default() -> Self { 85 | MemoryDefinition { min: 1, max: None, data: Vec::new() } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/builder/misc.rs: -------------------------------------------------------------------------------- 1 | use super::invoke::{Identity, Invoke}; 2 | use crate::elements; 3 | use alloc::vec::Vec; 4 | 5 | pub struct ValueTypeBuilder { 6 | callback: F, 7 | } 8 | 9 | impl ValueTypeBuilder 10 | where 11 | F: Invoke, 12 | { 13 | pub fn with_callback(callback: F) -> Self { 14 | ValueTypeBuilder { callback } 15 | } 16 | 17 | pub fn i32(self) -> F::Result { 18 | self.callback.invoke(elements::ValueType::I32) 19 | } 20 | 21 | pub fn i64(self) -> F::Result { 22 | self.callback.invoke(elements::ValueType::I64) 23 | } 24 | 25 | pub fn f32(self) -> F::Result { 26 | self.callback.invoke(elements::ValueType::F32) 27 | } 28 | 29 | pub fn f64(self) -> F::Result { 30 | self.callback.invoke(elements::ValueType::F64) 31 | } 32 | } 33 | 34 | pub struct ValueTypesBuilder { 35 | callback: F, 36 | value_types: Vec, 37 | } 38 | 39 | impl ValueTypesBuilder 40 | where 41 | F: Invoke>, 42 | { 43 | pub fn with_callback(callback: F) -> Self { 44 | ValueTypesBuilder { callback, value_types: Vec::new() } 45 | } 46 | 47 | pub fn i32(mut self) -> Self { 48 | self.value_types.push(elements::ValueType::I32); 49 | self 50 | } 51 | 52 | pub fn i64(mut self) -> Self { 53 | self.value_types.push(elements::ValueType::I64); 54 | self 55 | } 56 | 57 | pub fn f32(mut self) -> Self { 58 | self.value_types.push(elements::ValueType::F32); 59 | self 60 | } 61 | 62 | pub fn f64(mut self) -> Self { 63 | self.value_types.push(elements::ValueType::F64); 64 | self 65 | } 66 | 67 | pub fn build(self) -> F::Result { 68 | self.callback.invoke(self.value_types) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/builder/mod.rs: -------------------------------------------------------------------------------- 1 | //! Various builders to generate/alter wasm components 2 | 3 | mod code; 4 | mod data; 5 | mod export; 6 | mod global; 7 | mod import; 8 | mod invoke; 9 | mod memory; 10 | mod misc; 11 | mod module; 12 | mod table; 13 | 14 | pub use self::{ 15 | code::{ 16 | function, signature, signatures, FuncBodyBuilder, FunctionBuilder, FunctionDefinition, 17 | SignatureBuilder, SignaturesBuilder, TypeRefBuilder, 18 | }, 19 | data::DataSegmentBuilder, 20 | export::{export, ExportBuilder, ExportInternalBuilder}, 21 | global::{global, GlobalBuilder}, 22 | import::{import, ImportBuilder}, 23 | invoke::Identity, 24 | memory::MemoryBuilder, 25 | module::{from_module, module, CodeLocation, ModuleBuilder}, 26 | table::{TableBuilder, TableDefinition, TableEntryDefinition}, 27 | }; 28 | -------------------------------------------------------------------------------- /src/builder/module.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | code::{self, FunctionBuilder, SignaturesBuilder}, 3 | data, export, global, import, 4 | invoke::{Identity, Invoke}, 5 | memory::{self, MemoryBuilder}, 6 | table::{self, TableBuilder}, 7 | }; 8 | use crate::elements; 9 | use alloc::vec::Vec; 10 | 11 | /// Module builder 12 | pub struct ModuleBuilder { 13 | callback: F, 14 | module: ModuleScaffold, 15 | } 16 | 17 | /// Location of the internal module function 18 | pub struct CodeLocation { 19 | /// Location (index in 'functions' section) of the signature 20 | pub signature: u32, 21 | /// Location (index in the 'code' section) of the body 22 | pub body: u32, 23 | } 24 | 25 | #[derive(Default, PartialEq)] 26 | struct ModuleScaffold { 27 | pub types: elements::TypeSection, 28 | pub import: elements::ImportSection, 29 | pub functions: elements::FunctionSection, 30 | pub table: elements::TableSection, 31 | pub memory: elements::MemorySection, 32 | pub global: elements::GlobalSection, 33 | pub export: elements::ExportSection, 34 | pub start: Option, 35 | pub element: elements::ElementSection, 36 | pub code: elements::CodeSection, 37 | pub data: elements::DataSection, 38 | pub other: Vec, 39 | } 40 | 41 | impl From for ModuleScaffold { 42 | fn from(module: elements::Module) -> Self { 43 | let mut types: Option = None; 44 | let mut import: Option = None; 45 | let mut funcs: Option = None; 46 | let mut table: Option = None; 47 | let mut memory: Option = None; 48 | let mut global: Option = None; 49 | let mut export: Option = None; 50 | let mut start: Option = None; 51 | let mut element: Option = None; 52 | let mut code: Option = None; 53 | let mut data: Option = None; 54 | 55 | let mut other = Vec::new(); 56 | let mut sections = module.into_sections(); 57 | while let Some(section) = sections.pop() { 58 | match section { 59 | elements::Section::Type(sect) => { 60 | types = Some(sect); 61 | }, 62 | elements::Section::Import(sect) => { 63 | import = Some(sect); 64 | }, 65 | elements::Section::Function(sect) => { 66 | funcs = Some(sect); 67 | }, 68 | elements::Section::Table(sect) => { 69 | table = Some(sect); 70 | }, 71 | elements::Section::Memory(sect) => { 72 | memory = Some(sect); 73 | }, 74 | elements::Section::Global(sect) => { 75 | global = Some(sect); 76 | }, 77 | elements::Section::Export(sect) => { 78 | export = Some(sect); 79 | }, 80 | elements::Section::Start(index) => { 81 | start = Some(index); 82 | }, 83 | elements::Section::Element(sect) => { 84 | element = Some(sect); 85 | }, 86 | elements::Section::Code(sect) => { 87 | code = Some(sect); 88 | }, 89 | elements::Section::Data(sect) => { 90 | data = Some(sect); 91 | }, 92 | section => other.push(section), 93 | } 94 | } 95 | 96 | ModuleScaffold { 97 | types: types.unwrap_or_default(), 98 | import: import.unwrap_or_default(), 99 | functions: funcs.unwrap_or_default(), 100 | table: table.unwrap_or_default(), 101 | memory: memory.unwrap_or_default(), 102 | global: global.unwrap_or_default(), 103 | export: export.unwrap_or_default(), 104 | start, 105 | element: element.unwrap_or_default(), 106 | code: code.unwrap_or_default(), 107 | data: data.unwrap_or_default(), 108 | other, 109 | } 110 | } 111 | } 112 | 113 | impl From for elements::Module { 114 | fn from(module: ModuleScaffold) -> Self { 115 | let mut sections = Vec::new(); 116 | 117 | let types = module.types; 118 | if !types.types().is_empty() { 119 | sections.push(elements::Section::Type(types)); 120 | } 121 | let import = module.import; 122 | if !import.entries().is_empty() { 123 | sections.push(elements::Section::Import(import)); 124 | } 125 | let functions = module.functions; 126 | if !functions.entries().is_empty() { 127 | sections.push(elements::Section::Function(functions)); 128 | } 129 | let table = module.table; 130 | if !table.entries().is_empty() { 131 | sections.push(elements::Section::Table(table)); 132 | } 133 | let memory = module.memory; 134 | if !memory.entries().is_empty() { 135 | sections.push(elements::Section::Memory(memory)); 136 | } 137 | let global = module.global; 138 | if !global.entries().is_empty() { 139 | sections.push(elements::Section::Global(global)); 140 | } 141 | let export = module.export; 142 | if !export.entries().is_empty() { 143 | sections.push(elements::Section::Export(export)); 144 | } 145 | if let Some(start) = module.start { 146 | sections.push(elements::Section::Start(start)); 147 | } 148 | let element = module.element; 149 | if !element.entries().is_empty() { 150 | sections.push(elements::Section::Element(element)); 151 | } 152 | let code = module.code; 153 | if !code.bodies().is_empty() { 154 | sections.push(elements::Section::Code(code)); 155 | } 156 | let data = module.data; 157 | if !data.entries().is_empty() { 158 | sections.push(elements::Section::Data(data)); 159 | } 160 | sections.extend(module.other); 161 | elements::Module::new(sections) 162 | } 163 | } 164 | 165 | impl ModuleBuilder { 166 | /// New empty module builder 167 | pub fn new() -> Self { 168 | ModuleBuilder::with_callback(Identity) 169 | } 170 | } 171 | 172 | impl Default for ModuleBuilder { 173 | fn default() -> Self { 174 | Self::new() 175 | } 176 | } 177 | 178 | impl ModuleBuilder 179 | where 180 | F: Invoke, 181 | { 182 | /// New module builder with bound callback 183 | pub fn with_callback(callback: F) -> Self { 184 | ModuleBuilder { callback, module: Default::default() } 185 | } 186 | 187 | /// Builder from raw module 188 | pub fn with_module(mut self, module: elements::Module) -> Self { 189 | self.module = module.into(); 190 | self 191 | } 192 | 193 | /// Fill module with sections from iterator 194 | pub fn with_sections(mut self, sections: I) -> Self 195 | where 196 | I: IntoIterator, 197 | { 198 | self.module.other.extend(sections); 199 | self 200 | } 201 | 202 | /// Add additional section 203 | pub fn with_section(mut self, section: elements::Section) -> Self { 204 | self.module.other.push(section); 205 | self 206 | } 207 | 208 | /// Binds to the type section, creates additional types when required 209 | pub fn with_signatures(mut self, bindings: code::SignatureBindings) -> Self { 210 | self.push_signatures(bindings); 211 | self 212 | } 213 | 214 | /// Push stand-alone function definition, creating sections, signature and code blocks 215 | /// in corresponding sections. 216 | /// `FunctionDefinition` can be build using `builder::function` builder 217 | pub fn push_function(&mut self, func: code::FunctionDefinition) -> CodeLocation { 218 | let signature = func.signature; 219 | let body = func.code; 220 | 221 | let type_ref = self.resolve_type_ref(signature); 222 | 223 | self.module.functions.entries_mut().push(elements::Func::new(type_ref)); 224 | let signature_index = self.module.functions.entries_mut().len() as u32 - 1; 225 | self.module.code.bodies_mut().push(body); 226 | let body_index = self.module.code.bodies_mut().len() as u32 - 1; 227 | 228 | if func.is_main { 229 | self.module.start = Some(body_index); 230 | } 231 | 232 | CodeLocation { signature: signature_index, body: body_index } 233 | } 234 | 235 | /// Push linear memory region 236 | pub fn push_memory(&mut self, mut memory: memory::MemoryDefinition) -> u32 { 237 | let entries = self.module.memory.entries_mut(); 238 | entries.push(elements::MemoryType::new(memory.min, memory.max)); 239 | let memory_index = (entries.len() - 1) as u32; 240 | for data in memory.data.drain(..) { 241 | self.module.data.entries_mut().push(elements::DataSegment::new( 242 | memory_index, 243 | Some(data.offset), 244 | data.values, 245 | )) 246 | } 247 | memory_index 248 | } 249 | 250 | /// Push table 251 | pub fn push_table(&mut self, mut table: table::TableDefinition) -> u32 { 252 | let entries = self.module.table.entries_mut(); 253 | entries.push(elements::TableType::new(table.min, table.max)); 254 | let table_index = (entries.len() - 1) as u32; 255 | for entry in table.elements.drain(..) { 256 | self.module.element.entries_mut().push(elements::ElementSegment::new( 257 | table_index, 258 | Some(entry.offset), 259 | entry.values, 260 | )) 261 | } 262 | table_index 263 | } 264 | 265 | /// Push global. 266 | pub fn push_global(&mut self, global: elements::GlobalEntry) -> u32 { 267 | let entries = self.module.global.entries_mut(); 268 | entries.push(global); 269 | entries.len() as u32 - 1 270 | } 271 | 272 | fn resolve_type_ref(&mut self, signature: code::Signature) -> u32 { 273 | match signature { 274 | code::Signature::Inline(func_type) => { 275 | if let Some(existing_entry) = 276 | self.module.types.types().iter().enumerate().find(|(_idx, t)| { 277 | let elements::Type::Function(ref existing) = t; 278 | *existing == func_type 279 | }) { 280 | return existing_entry.0 as u32 281 | } 282 | self.module.types.types_mut().push(elements::Type::Function(func_type)); 283 | self.module.types.types().len() as u32 - 1 284 | }, 285 | code::Signature::TypeReference(type_ref) => type_ref, 286 | } 287 | } 288 | 289 | /// Push one function signature, returning it's calling index. 290 | /// Can create corresponding type in type section. 291 | pub fn push_signature(&mut self, signature: code::Signature) -> u32 { 292 | self.resolve_type_ref(signature) 293 | } 294 | 295 | /// Push signatures in the module, returning corresponding indices of pushed signatures 296 | pub fn push_signatures(&mut self, signatures: code::SignatureBindings) -> Vec { 297 | signatures.into_iter().map(|binding| self.resolve_type_ref(binding)).collect() 298 | } 299 | 300 | /// Push import entry to module. Note that this does not update calling indices in 301 | /// function bodies. 302 | pub fn push_import(&mut self, import: elements::ImportEntry) -> u32 { 303 | self.module.import.entries_mut().push(import); 304 | // todo: actually update calling addresses in function bodies 305 | // todo: also batch push 306 | 307 | self.module.import.entries_mut().len() as u32 - 1 308 | } 309 | 310 | /// Push export entry to module. 311 | pub fn push_export(&mut self, export: elements::ExportEntry) -> u32 { 312 | self.module.export.entries_mut().push(export); 313 | self.module.export.entries_mut().len() as u32 - 1 314 | } 315 | 316 | /// Add new function using dedicated builder 317 | pub fn function(self) -> FunctionBuilder { 318 | FunctionBuilder::with_callback(self) 319 | } 320 | 321 | /// Add new linear memory using dedicated builder 322 | pub fn memory(self) -> MemoryBuilder { 323 | MemoryBuilder::with_callback(self) 324 | } 325 | 326 | /// Add new table using dedicated builder 327 | pub fn table(self) -> TableBuilder { 328 | TableBuilder::with_callback(self) 329 | } 330 | 331 | /// Define functions section 332 | pub fn functions(self) -> SignaturesBuilder { 333 | SignaturesBuilder::with_callback(self) 334 | } 335 | 336 | /// With inserted export entry 337 | pub fn with_export(mut self, entry: elements::ExportEntry) -> Self { 338 | self.module.export.entries_mut().push(entry); 339 | self 340 | } 341 | 342 | /// With inserted import entry 343 | pub fn with_import(mut self, entry: elements::ImportEntry) -> Self { 344 | self.module.import.entries_mut().push(entry); 345 | self 346 | } 347 | 348 | /// Import entry builder 349 | /// # Examples 350 | /// ``` 351 | /// use parity_wasm::builder::module; 352 | /// 353 | /// let module = module() 354 | /// .import() 355 | /// .module("env") 356 | /// .field("memory") 357 | /// .external().memory(256, Some(256)) 358 | /// .build() 359 | /// .build(); 360 | /// 361 | /// assert_eq!(module.import_section().expect("import section to exist").entries().len(), 1); 362 | /// ``` 363 | pub fn import(self) -> import::ImportBuilder { 364 | import::ImportBuilder::with_callback(self) 365 | } 366 | 367 | /// With global variable 368 | pub fn with_global(mut self, global: elements::GlobalEntry) -> Self { 369 | self.module.global.entries_mut().push(global); 370 | self 371 | } 372 | 373 | /// With table 374 | pub fn with_table(mut self, table: elements::TableType) -> Self { 375 | self.module.table.entries_mut().push(table); 376 | self 377 | } 378 | 379 | /// Export entry builder 380 | /// # Examples 381 | /// ``` 382 | /// use parity_wasm::builder::module; 383 | /// use parity_wasm::elements::Instruction::*; 384 | /// 385 | /// let module = module() 386 | /// .global() 387 | /// .value_type().i32() 388 | /// .init_expr(I32Const(0)) 389 | /// .build() 390 | /// .export() 391 | /// .field("_zero") 392 | /// .internal().global(0) 393 | /// .build() 394 | /// .build(); 395 | /// 396 | /// assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1); 397 | /// ``` 398 | pub fn export(self) -> export::ExportBuilder { 399 | export::ExportBuilder::with_callback(self) 400 | } 401 | 402 | /// Glboal entry builder 403 | /// # Examples 404 | /// ``` 405 | /// use parity_wasm::builder::module; 406 | /// use parity_wasm::elements::Instruction::*; 407 | /// 408 | /// let module = module() 409 | /// .global() 410 | /// .value_type().i32() 411 | /// .init_expr(I32Const(0)) 412 | /// .build() 413 | /// .build(); 414 | /// 415 | /// assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1); 416 | /// ``` 417 | pub fn global(self) -> global::GlobalBuilder { 418 | global::GlobalBuilder::with_callback(self) 419 | } 420 | 421 | /// Add data segment to the builder 422 | pub fn with_data_segment(mut self, segment: elements::DataSegment) -> Self { 423 | self.module.data.entries_mut().push(segment); 424 | self 425 | } 426 | 427 | /// Data entry builder 428 | pub fn data(self) -> data::DataSegmentBuilder { 429 | data::DataSegmentBuilder::with_callback(self) 430 | } 431 | 432 | /// Build module (final step) 433 | pub fn build(self) -> F::Result { 434 | self.callback.invoke(self.module.into()) 435 | } 436 | } 437 | 438 | impl Invoke for ModuleBuilder 439 | where 440 | F: Invoke, 441 | { 442 | type Result = Self; 443 | 444 | fn invoke(self, section: elements::FunctionSection) -> Self { 445 | self.with_section(elements::Section::Function(section)) 446 | } 447 | } 448 | 449 | impl Invoke for ModuleBuilder 450 | where 451 | F: Invoke, 452 | { 453 | type Result = Self; 454 | 455 | fn invoke(self, bindings: code::SignatureBindings) -> Self { 456 | self.with_signatures(bindings) 457 | } 458 | } 459 | 460 | impl Invoke for ModuleBuilder 461 | where 462 | F: Invoke, 463 | { 464 | type Result = Self; 465 | 466 | fn invoke(self, def: code::FunctionDefinition) -> Self { 467 | let mut b = self; 468 | b.push_function(def); 469 | b 470 | } 471 | } 472 | 473 | impl Invoke for ModuleBuilder 474 | where 475 | F: Invoke, 476 | { 477 | type Result = Self; 478 | 479 | fn invoke(self, def: memory::MemoryDefinition) -> Self { 480 | let mut b = self; 481 | b.push_memory(def); 482 | b 483 | } 484 | } 485 | 486 | impl Invoke for ModuleBuilder 487 | where 488 | F: Invoke, 489 | { 490 | type Result = Self; 491 | 492 | fn invoke(self, def: table::TableDefinition) -> Self { 493 | let mut b = self; 494 | b.push_table(def); 495 | b 496 | } 497 | } 498 | 499 | impl Invoke for ModuleBuilder 500 | where 501 | F: Invoke, 502 | { 503 | type Result = Self; 504 | 505 | fn invoke(self, entry: elements::ImportEntry) -> Self::Result { 506 | self.with_import(entry) 507 | } 508 | } 509 | 510 | impl Invoke for ModuleBuilder 511 | where 512 | F: Invoke, 513 | { 514 | type Result = Self; 515 | 516 | fn invoke(self, entry: elements::ExportEntry) -> Self::Result { 517 | self.with_export(entry) 518 | } 519 | } 520 | 521 | impl Invoke for ModuleBuilder 522 | where 523 | F: Invoke, 524 | { 525 | type Result = Self; 526 | 527 | fn invoke(self, entry: elements::GlobalEntry) -> Self::Result { 528 | self.with_global(entry) 529 | } 530 | } 531 | 532 | impl Invoke for ModuleBuilder 533 | where 534 | F: Invoke, 535 | { 536 | type Result = Self; 537 | 538 | fn invoke(self, segment: elements::DataSegment) -> Self { 539 | self.with_data_segment(segment) 540 | } 541 | } 542 | 543 | /// Start new module builder 544 | /// # Examples 545 | /// 546 | /// ``` 547 | /// use parity_wasm::builder; 548 | /// 549 | /// let module = builder::module() 550 | /// .function() 551 | /// .signature().param().i32().build() 552 | /// .body().build() 553 | /// .build() 554 | /// .build(); 555 | /// 556 | /// assert_eq!(module.type_section().expect("type section to exist").types().len(), 1); 557 | /// assert_eq!(module.function_section().expect("function section to exist").entries().len(), 1); 558 | /// assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1); 559 | /// ``` 560 | pub fn module() -> ModuleBuilder { 561 | ModuleBuilder::new() 562 | } 563 | 564 | /// Start builder to extend existing module 565 | pub fn from_module(module: elements::Module) -> ModuleBuilder { 566 | ModuleBuilder::new().with_module(module) 567 | } 568 | 569 | #[cfg(test)] 570 | mod tests { 571 | 572 | use super::module; 573 | use crate::elements; 574 | 575 | #[test] 576 | fn smoky() { 577 | let module = module().build(); 578 | assert_eq!(module.sections().len(), 0); 579 | } 580 | 581 | #[test] 582 | fn functions() { 583 | let module = module() 584 | .function() 585 | .signature() 586 | .param() 587 | .i32() 588 | .build() 589 | .body() 590 | .build() 591 | .build() 592 | .build(); 593 | 594 | assert_eq!(module.type_section().expect("type section to exist").types().len(), 1); 595 | assert_eq!( 596 | module.function_section().expect("function section to exist").entries().len(), 597 | 1 598 | ); 599 | assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1); 600 | } 601 | 602 | #[test] 603 | fn export() { 604 | let module = module().export().field("call").internal().func(0).build().build(); 605 | 606 | assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1); 607 | } 608 | 609 | #[test] 610 | fn global() { 611 | let module = module() 612 | .global() 613 | .value_type() 614 | .i64() 615 | .mutable() 616 | .init_expr(elements::Instruction::I64Const(5)) 617 | .build() 618 | .build(); 619 | 620 | assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1); 621 | } 622 | 623 | #[test] 624 | fn data() { 625 | let module = module() 626 | .data() 627 | .offset(elements::Instruction::I32Const(16)) 628 | .value(vec![0u8, 15, 10, 5, 25]) 629 | .build() 630 | .build(); 631 | 632 | assert_eq!(module.data_section().expect("data section to exist").entries().len(), 1); 633 | } 634 | 635 | #[test] 636 | fn reuse_types() { 637 | let module = module() 638 | .function() 639 | .signature() 640 | .param() 641 | .i32() 642 | .build() 643 | .body() 644 | .build() 645 | .build() 646 | .function() 647 | .signature() 648 | .param() 649 | .i32() 650 | .build() 651 | .body() 652 | .build() 653 | .build() 654 | .build(); 655 | 656 | assert_eq!(module.type_section().expect("type section failed").types().len(), 1); 657 | } 658 | } 659 | -------------------------------------------------------------------------------- /src/builder/table.rs: -------------------------------------------------------------------------------- 1 | use super::invoke::{Identity, Invoke}; 2 | use crate::elements; 3 | use alloc::vec::Vec; 4 | 5 | /// Table definition 6 | #[derive(Debug, PartialEq, Default)] 7 | pub struct TableDefinition { 8 | /// Minimum length 9 | pub min: u32, 10 | /// Maximum length, if any 11 | pub max: Option, 12 | /// Element segments, if any 13 | pub elements: Vec, 14 | } 15 | 16 | /// Table elements entry definition 17 | #[derive(Debug, PartialEq)] 18 | pub struct TableEntryDefinition { 19 | /// Offset initialization expression 20 | pub offset: elements::InitExpr, 21 | /// Values of initialization 22 | pub values: Vec, 23 | } 24 | 25 | /// Table builder 26 | pub struct TableBuilder { 27 | callback: F, 28 | table: TableDefinition, 29 | } 30 | 31 | impl TableBuilder { 32 | /// New table builder 33 | pub fn new() -> Self { 34 | TableBuilder::with_callback(Identity) 35 | } 36 | } 37 | 38 | impl Default for TableBuilder { 39 | fn default() -> Self { 40 | Self::new() 41 | } 42 | } 43 | 44 | impl TableBuilder 45 | where 46 | F: Invoke, 47 | { 48 | /// New table builder with callback in chained context 49 | pub fn with_callback(callback: F) -> Self { 50 | TableBuilder { callback, table: Default::default() } 51 | } 52 | 53 | /// Set/override minimum length 54 | pub fn with_min(mut self, min: u32) -> Self { 55 | self.table.min = min; 56 | self 57 | } 58 | 59 | /// Set/override maximum length 60 | pub fn with_max(mut self, max: Option) -> Self { 61 | self.table.max = max; 62 | self 63 | } 64 | 65 | /// Generate initialization expression and element values on specified index 66 | pub fn with_element(mut self, index: u32, values: Vec) -> Self { 67 | self.table.elements.push(TableEntryDefinition { 68 | offset: elements::InitExpr::new(vec![ 69 | elements::Instruction::I32Const(index as i32), 70 | elements::Instruction::End, 71 | ]), 72 | values, 73 | }); 74 | self 75 | } 76 | 77 | /// Finalize current builder spawning resulting struct 78 | pub fn build(self) -> F::Result { 79 | self.callback.invoke(self.table) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/elements/export_entry.rs: -------------------------------------------------------------------------------- 1 | use super::{Deserialize, Error, Serialize, VarUint32, VarUint7}; 2 | use crate::io; 3 | use alloc::string::String; 4 | 5 | /// Internal reference of the exported entry. 6 | #[derive(Debug, Clone, Copy, PartialEq)] 7 | pub enum Internal { 8 | /// Function reference. 9 | Function(u32), 10 | /// Table reference. 11 | Table(u32), 12 | /// Memory reference. 13 | Memory(u32), 14 | /// Global reference. 15 | Global(u32), 16 | } 17 | 18 | impl Deserialize for Internal { 19 | type Error = Error; 20 | 21 | fn deserialize(reader: &mut R) -> Result { 22 | let kind = VarUint7::deserialize(reader)?; 23 | match kind.into() { 24 | 0x00 => Ok(Internal::Function(VarUint32::deserialize(reader)?.into())), 25 | 0x01 => Ok(Internal::Table(VarUint32::deserialize(reader)?.into())), 26 | 0x02 => Ok(Internal::Memory(VarUint32::deserialize(reader)?.into())), 27 | 0x03 => Ok(Internal::Global(VarUint32::deserialize(reader)?.into())), 28 | _ => Err(Error::UnknownInternalKind(kind.into())), 29 | } 30 | } 31 | } 32 | 33 | impl Serialize for Internal { 34 | type Error = Error; 35 | 36 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 37 | let (bt, arg) = match self { 38 | Internal::Function(arg) => (0x00, arg), 39 | Internal::Table(arg) => (0x01, arg), 40 | Internal::Memory(arg) => (0x02, arg), 41 | Internal::Global(arg) => (0x03, arg), 42 | }; 43 | 44 | VarUint7::from(bt).serialize(writer)?; 45 | VarUint32::from(arg).serialize(writer)?; 46 | 47 | Ok(()) 48 | } 49 | } 50 | 51 | /// Export entry. 52 | #[derive(Debug, Clone, PartialEq)] 53 | pub struct ExportEntry { 54 | field_str: String, 55 | internal: Internal, 56 | } 57 | 58 | impl ExportEntry { 59 | /// New export entry. 60 | pub fn new(field_str: String, internal: Internal) -> Self { 61 | ExportEntry { field_str, internal } 62 | } 63 | 64 | /// Public name. 65 | pub fn field(&self) -> &str { 66 | &self.field_str 67 | } 68 | 69 | /// Public name (mutable). 70 | pub fn field_mut(&mut self) -> &mut String { 71 | &mut self.field_str 72 | } 73 | 74 | /// Internal reference of the export entry. 75 | pub fn internal(&self) -> &Internal { 76 | &self.internal 77 | } 78 | 79 | /// Internal reference of the export entry (mutable). 80 | pub fn internal_mut(&mut self) -> &mut Internal { 81 | &mut self.internal 82 | } 83 | } 84 | 85 | impl Deserialize for ExportEntry { 86 | type Error = Error; 87 | 88 | fn deserialize(reader: &mut R) -> Result { 89 | let field_str = String::deserialize(reader)?; 90 | let internal = Internal::deserialize(reader)?; 91 | 92 | Ok(ExportEntry { field_str, internal }) 93 | } 94 | } 95 | 96 | impl Serialize for ExportEntry { 97 | type Error = Error; 98 | 99 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 100 | self.field_str.serialize(writer)?; 101 | self.internal.serialize(writer)?; 102 | Ok(()) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/elements/func.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | CountedList, CountedListWriter, CountedWriter, Deserialize, Error, Instructions, Serialize, 3 | ValueType, VarUint32, 4 | }; 5 | use crate::{elements::section::SectionReader, io}; 6 | use alloc::vec::Vec; 7 | 8 | /// Function signature (type reference) 9 | #[derive(Debug, Copy, Clone, PartialEq)] 10 | pub struct Func(u32); 11 | 12 | impl Func { 13 | /// New function signature 14 | pub fn new(type_ref: u32) -> Self { 15 | Func(type_ref) 16 | } 17 | 18 | /// Function signature type reference. 19 | pub fn type_ref(&self) -> u32 { 20 | self.0 21 | } 22 | 23 | /// Function signature type reference (mutable). 24 | pub fn type_ref_mut(&mut self) -> &mut u32 { 25 | &mut self.0 26 | } 27 | } 28 | 29 | impl Serialize for Func { 30 | type Error = Error; 31 | 32 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 33 | VarUint32::from(self.0).serialize(writer) 34 | } 35 | } 36 | 37 | impl Deserialize for Func { 38 | type Error = Error; 39 | 40 | fn deserialize(reader: &mut R) -> Result { 41 | Ok(Func(VarUint32::deserialize(reader)?.into())) 42 | } 43 | } 44 | 45 | /// Local definition inside the function body. 46 | #[derive(Debug, Copy, Clone, PartialEq)] 47 | pub struct Local { 48 | count: u32, 49 | value_type: ValueType, 50 | } 51 | 52 | impl Local { 53 | /// New local with `count` and `value_type`. 54 | pub fn new(count: u32, value_type: ValueType) -> Self { 55 | Local { count, value_type } 56 | } 57 | 58 | /// Number of locals with the shared type. 59 | pub fn count(&self) -> u32 { 60 | self.count 61 | } 62 | 63 | /// Type of the locals. 64 | pub fn value_type(&self) -> ValueType { 65 | self.value_type 66 | } 67 | } 68 | 69 | impl Deserialize for Local { 70 | type Error = Error; 71 | 72 | fn deserialize(reader: &mut R) -> Result { 73 | let count = VarUint32::deserialize(reader)?; 74 | let value_type = ValueType::deserialize(reader)?; 75 | Ok(Local { count: count.into(), value_type }) 76 | } 77 | } 78 | 79 | impl Serialize for Local { 80 | type Error = Error; 81 | 82 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 83 | VarUint32::from(self.count).serialize(writer)?; 84 | self.value_type.serialize(writer)?; 85 | Ok(()) 86 | } 87 | } 88 | 89 | /// Function body definition. 90 | #[derive(Debug, Clone, PartialEq)] 91 | pub struct FuncBody { 92 | locals: Vec, 93 | instructions: Instructions, 94 | } 95 | 96 | impl FuncBody { 97 | /// New function body with given `locals` and `instructions`. 98 | pub fn new(locals: Vec, instructions: Instructions) -> Self { 99 | FuncBody { locals, instructions } 100 | } 101 | 102 | /// List of individual instructions. 103 | pub fn empty() -> Self { 104 | FuncBody { locals: Vec::new(), instructions: Instructions::empty() } 105 | } 106 | 107 | /// Locals declared in function body. 108 | pub fn locals(&self) -> &[Local] { 109 | &self.locals 110 | } 111 | 112 | /// Instruction list of the function body. Minimal instruction list 113 | /// 114 | /// is just `&[Instruction::End]` 115 | pub fn code(&self) -> &Instructions { 116 | &self.instructions 117 | } 118 | 119 | /// Locals declared in function body (mutable). 120 | pub fn locals_mut(&mut self) -> &mut Vec { 121 | &mut self.locals 122 | } 123 | 124 | /// Instruction list of the function body (mutable). 125 | pub fn code_mut(&mut self) -> &mut Instructions { 126 | &mut self.instructions 127 | } 128 | } 129 | 130 | impl Deserialize for FuncBody { 131 | type Error = Error; 132 | 133 | fn deserialize(reader: &mut R) -> Result { 134 | let mut body_reader = SectionReader::new(reader)?; 135 | let locals: Vec = CountedList::::deserialize(&mut body_reader)?.into_inner(); 136 | 137 | // The specification obliges us to count the total number of local variables while 138 | // decoding the binary format. 139 | locals 140 | .iter() 141 | .try_fold(0u32, |acc, &Local { count, .. }| acc.checked_add(count)) 142 | .ok_or(Error::TooManyLocals)?; 143 | 144 | let instructions = Instructions::deserialize(&mut body_reader)?; 145 | body_reader.close()?; 146 | Ok(FuncBody { locals, instructions }) 147 | } 148 | } 149 | 150 | impl Serialize for FuncBody { 151 | type Error = Error; 152 | 153 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 154 | let mut counted_writer = CountedWriter::new(writer); 155 | 156 | let data = self.locals; 157 | let counted_list = 158 | CountedListWriter::(data.len(), data.into_iter().map(Into::into)); 159 | counted_list.serialize(&mut counted_writer)?; 160 | 161 | let code = self.instructions; 162 | code.serialize(&mut counted_writer)?; 163 | 164 | counted_writer.done()?; 165 | 166 | Ok(()) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/elements/global_entry.rs: -------------------------------------------------------------------------------- 1 | use super::{Deserialize, Error, GlobalType, InitExpr, Serialize}; 2 | use crate::io; 3 | 4 | /// Global entry in the module. 5 | #[derive(Clone, Debug, PartialEq)] 6 | pub struct GlobalEntry { 7 | global_type: GlobalType, 8 | init_expr: InitExpr, 9 | } 10 | 11 | impl GlobalEntry { 12 | /// New global entry. 13 | pub fn new(global_type: GlobalType, init_expr: InitExpr) -> Self { 14 | GlobalEntry { global_type, init_expr } 15 | } 16 | /// Global type. 17 | pub fn global_type(&self) -> &GlobalType { 18 | &self.global_type 19 | } 20 | /// Initialization expression (instructions) for global. 21 | pub fn init_expr(&self) -> &InitExpr { 22 | &self.init_expr 23 | } 24 | /// Global type (mutable). 25 | pub fn global_type_mut(&mut self) -> &mut GlobalType { 26 | &mut self.global_type 27 | } 28 | /// Initialization expression (instructions) for global (mutable). 29 | pub fn init_expr_mut(&mut self) -> &mut InitExpr { 30 | &mut self.init_expr 31 | } 32 | } 33 | 34 | impl Deserialize for GlobalEntry { 35 | type Error = Error; 36 | 37 | fn deserialize(reader: &mut R) -> Result { 38 | let global_type = GlobalType::deserialize(reader)?; 39 | let init_expr = InitExpr::deserialize(reader)?; 40 | 41 | Ok(GlobalEntry { global_type, init_expr }) 42 | } 43 | } 44 | 45 | impl Serialize for GlobalEntry { 46 | type Error = Error; 47 | 48 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 49 | self.global_type.serialize(writer)?; 50 | self.init_expr.serialize(writer) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/elements/import_entry.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | Deserialize, Error, Serialize, TableElementType, Uint8, ValueType, VarInt7, VarUint1, 3 | VarUint32, VarUint7, 4 | }; 5 | use crate::io; 6 | use alloc::string::String; 7 | 8 | const FLAG_HAS_MAX: u8 = 0x01; 9 | #[cfg(feature = "atomics")] 10 | const FLAG_SHARED: u8 = 0x02; 11 | 12 | /// Global definition struct 13 | #[derive(Debug, Copy, Clone, PartialEq)] 14 | pub struct GlobalType { 15 | content_type: ValueType, 16 | is_mutable: bool, 17 | } 18 | 19 | impl GlobalType { 20 | /// New global type 21 | pub fn new(content_type: ValueType, is_mutable: bool) -> Self { 22 | GlobalType { content_type, is_mutable } 23 | } 24 | 25 | /// Type of the global entry 26 | pub fn content_type(&self) -> ValueType { 27 | self.content_type 28 | } 29 | 30 | /// Is global entry is declared as mutable 31 | pub fn is_mutable(&self) -> bool { 32 | self.is_mutable 33 | } 34 | } 35 | 36 | impl Deserialize for GlobalType { 37 | type Error = Error; 38 | 39 | fn deserialize(reader: &mut R) -> Result { 40 | let content_type = ValueType::deserialize(reader)?; 41 | let is_mutable = VarUint1::deserialize(reader)?; 42 | Ok(GlobalType { content_type, is_mutable: is_mutable.into() }) 43 | } 44 | } 45 | 46 | impl Serialize for GlobalType { 47 | type Error = Error; 48 | 49 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 50 | self.content_type.serialize(writer)?; 51 | VarUint1::from(self.is_mutable).serialize(writer)?; 52 | Ok(()) 53 | } 54 | } 55 | 56 | /// Table entry 57 | #[derive(Debug, Copy, Clone, PartialEq)] 58 | pub struct TableType { 59 | elem_type: TableElementType, 60 | limits: ResizableLimits, 61 | } 62 | 63 | impl TableType { 64 | /// New table definition 65 | pub fn new(min: u32, max: Option) -> Self { 66 | TableType { elem_type: TableElementType::AnyFunc, limits: ResizableLimits::new(min, max) } 67 | } 68 | 69 | /// Table memory specification 70 | pub fn limits(&self) -> &ResizableLimits { 71 | &self.limits 72 | } 73 | 74 | /// Table element type 75 | pub fn elem_type(&self) -> TableElementType { 76 | self.elem_type 77 | } 78 | } 79 | 80 | impl Deserialize for TableType { 81 | type Error = Error; 82 | 83 | fn deserialize(reader: &mut R) -> Result { 84 | let elem_type = TableElementType::deserialize(reader)?; 85 | let limits = ResizableLimits::deserialize(reader)?; 86 | Ok(TableType { elem_type, limits }) 87 | } 88 | } 89 | 90 | impl Serialize for TableType { 91 | type Error = Error; 92 | 93 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 94 | self.elem_type.serialize(writer)?; 95 | self.limits.serialize(writer) 96 | } 97 | } 98 | 99 | /// Memory and table limits. 100 | #[derive(Debug, Copy, Clone, PartialEq)] 101 | pub struct ResizableLimits { 102 | initial: u32, 103 | maximum: Option, 104 | #[cfg(feature = "atomics")] 105 | shared: bool, 106 | } 107 | 108 | impl ResizableLimits { 109 | /// New memory limits definition. 110 | pub fn new(min: u32, max: Option) -> Self { 111 | ResizableLimits { 112 | initial: min, 113 | maximum: max, 114 | #[cfg(feature = "atomics")] 115 | shared: false, 116 | } 117 | } 118 | /// Initial size. 119 | pub fn initial(&self) -> u32 { 120 | self.initial 121 | } 122 | /// Maximum size. 123 | pub fn maximum(&self) -> Option { 124 | self.maximum 125 | } 126 | 127 | #[cfg(feature = "atomics")] 128 | /// Whether or not this is a shared array buffer. 129 | pub fn shared(&self) -> bool { 130 | self.shared 131 | } 132 | } 133 | 134 | impl Deserialize for ResizableLimits { 135 | type Error = Error; 136 | 137 | fn deserialize(reader: &mut R) -> Result { 138 | let flags: u8 = Uint8::deserialize(reader)?.into(); 139 | match flags { 140 | // Default flags are always supported. This is simply: FLAG_HAS_MAX={true, false}. 141 | 0x00 | 0x01 => {}, 142 | 143 | // Atomics proposal introduce FLAG_SHARED (0x02). Shared memories can be used only 144 | // together with FLAG_HAS_MAX (0x01), hence 0x03. 145 | #[cfg(feature = "atomics")] 146 | 0x03 => {}, 147 | 148 | _ => return Err(Error::InvalidLimitsFlags(flags)), 149 | } 150 | 151 | let initial = VarUint32::deserialize(reader)?; 152 | let maximum = if flags & FLAG_HAS_MAX != 0 { 153 | Some(VarUint32::deserialize(reader)?.into()) 154 | } else { 155 | None 156 | }; 157 | 158 | Ok(ResizableLimits { 159 | initial: initial.into(), 160 | maximum, 161 | 162 | #[cfg(feature = "atomics")] 163 | shared: flags & FLAG_SHARED != 0, 164 | }) 165 | } 166 | } 167 | 168 | impl Serialize for ResizableLimits { 169 | type Error = Error; 170 | 171 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 172 | let mut flags: u8 = 0; 173 | if self.maximum.is_some() { 174 | flags |= FLAG_HAS_MAX; 175 | } 176 | 177 | #[cfg(feature = "atomics")] 178 | { 179 | // If the atomics feature is enabled and if the shared flag is set, add logically 180 | // it to the flags. 181 | if self.shared { 182 | flags |= FLAG_SHARED; 183 | } 184 | } 185 | Uint8::from(flags).serialize(writer)?; 186 | VarUint32::from(self.initial).serialize(writer)?; 187 | if let Some(max) = self.maximum { 188 | VarUint32::from(max).serialize(writer)?; 189 | } 190 | Ok(()) 191 | } 192 | } 193 | 194 | /// Memory entry. 195 | #[derive(Debug, Copy, Clone, PartialEq)] 196 | pub struct MemoryType(ResizableLimits); 197 | 198 | impl MemoryType { 199 | /// New memory definition 200 | pub fn new(min: u32, max: Option) -> Self { 201 | let r = ResizableLimits::new(min, max); 202 | MemoryType(r) 203 | } 204 | 205 | /// Set the `shared` flag that denotes a memory that can be shared between threads. 206 | /// 207 | /// `false` by default. This is only available if the `atomics` feature is enabled. 208 | #[cfg(feature = "atomics")] 209 | pub fn set_shared(&mut self, shared: bool) { 210 | self.0.shared = shared; 211 | } 212 | 213 | /// Limits of the memory entry. 214 | pub fn limits(&self) -> &ResizableLimits { 215 | &self.0 216 | } 217 | } 218 | 219 | impl Deserialize for MemoryType { 220 | type Error = Error; 221 | 222 | fn deserialize(reader: &mut R) -> Result { 223 | Ok(MemoryType(ResizableLimits::deserialize(reader)?)) 224 | } 225 | } 226 | 227 | impl Serialize for MemoryType { 228 | type Error = Error; 229 | 230 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 231 | self.0.serialize(writer) 232 | } 233 | } 234 | 235 | /// External to local binding. 236 | #[derive(Debug, Copy, Clone, PartialEq)] 237 | pub enum External { 238 | /// Binds to a function whose type is associated with the given index in the 239 | /// type section. 240 | Function(u32), 241 | /// Describes local table definition to be imported as. 242 | Table(TableType), 243 | /// Describes local memory definition to be imported as. 244 | Memory(MemoryType), 245 | /// Describes local global entry to be imported as. 246 | Global(GlobalType), 247 | } 248 | 249 | impl Deserialize for External { 250 | type Error = Error; 251 | 252 | fn deserialize(reader: &mut R) -> Result { 253 | let kind = VarUint7::deserialize(reader)?; 254 | match kind.into() { 255 | 0x00 => Ok(External::Function(VarUint32::deserialize(reader)?.into())), 256 | 0x01 => Ok(External::Table(TableType::deserialize(reader)?)), 257 | 0x02 => Ok(External::Memory(MemoryType::deserialize(reader)?)), 258 | 0x03 => Ok(External::Global(GlobalType::deserialize(reader)?)), 259 | _ => Err(Error::UnknownExternalKind(kind.into())), 260 | } 261 | } 262 | } 263 | 264 | impl Serialize for External { 265 | type Error = Error; 266 | 267 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 268 | use self::External::*; 269 | 270 | match self { 271 | Function(index) => { 272 | VarUint7::from(0x00).serialize(writer)?; 273 | VarUint32::from(index).serialize(writer)?; 274 | }, 275 | Table(tt) => { 276 | VarInt7::from(0x01).serialize(writer)?; 277 | tt.serialize(writer)?; 278 | }, 279 | Memory(mt) => { 280 | VarInt7::from(0x02).serialize(writer)?; 281 | mt.serialize(writer)?; 282 | }, 283 | Global(gt) => { 284 | VarInt7::from(0x03).serialize(writer)?; 285 | gt.serialize(writer)?; 286 | }, 287 | } 288 | 289 | Ok(()) 290 | } 291 | } 292 | 293 | /// Import entry. 294 | #[derive(Debug, Clone, PartialEq)] 295 | pub struct ImportEntry { 296 | module_str: String, 297 | field_str: String, 298 | external: External, 299 | } 300 | 301 | impl ImportEntry { 302 | /// New import entry. 303 | pub fn new(module_str: String, field_str: String, external: External) -> Self { 304 | ImportEntry { module_str, field_str, external } 305 | } 306 | 307 | /// Module reference of the import entry. 308 | pub fn module(&self) -> &str { 309 | &self.module_str 310 | } 311 | 312 | /// Module reference of the import entry (mutable). 313 | pub fn module_mut(&mut self) -> &mut String { 314 | &mut self.module_str 315 | } 316 | 317 | /// Field reference of the import entry. 318 | pub fn field(&self) -> &str { 319 | &self.field_str 320 | } 321 | 322 | /// Field reference of the import entry (mutable) 323 | pub fn field_mut(&mut self) -> &mut String { 324 | &mut self.field_str 325 | } 326 | 327 | /// Local binidng of the import entry. 328 | pub fn external(&self) -> &External { 329 | &self.external 330 | } 331 | 332 | /// Local binidng of the import entry (mutable) 333 | pub fn external_mut(&mut self) -> &mut External { 334 | &mut self.external 335 | } 336 | } 337 | 338 | impl Deserialize for ImportEntry { 339 | type Error = Error; 340 | 341 | fn deserialize(reader: &mut R) -> Result { 342 | let module_str = String::deserialize(reader)?; 343 | let field_str = String::deserialize(reader)?; 344 | let external = External::deserialize(reader)?; 345 | 346 | Ok(ImportEntry { module_str, field_str, external }) 347 | } 348 | } 349 | 350 | impl Serialize for ImportEntry { 351 | type Error = Error; 352 | 353 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 354 | self.module_str.serialize(writer)?; 355 | self.field_str.serialize(writer)?; 356 | self.external.serialize(writer) 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /src/elements/index_map.rs: -------------------------------------------------------------------------------- 1 | use crate::io; 2 | use alloc::vec::Vec; 3 | 4 | use super::{Deserialize, Error, Serialize, VarUint32}; 5 | 6 | use alloc::vec; 7 | use core::{ 8 | cmp::min, 9 | iter::{FromIterator, IntoIterator}, 10 | mem, slice, 11 | }; 12 | 13 | /// A map from non-contiguous `u32` keys to values of type `T`, which is 14 | /// serialized and deserialized ascending order of the keys. Normally used for 15 | /// relative dense maps with occasional "holes", and stored as an array. 16 | /// 17 | /// **SECURITY WARNING:** This code is currently subject to a denial of service 18 | /// attack if you create a map containing the key `u32::MAX`, which should never 19 | /// happen in normal data. It would be pretty easy to provide a safe 20 | /// deserializing mechanism which addressed this problem. 21 | #[derive(Debug, Default)] 22 | pub struct IndexMap { 23 | /// The number of non-`None` entries in this map. 24 | len: usize, 25 | 26 | /// A vector of entries. Missing entries are represented as `None`. 27 | entries: Vec>, 28 | } 29 | 30 | impl IndexMap { 31 | /// Create an empty `IndexMap`, preallocating enough space to store 32 | /// `capacity` entries without needing to reallocate the underlying memory. 33 | pub fn with_capacity(capacity: usize) -> IndexMap { 34 | IndexMap { len: 0, entries: Vec::with_capacity(capacity) } 35 | } 36 | 37 | /// Clear the map. 38 | pub fn clear(&mut self) { 39 | self.entries.clear(); 40 | self.len = 0; 41 | } 42 | 43 | /// Return the name for the specified index, if it exists. 44 | pub fn get(&self, idx: u32) -> Option<&T> { 45 | match self.entries.get(idx as usize) { 46 | Some(&Some(ref value)) => Some(value), 47 | Some(&None) | None => None, 48 | } 49 | } 50 | 51 | /// Does the map contain an entry for the specified index? 52 | pub fn contains_key(&self, idx: u32) -> bool { 53 | match self.entries.get(idx as usize) { 54 | Some(&Some(_)) => true, 55 | Some(&None) | None => false, 56 | } 57 | } 58 | 59 | /// Insert a name into our map, returning the existing value if present. 60 | /// 61 | /// Note: This API is designed for reasonably dense indices based on valid 62 | /// data. Inserting a huge `idx` will use up a lot of RAM, and this function 63 | /// will not try to protect you against that. 64 | pub fn insert(&mut self, idx: u32, value: T) -> Option { 65 | let idx = idx as usize; 66 | let result = if idx >= self.entries.len() { 67 | // We need to grow the array, and add the new element at the end. 68 | for _ in 0..(idx - self.entries.len()) { 69 | // We can't use `extend(repeat(None)).take(n)`, because that 70 | // would require `T` to implement `Clone`. 71 | self.entries.push(None); 72 | } 73 | self.entries.push(Some(value)); 74 | debug_assert_eq!(idx + 1, self.entries.len()); 75 | self.len += 1; 76 | None 77 | } else { 78 | // We're either replacing an existing element, or filling in a 79 | // missing one. 80 | let existing = self.entries[idx].take(); 81 | if existing.is_none() { 82 | self.len += 1; 83 | } 84 | self.entries[idx] = Some(value); 85 | existing 86 | }; 87 | if mem::size_of::() > 4 { 88 | debug_assert!(self.entries.len() <= (u32::max_value() as usize) + 1); 89 | } 90 | #[cfg(slow_assertions)] 91 | debug_assert_eq!(self.len, self.slow_len()); 92 | result 93 | } 94 | 95 | /// Remove an item if present and return it. 96 | pub fn remove(&mut self, idx: u32) -> Option { 97 | let result = match self.entries.get_mut(idx as usize) { 98 | Some(value @ &mut Some(_)) => { 99 | self.len -= 1; 100 | value.take() 101 | }, 102 | Some(&mut None) | None => None, 103 | }; 104 | #[cfg(slow_assertions)] 105 | debug_assert_eq!(self.len, self.slow_len()); 106 | result 107 | } 108 | 109 | /// The number of items in this map. 110 | pub fn len(&self) -> usize { 111 | #[cfg(slow_assertions)] 112 | debug_assert_eq!(self.len, self.slow_len()); 113 | self.len 114 | } 115 | 116 | /// Is this map empty? 117 | pub fn is_empty(&self) -> bool { 118 | self.len == 0 119 | } 120 | 121 | /// This function is only compiled when `--cfg slow_assertions` is enabled. 122 | /// It computes the `len` value using a slow algorithm. 123 | /// 124 | /// WARNING: This turns a bunch of O(n) operations into O(n^2) operations. 125 | /// We may want to remove it once the code is tested, or to put it behind 126 | /// a feature flag named `slow_debug_checks`, or something like that. 127 | #[cfg(slow_assertions)] 128 | fn slow_len(&self) -> usize { 129 | self.entries.iter().filter(|entry| entry.is_some()).count() 130 | } 131 | 132 | /// Create a non-consuming iterator over this `IndexMap`'s keys and values. 133 | pub fn iter(&self) -> Iter { 134 | // Note that this does the right thing because we use `&self`. 135 | self.into_iter() 136 | } 137 | 138 | /// Custom deserialization routine. 139 | /// 140 | /// We will allocate an underlying array no larger than `max_entry_space` to 141 | /// hold the data, so the maximum index must be less than `max_entry_space`. 142 | /// This prevents mallicious *.wasm files from having a single entry with 143 | /// the index `u32::MAX`, which would consume far too much memory. 144 | /// 145 | /// The `deserialize_value` function will be passed the index of the value 146 | /// being deserialized, and must deserialize the value. 147 | pub fn deserialize_with( 148 | max_entry_space: usize, 149 | deserialize_value: &F, 150 | rdr: &mut R, 151 | ) -> Result, Error> 152 | where 153 | R: io::Read, 154 | F: Fn(u32, &mut R) -> Result, 155 | { 156 | let len: u32 = VarUint32::deserialize(rdr)?.into(); 157 | let mut map = IndexMap::with_capacity(len as usize); 158 | let mut prev_idx = None; 159 | for _ in 0..len { 160 | let idx: u32 = VarUint32::deserialize(rdr)?.into(); 161 | if idx as usize >= max_entry_space { 162 | return Err(Error::Other("index is larger than expected")) 163 | } 164 | match prev_idx { 165 | Some(prev) if prev >= idx => { 166 | // Supposedly these names must be "sorted by index", so 167 | // let's try enforcing that and seeing what happens. 168 | return Err(Error::Other("indices are out of order")) 169 | }, 170 | _ => { 171 | prev_idx = Some(idx); 172 | }, 173 | } 174 | let val = deserialize_value(idx, rdr)?; 175 | map.insert(idx, val); 176 | } 177 | Ok(map) 178 | } 179 | } 180 | 181 | impl Clone for IndexMap { 182 | fn clone(&self) -> IndexMap { 183 | IndexMap { len: self.len, entries: self.entries.clone() } 184 | } 185 | } 186 | 187 | impl PartialEq> for IndexMap { 188 | fn eq(&self, other: &IndexMap) -> bool { 189 | if self.len() != other.len() { 190 | // If the number of non-`None` entries is different, we can't match. 191 | false 192 | } else { 193 | // This is tricky, because one `Vec` might have a bunch of empty 194 | // entries at the end which we want to ignore. 195 | let smallest_len = min(self.entries.len(), other.entries.len()); 196 | self.entries[0..smallest_len].eq(&other.entries[0..smallest_len]) 197 | } 198 | } 199 | } 200 | 201 | impl Eq for IndexMap {} 202 | 203 | impl FromIterator<(u32, T)> for IndexMap { 204 | /// Create an `IndexMap` from an iterator. 205 | /// 206 | /// Note: This API is designed for reasonably dense indices based on valid 207 | /// data. Inserting a huge `idx` will use up a lot of RAM, and this function 208 | /// will not try to protect you against that. 209 | fn from_iter(iter: I) -> Self 210 | where 211 | I: IntoIterator, 212 | { 213 | let iter = iter.into_iter(); 214 | let (lower, upper_opt) = iter.size_hint(); 215 | let mut map = IndexMap::with_capacity(upper_opt.unwrap_or(lower)); 216 | for (idx, value) in iter { 217 | map.insert(idx, value); 218 | } 219 | map 220 | } 221 | } 222 | 223 | /// An iterator over an `IndexMap` which takes ownership of it. 224 | pub struct IntoIter { 225 | next_idx: u32, 226 | remaining_len: usize, 227 | iter: vec::IntoIter>, 228 | } 229 | 230 | impl Iterator for IntoIter { 231 | type Item = (u32, T); 232 | 233 | fn size_hint(&self) -> (usize, Option) { 234 | (self.remaining_len, Some(self.remaining_len)) 235 | } 236 | 237 | fn next(&mut self) -> Option { 238 | // Bail early if we know there are no more items. This also keeps us 239 | // from repeatedly calling `self.iter.next()` once it has been 240 | // exhausted, which is not guaranteed to keep returning `None`. 241 | if self.remaining_len == 0 { 242 | return None 243 | } 244 | for value_opt in &mut self.iter { 245 | let idx = self.next_idx; 246 | self.next_idx += 1; 247 | if let Some(value) = value_opt { 248 | self.remaining_len -= 1; 249 | return Some((idx, value)) 250 | } 251 | } 252 | debug_assert_eq!(self.remaining_len, 0); 253 | None 254 | } 255 | } 256 | 257 | impl IntoIterator for IndexMap { 258 | type Item = (u32, T); 259 | type IntoIter = IntoIter; 260 | 261 | fn into_iter(self) -> IntoIter { 262 | IntoIter { next_idx: 0, remaining_len: self.len, iter: self.entries.into_iter() } 263 | } 264 | } 265 | 266 | /// An iterator over a borrowed `IndexMap`. 267 | pub struct Iter<'a, T: 'static> { 268 | next_idx: u32, 269 | remaining_len: usize, 270 | iter: slice::Iter<'a, Option>, 271 | } 272 | 273 | impl<'a, T: 'static> Iterator for Iter<'a, T> { 274 | type Item = (u32, &'a T); 275 | 276 | fn size_hint(&self) -> (usize, Option) { 277 | (self.remaining_len, Some(self.remaining_len)) 278 | } 279 | 280 | fn next(&mut self) -> Option { 281 | // Bail early if we know there are no more items. This also keeps us 282 | // from repeatedly calling `self.iter.next()` once it has been 283 | // exhausted, which is not guaranteed to keep returning `None`. 284 | if self.remaining_len == 0 { 285 | return None 286 | } 287 | for value_opt in &mut self.iter { 288 | let idx = self.next_idx; 289 | self.next_idx += 1; 290 | if let Some(ref value) = *value_opt { 291 | self.remaining_len -= 1; 292 | return Some((idx, value)) 293 | } 294 | } 295 | debug_assert_eq!(self.remaining_len, 0); 296 | None 297 | } 298 | } 299 | 300 | impl<'a, T: 'static> IntoIterator for &'a IndexMap { 301 | type Item = (u32, &'a T); 302 | type IntoIter = Iter<'a, T>; 303 | 304 | fn into_iter(self) -> Iter<'a, T> { 305 | Iter { next_idx: 0, remaining_len: self.len, iter: self.entries.iter() } 306 | } 307 | } 308 | 309 | impl Serialize for IndexMap 310 | where 311 | T: Serialize, 312 | Error: From<::Error>, 313 | { 314 | type Error = Error; 315 | 316 | fn serialize(self, wtr: &mut W) -> Result<(), Self::Error> { 317 | VarUint32::from(self.len()).serialize(wtr)?; 318 | for (idx, value) in self { 319 | VarUint32::from(idx).serialize(wtr)?; 320 | value.serialize(wtr)?; 321 | } 322 | Ok(()) 323 | } 324 | } 325 | 326 | impl IndexMap 327 | where 328 | T: Deserialize, 329 | Error: From<::Error>, 330 | { 331 | /// Deserialize a map containing simple values that support `Deserialize`. 332 | /// We will allocate an underlying array no larger than `max_entry_space` to 333 | /// hold the data, so the maximum index must be less than `max_entry_space`. 334 | pub fn deserialize(max_entry_space: usize, rdr: &mut R) -> Result { 335 | let deserialize_value: fn(u32, &mut R) -> Result = 336 | |_idx, rdr| T::deserialize(rdr).map_err(Error::from); 337 | Self::deserialize_with(max_entry_space, &deserialize_value, rdr) 338 | } 339 | } 340 | 341 | #[cfg(test)] 342 | mod tests { 343 | use super::*; 344 | use crate::io; 345 | 346 | #[test] 347 | fn default_is_empty_no_matter_how_we_look_at_it() { 348 | let map = IndexMap::::default(); 349 | assert_eq!(map.len(), 0); 350 | assert!(map.is_empty()); 351 | assert_eq!(map.iter().count(), 0); 352 | assert_eq!(map.into_iter().count(), 0); 353 | } 354 | 355 | #[test] 356 | fn with_capacity_creates_empty_map() { 357 | let map = IndexMap::::with_capacity(10); 358 | assert!(map.is_empty()); 359 | } 360 | 361 | #[test] 362 | fn clear_removes_all_values() { 363 | let mut map = IndexMap::::default(); 364 | map.insert(0, "sample value".to_string()); 365 | assert_eq!(map.len(), 1); 366 | map.clear(); 367 | assert_eq!(map.len(), 0); 368 | } 369 | 370 | #[test] 371 | fn get_returns_elements_that_are_there_but_nothing_else() { 372 | let mut map = IndexMap::::default(); 373 | map.insert(1, "sample value".to_string()); 374 | assert_eq!(map.len(), 1); 375 | assert_eq!(map.get(0), None); 376 | assert_eq!(map.get(1), Some(&"sample value".to_string())); 377 | assert_eq!(map.get(2), None); 378 | } 379 | 380 | #[test] 381 | fn contains_key_returns_true_when_a_key_is_present() { 382 | let mut map = IndexMap::::default(); 383 | map.insert(1, "sample value".to_string()); 384 | assert!(!map.contains_key(0)); 385 | assert!(map.contains_key(1)); 386 | assert!(!map.contains_key(2)); 387 | } 388 | 389 | #[test] 390 | fn insert_behaves_like_other_maps() { 391 | let mut map = IndexMap::::default(); 392 | 393 | // Insert a key which requires extending our storage. 394 | assert_eq!(map.insert(1, "val 1".to_string()), None); 395 | assert_eq!(map.len(), 1); 396 | assert!(map.contains_key(1)); 397 | 398 | // Insert a key which requires filling in a hole. 399 | assert_eq!(map.insert(0, "val 0".to_string()), None); 400 | assert_eq!(map.len(), 2); 401 | assert!(map.contains_key(0)); 402 | 403 | // Insert a key which replaces an existing key. 404 | assert_eq!(map.insert(1, "val 1.1".to_string()), Some("val 1".to_string())); 405 | assert_eq!(map.len(), 2); 406 | assert!(map.contains_key(1)); 407 | assert_eq!(map.get(1), Some(&"val 1.1".to_string())); 408 | } 409 | 410 | #[test] 411 | fn remove_behaves_like_other_maps() { 412 | let mut map = IndexMap::::default(); 413 | assert_eq!(map.insert(1, "val 1".to_string()), None); 414 | 415 | // Remove an out-of-bounds element. 416 | assert_eq!(map.remove(2), None); 417 | assert_eq!(map.len(), 1); 418 | 419 | // Remove an in-bounds but missing element. 420 | assert_eq!(map.remove(0), None); 421 | assert_eq!(map.len(), 1); 422 | 423 | // Remove an existing element. 424 | assert_eq!(map.remove(1), Some("val 1".to_string())); 425 | assert_eq!(map.len(), 0); 426 | } 427 | 428 | #[test] 429 | fn partial_eq_works_as_expected_in_simple_cases() { 430 | let mut map1 = IndexMap::::default(); 431 | let mut map2 = IndexMap::::default(); 432 | assert_eq!(map1, map2); 433 | 434 | map1.insert(1, "a".to_string()); 435 | map2.insert(1, "a".to_string()); 436 | assert_eq!(map1, map2); 437 | 438 | map1.insert(0, "b".to_string()); 439 | assert_ne!(map1, map2); 440 | map1.remove(0); 441 | assert_eq!(map1, map2); 442 | 443 | map1.insert(1, "not a".to_string()); 444 | assert_ne!(map1, map2); 445 | } 446 | 447 | #[test] 448 | fn partial_eq_is_smart_about_none_values_at_the_end() { 449 | let mut map1 = IndexMap::::default(); 450 | let mut map2 = IndexMap::::default(); 451 | 452 | map1.insert(1, "a".to_string()); 453 | map2.insert(1, "a".to_string()); 454 | 455 | // Both maps have the same (idx, value) pairs, but map2 has extra space. 456 | map2.insert(10, "b".to_string()); 457 | map2.remove(10); 458 | assert_eq!(map1, map2); 459 | 460 | // Both maps have the same (idx, value) pairs, but map1 has extra space. 461 | map1.insert(100, "b".to_string()); 462 | map1.remove(100); 463 | assert_eq!(map1, map2); 464 | 465 | // Let's be paranoid. 466 | map2.insert(1, "b".to_string()); 467 | assert_ne!(map1, map2); 468 | } 469 | 470 | #[test] 471 | fn from_iterator_builds_a_map() { 472 | let data = &[ 473 | // We support out-of-order values here! 474 | (3, "val 3"), 475 | (2, "val 2"), 476 | (5, "val 5"), 477 | ]; 478 | let iter = data.iter().map(|&(idx, val)| (idx, val.to_string())); 479 | let map = iter.collect::>(); 480 | assert_eq!(map.len(), 3); 481 | assert_eq!(map.get(2), Some(&"val 2".to_string())); 482 | assert_eq!(map.get(3), Some(&"val 3".to_string())); 483 | assert_eq!(map.get(5), Some(&"val 5".to_string())); 484 | } 485 | 486 | #[test] 487 | fn iterators_are_well_behaved() { 488 | // Create a map with reasonably complex internal structure, making 489 | // sure that we have both internal missing elements, and a bunch of 490 | // missing elements at the end. 491 | let data = &[(3, "val 3"), (2, "val 2"), (5, "val 5")]; 492 | let src_iter = data.iter().map(|&(idx, val)| (idx, val.to_string())); 493 | let mut map = src_iter.collect::>(); 494 | map.remove(5); 495 | 496 | // Make sure `size_hint` and `next` behave as we expect at each step. 497 | { 498 | let mut iter1 = map.iter(); 499 | assert_eq!(iter1.size_hint(), (2, Some(2))); 500 | assert_eq!(iter1.next(), Some((2, &"val 2".to_string()))); 501 | assert_eq!(iter1.size_hint(), (1, Some(1))); 502 | assert_eq!(iter1.next(), Some((3, &"val 3".to_string()))); 503 | assert_eq!(iter1.size_hint(), (0, Some(0))); 504 | assert_eq!(iter1.next(), None); 505 | assert_eq!(iter1.size_hint(), (0, Some(0))); 506 | assert_eq!(iter1.next(), None); 507 | assert_eq!(iter1.size_hint(), (0, Some(0))); 508 | } 509 | 510 | // Now do the same for a consuming iterator. 511 | let mut iter2 = map.into_iter(); 512 | assert_eq!(iter2.size_hint(), (2, Some(2))); 513 | assert_eq!(iter2.next(), Some((2, "val 2".to_string()))); 514 | assert_eq!(iter2.size_hint(), (1, Some(1))); 515 | assert_eq!(iter2.next(), Some((3, "val 3".to_string()))); 516 | assert_eq!(iter2.size_hint(), (0, Some(0))); 517 | assert_eq!(iter2.next(), None); 518 | assert_eq!(iter2.size_hint(), (0, Some(0))); 519 | assert_eq!(iter2.next(), None); 520 | assert_eq!(iter2.size_hint(), (0, Some(0))); 521 | } 522 | 523 | #[test] 524 | fn serialize_and_deserialize() { 525 | let mut map = IndexMap::::default(); 526 | map.insert(1, "val 1".to_string()); 527 | 528 | let mut output = vec![]; 529 | map.clone().serialize(&mut output).expect("serialize failed"); 530 | 531 | let mut input = io::Cursor::new(&output); 532 | let deserialized = IndexMap::deserialize(2, &mut input).expect("deserialize failed"); 533 | 534 | assert_eq!(deserialized, map); 535 | } 536 | 537 | #[test] 538 | fn deserialize_requires_elements_to_be_in_order() { 539 | // Build a in-order example by hand. 540 | let mut valid = vec![]; 541 | VarUint32::from(2u32).serialize(&mut valid).unwrap(); 542 | VarUint32::from(0u32).serialize(&mut valid).unwrap(); 543 | "val 0".to_string().serialize(&mut valid).unwrap(); 544 | VarUint32::from(1u32).serialize(&mut valid).unwrap(); 545 | "val 1".to_string().serialize(&mut valid).unwrap(); 546 | let map = IndexMap::::deserialize(2, &mut io::Cursor::new(valid)) 547 | .expect("unexpected error deserializing"); 548 | assert_eq!(map.len(), 2); 549 | 550 | // Build an out-of-order example by hand. 551 | let mut invalid = vec![]; 552 | VarUint32::from(2u32).serialize(&mut invalid).unwrap(); 553 | VarUint32::from(1u32).serialize(&mut invalid).unwrap(); 554 | "val 1".to_string().serialize(&mut invalid).unwrap(); 555 | VarUint32::from(0u32).serialize(&mut invalid).unwrap(); 556 | "val 0".to_string().serialize(&mut invalid).unwrap(); 557 | let res = IndexMap::::deserialize(2, &mut io::Cursor::new(invalid)); 558 | assert!(res.is_err()); 559 | } 560 | 561 | #[test] 562 | fn deserialize_enforces_max_idx() { 563 | // Build an example with an out-of-bounds index by hand. 564 | let mut invalid = vec![]; 565 | VarUint32::from(1u32).serialize(&mut invalid).unwrap(); 566 | VarUint32::from(5u32).serialize(&mut invalid).unwrap(); 567 | "val 5".to_string().serialize(&mut invalid).unwrap(); 568 | let res = IndexMap::::deserialize(1, &mut io::Cursor::new(invalid)); 569 | assert!(res.is_err()); 570 | } 571 | } 572 | -------------------------------------------------------------------------------- /src/elements/mod.rs: -------------------------------------------------------------------------------- 1 | //! Elements of the WebAssembly binary format. 2 | 3 | use crate::io; 4 | use alloc::{string::String, vec::Vec}; 5 | 6 | use core::fmt; 7 | 8 | macro_rules! buffered_read { 9 | ($buffer_size: expr, $length: expr, $reader: expr) => {{ 10 | let mut vec_buf = Vec::new(); 11 | let mut total_read = 0; 12 | let mut buf = [0u8; $buffer_size]; 13 | while total_read < $length { 14 | let next_to_read = if $length - total_read > $buffer_size { 15 | $buffer_size 16 | } else { 17 | $length - total_read 18 | }; 19 | $reader.read(&mut buf[0..next_to_read])?; 20 | vec_buf.extend_from_slice(&buf[0..next_to_read]); 21 | total_read += next_to_read; 22 | } 23 | vec_buf 24 | }}; 25 | } 26 | 27 | mod export_entry; 28 | mod func; 29 | mod global_entry; 30 | mod import_entry; 31 | mod index_map; 32 | mod module; 33 | mod name_section; 34 | mod ops; 35 | mod primitives; 36 | mod reloc_section; 37 | mod section; 38 | mod segment; 39 | mod types; 40 | 41 | pub use self::{ 42 | export_entry::{ExportEntry, Internal}, 43 | global_entry::GlobalEntry, 44 | import_entry::{External, GlobalType, ImportEntry, MemoryType, ResizableLimits, TableType}, 45 | module::{peek_size, ImportCountType, Module}, 46 | ops::{opcodes, BrTableData, InitExpr, Instruction, Instructions}, 47 | primitives::{ 48 | CountedList, CountedListWriter, CountedWriter, Uint32, Uint64, Uint8, VarInt32, VarInt64, 49 | VarInt7, VarUint1, VarUint32, VarUint64, VarUint7, 50 | }, 51 | section::{ 52 | CodeSection, CustomSection, DataSection, ElementSection, ExportSection, FunctionSection, 53 | GlobalSection, ImportSection, MemorySection, Section, TableSection, TypeSection, 54 | }, 55 | types::{BlockType, FunctionType, TableElementType, Type, ValueType}, 56 | }; 57 | 58 | #[cfg(feature = "atomics")] 59 | pub use self::ops::AtomicsInstruction; 60 | 61 | #[cfg(feature = "simd")] 62 | pub use self::ops::SimdInstruction; 63 | 64 | #[cfg(feature = "sign_ext")] 65 | pub use self::ops::SignExtInstruction; 66 | 67 | #[cfg(feature = "bulk")] 68 | pub use self::ops::BulkInstruction; 69 | 70 | #[cfg(any(feature = "simd", feature = "atomics"))] 71 | pub use self::ops::MemArg; 72 | 73 | pub use self::{ 74 | func::{Func, FuncBody, Local}, 75 | index_map::IndexMap, 76 | name_section::{ 77 | FunctionNameSubsection, LocalNameSubsection, ModuleNameSubsection, NameMap, NameSection, 78 | }, 79 | reloc_section::{RelocSection, RelocationEntry}, 80 | segment::{DataSegment, ElementSegment}, 81 | }; 82 | 83 | /// Deserialization from serial i/o. 84 | pub trait Deserialize: Sized { 85 | /// Serialization error produced by deserialization routine. 86 | type Error: From; 87 | /// Deserialize type from serial i/o 88 | fn deserialize(reader: &mut R) -> Result; 89 | } 90 | 91 | /// Serialization to serial i/o. Takes self by value to consume less memory 92 | /// (parity-wasm IR is being partially freed by filling the result buffer). 93 | pub trait Serialize { 94 | /// Serialization error produced by serialization routine. 95 | type Error: From; 96 | /// Serialize type to serial i/o 97 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error>; 98 | } 99 | 100 | /// Deserialization/serialization error 101 | #[derive(Debug, Clone)] 102 | pub enum Error { 103 | /// Unexpected end of input. 104 | UnexpectedEof, 105 | /// Invalid magic. 106 | InvalidMagic, 107 | /// Unsupported version. 108 | UnsupportedVersion(u32), 109 | /// Inconsistence between declared and actual length. 110 | InconsistentLength { 111 | /// Expected length of the definition. 112 | expected: usize, 113 | /// Actual length of the definition. 114 | actual: usize, 115 | }, 116 | /// Other static error. 117 | Other(&'static str), 118 | /// Other allocated error. 119 | HeapOther(String), 120 | /// Invalid/unknown value type declaration. 121 | UnknownValueType(i8), 122 | /// Invalid block type declaration. 123 | UnknownBlockType(i32), 124 | /// Invalid/unknown table element type declaration. 125 | UnknownTableElementType(i8), 126 | /// Non-utf8 string. 127 | NonUtf8String, 128 | /// Unknown external kind code. 129 | UnknownExternalKind(u8), 130 | /// Unknown internal kind code. 131 | UnknownInternalKind(u8), 132 | /// Unknown opcode encountered. 133 | UnknownOpcode(u8), 134 | #[cfg(feature = "simd")] 135 | /// Unknown SIMD opcode encountered. 136 | UnknownSimdOpcode(u32), 137 | /// Invalid VarUint1 value. 138 | InvalidVarUint1(u8), 139 | /// Invalid VarInt32 value. 140 | InvalidVarInt32, 141 | /// Invalid VarInt64 value. 142 | InvalidVarInt64, 143 | /// Invalid VarUint32 value. 144 | InvalidVarUint32, 145 | /// Invalid VarUint64 value. 146 | InvalidVarUint64, 147 | /// Inconsistent metadata. 148 | InconsistentMetadata, 149 | /// Invalid section id. 150 | InvalidSectionId(u8), 151 | /// Sections are out of order. 152 | SectionsOutOfOrder, 153 | /// Duplicated sections. 154 | DuplicatedSections(u8), 155 | /// Invalid memory reference (should be 0). 156 | InvalidMemoryReference(u8), 157 | /// Invalid table reference (should be 0). 158 | InvalidTableReference(u8), 159 | /// Invalid value used for flags in limits type. 160 | InvalidLimitsFlags(u8), 161 | /// Unknown function form (should be 0x60). 162 | UnknownFunctionForm(u8), 163 | /// Invalid varint7 (should be in -64..63 range). 164 | InvalidVarInt7(u8), 165 | /// Number of function body entries and signatures does not match. 166 | InconsistentCode, 167 | /// Only flags 0, 1, and 2 are accepted on segments. 168 | InvalidSegmentFlags(u32), 169 | /// Sum of counts of locals is greater than 2^32. 170 | TooManyLocals, 171 | /// Duplicated name subsections. 172 | DuplicatedNameSubsections(u8), 173 | /// Unknown name subsection type. 174 | UnknownNameSubsectionType(u8), 175 | } 176 | 177 | impl fmt::Display for Error { 178 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 179 | match *self { 180 | Error::UnexpectedEof => write!(f, "Unexpected end of input"), 181 | Error::InvalidMagic => write!(f, "Invalid magic number at start of file"), 182 | Error::UnsupportedVersion(v) => write!(f, "Unsupported wasm version {}", v), 183 | Error::InconsistentLength { expected, actual } => { 184 | write!(f, "Expected length {}, found {}", expected, actual) 185 | }, 186 | Error::Other(msg) => write!(f, "{}", msg), 187 | Error::HeapOther(ref msg) => write!(f, "{}", msg), 188 | Error::UnknownValueType(ty) => write!(f, "Invalid or unknown value type {}", ty), 189 | Error::UnknownBlockType(ty) => write!(f, "Invalid or unknown block type {}", ty), 190 | Error::UnknownTableElementType(ty) => write!(f, "Unknown table element type {}", ty), 191 | Error::NonUtf8String => write!(f, "Non-UTF-8 string"), 192 | Error::UnknownExternalKind(kind) => write!(f, "Unknown external kind {}", kind), 193 | Error::UnknownInternalKind(kind) => write!(f, "Unknown internal kind {}", kind), 194 | Error::UnknownOpcode(opcode) => write!(f, "Unknown opcode {}", opcode), 195 | #[cfg(feature = "simd")] 196 | Error::UnknownSimdOpcode(opcode) => write!(f, "Unknown SIMD opcode {}", opcode), 197 | Error::InvalidVarUint1(val) => write!(f, "Not an unsigned 1-bit integer: {}", val), 198 | Error::InvalidVarInt7(val) => write!(f, "Not a signed 7-bit integer: {}", val), 199 | Error::InvalidVarInt32 => write!(f, "Not a signed 32-bit integer"), 200 | Error::InvalidVarUint32 => write!(f, "Not an unsigned 32-bit integer"), 201 | Error::InvalidVarInt64 => write!(f, "Not a signed 64-bit integer"), 202 | Error::InvalidVarUint64 => write!(f, "Not an unsigned 64-bit integer"), 203 | Error::InconsistentMetadata => write!(f, "Inconsistent metadata"), 204 | Error::InvalidSectionId(ref id) => write!(f, "Invalid section id: {}", id), 205 | Error::SectionsOutOfOrder => write!(f, "Sections out of order"), 206 | Error::DuplicatedSections(ref id) => write!(f, "Duplicated sections ({})", id), 207 | Error::InvalidMemoryReference(ref mem_ref) => 208 | write!(f, "Invalid memory reference ({})", mem_ref), 209 | Error::InvalidTableReference(ref table_ref) => 210 | write!(f, "Invalid table reference ({})", table_ref), 211 | Error::InvalidLimitsFlags(ref flags) => write!(f, "Invalid limits flags ({})", flags), 212 | Error::UnknownFunctionForm(ref form) => write!(f, "Unknown function form ({})", form), 213 | Error::InconsistentCode => 214 | write!(f, "Number of function body entries and signatures does not match"), 215 | Error::InvalidSegmentFlags(n) => write!(f, "Invalid segment flags: {}", n), 216 | Error::TooManyLocals => write!(f, "Too many locals"), 217 | Error::DuplicatedNameSubsections(n) => write!(f, "Duplicated name subsections: {}", n), 218 | Error::UnknownNameSubsectionType(n) => write!(f, "Unknown subsection type: {}", n), 219 | } 220 | } 221 | } 222 | 223 | #[cfg(feature = "std")] 224 | impl ::std::error::Error for Error { 225 | fn description(&self) -> &str { 226 | match *self { 227 | Error::UnexpectedEof => "Unexpected end of input", 228 | Error::InvalidMagic => "Invalid magic number at start of file", 229 | Error::UnsupportedVersion(_) => "Unsupported wasm version", 230 | Error::InconsistentLength { .. } => "Inconsistent length", 231 | Error::Other(msg) => msg, 232 | Error::HeapOther(ref msg) => &msg[..], 233 | Error::UnknownValueType(_) => "Invalid or unknown value type", 234 | Error::UnknownBlockType(_) => "Invalid or unknown block type", 235 | Error::UnknownTableElementType(_) => "Unknown table element type", 236 | Error::NonUtf8String => "Non-UTF-8 string", 237 | Error::UnknownExternalKind(_) => "Unknown external kind", 238 | Error::UnknownInternalKind(_) => "Unknown internal kind", 239 | Error::UnknownOpcode(_) => "Unknown opcode", 240 | #[cfg(feature = "simd")] 241 | Error::UnknownSimdOpcode(_) => "Unknown SIMD opcode", 242 | Error::InvalidVarUint1(_) => "Not an unsigned 1-bit integer", 243 | Error::InvalidVarInt32 => "Not a signed 32-bit integer", 244 | Error::InvalidVarInt7(_) => "Not a signed 7-bit integer", 245 | Error::InvalidVarUint32 => "Not an unsigned 32-bit integer", 246 | Error::InvalidVarInt64 => "Not a signed 64-bit integer", 247 | Error::InvalidVarUint64 => "Not an unsigned 64-bit integer", 248 | Error::InconsistentMetadata => "Inconsistent metadata", 249 | Error::InvalidSectionId(_) => "Invalid section id", 250 | Error::SectionsOutOfOrder => "Sections out of order", 251 | Error::DuplicatedSections(_) => "Duplicated section", 252 | Error::InvalidMemoryReference(_) => "Invalid memory reference", 253 | Error::InvalidTableReference(_) => "Invalid table reference", 254 | Error::InvalidLimitsFlags(_) => "Invalid limits flags", 255 | Error::UnknownFunctionForm(_) => "Unknown function form", 256 | Error::InconsistentCode => 257 | "Number of function body entries and signatures does not match", 258 | Error::InvalidSegmentFlags(_) => "Invalid segment flags", 259 | Error::TooManyLocals => "Too many locals", 260 | Error::DuplicatedNameSubsections(_) => "Duplicated name subsections", 261 | Error::UnknownNameSubsectionType(_) => "Unknown name subsections type", 262 | } 263 | } 264 | } 265 | 266 | impl From for Error { 267 | fn from(err: io::Error) -> Self { 268 | Error::HeapOther(format!("I/O Error: {:?}", err)) 269 | } 270 | } 271 | 272 | // These are emitted by section parsers, such as `parse_names` and `parse_reloc`. 273 | impl From<(Vec<(usize, Error)>, Module)> for Error { 274 | fn from(err: (Vec<(usize, Error)>, Module)) -> Self { 275 | let ret = err.0.iter().fold(String::new(), |mut acc, item| { 276 | acc.push_str(&format!("In section {}: {}\n", item.0, item.1)); 277 | acc 278 | }); 279 | Error::HeapOther(ret) 280 | } 281 | } 282 | 283 | /// Unparsed part of the module/section. 284 | pub struct Unparsed(pub Vec); 285 | 286 | impl Deserialize for Unparsed { 287 | type Error = Error; 288 | 289 | fn deserialize(reader: &mut R) -> Result { 290 | let len = VarUint32::deserialize(reader)?.into(); 291 | let mut vec = vec![0u8; len]; 292 | reader.read(&mut vec[..])?; 293 | Ok(Unparsed(vec)) 294 | } 295 | } 296 | 297 | impl From for Vec { 298 | fn from(u: Unparsed) -> Vec { 299 | u.0 300 | } 301 | } 302 | 303 | /// Deserialize deserializable type from buffer. 304 | pub fn deserialize_buffer(contents: &[u8]) -> Result { 305 | let mut reader = io::Cursor::new(contents); 306 | let result = T::deserialize(&mut reader)?; 307 | if reader.position() != contents.len() { 308 | // It's a TrailingData, since if there is not enough data then 309 | // UnexpectedEof must have been returned earlier in T::deserialize. 310 | return Err(io::Error::TrailingData.into()) 311 | } 312 | Ok(result) 313 | } 314 | 315 | /// Create buffer with serialized value. 316 | pub fn serialize(val: T) -> Result, T::Error> { 317 | let mut buf = Vec::new(); 318 | val.serialize(&mut buf)?; 319 | Ok(buf) 320 | } 321 | 322 | /// Deserialize module from the file. 323 | #[cfg(feature = "std")] 324 | pub fn deserialize_file>(p: P) -> Result { 325 | let mut f = ::std::fs::File::open(p) 326 | .map_err(|e| Error::HeapOther(format!("Can't read from the file: {:?}", e)))?; 327 | 328 | Module::deserialize(&mut f) 329 | } 330 | 331 | /// Serialize module to the file 332 | #[cfg(feature = "std")] 333 | pub fn serialize_to_file>(p: P, module: Module) -> Result<(), Error> { 334 | let mut io = ::std::fs::File::create(p) 335 | .map_err(|e| Error::HeapOther(format!("Can't create the file: {:?}", e)))?; 336 | 337 | module.serialize(&mut io)?; 338 | Ok(()) 339 | } 340 | -------------------------------------------------------------------------------- /src/elements/name_section.rs: -------------------------------------------------------------------------------- 1 | use crate::io; 2 | use alloc::string::String; 3 | 4 | use super::{ 5 | index_map::IndexMap, Deserialize, Error, Module, Serialize, Type, VarUint32, VarUint7, 6 | }; 7 | 8 | const NAME_TYPE_MODULE: u8 = 0; 9 | const NAME_TYPE_FUNCTION: u8 = 1; 10 | const NAME_TYPE_LOCAL: u8 = 2; 11 | 12 | /// Debug name information. 13 | #[derive(Clone, Debug, PartialEq)] 14 | pub struct NameSection { 15 | /// Module name subsection. 16 | module: Option, 17 | 18 | /// Function name subsection. 19 | functions: Option, 20 | 21 | /// Local name subsection. 22 | locals: Option, 23 | } 24 | 25 | impl NameSection { 26 | /// Creates a new name section. 27 | pub fn new( 28 | module: Option, 29 | functions: Option, 30 | locals: Option, 31 | ) -> Self { 32 | Self { module, functions, locals } 33 | } 34 | 35 | /// Module name subsection of this section. 36 | pub fn module(&self) -> Option<&ModuleNameSubsection> { 37 | self.module.as_ref() 38 | } 39 | 40 | /// Module name subsection of this section (mutable). 41 | pub fn module_mut(&mut self) -> &mut Option { 42 | &mut self.module 43 | } 44 | 45 | /// Functions name subsection of this section. 46 | pub fn functions(&self) -> Option<&FunctionNameSubsection> { 47 | self.functions.as_ref() 48 | } 49 | 50 | /// Functions name subsection of this section (mutable). 51 | pub fn functions_mut(&mut self) -> &mut Option { 52 | &mut self.functions 53 | } 54 | 55 | /// Local name subsection of this section. 56 | pub fn locals(&self) -> Option<&LocalNameSubsection> { 57 | self.locals.as_ref() 58 | } 59 | 60 | /// Local name subsection of this section (mutable). 61 | pub fn locals_mut(&mut self) -> &mut Option { 62 | &mut self.locals 63 | } 64 | } 65 | 66 | impl NameSection { 67 | /// Deserialize a name section. 68 | pub fn deserialize(module: &Module, rdr: &mut R) -> Result { 69 | let mut module_name: Option = None; 70 | let mut function_names: Option = None; 71 | let mut local_names: Option = None; 72 | 73 | while let Ok(raw_subsection_type) = VarUint7::deserialize(rdr) { 74 | let subsection_type = raw_subsection_type.into(); 75 | // deserialize the section size 76 | let size: usize = VarUint32::deserialize(rdr)?.into(); 77 | 78 | match subsection_type { 79 | NAME_TYPE_MODULE => { 80 | if module_name.is_some() { 81 | return Err(Error::DuplicatedNameSubsections(NAME_TYPE_FUNCTION)) 82 | } 83 | module_name = Some(ModuleNameSubsection::deserialize(rdr)?); 84 | }, 85 | 86 | NAME_TYPE_FUNCTION => { 87 | if function_names.is_some() { 88 | return Err(Error::DuplicatedNameSubsections(NAME_TYPE_FUNCTION)) 89 | } 90 | function_names = Some(FunctionNameSubsection::deserialize(module, rdr)?); 91 | }, 92 | 93 | NAME_TYPE_LOCAL => { 94 | if local_names.is_some() { 95 | return Err(Error::DuplicatedNameSubsections(NAME_TYPE_LOCAL)) 96 | } 97 | local_names = Some(LocalNameSubsection::deserialize(module, rdr)?); 98 | }, 99 | 100 | _ => { 101 | // Consume the entire subsection size and drop it. This allows other sections to still be 102 | // consumed if there are any. 103 | let mut buf = vec![0; size]; 104 | rdr.read(&mut buf)?; 105 | }, 106 | }; 107 | } 108 | 109 | Ok(Self { module: module_name, functions: function_names, locals: local_names }) 110 | } 111 | } 112 | 113 | impl Serialize for NameSection { 114 | type Error = Error; 115 | 116 | fn serialize(self, wtr: &mut W) -> Result<(), Error> { 117 | fn serialize_subsection( 118 | wtr: &mut W, 119 | name_type: u8, 120 | name_payload: &[u8], 121 | ) -> Result<(), Error> { 122 | VarUint7::from(name_type).serialize(wtr)?; 123 | VarUint32::from(name_payload.len()).serialize(wtr)?; 124 | wtr.write(name_payload).map_err(Into::into) 125 | } 126 | 127 | if let Some(module_name_subsection) = self.module { 128 | let mut buffer = vec![]; 129 | module_name_subsection.serialize(&mut buffer)?; 130 | serialize_subsection(wtr, NAME_TYPE_MODULE, &buffer)?; 131 | } 132 | 133 | if let Some(function_name_subsection) = self.functions { 134 | let mut buffer = vec![]; 135 | function_name_subsection.serialize(&mut buffer)?; 136 | serialize_subsection(wtr, NAME_TYPE_FUNCTION, &buffer)?; 137 | } 138 | 139 | if let Some(local_name_subsection) = self.locals { 140 | let mut buffer = vec![]; 141 | local_name_subsection.serialize(&mut buffer)?; 142 | serialize_subsection(wtr, NAME_TYPE_LOCAL, &buffer)?; 143 | } 144 | 145 | Ok(()) 146 | } 147 | } 148 | 149 | /// The name of this module. 150 | #[derive(Clone, Debug, PartialEq)] 151 | pub struct ModuleNameSubsection { 152 | name: String, 153 | } 154 | 155 | impl ModuleNameSubsection { 156 | /// Create a new module name section with the specified name. 157 | pub fn new>(name: S) -> ModuleNameSubsection { 158 | ModuleNameSubsection { name: name.into() } 159 | } 160 | 161 | /// The name of this module. 162 | pub fn name(&self) -> &str { 163 | &self.name 164 | } 165 | 166 | /// The name of this module (mutable). 167 | pub fn name_mut(&mut self) -> &mut String { 168 | &mut self.name 169 | } 170 | } 171 | 172 | impl Serialize for ModuleNameSubsection { 173 | type Error = Error; 174 | 175 | fn serialize(self, wtr: &mut W) -> Result<(), Error> { 176 | self.name.serialize(wtr) 177 | } 178 | } 179 | 180 | impl Deserialize for ModuleNameSubsection { 181 | type Error = Error; 182 | 183 | fn deserialize(rdr: &mut R) -> Result { 184 | let name = String::deserialize(rdr)?; 185 | Ok(ModuleNameSubsection { name }) 186 | } 187 | } 188 | 189 | /// The names of the functions in this module. 190 | #[derive(Clone, Debug, Default, PartialEq)] 191 | pub struct FunctionNameSubsection { 192 | names: NameMap, 193 | } 194 | 195 | impl FunctionNameSubsection { 196 | /// A map from function indices to names. 197 | pub fn names(&self) -> &NameMap { 198 | &self.names 199 | } 200 | 201 | /// A map from function indices to names (mutable). 202 | pub fn names_mut(&mut self) -> &mut NameMap { 203 | &mut self.names 204 | } 205 | 206 | /// Deserialize names, making sure that all names correspond to functions. 207 | pub fn deserialize( 208 | module: &Module, 209 | rdr: &mut R, 210 | ) -> Result { 211 | let names = IndexMap::deserialize(module.functions_space(), rdr)?; 212 | Ok(FunctionNameSubsection { names }) 213 | } 214 | } 215 | 216 | impl Serialize for FunctionNameSubsection { 217 | type Error = Error; 218 | 219 | fn serialize(self, wtr: &mut W) -> Result<(), Error> { 220 | self.names.serialize(wtr) 221 | } 222 | } 223 | 224 | /// The names of the local variables in this module's functions. 225 | #[derive(Clone, Debug, Default, PartialEq)] 226 | pub struct LocalNameSubsection { 227 | local_names: IndexMap, 228 | } 229 | 230 | impl LocalNameSubsection { 231 | /// A map from function indices to a map from variables indices to names. 232 | pub fn local_names(&self) -> &IndexMap { 233 | &self.local_names 234 | } 235 | 236 | /// A map from function indices to a map from variables indices to names 237 | /// (mutable). 238 | pub fn local_names_mut(&mut self) -> &mut IndexMap { 239 | &mut self.local_names 240 | } 241 | 242 | /// Deserialize names, making sure that all names correspond to local 243 | /// variables. 244 | pub fn deserialize( 245 | module: &Module, 246 | rdr: &mut R, 247 | ) -> Result { 248 | let max_entry_space = module.functions_space(); 249 | 250 | let max_signature_args = module 251 | .type_section() 252 | .map(|ts| { 253 | ts.types() 254 | .iter() 255 | .map(|x| { 256 | let Type::Function(ref func) = *x; 257 | func.params().len() 258 | }) 259 | .max() 260 | .unwrap_or(0) 261 | }) 262 | .unwrap_or(0); 263 | 264 | let max_locals = module 265 | .code_section() 266 | .map(|cs| { 267 | cs.bodies() 268 | .iter() 269 | .map(|f| f.locals().iter().map(|l| l.count() as usize).sum()) 270 | .max() 271 | .unwrap_or(0) 272 | }) 273 | .unwrap_or(0); 274 | 275 | let max_space = max_signature_args + max_locals; 276 | 277 | let deserialize_locals = |_: u32, rdr: &mut R| IndexMap::deserialize(max_space, rdr); 278 | 279 | let local_names = IndexMap::deserialize_with(max_entry_space, &deserialize_locals, rdr)?; 280 | Ok(LocalNameSubsection { local_names }) 281 | } 282 | } 283 | 284 | impl Serialize for LocalNameSubsection { 285 | type Error = Error; 286 | 287 | fn serialize(self, wtr: &mut W) -> Result<(), Error> { 288 | self.local_names.serialize(wtr) 289 | } 290 | } 291 | 292 | /// A map from indices to names. 293 | pub type NameMap = IndexMap; 294 | 295 | #[cfg(test)] 296 | mod tests { 297 | use super::*; 298 | 299 | // A helper function for the tests. Serialize a section, deserialize it, 300 | // and make sure it matches the original. 301 | fn serialize_test(original: NameSection) -> Vec { 302 | let mut buffer = vec![]; 303 | original.serialize(&mut buffer).expect("serialize error"); 304 | buffer 305 | // todo: add deserialization to this test 306 | } 307 | 308 | #[test] 309 | fn serialize_module_name() { 310 | let module_name_subsection = ModuleNameSubsection::new("my_mod"); 311 | let original = NameSection::new(Some(module_name_subsection), None, None); 312 | serialize_test(original); 313 | } 314 | 315 | #[test] 316 | fn serialize_function_names() { 317 | let mut function_name_subsection = FunctionNameSubsection::default(); 318 | function_name_subsection.names_mut().insert(0, "hello_world".to_string()); 319 | let name_section = NameSection::new(None, Some(function_name_subsection), None); 320 | serialize_test(name_section); 321 | } 322 | 323 | #[test] 324 | fn serialize_local_names() { 325 | let mut local_name_subsection = LocalNameSubsection::default(); 326 | let mut locals = NameMap::default(); 327 | locals.insert(0, "msg".to_string()); 328 | local_name_subsection.local_names_mut().insert(0, locals); 329 | 330 | let name_section = NameSection::new(None, None, Some(local_name_subsection)); 331 | serialize_test(name_section); 332 | } 333 | 334 | #[test] 335 | fn serialize_all_subsections() { 336 | let module_name_subsection = ModuleNameSubsection::new("ModuleNameSubsection"); 337 | 338 | let mut function_name_subsection = FunctionNameSubsection::default(); 339 | function_name_subsection.names_mut().insert(0, "foo".to_string()); 340 | function_name_subsection.names_mut().insert(1, "bar".to_string()); 341 | 342 | let mut local_name_subsection = LocalNameSubsection::default(); 343 | let mut locals = NameMap::default(); 344 | locals.insert(0, "msg1".to_string()); 345 | locals.insert(1, "msg2".to_string()); 346 | local_name_subsection.local_names_mut().insert(0, locals); 347 | 348 | let name_section = NameSection::new( 349 | Some(module_name_subsection), 350 | Some(function_name_subsection), 351 | Some(local_name_subsection), 352 | ); 353 | serialize_test(name_section); 354 | } 355 | 356 | #[test] 357 | fn deserialize_local_names() { 358 | let module = super::super::deserialize_file("./res/cases/v1/names_with_imports.wasm") 359 | .expect("Should be deserialized") 360 | .parse_names() 361 | .expect("Names to be parsed"); 362 | 363 | let name_section = module.names_section().expect("name_section should be present"); 364 | let local_names = name_section.locals().expect("local_name_section should be present"); 365 | 366 | let locals = local_names.local_names().get(0).expect("entry #0 should be present"); 367 | assert_eq!(locals.get(0).expect("entry #0 should be present"), "abc"); 368 | 369 | let locals = local_names.local_names().get(1).expect("entry #1 should be present"); 370 | assert_eq!(locals.get(0).expect("entry #0 should be present"), "def"); 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /src/elements/primitives.rs: -------------------------------------------------------------------------------- 1 | use super::{Deserialize, Error, Serialize}; 2 | use crate::{elements, io}; 3 | use alloc::{string::String, vec::Vec}; 4 | 5 | #[cfg(feature = "reduced-stack-buffer")] 6 | const PRIMITIVES_BUFFER_LENGTH: usize = 256; 7 | 8 | #[cfg(not(feature = "reduced-stack-buffer"))] 9 | const PRIMITIVES_BUFFER_LENGTH: usize = 1024; 10 | 11 | /// Unsigned variable-length integer, limited to 32 bits, 12 | /// represented by at most 5 bytes that may contain padding 0x80 bytes. 13 | #[derive(Debug, Copy, Clone, PartialEq)] 14 | pub struct VarUint32(u32); 15 | 16 | impl From for usize { 17 | fn from(var: VarUint32) -> usize { 18 | var.0 as usize 19 | } 20 | } 21 | 22 | impl From for u32 { 23 | fn from(var: VarUint32) -> u32 { 24 | var.0 25 | } 26 | } 27 | 28 | impl From for VarUint32 { 29 | fn from(i: u32) -> VarUint32 { 30 | VarUint32(i) 31 | } 32 | } 33 | 34 | impl From for VarUint32 { 35 | fn from(i: usize) -> VarUint32 { 36 | assert!(i <= u32::max_value() as usize); 37 | VarUint32(i as u32) 38 | } 39 | } 40 | 41 | impl Deserialize for VarUint32 { 42 | type Error = Error; 43 | 44 | fn deserialize(reader: &mut R) -> Result { 45 | let mut res = 0; 46 | let mut shift = 0; 47 | let mut u8buf = [0u8; 1]; 48 | loop { 49 | if shift > 31 { 50 | return Err(Error::InvalidVarUint32) 51 | } 52 | 53 | reader.read(&mut u8buf)?; 54 | let b = u8buf[0] as u32; 55 | res |= (b & 0x7f).checked_shl(shift).ok_or(Error::InvalidVarUint32)?; 56 | shift += 7; 57 | if (b >> 7) == 0 { 58 | if shift >= 32 && (b as u8).leading_zeros() < 4 { 59 | return Err(Error::InvalidVarInt32) 60 | } 61 | break 62 | } 63 | } 64 | Ok(VarUint32(res)) 65 | } 66 | } 67 | 68 | impl Serialize for VarUint32 { 69 | type Error = Error; 70 | 71 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 72 | let mut buf = [0u8; 1]; 73 | let mut v = self.0; 74 | loop { 75 | buf[0] = (v & 0b0111_1111) as u8; 76 | v >>= 7; 77 | if v > 0 { 78 | buf[0] |= 0b1000_0000; 79 | } 80 | writer.write(&buf[..])?; 81 | if v == 0 { 82 | break 83 | } 84 | } 85 | 86 | Ok(()) 87 | } 88 | } 89 | 90 | /// Unsigned variable-length integer, limited to 64 bits, 91 | /// represented by at most 9 bytes that may contain padding 0x80 bytes. 92 | #[derive(Debug, Copy, Clone, PartialEq)] 93 | pub struct VarUint64(u64); 94 | 95 | impl From for u64 { 96 | fn from(var: VarUint64) -> u64 { 97 | var.0 98 | } 99 | } 100 | 101 | impl Deserialize for VarUint64 { 102 | type Error = Error; 103 | 104 | fn deserialize(reader: &mut R) -> Result { 105 | let mut res = 0; 106 | let mut shift = 0; 107 | let mut u8buf = [0u8; 1]; 108 | loop { 109 | if shift > 63 { 110 | return Err(Error::InvalidVarUint64) 111 | } 112 | 113 | reader.read(&mut u8buf)?; 114 | let b = u8buf[0] as u64; 115 | res |= (b & 0x7f).checked_shl(shift).ok_or(Error::InvalidVarUint64)?; 116 | shift += 7; 117 | if (b >> 7) == 0 { 118 | if shift >= 64 && (b as u8).leading_zeros() < 7 { 119 | return Err(Error::InvalidVarInt64) 120 | } 121 | break 122 | } 123 | } 124 | Ok(VarUint64(res)) 125 | } 126 | } 127 | 128 | impl Serialize for VarUint64 { 129 | type Error = Error; 130 | 131 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 132 | let mut buf = [0u8; 1]; 133 | let mut v = self.0; 134 | loop { 135 | buf[0] = (v & 0b0111_1111) as u8; 136 | v >>= 7; 137 | if v > 0 { 138 | buf[0] |= 0b1000_0000; 139 | } 140 | writer.write(&buf[..])?; 141 | if v == 0 { 142 | break 143 | } 144 | } 145 | 146 | Ok(()) 147 | } 148 | } 149 | 150 | impl From for VarUint64 { 151 | fn from(u: u64) -> VarUint64 { 152 | VarUint64(u) 153 | } 154 | } 155 | 156 | /// 7-bit unsigned integer, encoded in LEB128 (always 1 byte length). 157 | #[derive(Debug, Copy, Clone, PartialEq)] 158 | pub struct VarUint7(u8); 159 | 160 | impl From for u8 { 161 | fn from(v: VarUint7) -> u8 { 162 | v.0 163 | } 164 | } 165 | 166 | impl From for VarUint7 { 167 | fn from(v: u8) -> Self { 168 | VarUint7(v) 169 | } 170 | } 171 | 172 | impl Deserialize for VarUint7 { 173 | type Error = Error; 174 | 175 | fn deserialize(reader: &mut R) -> Result { 176 | let mut u8buf = [0u8; 1]; 177 | reader.read(&mut u8buf)?; 178 | Ok(VarUint7(u8buf[0])) 179 | } 180 | } 181 | 182 | impl Serialize for VarUint7 { 183 | type Error = Error; 184 | 185 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 186 | // todo check range? 187 | writer.write(&[self.0])?; 188 | Ok(()) 189 | } 190 | } 191 | 192 | /// 7-bit signed integer, encoded in LEB128 (always 1 byte length) 193 | #[derive(Debug, Copy, Clone, PartialEq)] 194 | pub struct VarInt7(i8); 195 | 196 | impl From for i8 { 197 | fn from(v: VarInt7) -> i8 { 198 | v.0 199 | } 200 | } 201 | 202 | impl From for VarInt7 { 203 | fn from(v: i8) -> VarInt7 { 204 | VarInt7(v) 205 | } 206 | } 207 | 208 | impl Deserialize for VarInt7 { 209 | type Error = Error; 210 | 211 | fn deserialize(reader: &mut R) -> Result { 212 | let mut u8buf = [0u8; 1]; 213 | reader.read(&mut u8buf)?; 214 | 215 | // check if number is not continued! 216 | if u8buf[0] & 0b1000_0000 != 0 { 217 | return Err(Error::InvalidVarInt7(u8buf[0])) 218 | } 219 | 220 | // expand sign 221 | if u8buf[0] & 0b0100_0000 == 0b0100_0000 { 222 | u8buf[0] |= 0b1000_0000 223 | } 224 | 225 | Ok(VarInt7(u8buf[0] as i8)) 226 | } 227 | } 228 | 229 | impl Serialize for VarInt7 { 230 | type Error = Error; 231 | 232 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 233 | // todo check range? 234 | let mut b: u8 = self.0 as u8; 235 | if self.0 < 0 { 236 | b |= 0b0100_0000; 237 | b &= 0b0111_1111; 238 | } 239 | writer.write(&[b])?; 240 | Ok(()) 241 | } 242 | } 243 | 244 | /// 8-bit unsigned integer, NOT encoded in LEB128; 245 | /// it's just a single byte. 246 | #[derive(Debug, Copy, Clone, PartialEq)] 247 | pub struct Uint8(u8); 248 | 249 | impl From for u8 { 250 | fn from(v: Uint8) -> u8 { 251 | v.0 252 | } 253 | } 254 | 255 | impl From for Uint8 { 256 | fn from(v: u8) -> Self { 257 | Uint8(v) 258 | } 259 | } 260 | 261 | impl Deserialize for Uint8 { 262 | type Error = Error; 263 | 264 | fn deserialize(reader: &mut R) -> Result { 265 | let mut u8buf = [0u8; 1]; 266 | reader.read(&mut u8buf)?; 267 | Ok(Uint8(u8buf[0])) 268 | } 269 | } 270 | 271 | impl Serialize for Uint8 { 272 | type Error = Error; 273 | 274 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 275 | writer.write(&[self.0])?; 276 | Ok(()) 277 | } 278 | } 279 | 280 | /// 32-bit signed integer, encoded in LEB128 (can be 1-5 bytes length). 281 | #[derive(Debug, Copy, Clone, PartialEq)] 282 | pub struct VarInt32(i32); 283 | 284 | impl From for i32 { 285 | fn from(v: VarInt32) -> i32 { 286 | v.0 287 | } 288 | } 289 | 290 | impl From for VarInt32 { 291 | fn from(v: i32) -> VarInt32 { 292 | VarInt32(v) 293 | } 294 | } 295 | 296 | impl Deserialize for VarInt32 { 297 | type Error = Error; 298 | 299 | fn deserialize(reader: &mut R) -> Result { 300 | let mut res = 0; 301 | let mut shift = 0; 302 | let mut u8buf = [0u8; 1]; 303 | loop { 304 | if shift > 31 { 305 | return Err(Error::InvalidVarInt32) 306 | } 307 | reader.read(&mut u8buf)?; 308 | let b = u8buf[0]; 309 | 310 | res |= ((b & 0x7f) as i32).checked_shl(shift).ok_or(Error::InvalidVarInt32)?; 311 | 312 | shift += 7; 313 | if (b >> 7) == 0 { 314 | if shift < 32 && b & 0b0100_0000 == 0b0100_0000 { 315 | res |= (1i32 << shift).wrapping_neg(); 316 | } else if shift >= 32 && b & 0b0100_0000 == 0b0100_0000 { 317 | if (!(b | 0b1000_0000)).leading_zeros() < 5 { 318 | return Err(Error::InvalidVarInt32) 319 | } 320 | } else if shift >= 32 && b & 0b0100_0000 == 0 && b.leading_zeros() < 5 { 321 | return Err(Error::InvalidVarInt32) 322 | } 323 | break 324 | } 325 | } 326 | Ok(VarInt32(res)) 327 | } 328 | } 329 | 330 | impl Serialize for VarInt32 { 331 | type Error = Error; 332 | 333 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 334 | let mut buf = [0u8; 1]; 335 | let mut v = self.0; 336 | let mut more = true; 337 | while more { 338 | buf[0] = (v & 0b0111_1111) as u8; 339 | v >>= 7; 340 | if (v == 0 && buf[0] & 0b0100_0000 == 0) || 341 | (v == -1 && buf[0] & 0b0100_0000 == 0b0100_0000) 342 | { 343 | more = false 344 | } else { 345 | buf[0] |= 0b1000_0000 346 | } 347 | 348 | writer.write(&buf[..])?; 349 | } 350 | 351 | Ok(()) 352 | } 353 | } 354 | 355 | /// 64-bit signed integer, encoded in LEB128 (can be 1-9 bytes length). 356 | #[derive(Debug, Copy, Clone, PartialEq)] 357 | pub struct VarInt64(i64); 358 | 359 | impl From for i64 { 360 | fn from(v: VarInt64) -> i64 { 361 | v.0 362 | } 363 | } 364 | 365 | impl From for VarInt64 { 366 | fn from(v: i64) -> VarInt64 { 367 | VarInt64(v) 368 | } 369 | } 370 | 371 | impl Deserialize for VarInt64 { 372 | type Error = Error; 373 | 374 | fn deserialize(reader: &mut R) -> Result { 375 | let mut res = 0i64; 376 | let mut shift = 0; 377 | let mut u8buf = [0u8; 1]; 378 | 379 | loop { 380 | if shift > 63 { 381 | return Err(Error::InvalidVarInt64) 382 | } 383 | reader.read(&mut u8buf)?; 384 | let b = u8buf[0]; 385 | 386 | res |= ((b & 0x7f) as i64).checked_shl(shift).ok_or(Error::InvalidVarInt64)?; 387 | 388 | shift += 7; 389 | if (b >> 7) == 0 { 390 | if shift < 64 && b & 0b0100_0000 == 0b0100_0000 { 391 | res |= (1i64 << shift).wrapping_neg(); 392 | } else if shift >= 64 && b & 0b0100_0000 == 0b0100_0000 { 393 | if (b | 0b1000_0000) as i8 != -1 { 394 | return Err(Error::InvalidVarInt64) 395 | } 396 | } else if shift >= 64 && b != 0 { 397 | return Err(Error::InvalidVarInt64) 398 | } 399 | break 400 | } 401 | } 402 | Ok(VarInt64(res)) 403 | } 404 | } 405 | 406 | impl Serialize for VarInt64 { 407 | type Error = Error; 408 | 409 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 410 | let mut buf = [0u8; 1]; 411 | let mut v = self.0; 412 | let mut more = true; 413 | while more { 414 | buf[0] = (v & 0b0111_1111) as u8; 415 | v >>= 7; 416 | if (v == 0 && buf[0] & 0b0100_0000 == 0) || 417 | (v == -1 && buf[0] & 0b0100_0000 == 0b0100_0000) 418 | { 419 | more = false 420 | } else { 421 | buf[0] |= 0b1000_0000 422 | } 423 | 424 | writer.write(&buf[..])?; 425 | } 426 | 427 | Ok(()) 428 | } 429 | } 430 | 431 | /// 32-bit unsigned integer, encoded in little endian. 432 | #[derive(Debug, Copy, Clone, PartialEq)] 433 | pub struct Uint32(u32); 434 | 435 | impl Deserialize for Uint32 { 436 | type Error = Error; 437 | 438 | fn deserialize(reader: &mut R) -> Result { 439 | let mut buf = [0u8; 4]; 440 | reader.read(&mut buf)?; 441 | // todo check range 442 | Ok(u32::from_le_bytes(buf).into()) 443 | } 444 | } 445 | 446 | impl From for u32 { 447 | fn from(var: Uint32) -> u32 { 448 | var.0 449 | } 450 | } 451 | 452 | impl Serialize for Uint32 { 453 | type Error = Error; 454 | 455 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 456 | writer.write(&self.0.to_le_bytes())?; 457 | Ok(()) 458 | } 459 | } 460 | 461 | impl From for Uint32 { 462 | fn from(u: u32) -> Self { 463 | Uint32(u) 464 | } 465 | } 466 | 467 | /// 64-bit unsigned integer, encoded in little endian. 468 | #[derive(Debug, Copy, Clone, PartialEq)] 469 | pub struct Uint64(u64); 470 | 471 | impl Deserialize for Uint64 { 472 | type Error = Error; 473 | 474 | fn deserialize(reader: &mut R) -> Result { 475 | let mut buf = [0u8; 8]; 476 | reader.read(&mut buf)?; 477 | // todo check range 478 | Ok(u64::from_le_bytes(buf).into()) 479 | } 480 | } 481 | 482 | impl Serialize for Uint64 { 483 | type Error = Error; 484 | 485 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 486 | writer.write(&self.0.to_le_bytes())?; 487 | Ok(()) 488 | } 489 | } 490 | 491 | impl From for Uint64 { 492 | fn from(u: u64) -> Self { 493 | Uint64(u) 494 | } 495 | } 496 | 497 | impl From for u64 { 498 | fn from(var: Uint64) -> u64 { 499 | var.0 500 | } 501 | } 502 | 503 | /// VarUint1, 1-bit value (0/1). 504 | #[derive(Debug, Copy, Clone, PartialEq)] 505 | pub struct VarUint1(bool); 506 | 507 | impl From for bool { 508 | fn from(v: VarUint1) -> bool { 509 | v.0 510 | } 511 | } 512 | 513 | impl From for VarUint1 { 514 | fn from(b: bool) -> Self { 515 | VarUint1(b) 516 | } 517 | } 518 | 519 | impl Deserialize for VarUint1 { 520 | type Error = Error; 521 | 522 | fn deserialize(reader: &mut R) -> Result { 523 | let mut u8buf = [0u8; 1]; 524 | reader.read(&mut u8buf)?; 525 | match u8buf[0] { 526 | 0 => Ok(VarUint1(false)), 527 | 1 => Ok(VarUint1(true)), 528 | v => Err(Error::InvalidVarUint1(v)), 529 | } 530 | } 531 | } 532 | 533 | impl Serialize for VarUint1 { 534 | type Error = Error; 535 | 536 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 537 | writer.write(&[if self.0 { 1u8 } else { 0u8 }])?; 538 | Ok(()) 539 | } 540 | } 541 | 542 | impl Deserialize for String { 543 | type Error = Error; 544 | 545 | fn deserialize(reader: &mut R) -> Result { 546 | let length = u32::from(VarUint32::deserialize(reader)?) as usize; 547 | if length > 0 { 548 | String::from_utf8(buffered_read!(PRIMITIVES_BUFFER_LENGTH, length, reader)) 549 | .map_err(|_| Error::NonUtf8String) 550 | } else { 551 | Ok(String::new()) 552 | } 553 | } 554 | } 555 | 556 | impl Serialize for String { 557 | type Error = Error; 558 | 559 | fn serialize(self, writer: &mut W) -> Result<(), Error> { 560 | VarUint32::from(self.len()).serialize(writer)?; 561 | writer.write(&self.into_bytes()[..])?; 562 | Ok(()) 563 | } 564 | } 565 | 566 | /// List for reading sequence of elements typed `T`, given 567 | /// they are preceded by length (serialized as VarUint32). 568 | #[derive(Debug, Clone)] 569 | pub struct CountedList(Vec); 570 | 571 | impl CountedList { 572 | /// Destroy counted list returing inner vector. 573 | pub fn into_inner(self) -> Vec { 574 | self.0 575 | } 576 | } 577 | 578 | impl Deserialize for CountedList 579 | where 580 | T::Error: From, 581 | { 582 | type Error = T::Error; 583 | 584 | fn deserialize(reader: &mut R) -> Result { 585 | let count: usize = VarUint32::deserialize(reader)?.into(); 586 | let mut result = Vec::new(); 587 | for _ in 0..count { 588 | result.push(T::deserialize(reader)?); 589 | } 590 | Ok(CountedList(result)) 591 | } 592 | } 593 | 594 | /// Helper struct to write payload which is preceded by 595 | /// it's own length in bytes. 596 | #[derive(Debug)] 597 | pub struct CountedWriter<'a, W: 'a + io::Write> { 598 | writer: &'a mut W, 599 | data: Vec, 600 | } 601 | 602 | impl<'a, W: 'a + io::Write> CountedWriter<'a, W> { 603 | /// New counted writer on top of the given serial writer. 604 | pub fn new(writer: &'a mut W) -> Self { 605 | CountedWriter { writer, data: Vec::new() } 606 | } 607 | 608 | /// Finish counted writer routing, which writes accumulated length 609 | /// and actual payload. 610 | pub fn done(self) -> io::Result<()> { 611 | let writer = self.writer; 612 | let data = self.data; 613 | VarUint32::from(data.len()) 614 | .serialize(writer) 615 | .map_err(|_| io::Error::InvalidData)?; 616 | writer.write(&data[..])?; 617 | Ok(()) 618 | } 619 | } 620 | 621 | impl<'a, W: 'a + io::Write> io::Write for CountedWriter<'a, W> { 622 | fn write(&mut self, buf: &[u8]) -> io::Result<()> { 623 | self.data.extend_from_slice(buf); 624 | Ok(()) 625 | } 626 | } 627 | 628 | /// Helper struct to write series of `T` preceded by the length of the sequence 629 | /// serialized as VarUint32. 630 | #[derive(Debug, Clone)] 631 | pub struct CountedListWriter, T: IntoIterator>( 632 | pub usize, 633 | pub T, 634 | ); 635 | 636 | impl, T: IntoIterator> Serialize 637 | for CountedListWriter 638 | { 639 | type Error = Error; 640 | 641 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 642 | let len_us = self.0; 643 | let data = self.1; 644 | let len: VarUint32 = len_us.into(); 645 | len.serialize(writer)?; 646 | for data_element in data { 647 | data_element.serialize(writer)? 648 | } 649 | 650 | Ok(()) 651 | } 652 | } 653 | 654 | #[cfg(test)] 655 | mod tests { 656 | 657 | use super::{ 658 | super::{deserialize_buffer, Serialize}, 659 | CountedList, VarInt32, VarInt64, VarInt7, VarUint32, VarUint64, 660 | }; 661 | use crate::elements::Error; 662 | 663 | fn varuint32_ser_test(val: u32, expected: Vec) { 664 | let mut buf = Vec::new(); 665 | let v1: VarUint32 = val.into(); 666 | v1.serialize(&mut buf).expect("to be serialized ok"); 667 | assert_eq!(expected, buf); 668 | } 669 | 670 | fn varuint32_de_test(dt: Vec, expected: u32) { 671 | let val: VarUint32 = super::super::deserialize_buffer(&dt).expect("buf to be serialized"); 672 | assert_eq!(expected, val.into()); 673 | } 674 | 675 | fn varuint32_serde_test(dt: Vec, val: u32) { 676 | varuint32_de_test(dt.clone(), val); 677 | varuint32_ser_test(val, dt); 678 | } 679 | 680 | fn varint32_ser_test(val: i32, expected: Vec) { 681 | let mut buf = Vec::new(); 682 | let v1: VarInt32 = val.into(); 683 | v1.serialize(&mut buf).expect("to be serialized ok"); 684 | assert_eq!(expected, buf); 685 | } 686 | 687 | fn varint32_de_test(dt: Vec, expected: i32) { 688 | let val: VarInt32 = super::super::deserialize_buffer(&dt).expect("buf to be serialized"); 689 | assert_eq!(expected, val.into()); 690 | } 691 | 692 | fn varint32_serde_test(dt: Vec, val: i32) { 693 | varint32_de_test(dt.clone(), val); 694 | varint32_ser_test(val, dt); 695 | } 696 | 697 | fn varuint64_ser_test(val: u64, expected: Vec) { 698 | let mut buf = Vec::new(); 699 | let v1: VarUint64 = val.into(); 700 | v1.serialize(&mut buf).expect("to be serialized ok"); 701 | assert_eq!(expected, buf); 702 | } 703 | 704 | fn varuint64_de_test(dt: Vec, expected: u64) { 705 | let val: VarUint64 = super::super::deserialize_buffer(&dt).expect("buf to be serialized"); 706 | assert_eq!(expected, val.into()); 707 | } 708 | 709 | fn varuint64_serde_test(dt: Vec, val: u64) { 710 | varuint64_de_test(dt.clone(), val); 711 | varuint64_ser_test(val, dt); 712 | } 713 | 714 | fn varint64_ser_test(val: i64, expected: Vec) { 715 | let mut buf = Vec::new(); 716 | let v1: VarInt64 = val.into(); 717 | v1.serialize(&mut buf).expect("to be serialized ok"); 718 | assert_eq!(expected, buf); 719 | } 720 | 721 | fn varint64_de_test(dt: Vec, expected: i64) { 722 | let val: VarInt64 = super::super::deserialize_buffer(&dt).expect("buf to be serialized"); 723 | assert_eq!(expected, val.into()); 724 | } 725 | 726 | fn varint64_serde_test(dt: Vec, val: i64) { 727 | varint64_de_test(dt.clone(), val); 728 | varint64_ser_test(val, dt); 729 | } 730 | 731 | #[test] 732 | fn varuint32_0() { 733 | varuint32_serde_test(vec![0u8; 1], 0); 734 | } 735 | 736 | #[test] 737 | fn varuint32_1() { 738 | varuint32_serde_test(vec![1u8; 1], 1); 739 | } 740 | 741 | #[test] 742 | fn varuint32_135() { 743 | varuint32_serde_test(vec![135u8, 0x01], 135); 744 | } 745 | 746 | #[test] 747 | fn varuint32_8192() { 748 | varuint32_serde_test(vec![0x80, 0x40], 8192); 749 | } 750 | 751 | #[test] 752 | fn varint32_8192() { 753 | varint32_serde_test(vec![0x80, 0xc0, 0x00], 8192); 754 | } 755 | 756 | #[test] 757 | fn varint32_neg_8192() { 758 | varint32_serde_test(vec![0x80, 0x40], -8192); 759 | } 760 | 761 | #[test] 762 | fn varuint64_0() { 763 | varuint64_serde_test(vec![0u8; 1], 0); 764 | } 765 | 766 | #[test] 767 | fn varuint64_1() { 768 | varuint64_serde_test(vec![1u8; 1], 1); 769 | } 770 | 771 | #[test] 772 | fn varuint64_135() { 773 | varuint64_serde_test(vec![135u8, 0x01], 135); 774 | } 775 | 776 | #[test] 777 | fn varuint64_8192() { 778 | varuint64_serde_test(vec![0x80, 0x40], 8192); 779 | } 780 | 781 | #[test] 782 | fn varint64_8192() { 783 | varint64_serde_test(vec![0x80, 0xc0, 0x00], 8192); 784 | } 785 | 786 | #[test] 787 | fn varint64_neg_8192() { 788 | varint64_serde_test(vec![0x80, 0x40], -8192); 789 | } 790 | 791 | #[test] 792 | fn varint64_min() { 793 | varint64_serde_test( 794 | vec![0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f], 795 | -9223372036854775808, 796 | ); 797 | } 798 | 799 | #[test] 800 | fn varint64_bad_extended() { 801 | let res = deserialize_buffer::( 802 | &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x6f][..], 803 | ); 804 | assert!(res.is_err()); 805 | } 806 | 807 | #[test] 808 | fn varint32_bad_extended() { 809 | let res = deserialize_buffer::(&[0x80, 0x80, 0x80, 0x80, 0x6f][..]); 810 | assert!(res.is_err()); 811 | } 812 | 813 | #[test] 814 | fn varint32_bad_extended2() { 815 | let res = deserialize_buffer::(&[0x80, 0x80, 0x80, 0x80, 0x41][..]); 816 | assert!(res.is_err()); 817 | } 818 | 819 | #[test] 820 | fn varint64_max() { 821 | varint64_serde_test( 822 | vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00], 823 | 9223372036854775807, 824 | ); 825 | } 826 | 827 | #[test] 828 | fn varint64_too_long() { 829 | assert!(deserialize_buffer::( 830 | &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..], 831 | ) 832 | .is_err()); 833 | } 834 | 835 | #[test] 836 | fn varint32_too_long() { 837 | assert!(deserialize_buffer::(&[0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..],).is_err()); 838 | } 839 | 840 | #[test] 841 | fn varuint64_too_long() { 842 | assert!(deserialize_buffer::( 843 | &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..], 844 | ) 845 | .is_err()); 846 | } 847 | 848 | #[test] 849 | fn varuint32_too_long() { 850 | assert!( 851 | deserialize_buffer::(&[0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..],).is_err() 852 | ); 853 | } 854 | 855 | #[test] 856 | fn varuint32_too_long_trailing() { 857 | assert!(deserialize_buffer::(&[0xff, 0xff, 0xff, 0xff, 0x7f][..],).is_err()); 858 | } 859 | 860 | #[test] 861 | fn varuint64_too_long_trailing() { 862 | assert!(deserialize_buffer::( 863 | &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04][..], 864 | ) 865 | .is_err()); 866 | } 867 | 868 | #[test] 869 | fn varint32_min() { 870 | varint32_serde_test(vec![0x80, 0x80, 0x80, 0x80, 0x78], -2147483648); 871 | } 872 | 873 | #[test] 874 | fn varint7_invalid() { 875 | match deserialize_buffer::(&[240]) { 876 | Err(Error::InvalidVarInt7(_)) => {}, 877 | _ => panic!("Should be invalid varint7 error!"), 878 | } 879 | } 880 | 881 | #[test] 882 | fn varint7_neg() { 883 | assert_eq!(-0x10i8, deserialize_buffer::(&[0x70]).expect("fail").into()); 884 | } 885 | 886 | #[test] 887 | fn varuint32_too_long_nulled() { 888 | match deserialize_buffer::(&[ 889 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x78, 890 | ]) { 891 | Err(Error::InvalidVarUint32) => {}, 892 | _ => panic!("Should be invalid varuint32"), 893 | } 894 | } 895 | 896 | #[test] 897 | fn varint32_max() { 898 | varint32_serde_test(vec![0xff, 0xff, 0xff, 0xff, 0x07], 2147483647); 899 | } 900 | 901 | #[test] 902 | fn counted_list() { 903 | let payload = [ 904 | 133u8, //(128+5), length is 5 905 | 0x80, 0x80, 0x80, 0x0, // padding 906 | 0x01, 0x7d, 0x05, 0x07, 0x09, 907 | ]; 908 | 909 | let list: CountedList = 910 | deserialize_buffer(&payload).expect("type_section be deserialized"); 911 | 912 | let vars = list.into_inner(); 913 | assert_eq!(5, vars.len()); 914 | let v3: i8 = (*vars.get(1).unwrap()).into(); 915 | assert_eq!(-0x03i8, v3); 916 | } 917 | } 918 | -------------------------------------------------------------------------------- /src/elements/reloc_section.rs: -------------------------------------------------------------------------------- 1 | use crate::io; 2 | use alloc::{string::String, vec::Vec}; 3 | 4 | use super::{ 5 | CountedList, CountedListWriter, CountedWriter, Deserialize, Error, Serialize, VarInt32, 6 | VarUint32, VarUint7, 7 | }; 8 | 9 | const FUNCTION_INDEX_LEB: u8 = 0; 10 | const TABLE_INDEX_SLEB: u8 = 1; 11 | const TABLE_INDEX_I32: u8 = 2; 12 | const MEMORY_ADDR_LEB: u8 = 3; 13 | const MEMORY_ADDR_SLEB: u8 = 4; 14 | const MEMORY_ADDR_I32: u8 = 5; 15 | const TYPE_INDEX_LEB: u8 = 6; 16 | const GLOBAL_INDEX_LEB: u8 = 7; 17 | 18 | /// Relocation information. 19 | #[derive(Clone, Debug, PartialEq)] 20 | pub struct RelocSection { 21 | /// Name of this section. 22 | name: String, 23 | 24 | /// ID of the section containing the relocations described in this section. 25 | section_id: u32, 26 | 27 | /// Name of the section containing the relocations described in this section. Only set if section_id is 0. 28 | relocation_section_name: Option, 29 | 30 | /// Relocation entries. 31 | entries: Vec, 32 | } 33 | 34 | impl RelocSection { 35 | /// Name of this section. 36 | pub fn name(&self) -> &str { 37 | &self.name 38 | } 39 | 40 | /// Name of this section (mutable). 41 | pub fn name_mut(&mut self) -> &mut String { 42 | &mut self.name 43 | } 44 | 45 | /// ID of the section containing the relocations described in this section. 46 | pub fn section_id(&self) -> u32 { 47 | self.section_id 48 | } 49 | 50 | /// ID of the section containing the relocations described in this section (mutable). 51 | pub fn section_id_mut(&mut self) -> &mut u32 { 52 | &mut self.section_id 53 | } 54 | 55 | /// Name of the section containing the relocations described in this section. 56 | pub fn relocation_section_name(&self) -> Option<&str> { 57 | self.relocation_section_name.as_deref() 58 | } 59 | 60 | /// Name of the section containing the relocations described in this section (mutable). 61 | pub fn relocation_section_name_mut(&mut self) -> &mut Option { 62 | &mut self.relocation_section_name 63 | } 64 | 65 | /// List of relocation entries. 66 | pub fn entries(&self) -> &[RelocationEntry] { 67 | &self.entries 68 | } 69 | 70 | /// List of relocation entries (mutable). 71 | pub fn entries_mut(&mut self) -> &mut Vec { 72 | &mut self.entries 73 | } 74 | } 75 | 76 | impl RelocSection { 77 | /// Deserialize a reloc section. 78 | pub fn deserialize(name: String, rdr: &mut R) -> Result { 79 | let section_id = VarUint32::deserialize(rdr)?.into(); 80 | 81 | let relocation_section_name = 82 | if section_id == 0 { Some(String::deserialize(rdr)?) } else { None }; 83 | 84 | let entries = CountedList::deserialize(rdr)?.into_inner(); 85 | 86 | Ok(RelocSection { name, section_id, relocation_section_name, entries }) 87 | } 88 | } 89 | 90 | impl Serialize for RelocSection { 91 | type Error = Error; 92 | 93 | fn serialize(self, wtr: &mut W) -> Result<(), Error> { 94 | let mut counted_writer = CountedWriter::new(wtr); 95 | 96 | self.name.serialize(&mut counted_writer)?; 97 | 98 | VarUint32::from(self.section_id).serialize(&mut counted_writer)?; 99 | 100 | if let Some(relocation_section_name) = self.relocation_section_name { 101 | relocation_section_name.serialize(&mut counted_writer)?; 102 | } 103 | 104 | let counted_list = CountedListWriter(self.entries.len(), self.entries.into_iter()); 105 | counted_list.serialize(&mut counted_writer)?; 106 | 107 | counted_writer.done()?; 108 | 109 | Ok(()) 110 | } 111 | } 112 | 113 | /// Relocation entry. 114 | #[derive(Clone, Copy, Debug, PartialEq)] 115 | pub enum RelocationEntry { 116 | /// Function index. 117 | FunctionIndexLeb { 118 | /// Offset of the value to rewrite. 119 | offset: u32, 120 | 121 | /// Index of the function symbol in the symbol table. 122 | index: u32, 123 | }, 124 | 125 | /// Function table index. 126 | TableIndexSleb { 127 | /// Offset of the value to rewrite. 128 | offset: u32, 129 | 130 | /// Index of the function symbol in the symbol table. 131 | index: u32, 132 | }, 133 | 134 | /// Function table index. 135 | TableIndexI32 { 136 | /// Offset of the value to rewrite. 137 | offset: u32, 138 | 139 | /// Index of the function symbol in the symbol table. 140 | index: u32, 141 | }, 142 | 143 | /// Linear memory index. 144 | MemoryAddressLeb { 145 | /// Offset of the value to rewrite. 146 | offset: u32, 147 | 148 | /// Index of the data symbol in the symbol table. 149 | index: u32, 150 | 151 | /// Addend to add to the address. 152 | addend: i32, 153 | }, 154 | 155 | /// Linear memory index. 156 | MemoryAddressSleb { 157 | /// Offset of the value to rewrite. 158 | offset: u32, 159 | 160 | /// Index of the data symbol in the symbol table. 161 | index: u32, 162 | 163 | /// Addend to add to the address. 164 | addend: i32, 165 | }, 166 | 167 | /// Linear memory index. 168 | MemoryAddressI32 { 169 | /// Offset of the value to rewrite. 170 | offset: u32, 171 | 172 | /// Index of the data symbol in the symbol table. 173 | index: u32, 174 | 175 | /// Addend to add to the address. 176 | addend: i32, 177 | }, 178 | 179 | /// Type table index. 180 | TypeIndexLeb { 181 | /// Offset of the value to rewrite. 182 | offset: u32, 183 | 184 | /// Index of the type used. 185 | index: u32, 186 | }, 187 | 188 | /// Global index. 189 | GlobalIndexLeb { 190 | /// Offset of the value to rewrite. 191 | offset: u32, 192 | 193 | /// Index of the global symbol in the symbol table. 194 | index: u32, 195 | }, 196 | } 197 | 198 | impl Deserialize for RelocationEntry { 199 | type Error = Error; 200 | 201 | fn deserialize(rdr: &mut R) -> Result { 202 | match VarUint7::deserialize(rdr)?.into() { 203 | FUNCTION_INDEX_LEB => Ok(RelocationEntry::FunctionIndexLeb { 204 | offset: VarUint32::deserialize(rdr)?.into(), 205 | index: VarUint32::deserialize(rdr)?.into(), 206 | }), 207 | 208 | TABLE_INDEX_SLEB => Ok(RelocationEntry::TableIndexSleb { 209 | offset: VarUint32::deserialize(rdr)?.into(), 210 | index: VarUint32::deserialize(rdr)?.into(), 211 | }), 212 | 213 | TABLE_INDEX_I32 => Ok(RelocationEntry::TableIndexI32 { 214 | offset: VarUint32::deserialize(rdr)?.into(), 215 | index: VarUint32::deserialize(rdr)?.into(), 216 | }), 217 | 218 | MEMORY_ADDR_LEB => Ok(RelocationEntry::MemoryAddressLeb { 219 | offset: VarUint32::deserialize(rdr)?.into(), 220 | index: VarUint32::deserialize(rdr)?.into(), 221 | addend: VarInt32::deserialize(rdr)?.into(), 222 | }), 223 | 224 | MEMORY_ADDR_SLEB => Ok(RelocationEntry::MemoryAddressSleb { 225 | offset: VarUint32::deserialize(rdr)?.into(), 226 | index: VarUint32::deserialize(rdr)?.into(), 227 | addend: VarInt32::deserialize(rdr)?.into(), 228 | }), 229 | 230 | MEMORY_ADDR_I32 => Ok(RelocationEntry::MemoryAddressI32 { 231 | offset: VarUint32::deserialize(rdr)?.into(), 232 | index: VarUint32::deserialize(rdr)?.into(), 233 | addend: VarInt32::deserialize(rdr)?.into(), 234 | }), 235 | 236 | TYPE_INDEX_LEB => Ok(RelocationEntry::TypeIndexLeb { 237 | offset: VarUint32::deserialize(rdr)?.into(), 238 | index: VarUint32::deserialize(rdr)?.into(), 239 | }), 240 | 241 | GLOBAL_INDEX_LEB => Ok(RelocationEntry::GlobalIndexLeb { 242 | offset: VarUint32::deserialize(rdr)?.into(), 243 | index: VarUint32::deserialize(rdr)?.into(), 244 | }), 245 | 246 | entry_type => Err(Error::UnknownValueType(entry_type as i8)), 247 | } 248 | } 249 | } 250 | 251 | impl Serialize for RelocationEntry { 252 | type Error = Error; 253 | 254 | fn serialize(self, wtr: &mut W) -> Result<(), Error> { 255 | match self { 256 | RelocationEntry::FunctionIndexLeb { offset, index } => { 257 | VarUint7::from(FUNCTION_INDEX_LEB).serialize(wtr)?; 258 | VarUint32::from(offset).serialize(wtr)?; 259 | VarUint32::from(index).serialize(wtr)?; 260 | }, 261 | 262 | RelocationEntry::TableIndexSleb { offset, index } => { 263 | VarUint7::from(TABLE_INDEX_SLEB).serialize(wtr)?; 264 | VarUint32::from(offset).serialize(wtr)?; 265 | VarUint32::from(index).serialize(wtr)?; 266 | }, 267 | 268 | RelocationEntry::TableIndexI32 { offset, index } => { 269 | VarUint7::from(TABLE_INDEX_I32).serialize(wtr)?; 270 | VarUint32::from(offset).serialize(wtr)?; 271 | VarUint32::from(index).serialize(wtr)?; 272 | }, 273 | 274 | RelocationEntry::MemoryAddressLeb { offset, index, addend } => { 275 | VarUint7::from(MEMORY_ADDR_LEB).serialize(wtr)?; 276 | VarUint32::from(offset).serialize(wtr)?; 277 | VarUint32::from(index).serialize(wtr)?; 278 | VarInt32::from(addend).serialize(wtr)?; 279 | }, 280 | 281 | RelocationEntry::MemoryAddressSleb { offset, index, addend } => { 282 | VarUint7::from(MEMORY_ADDR_SLEB).serialize(wtr)?; 283 | VarUint32::from(offset).serialize(wtr)?; 284 | VarUint32::from(index).serialize(wtr)?; 285 | VarInt32::from(addend).serialize(wtr)?; 286 | }, 287 | 288 | RelocationEntry::MemoryAddressI32 { offset, index, addend } => { 289 | VarUint7::from(MEMORY_ADDR_I32).serialize(wtr)?; 290 | VarUint32::from(offset).serialize(wtr)?; 291 | VarUint32::from(index).serialize(wtr)?; 292 | VarInt32::from(addend).serialize(wtr)?; 293 | }, 294 | 295 | RelocationEntry::TypeIndexLeb { offset, index } => { 296 | VarUint7::from(TYPE_INDEX_LEB).serialize(wtr)?; 297 | VarUint32::from(offset).serialize(wtr)?; 298 | VarUint32::from(index).serialize(wtr)?; 299 | }, 300 | 301 | RelocationEntry::GlobalIndexLeb { offset, index } => { 302 | VarUint7::from(GLOBAL_INDEX_LEB).serialize(wtr)?; 303 | VarUint32::from(offset).serialize(wtr)?; 304 | VarUint32::from(index).serialize(wtr)?; 305 | }, 306 | } 307 | 308 | Ok(()) 309 | } 310 | } 311 | 312 | #[cfg(test)] 313 | mod tests { 314 | use super::{ 315 | super::{deserialize_file, Section}, 316 | RelocationEntry, 317 | }; 318 | 319 | #[test] 320 | fn reloc_section() { 321 | let module = deserialize_file("./res/cases/v1/relocatable.wasm") 322 | .expect("Module should be deserialized") 323 | .parse_reloc() 324 | .expect("Reloc section should be deserialized"); 325 | let mut found = false; 326 | for section in module.sections() { 327 | if let Section::Reloc(ref reloc_section) = *section { 328 | assert_eq!( 329 | vec![ 330 | RelocationEntry::MemoryAddressSleb { offset: 4, index: 0, addend: 0 }, 331 | RelocationEntry::FunctionIndexLeb { offset: 12, index: 0 }, 332 | ], 333 | reloc_section.entries() 334 | ); 335 | found = true 336 | } 337 | } 338 | assert!(found, "There should be a reloc section in relocatable.wasm"); 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /src/elements/segment.rs: -------------------------------------------------------------------------------- 1 | use super::{CountedList, CountedListWriter, Deserialize, Error, InitExpr, Serialize, VarUint32}; 2 | use crate::io; 3 | use alloc::vec::Vec; 4 | 5 | #[cfg(feature = "bulk")] 6 | const FLAG_MEMZERO: u32 = 0; 7 | #[cfg(feature = "bulk")] 8 | const FLAG_PASSIVE: u32 = 1; 9 | #[cfg(feature = "bulk")] 10 | const FLAG_MEM_NONZERO: u32 = 2; 11 | 12 | #[cfg(feature = "reduced-stack-buffer")] 13 | const VALUES_BUFFER_LENGTH: usize = 256; 14 | 15 | #[cfg(not(feature = "reduced-stack-buffer"))] 16 | const VALUES_BUFFER_LENGTH: usize = 16384; 17 | 18 | /// Entry in the element section. 19 | #[derive(Debug, Clone, PartialEq)] 20 | pub struct ElementSegment { 21 | index: u32, 22 | offset: Option, 23 | members: Vec, 24 | 25 | #[cfg(feature = "bulk")] 26 | passive: bool, 27 | } 28 | 29 | impl ElementSegment { 30 | /// New element segment. 31 | pub fn new(index: u32, offset: Option, members: Vec) -> Self { 32 | ElementSegment { 33 | index, 34 | offset, 35 | members, 36 | 37 | #[cfg(feature = "bulk")] 38 | passive: false, 39 | } 40 | } 41 | 42 | /// Sequence of function indices. 43 | pub fn members(&self) -> &[u32] { 44 | &self.members 45 | } 46 | 47 | /// Sequence of function indices (mutable) 48 | pub fn members_mut(&mut self) -> &mut Vec { 49 | &mut self.members 50 | } 51 | 52 | /// Table index (currently valid only value of `0`) 53 | pub fn index(&self) -> u32 { 54 | self.index 55 | } 56 | 57 | /// An i32 initializer expression that computes the offset at which to place the elements. 58 | /// 59 | /// Note that this return `None` if the segment is `passive`. 60 | pub fn offset(&self) -> &Option { 61 | &self.offset 62 | } 63 | 64 | /// An i32 initializer expression that computes the offset at which to place the elements (mutable) 65 | /// 66 | /// Note that this return `None` if the segment is `passive`. 67 | pub fn offset_mut(&mut self) -> &mut Option { 68 | &mut self.offset 69 | } 70 | } 71 | 72 | #[cfg(feature = "bulk")] 73 | impl ElementSegment { 74 | /// Whether or not this table segment is "passive" 75 | pub fn passive(&self) -> bool { 76 | self.passive 77 | } 78 | 79 | /// Whether or not this table segment is "passive" 80 | pub fn passive_mut(&mut self) -> &mut bool { 81 | &mut self.passive 82 | } 83 | 84 | /// Set whether or not this table segment is "passive" 85 | pub fn set_passive(&mut self, passive: bool) { 86 | self.passive = passive; 87 | } 88 | } 89 | 90 | impl Deserialize for ElementSegment { 91 | type Error = Error; 92 | 93 | #[cfg(not(feature = "bulk"))] 94 | fn deserialize(reader: &mut R) -> Result { 95 | let index: u32 = VarUint32::deserialize(reader)?.into(); 96 | let offset = InitExpr::deserialize(reader)?; 97 | let members: Vec = CountedList::::deserialize(reader)? 98 | .into_inner() 99 | .into_iter() 100 | .map(Into::into) 101 | .collect(); 102 | 103 | Ok(ElementSegment { index, offset: Some(offset), members }) 104 | } 105 | 106 | #[cfg(feature = "bulk")] 107 | fn deserialize(reader: &mut R) -> Result { 108 | // This piece of data was treated as `index` [of the table], but was repurposed 109 | // for flags in bulk-memory operations proposal. 110 | let flags: u32 = VarUint32::deserialize(reader)?.into(); 111 | let index = if flags == FLAG_MEMZERO || flags == FLAG_PASSIVE { 112 | 0u32 113 | } else if flags == FLAG_MEM_NONZERO { 114 | VarUint32::deserialize(reader)?.into() 115 | } else { 116 | return Err(Error::InvalidSegmentFlags(flags)) 117 | }; 118 | let offset = 119 | if flags == FLAG_PASSIVE { None } else { Some(InitExpr::deserialize(reader)?) }; 120 | 121 | let members: Vec = CountedList::::deserialize(reader)? 122 | .into_inner() 123 | .into_iter() 124 | .map(Into::into) 125 | .collect(); 126 | 127 | Ok(ElementSegment { index, offset, members, passive: flags == FLAG_PASSIVE }) 128 | } 129 | } 130 | 131 | impl Serialize for ElementSegment { 132 | type Error = Error; 133 | 134 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 135 | #[cfg(feature = "bulk")] 136 | { 137 | if self.passive { 138 | VarUint32::from(FLAG_PASSIVE).serialize(writer)?; 139 | } else if self.index != 0 { 140 | VarUint32::from(FLAG_MEM_NONZERO).serialize(writer)?; 141 | VarUint32::from(self.index).serialize(writer)?; 142 | } else { 143 | VarUint32::from(FLAG_MEMZERO).serialize(writer)?; 144 | } 145 | } 146 | #[cfg(not(feature = "bulk"))] 147 | VarUint32::from(self.index).serialize(writer)?; 148 | 149 | if let Some(offset) = self.offset { 150 | offset.serialize(writer)?; 151 | } 152 | let data = self.members; 153 | let counted_list = 154 | CountedListWriter::(data.len(), data.into_iter().map(Into::into)); 155 | counted_list.serialize(writer)?; 156 | Ok(()) 157 | } 158 | } 159 | 160 | /// Data segment definition. 161 | #[derive(Clone, Debug, PartialEq)] 162 | pub struct DataSegment { 163 | index: u32, 164 | offset: Option, 165 | value: Vec, 166 | 167 | #[cfg(feature = "bulk")] 168 | passive: bool, 169 | } 170 | 171 | impl DataSegment { 172 | /// New data segments. 173 | pub fn new(index: u32, offset: Option, value: Vec) -> Self { 174 | DataSegment { 175 | index, 176 | offset, 177 | value, 178 | 179 | #[cfg(feature = "bulk")] 180 | passive: false, 181 | } 182 | } 183 | 184 | /// Linear memory index (currently the only valid value is `0`). 185 | pub fn index(&self) -> u32 { 186 | self.index 187 | } 188 | 189 | /// An i32 initializer expression that computes the offset at which to place the data. 190 | /// 191 | /// Note that this return `None` if the segment is `passive`. 192 | pub fn offset(&self) -> &Option { 193 | &self.offset 194 | } 195 | 196 | /// An i32 initializer expression that computes the offset at which to place the data (mutable) 197 | /// 198 | /// Note that this return `None` if the segment is `passive`. 199 | pub fn offset_mut(&mut self) -> &mut Option { 200 | &mut self.offset 201 | } 202 | 203 | /// Initial value of the data segment. 204 | pub fn value(&self) -> &[u8] { 205 | &self.value 206 | } 207 | 208 | /// Initial value of the data segment (mutable). 209 | pub fn value_mut(&mut self) -> &mut Vec { 210 | &mut self.value 211 | } 212 | } 213 | 214 | #[cfg(feature = "bulk")] 215 | impl DataSegment { 216 | /// Whether or not this data segment is "passive". 217 | pub fn passive(&self) -> bool { 218 | self.passive 219 | } 220 | 221 | /// Whether or not this data segment is "passive" (mutable). 222 | pub fn passive_mut(&mut self) -> &mut bool { 223 | &mut self.passive 224 | } 225 | 226 | /// Set whether or not this table segment is "passive" 227 | pub fn set_passive(&mut self, passive: bool) { 228 | self.passive = passive; 229 | } 230 | } 231 | 232 | impl Deserialize for DataSegment { 233 | type Error = Error; 234 | 235 | #[cfg(not(feature = "bulk"))] 236 | fn deserialize(reader: &mut R) -> Result { 237 | let index = VarUint32::deserialize(reader)?; 238 | let offset = InitExpr::deserialize(reader)?; 239 | let value_len = u32::from(VarUint32::deserialize(reader)?) as usize; 240 | let value = buffered_read!(VALUES_BUFFER_LENGTH, value_len, reader); 241 | 242 | Ok(DataSegment { index: index.into(), offset: Some(offset), value }) 243 | } 244 | 245 | #[cfg(feature = "bulk")] 246 | fn deserialize(reader: &mut R) -> Result { 247 | let flags: u32 = VarUint32::deserialize(reader)?.into(); 248 | let index = if flags == FLAG_MEMZERO || flags == FLAG_PASSIVE { 249 | 0u32 250 | } else if flags == FLAG_MEM_NONZERO { 251 | VarUint32::deserialize(reader)?.into() 252 | } else { 253 | return Err(Error::InvalidSegmentFlags(flags)) 254 | }; 255 | let offset = 256 | if flags == FLAG_PASSIVE { None } else { Some(InitExpr::deserialize(reader)?) }; 257 | let value_len = u32::from(VarUint32::deserialize(reader)?) as usize; 258 | let value = buffered_read!(VALUES_BUFFER_LENGTH, value_len, reader); 259 | 260 | Ok(DataSegment { index, offset, value, passive: flags == FLAG_PASSIVE }) 261 | } 262 | } 263 | 264 | impl Serialize for DataSegment { 265 | type Error = Error; 266 | 267 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 268 | #[cfg(feature = "bulk")] 269 | { 270 | if self.passive { 271 | VarUint32::from(FLAG_PASSIVE).serialize(writer)?; 272 | } else if self.index != 0 { 273 | VarUint32::from(FLAG_MEM_NONZERO).serialize(writer)?; 274 | VarUint32::from(self.index).serialize(writer)?; 275 | } else { 276 | VarUint32::from(FLAG_MEMZERO).serialize(writer)?; 277 | } 278 | } 279 | #[cfg(not(feature = "bulk"))] 280 | VarUint32::from(self.index).serialize(writer)?; 281 | 282 | if let Some(offset) = self.offset { 283 | offset.serialize(writer)?; 284 | } 285 | 286 | let value = self.value; 287 | VarUint32::from(value.len()).serialize(writer)?; 288 | writer.write(&value[..])?; 289 | Ok(()) 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/elements/types.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | CountedList, CountedListWriter, Deserialize, Error, Serialize, VarInt32, VarInt7, VarUint7, 3 | }; 4 | use crate::io; 5 | use alloc::vec::Vec; 6 | use core::fmt; 7 | 8 | /// Type definition in types section. Currently can be only of the function type. 9 | #[derive(Debug, Clone, PartialEq, Hash, Eq)] 10 | pub enum Type { 11 | /// Function type. 12 | Function(FunctionType), 13 | } 14 | 15 | impl Deserialize for Type { 16 | type Error = Error; 17 | 18 | fn deserialize(reader: &mut R) -> Result { 19 | Ok(Type::Function(FunctionType::deserialize(reader)?)) 20 | } 21 | } 22 | 23 | impl Serialize for Type { 24 | type Error = Error; 25 | 26 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 27 | match self { 28 | Type::Function(fn_type) => fn_type.serialize(writer), 29 | } 30 | } 31 | } 32 | 33 | /// Value type. 34 | #[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] 35 | pub enum ValueType { 36 | /// 32-bit signed integer 37 | I32, 38 | /// 64-bit signed integer 39 | I64, 40 | /// 32-bit float 41 | F32, 42 | /// 64-bit float 43 | F64, 44 | #[cfg(feature = "simd")] 45 | /// 128-bit SIMD register 46 | V128, 47 | } 48 | 49 | impl Deserialize for ValueType { 50 | type Error = Error; 51 | 52 | fn deserialize(reader: &mut R) -> Result { 53 | let val = VarInt7::deserialize(reader)?; 54 | 55 | match val.into() { 56 | -0x01 => Ok(ValueType::I32), 57 | -0x02 => Ok(ValueType::I64), 58 | -0x03 => Ok(ValueType::F32), 59 | -0x04 => Ok(ValueType::F64), 60 | #[cfg(feature = "simd")] 61 | -0x05 => Ok(ValueType::V128), 62 | _ => Err(Error::UnknownValueType(val.into())), 63 | } 64 | } 65 | } 66 | 67 | impl Serialize for ValueType { 68 | type Error = Error; 69 | 70 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 71 | let val: VarInt7 = match self { 72 | ValueType::I32 => -0x01, 73 | ValueType::I64 => -0x02, 74 | ValueType::F32 => -0x03, 75 | ValueType::F64 => -0x04, 76 | #[cfg(feature = "simd")] 77 | ValueType::V128 => -0x05, 78 | } 79 | .into(); 80 | val.serialize(writer)?; 81 | Ok(()) 82 | } 83 | } 84 | 85 | impl fmt::Display for ValueType { 86 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 87 | match *self { 88 | ValueType::I32 => write!(f, "i32"), 89 | ValueType::I64 => write!(f, "i64"), 90 | ValueType::F32 => write!(f, "f32"), 91 | ValueType::F64 => write!(f, "f64"), 92 | #[cfg(feature = "simd")] 93 | ValueType::V128 => write!(f, "v128"), 94 | } 95 | } 96 | } 97 | 98 | /// Block type which is basically `ValueType` + NoResult (to define blocks that have no return type) 99 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 100 | pub enum BlockType { 101 | /// No specified block type 102 | NoResult, 103 | /// Inline value type. 104 | Value(ValueType), 105 | /// Reference to a signature. 106 | #[cfg(feature = "multi_value")] 107 | TypeIndex(u32), 108 | } 109 | 110 | impl Deserialize for BlockType { 111 | type Error = Error; 112 | 113 | fn deserialize(reader: &mut R) -> Result { 114 | let val = VarInt32::deserialize(reader)?; 115 | 116 | match val.into() { 117 | -0x40 => Ok(BlockType::NoResult), 118 | -0x01 => Ok(BlockType::Value(ValueType::I32)), 119 | -0x02 => Ok(BlockType::Value(ValueType::I64)), 120 | -0x03 => Ok(BlockType::Value(ValueType::F32)), 121 | -0x04 => Ok(BlockType::Value(ValueType::F64)), 122 | #[cfg(feature = "simd")] 123 | -0x05 => Ok(BlockType::Value(ValueType::V128)), 124 | #[cfg(feature = "multi_value")] 125 | idx => { 126 | let idx = idx.try_into().map_err(|_| Error::UnknownBlockType(idx))?; 127 | Ok(BlockType::TypeIndex(idx)) 128 | }, 129 | #[cfg(not(feature = "multi_value"))] 130 | _ => Err(Error::UnknownBlockType(val.into())), 131 | } 132 | } 133 | } 134 | 135 | impl Serialize for BlockType { 136 | type Error = Error; 137 | 138 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 139 | let val: VarInt32 = match self { 140 | BlockType::NoResult => -0x40, 141 | BlockType::Value(ValueType::I32) => -0x01, 142 | BlockType::Value(ValueType::I64) => -0x02, 143 | BlockType::Value(ValueType::F32) => -0x03, 144 | BlockType::Value(ValueType::F64) => -0x04, 145 | #[cfg(feature = "simd")] 146 | BlockType::Value(ValueType::V128) => -0x05, 147 | #[cfg(feature = "multi_value")] 148 | BlockType::TypeIndex(idx) => idx as i32, 149 | } 150 | .into(); 151 | val.serialize(writer)?; 152 | Ok(()) 153 | } 154 | } 155 | 156 | /// Function signature type. 157 | #[derive(Debug, Clone, PartialEq, Hash, Eq)] 158 | pub struct FunctionType { 159 | form: u8, 160 | params: Vec, 161 | results: Vec, 162 | } 163 | 164 | impl Default for FunctionType { 165 | fn default() -> Self { 166 | FunctionType { form: 0x60, params: Vec::new(), results: Vec::new() } 167 | } 168 | } 169 | 170 | impl FunctionType { 171 | /// New function type given the params and results as vectors 172 | pub fn new(params: Vec, results: Vec) -> Self { 173 | FunctionType { form: 0x60, params, results } 174 | } 175 | /// Function form (currently only valid value is `0x60`) 176 | pub fn form(&self) -> u8 { 177 | self.form 178 | } 179 | /// Parameters in the function signature. 180 | pub fn params(&self) -> &[ValueType] { 181 | &self.params 182 | } 183 | /// Mutable parameters in the function signature. 184 | pub fn params_mut(&mut self) -> &mut Vec { 185 | &mut self.params 186 | } 187 | /// Results in the function signature, if any. 188 | pub fn results(&self) -> &[ValueType] { 189 | &self.results 190 | } 191 | /// Mutable type in the function signature, if any. 192 | pub fn results_mut(&mut self) -> &mut Vec { 193 | &mut self.results 194 | } 195 | } 196 | 197 | impl Deserialize for FunctionType { 198 | type Error = Error; 199 | 200 | fn deserialize(reader: &mut R) -> Result { 201 | let form: u8 = VarUint7::deserialize(reader)?.into(); 202 | 203 | if form != 0x60 { 204 | return Err(Error::UnknownFunctionForm(form)) 205 | } 206 | 207 | let params: Vec = CountedList::deserialize(reader)?.into_inner(); 208 | let results: Vec = CountedList::deserialize(reader)?.into_inner(); 209 | 210 | #[cfg(not(feature = "multi_value"))] 211 | if results.len() > 1 { 212 | return Err(Error::Other( 213 | "Enable the multi_value feature to deserialize more than one function result", 214 | )) 215 | } 216 | 217 | Ok(FunctionType { form, params, results }) 218 | } 219 | } 220 | 221 | impl Serialize for FunctionType { 222 | type Error = Error; 223 | 224 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 225 | VarUint7::from(self.form).serialize(writer)?; 226 | 227 | let params_counted_list = CountedListWriter::( 228 | self.params.len(), 229 | self.params.into_iter().map(Into::into), 230 | ); 231 | params_counted_list.serialize(writer)?; 232 | 233 | let results_counted_list = CountedListWriter::( 234 | self.results.len(), 235 | self.results.into_iter().map(Into::into), 236 | ); 237 | results_counted_list.serialize(writer)?; 238 | 239 | Ok(()) 240 | } 241 | } 242 | 243 | /// Table element type. 244 | #[derive(Clone, Copy, Debug, PartialEq)] 245 | pub enum TableElementType { 246 | /// A reference to a function with any signature. 247 | AnyFunc, 248 | } 249 | 250 | impl Deserialize for TableElementType { 251 | type Error = Error; 252 | 253 | fn deserialize(reader: &mut R) -> Result { 254 | let val = VarInt7::deserialize(reader)?; 255 | 256 | match val.into() { 257 | -0x10 => Ok(TableElementType::AnyFunc), 258 | _ => Err(Error::UnknownTableElementType(val.into())), 259 | } 260 | } 261 | } 262 | 263 | impl Serialize for TableElementType { 264 | type Error = Error; 265 | 266 | fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { 267 | let val: VarInt7 = match self { 268 | TableElementType::AnyFunc => -0x10, 269 | } 270 | .into(); 271 | val.serialize(writer)?; 272 | Ok(()) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/io.rs: -------------------------------------------------------------------------------- 1 | //! Simple abstractions for the IO operations. 2 | //! 3 | //! Basically it just a replacement for the std::io that is usable from 4 | //! the `no_std` environment. 5 | 6 | #[cfg(feature = "std")] 7 | use std::io; 8 | 9 | /// IO specific error. 10 | #[derive(Debug)] 11 | pub enum Error { 12 | /// Some unexpected data left in the buffer after reading all data. 13 | TrailingData, 14 | 15 | /// Unexpected End-Of-File 16 | UnexpectedEof, 17 | 18 | /// Invalid data is encountered. 19 | InvalidData, 20 | 21 | #[cfg(feature = "std")] 22 | Io(std::io::Error), 23 | } 24 | 25 | /// IO specific Result. 26 | pub type Result = core::result::Result; 27 | 28 | pub trait Write { 29 | /// Write a buffer of data into this write. 30 | /// 31 | /// All data is written at once. 32 | fn write(&mut self, buf: &[u8]) -> Result<()>; 33 | } 34 | 35 | pub trait Read { 36 | /// Read a data from this read to a buffer. 37 | /// 38 | /// If there is not enough data in this read then `UnexpectedEof` will be returned. 39 | fn read(&mut self, buf: &mut [u8]) -> Result<()>; 40 | } 41 | 42 | /// Reader that saves the last position. 43 | pub struct Cursor { 44 | inner: T, 45 | pos: usize, 46 | } 47 | 48 | impl Cursor { 49 | pub fn new(inner: T) -> Cursor { 50 | Cursor { inner, pos: 0 } 51 | } 52 | 53 | pub fn position(&self) -> usize { 54 | self.pos 55 | } 56 | } 57 | 58 | impl> Read for Cursor { 59 | fn read(&mut self, buf: &mut [u8]) -> Result<()> { 60 | let slice = self.inner.as_ref(); 61 | let remainder = slice.len() - self.pos; 62 | let requested = buf.len(); 63 | if requested > remainder { 64 | return Err(Error::UnexpectedEof) 65 | } 66 | buf.copy_from_slice(&slice[self.pos..(self.pos + requested)]); 67 | self.pos += requested; 68 | Ok(()) 69 | } 70 | } 71 | 72 | #[cfg(not(feature = "std"))] 73 | impl Write for alloc::vec::Vec { 74 | fn write(&mut self, buf: &[u8]) -> Result<()> { 75 | self.extend(buf); 76 | Ok(()) 77 | } 78 | } 79 | 80 | #[cfg(feature = "std")] 81 | impl Read for T { 82 | fn read(&mut self, buf: &mut [u8]) -> Result<()> { 83 | self.read_exact(buf).map_err(Error::Io) 84 | } 85 | } 86 | 87 | #[cfg(feature = "std")] 88 | impl Write for T { 89 | fn write(&mut self, buf: &[u8]) -> Result<()> { 90 | self.write_all(buf).map_err(Error::Io) 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod tests { 96 | use super::*; 97 | 98 | #[test] 99 | fn cursor() { 100 | let mut cursor = Cursor::new(vec![0xFFu8, 0x7Fu8]); 101 | assert_eq!(cursor.position(), 0); 102 | 103 | let mut buf = [0u8]; 104 | assert!(cursor.read(&mut buf[..]).is_ok()); 105 | assert_eq!(cursor.position(), 1); 106 | assert_eq!(buf[0], 0xFFu8); 107 | assert!(cursor.read(&mut buf[..]).is_ok()); 108 | assert_eq!(buf[0], 0x7Fu8); 109 | assert_eq!(cursor.position(), 2); 110 | } 111 | 112 | #[test] 113 | fn overflow_in_cursor() { 114 | let mut cursor = Cursor::new(vec![0u8]); 115 | let mut buf = [0, 1, 2]; 116 | assert!(cursor.read(&mut buf[..]).is_err()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! WebAssembly format library 2 | #![warn(missing_docs)] 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | 5 | #[macro_use] 6 | extern crate alloc; 7 | 8 | pub mod builder; 9 | pub mod elements; 10 | mod io; 11 | 12 | pub use elements::{deserialize_buffer, peek_size, serialize, Error as SerializationError}; 13 | 14 | #[cfg(feature = "std")] 15 | pub use elements::{deserialize_file, serialize_to_file}; 16 | -------------------------------------------------------------------------------- /testsuite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parity-wasm-testsuite" 3 | version = "0.0.0" 4 | authors = ["NikVolf "] 5 | publish = false 6 | license = "MIT/Apache-2.0" 7 | readme = "README.md" 8 | repository = "https://github.com/nikvolf/parity-wasm" 9 | homepage = "https://github.com/nikvolf/parity-wasm" 10 | description = "parity-wasm testsuite" 11 | edition = "2021" 12 | rust-version = "1.56.1" 13 | 14 | [dependencies] 15 | wast = "38" 16 | test-generator = "0.3" 17 | 18 | [dependencies.parity-wasm] 19 | path = ".." 20 | features = [ 21 | "atomics", 22 | "simd", 23 | "sign_ext", 24 | "bulk", 25 | "multi_value", 26 | ] 27 | -------------------------------------------------------------------------------- /testsuite/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use std::ffi::OsStr; 4 | 5 | mod run; 6 | 7 | const BASIC_BLACKLIST: [&str; 2] = [ 8 | // those use unsupported i32_trunc_sat_* instructions 9 | "binary-leb128.wast", 10 | "conversions.wast", 11 | ]; 12 | 13 | #[test_generator::test_resources("testsuite/spec/*.wast")] 14 | fn basic(path: &str) { 15 | let blacklisted = std::path::Path::new(path) 16 | .file_name() 17 | .map(|file| BASIC_BLACKLIST.iter().any(|black| OsStr::new(black) == file)) 18 | .unwrap_or(false); 19 | 20 | if !blacklisted { 21 | run::check(path); 22 | } 23 | } 24 | 25 | #[test_generator::test_resources("testsuite/spec/proposals/threads/*.wast")] 26 | fn threads(path: &str) { 27 | run::check(path); 28 | } 29 | -------------------------------------------------------------------------------- /testsuite/src/run.rs: -------------------------------------------------------------------------------- 1 | use parity_wasm::elements::{deserialize_buffer, serialize, Module}; 2 | use wast::{ 3 | parser::{parse, ParseBuffer}, 4 | QuoteModule, Wast, WastDirective, 5 | }; 6 | 7 | pub fn check(path: &str) { 8 | let path = path.strip_prefix("testsuite/").unwrap(); 9 | let source = std::fs::read_to_string(path).unwrap(); 10 | let buffer = ParseBuffer::new(&source).unwrap(); 11 | let wast = parse::(&buffer).unwrap(); 12 | for kind in wast.directives { 13 | match kind { 14 | WastDirective::Module(mut module) => { 15 | let (line, _col) = module.span.linecol_in(&source); 16 | println!("Parsing module at line {}", line + 1); 17 | let orig_bytes = module.encode().unwrap(); 18 | let parsed = 19 | deserialize_buffer::(&orig_bytes).expect("Failed to parse module"); 20 | serialize(parsed).expect("Failed to serialize module"); 21 | }, 22 | WastDirective::AssertMalformed { 23 | module: QuoteModule::Module(mut module), 24 | message, 25 | span, 26 | } => { 27 | let (line, _col) = span.linecol_in(&source); 28 | println!("Parsing assert_malformed at line {}", line + 1); 29 | let parsed = deserialize_buffer::(&module.encode().unwrap()); 30 | if parsed.is_ok() { 31 | panic!("Module should be malformed because: {}", message); 32 | } 33 | }, 34 | _ => (), 35 | } 36 | } 37 | } 38 | --------------------------------------------------------------------------------