parity-wasm-0.45.0/.cargo_vcs_info.json0000644000000001360000000000100133770ustar { "git": { "sha1": "0e9519711970b75d2c0e0a9ce0040417ba3b8a3d" }, "path_in_vcs": "" }parity-wasm-0.45.0/Cargo.lock0000644000000014750000000000100113610ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "num_threads" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] [[package]] name = "parity-wasm" version = "0.45.0" dependencies = [ "time", ] [[package]] name = "time" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" dependencies = [ "libc", "num_threads", ] parity-wasm-0.45.0/Cargo.toml0000644000000025300000000000100113750ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56.1" name = "parity-wasm" version = "0.45.0" authors = [ "Nikolay Volf ", "Svyatoslav Nikolsky ", "Sergey Shulepov ", ] include = [ "src/**/*", "LICENSE-*", "README.md", ] description = "WebAssembly low-level format library" homepage = "https://github.com/paritytech/parity-wasm" documentation = "https://docs.rs/parity-wasm" readme = "README.md" keywords = [ "wasm", "webassembly", "bytecode", "serde", "interpreter", ] categories = [ "wasm", "parser-implementations", ] license = "MIT/Apache-2.0" repository = "https://github.com/paritytech/parity-wasm" resolver = "2" [dev-dependencies.time] version = "0.3" [features] atomics = [] bulk = [] default = ["std"] multi_value = [] reduced-stack-buffer = [] sign_ext = [] simd = [] std = [] parity-wasm-0.45.0/Cargo.toml.orig000064400000000000000000000027750072674642500151210ustar 00000000000000[package] name = "parity-wasm" version = "0.45.0" authors = ["Nikolay Volf ", "Svyatoslav Nikolsky ", "Sergey Shulepov "] license = "MIT/Apache-2.0" readme = "README.md" repository = "https://github.com/paritytech/parity-wasm" homepage = "https://github.com/paritytech/parity-wasm" documentation = "https://docs.rs/parity-wasm" description = "WebAssembly low-level format library" keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"] categories = ["wasm", "parser-implementations"] include = ["src/**/*", "LICENSE-*", "README.md"] edition = "2021" rust-version = "1.56.1" [workspace] members = ["testsuite"] [dev-dependencies] time = "0.3" [features] default = ["std"] std = [] # Reduce stack usage for buffered read operations. # This feature is useful when integrating on resource constrained devices such as microcontroler # where the stack size is fixed (stacks do not grow) and limited to a few (k)bytes. reduced-stack-buffer = [] # # Features for enabling non-MVP proposals. # These features should be tested as part of Travis CI build. # # Atomics aka threading. # https://github.com/webassembly/threads/ atomics = [] # SIMD # https://github.com/WebAssembly/simd/ simd = [] # Sign-extension operators # https://github.com/WebAssembly/sign-extension-ops/ sign_ext = [] # Bulk-memory operators # https://github.com/WebAssembly/bulk-memory-operations/ bulk = [] # Multi-value # https://github.com/WebAssembly/multi-value/ multi_value = [] parity-wasm-0.45.0/LICENSE-APACHE000064400000000000000000000251370072674642500141530ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. parity-wasm-0.45.0/LICENSE-MIT000064400000000000000000000020640072674642500136550ustar 00000000000000Copyright (c) 2017-2018 Parity Technologies Limited Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. parity-wasm-0.45.0/README.md000064400000000000000000000043720072674642500135040ustar 00000000000000# parity-wasm Low-level WebAssembly format library. [![Build Status](https://travis-ci.org/paritytech/parity-wasm.svg?branch=master)](https://travis-ci.org/paritytech/parity-wasm) [![crates.io link](https://img.shields.io/crates/v/parity-wasm.svg)](https://crates.io/crates/parity-wasm) [Documentation](https://docs.rs/parity-wasm) ## Rust WebAssembly format serializing/deserializing Add to Cargo.toml ```toml [dependencies] parity-wasm = "0.42" ``` and then ```rust let module = parity_wasm::deserialize_file("./res/cases/v1/hello.wasm").unwrap(); assert!(module.code_section().is_some()); let code_section = module.code_section().unwrap(); // Part of the module with functions code println!("Function count in wasm file: {}", code_section.bodies().len()); ``` ## Wabt Test suite `parity-wasm` supports full [wasm testsuite](https://github.com/WebAssembly/testsuite), running asserts that involves deserialization. To run testsuite: - checkout with submodules (`git submodule update --init --recursive`) - run `cargo test --release --workspace` Decoder can be fuzzed with `cargo-fuzz` using [`wasm-opt`](https://github.com/WebAssembly/binaryen): - make sure you have all prerequisites to build `binaryen` and `cargo-fuzz` (`cmake` and a C++11 toolchain) - checkout with submodules (`git submodule update --init --recursive`) - install `cargo fuzz` subcommand with `cargo install cargo-fuzz` - set rustup to use a nightly toolchain, because `cargo fuzz` uses a rust compiler plugin: `rustup override set nightly` - run `cargo fuzz run deserialize` ## `no_std` crates This crate has a feature, `std`, that is enabled by default. To use this crate in a `no_std` context, add the following to your `Cargo.toml` (still requires allocator though): ```toml [dependencies] parity-wasm = { version = "0.41", default-features = false } ``` ## License `parity-wasm` is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), at your choice. See LICENSE-APACHE, and LICENSE-MIT for details. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in parity-wasm by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. parity-wasm-0.45.0/src/builder/code.rs000064400000000000000000000244720072674642500157250ustar 00000000000000use super::{ invoke::{Identity, Invoke}, misc::{ValueTypeBuilder, ValueTypesBuilder}, }; use crate::elements; use alloc::vec::Vec; /// Signature template description pub enum Signature { TypeReference(u32), Inline(elements::FunctionType), } /// Signature builder pub struct SignatureBuilder { callback: F, signature: elements::FunctionType, } impl SignatureBuilder { /// New signature builder pub fn new() -> Self { SignatureBuilder::with_callback(Identity) } } impl Default for SignatureBuilder { fn default() -> Self { Self::new() } } impl SignatureBuilder where F: Invoke, { /// New builder with callback function specified pub fn with_callback(callback: F) -> Self { SignatureBuilder { callback, signature: elements::FunctionType::default() } } /// Add argument to signature builder pub fn with_param(mut self, value_type: elements::ValueType) -> Self { self.signature.params_mut().push(value_type); self } /// Add multiple arguments to signature builder pub fn with_params(mut self, value_types: I) -> Self where I: IntoIterator, { self.signature.params_mut().extend(value_types); self } /// Start build new argument pub fn param(self) -> ValueTypeBuilder { ValueTypeBuilder::with_callback(self) } /// Start build multiple arguments pub fn params(self) -> ValueTypesBuilder { ValueTypesBuilder::with_callback(self) } /// Add result to signature builder pub fn with_result(mut self, value_type: elements::ValueType) -> Self { self.signature.results_mut().push(value_type); self } /// Add multiple results to signature builder pub fn with_results(mut self, value_types: I) -> Self where I: IntoIterator, { self.signature.results_mut().extend(value_types); self } /// Start building new result pub fn result(self) -> ValueTypeBuilder { ValueTypeBuilder::with_callback(self) } /// Start building multiple results pub fn results(self) -> ValueTypeBuilder { ValueTypeBuilder::with_callback(self) } /// Finish current builder pub fn build(self) -> F::Result { self.callback.invoke(self.signature) } /// Finish current builder returning intermediate `Signature` struct pub fn build_sig(self) -> Signature { Signature::Inline(self.signature) } } impl Invoke for SignatureBuilder where F: Invoke, I: IntoIterator, { type Result = Self; fn invoke(self, args: I) -> Self { self.with_params(args) } } impl Invoke for SignatureBuilder where F: Invoke, { type Result = Self; fn invoke(self, arg: elements::ValueType) -> Self { self.with_result(arg) } } /// Type (signature) reference builder (for function/import/indirect call) pub struct TypeRefBuilder { callback: F, type_ref: u32, } impl TypeRefBuilder where F: Invoke, { /// New builder chained with specified callback pub fn with_callback(callback: F) -> Self { TypeRefBuilder { callback, type_ref: 0 } } /// Set/override of type reference pub fn val(mut self, val: u32) -> Self { self.type_ref = val; self } /// Finish current builder pub fn build(self) -> F::Result { self.callback.invoke(self.type_ref) } } /// Multiple signatures builder pub struct SignaturesBuilder { callback: F, section: Vec, } impl SignaturesBuilder { /// New empty functions section builder pub fn new() -> Self { SignaturesBuilder::with_callback(Identity) } } impl Default for SignaturesBuilder { fn default() -> Self { Self::new() } } impl SignaturesBuilder { /// New builder chained with specified callback pub fn with_callback(callback: F) -> Self { SignaturesBuilder { callback, section: Vec::new() } } /// Push new signature into the builder output pub fn with_signature(mut self, signature: Signature) -> Self { self.section.push(signature); self } /// Start building new signature with `TypeRefBuilder` pub fn type_ref(self) -> TypeRefBuilder { TypeRefBuilder::with_callback(self) } } impl SignaturesBuilder where F: Invoke, { /// Start building new signature with dedicated builder pub fn signature(self) -> SignatureBuilder { SignatureBuilder::with_callback(self) } } impl Invoke for SignaturesBuilder { type Result = Self; fn invoke(self, signature: elements::FunctionType) -> Self { self.with_signature(Signature::Inline(signature)) } } impl Invoke for SignaturesBuilder { type Result = Self; fn invoke(self, type_ref: u32) -> Self { self.with_signature(Signature::TypeReference(type_ref)) } } impl SignaturesBuilder where F: Invoke, { /// Finalize builder spawning element pub fn build(self) -> F::Result { let mut result = elements::FunctionSection::default(); for f in self.section.into_iter() { if let Signature::TypeReference(type_ref) = f { result.entries_mut().push(elements::Func::new(type_ref)); } else { unreachable!(); // never possible with current generics impl-s } } self.callback.invoke(result) } } /// Signature bindings pub type SignatureBindings = Vec; impl SignaturesBuilder where F: Invoke, { /// Bind signature list pub fn bind(self) -> F::Result { self.callback.invoke(self.section) } } /// Function body (code) builder pub struct FuncBodyBuilder { callback: F, body: elements::FuncBody, } impl FuncBodyBuilder { /// New body (code) builder given the chain callback pub fn with_callback(callback: F) -> Self { FuncBodyBuilder { callback, body: elements::FuncBody::new(Vec::new(), elements::Instructions::empty()), } } } impl FuncBodyBuilder where F: Invoke, { /// Set/override entirely with FuncBody struct pub fn with_func(mut self, func: elements::FuncBody) -> Self { self.body = func; self } /// Extend function local list with new entries pub fn with_locals(mut self, locals: I) -> Self where I: IntoIterator, { self.body.locals_mut().extend(locals); self } /// Set code of the function pub fn with_instructions(mut self, instructions: elements::Instructions) -> Self { *self.body.code_mut() = instructions; self } /// Finish current builder spawning resulting struct pub fn build(self) -> F::Result { self.callback.invoke(self.body) } } /// Function definition (extended structure to specify function entirely, incl. signature, mainness and code) pub struct FunctionDefinition { /// Is this function is start function pub is_main: bool, /// Signature description pub signature: Signature, /// Body (code) of the function pub code: elements::FuncBody, } impl Default for FunctionDefinition { fn default() -> Self { FunctionDefinition { is_main: false, signature: Signature::TypeReference(0), code: elements::FuncBody::empty(), } } } /// Function definition builder pub struct FunctionBuilder { callback: F, func: FunctionDefinition, } impl FunctionBuilder { /// New function builder pub fn new() -> Self { FunctionBuilder::with_callback(Identity) } } impl Default for FunctionBuilder { fn default() -> Self { Self::new() } } impl FunctionBuilder where F: Invoke, { /// New function builder with chained callback pub fn with_callback(callback: F) -> Self { FunctionBuilder { callback, func: Default::default() } } /// Set that this function is main entry point pub fn main(mut self) -> Self { self.func.is_main = true; self } /// Start signature builder of the function pub fn signature(self) -> SignatureBuilder { SignatureBuilder::with_callback(self) } /// Override current signature entirely with new one from known struct pub fn with_signature(mut self, signature: Signature) -> Self { self.func.signature = signature; self } /// Start code (body) builder pub fn body(self) -> FuncBodyBuilder { FuncBodyBuilder::with_callback(self) } /// Set body (code) for this function pub fn with_body(mut self, body: elements::FuncBody) -> Self { self.func.code = body; self } /// Finalize current builder spawning resulting struct in the callback pub fn build(self) -> F::Result { self.callback.invoke(self.func) } } impl Invoke for FunctionBuilder where F: Invoke, { type Result = Self; fn invoke(self, signature: elements::FunctionType) -> Self { self.with_signature(Signature::Inline(signature)) } } impl Invoke for FunctionBuilder where F: Invoke, { type Result = Self; fn invoke(self, type_ref: u32) -> Self { self.with_signature(Signature::TypeReference(type_ref)) } } impl Invoke for FunctionBuilder where F: Invoke, { type Result = Self; fn invoke(self, body: elements::FuncBody) -> Self::Result { self.with_body(body) } } /// New builder of signature list pub fn signatures() -> SignaturesBuilder { SignaturesBuilder::new() } /// New signature builder pub fn signature() -> SignatureBuilder { SignatureBuilder::new() } /// New builder of function (signature & body) pub fn function() -> FunctionBuilder { FunctionBuilder::new() } #[cfg(test)] mod tests { use super::{function, signatures}; use crate::elements; #[test] fn example() { let result = signatures().type_ref().val(1).build().build(); assert_eq!(result.entries().len(), 1); let result = signatures() .signature() .param() .i32() .param() .i32() .result() .i64() .build() .bind(); assert_eq!(result.len(), 1); } #[test] fn func_example() { let func = function() .signature() .param() .i32() .result() .i32() .build() .body() .with_instructions(elements::Instructions::empty()) .build() .build(); assert_eq!(func.code.locals().len(), 0); assert_eq!(func.code.code().elements().len(), 1); } #[test] fn func_example_multi_result() { let func = function() .signature() .param() .i32() .result() .i32() .result() .i32() .build() .body() .with_instructions(elements::Instructions::empty()) .build() .build(); assert_eq!(func.code.locals().len(), 0); assert_eq!(func.code.code().elements().len(), 1); } } parity-wasm-0.45.0/src/builder/data.rs000064400000000000000000000026200072674642500157130ustar 00000000000000use super::invoke::{Identity, Invoke}; use crate::elements; use alloc::vec::Vec; /// Data segment builder pub struct DataSegmentBuilder { callback: F, // todo: add mapper once multiple memory refs possible mem_index: u32, offset: elements::InitExpr, value: Vec, } impl DataSegmentBuilder { /// New data segment builder pub fn new() -> Self { DataSegmentBuilder::with_callback(Identity) } } impl Default for DataSegmentBuilder { fn default() -> Self { Self::new() } } impl DataSegmentBuilder { /// New data segment builder inside the chain context pub fn with_callback(callback: F) -> Self { DataSegmentBuilder { callback, mem_index: 0, offset: elements::InitExpr::empty(), value: Vec::new(), } } /// Set offset initialization instruction. `End` instruction will be added automatically. pub fn offset(mut self, instruction: elements::Instruction) -> Self { self.offset = elements::InitExpr::new(vec![instruction, elements::Instruction::End]); self } /// Set the bytes value of the segment pub fn value(mut self, value: Vec) -> Self { self.value = value; self } } impl DataSegmentBuilder where F: Invoke, { /// Finish current builder, spawning resulting struct pub fn build(self) -> F::Result { self.callback.invoke(elements::DataSegment::new( self.mem_index, Some(self.offset), self.value, )) } } parity-wasm-0.45.0/src/builder/export.rs000064400000000000000000000055060072674642500163310ustar 00000000000000use super::invoke::{Identity, Invoke}; use crate::elements; use alloc::{borrow::ToOwned, string::String}; /// Export entry builder pub struct ExportBuilder { callback: F, field: String, binding: elements::Internal, } impl ExportBuilder { /// New export builder pub fn new() -> Self { ExportBuilder::with_callback(Identity) } } impl Default for ExportBuilder { fn default() -> Self { Self::new() } } impl ExportBuilder { /// New export entry builder in the specified chained context pub fn with_callback(callback: F) -> Self { ExportBuilder { callback, field: String::new(), binding: elements::Internal::Function(0) } } /// Set the field name of the export entry pub fn field(mut self, field: &str) -> Self { self.field = field.to_owned(); self } /// Specify the internal module mapping for this entry pub fn with_internal(mut self, external: elements::Internal) -> Self { self.binding = external; self } /// Start the internal builder for this export entry pub fn internal(self) -> ExportInternalBuilder { ExportInternalBuilder::with_callback(self) } } impl ExportBuilder where F: Invoke, { /// Finalize export entry builder spawning the resulting struct pub fn build(self) -> F::Result { self.callback.invoke(elements::ExportEntry::new(self.field, self.binding)) } } impl Invoke for ExportBuilder { type Result = Self; fn invoke(self, val: elements::Internal) -> Self { self.with_internal(val) } } /// Internal mapping builder for export entry pub struct ExportInternalBuilder { callback: F, binding: elements::Internal, } impl ExportInternalBuilder where F: Invoke, { /// New export entry internal mapping for the chained context pub fn with_callback(callback: F) -> Self { ExportInternalBuilder { callback, binding: elements::Internal::Function(0) } } /// Map to function by index pub fn func(mut self, index: u32) -> F::Result { self.binding = elements::Internal::Function(index); self.callback.invoke(self.binding) } /// Map to memory pub fn memory(mut self, index: u32) -> F::Result { self.binding = elements::Internal::Memory(index); self.callback.invoke(self.binding) } /// Map to table pub fn table(mut self, index: u32) -> F::Result { self.binding = elements::Internal::Table(index); self.callback.invoke(self.binding) } /// Map to global pub fn global(mut self, index: u32) -> F::Result { self.binding = elements::Internal::Global(index); self.callback.invoke(self.binding) } } /// New builder for export entry pub fn export() -> ExportBuilder { ExportBuilder::new() } #[cfg(test)] mod tests { use super::export; #[test] fn example() { let entry = export().field("memory").internal().memory(0).build(); assert_eq!(entry.field(), "memory"); } } parity-wasm-0.45.0/src/builder/global.rs000064400000000000000000000042710072674642500162460ustar 00000000000000use super::{ invoke::{Identity, Invoke}, misc::ValueTypeBuilder, }; use crate::elements; /// Global builder pub struct GlobalBuilder { callback: F, value_type: elements::ValueType, is_mutable: bool, init_expr: elements::InitExpr, } impl GlobalBuilder { /// New global builder pub fn new() -> Self { GlobalBuilder::with_callback(Identity) } } impl Default for GlobalBuilder { fn default() -> Self { Self::new() } } impl GlobalBuilder { /// New global builder with callback (in chained context) pub fn with_callback(callback: F) -> Self { GlobalBuilder { callback, value_type: elements::ValueType::I32, init_expr: elements::InitExpr::empty(), is_mutable: false, } } /// Set/override resulting global type pub fn with_type(mut self, value_type: elements::ValueType) -> Self { self.value_type = value_type; self } /// Set mutabilty to true pub fn mutable(mut self) -> Self { self.is_mutable = true; self } /// Set initialization expression instruction for this global (`end` instruction will be added automatically) pub fn init_expr(mut self, instruction: elements::Instruction) -> Self { self.init_expr = elements::InitExpr::new(vec![instruction, elements::Instruction::End]); self } /// Start value type builder pub fn value_type(self) -> ValueTypeBuilder { ValueTypeBuilder::with_callback(self) } } impl GlobalBuilder where F: Invoke, { /// Finalize current builder spawning resulting struct pub fn build(self) -> F::Result { self.callback.invoke(elements::GlobalEntry::new( elements::GlobalType::new(self.value_type, self.is_mutable), self.init_expr, )) } } impl Invoke for GlobalBuilder { type Result = Self; fn invoke(self, the_type: elements::ValueType) -> Self { self.with_type(the_type) } } /// New builder for export entry pub fn global() -> GlobalBuilder { GlobalBuilder::new() } #[cfg(test)] mod tests { use super::global; use crate::elements; #[test] fn example() { let entry = global().value_type().i32().build(); assert_eq!(entry.global_type().content_type(), elements::ValueType::I32); assert!(!entry.global_type().is_mutable()); } } parity-wasm-0.45.0/src/builder/import.rs000064400000000000000000000066660072674642500163320ustar 00000000000000use super::invoke::{Identity, Invoke}; use crate::elements; use alloc::{borrow::ToOwned, string::String}; /// Import builder pub struct ImportBuilder { callback: F, module: String, field: String, binding: elements::External, } impl ImportBuilder { /// New import builder pub fn new() -> Self { ImportBuilder::with_callback(Identity) } } impl Default for ImportBuilder { fn default() -> Self { Self::new() } } impl ImportBuilder { /// New import builder with callback (in chained context) pub fn with_callback(callback: F) -> Self { ImportBuilder { callback, module: String::new(), field: String::new(), binding: elements::External::Function(0), } } /// Set/override module name pub fn module(mut self, name: &str) -> Self { self.module = name.to_owned(); self } /// Set/override field name pub fn field(mut self, name: &str) -> Self { self.field = name.to_owned(); self } /// Set/override both module name and field name pub fn path(self, module: &str, field: &str) -> Self { self.module(module).field(field) } /// Set/override external mapping for this import pub fn with_external(mut self, external: elements::External) -> Self { self.binding = external; self } /// Start new external mapping builder pub fn external(self) -> ImportExternalBuilder { ImportExternalBuilder::with_callback(self) } } impl ImportBuilder where F: Invoke, { /// Finalize current builder spawning the resulting struct pub fn build(self) -> F::Result { self.callback .invoke(elements::ImportEntry::new(self.module, self.field, self.binding)) } } impl Invoke for ImportBuilder { type Result = Self; fn invoke(self, val: elements::External) -> Self { self.with_external(val) } } /// Import to external mapping builder pub struct ImportExternalBuilder { callback: F, binding: elements::External, } impl ImportExternalBuilder where F: Invoke, { /// New import to external mapping builder with callback (in chained context) pub fn with_callback(callback: F) -> Self { ImportExternalBuilder { callback, binding: elements::External::Function(0) } } /// Function mapping with type reference pub fn func(mut self, index: u32) -> F::Result { self.binding = elements::External::Function(index); self.callback.invoke(self.binding) } /// Memory mapping with specified limits pub fn memory(mut self, min: u32, max: Option) -> F::Result { self.binding = elements::External::Memory(elements::MemoryType::new(min, max)); self.callback.invoke(self.binding) } /// Table mapping with specified limits pub fn table(mut self, min: u32, max: Option) -> F::Result { self.binding = elements::External::Table(elements::TableType::new(min, max)); self.callback.invoke(self.binding) } /// Global mapping with speciifed type and mutability pub fn global(mut self, value_type: elements::ValueType, is_mut: bool) -> F::Result { self.binding = elements::External::Global(elements::GlobalType::new(value_type, is_mut)); self.callback.invoke(self.binding) } } /// New builder for import entry pub fn import() -> ImportBuilder { ImportBuilder::new() } #[cfg(test)] mod tests { use super::import; #[test] fn example() { let entry = import().module("env").field("memory").external().memory(256, Some(256)).build(); assert_eq!(entry.module(), "env"); assert_eq!(entry.field(), "memory"); } } parity-wasm-0.45.0/src/builder/invoke.rs000064400000000000000000000004300072674642500162720ustar 00000000000000//! invoke helper /// Helper trait to allow chaining pub trait Invoke { type Result; fn invoke(self, arg: A) -> Self::Result; } /// Identity chain element pub struct Identity; impl Invoke for Identity { type Result = A; fn invoke(self, arg: A) -> A { arg } } parity-wasm-0.45.0/src/builder/memory.rs000064400000000000000000000036650072674642500163240ustar 00000000000000use super::invoke::{Identity, Invoke}; use crate::elements; use alloc::vec::Vec; /// Memory definition struct #[derive(Debug, PartialEq)] pub struct MemoryDefinition { /// Minimum memory size pub min: u32, /// Maximum memory size pub max: Option, /// Memory data segments (static regions) pub data: Vec, } /// Memory static region entry definition #[derive(Debug, PartialEq)] pub struct MemoryDataDefinition { /// Segment initialization expression for offset pub offset: elements::InitExpr, /// Raw bytes of static region pub values: Vec, } /// Memory and static regions builder pub struct MemoryBuilder { callback: F, memory: MemoryDefinition, } impl MemoryBuilder { /// New memory builder pub fn new() -> Self { MemoryBuilder::with_callback(Identity) } } impl Default for MemoryBuilder { fn default() -> Self { Self::new() } } impl MemoryBuilder where F: Invoke, { /// New memory builder with callback (in chained context) pub fn with_callback(callback: F) -> Self { MemoryBuilder { callback, memory: Default::default() } } /// Set/override minimum size pub fn with_min(mut self, min: u32) -> Self { self.memory.min = min; self } /// Set/override maximum size pub fn with_max(mut self, max: Option) -> Self { self.memory.max = max; self } /// Push new static region with initialized offset expression and raw bytes pub fn with_data(mut self, index: u32, values: Vec) -> Self { self.memory.data.push(MemoryDataDefinition { offset: elements::InitExpr::new(vec![ elements::Instruction::I32Const(index as i32), elements::Instruction::End, ]), values, }); self } /// Finalize current builder, spawning resulting struct pub fn build(self) -> F::Result { self.callback.invoke(self.memory) } } impl Default for MemoryDefinition { fn default() -> Self { MemoryDefinition { min: 1, max: None, data: Vec::new() } } } parity-wasm-0.45.0/src/builder/misc.rs000064400000000000000000000025440072674642500157420ustar 00000000000000use super::invoke::{Identity, Invoke}; use crate::elements; use alloc::vec::Vec; pub struct ValueTypeBuilder { callback: F, } impl ValueTypeBuilder where F: Invoke, { pub fn with_callback(callback: F) -> Self { ValueTypeBuilder { callback } } pub fn i32(self) -> F::Result { self.callback.invoke(elements::ValueType::I32) } pub fn i64(self) -> F::Result { self.callback.invoke(elements::ValueType::I64) } pub fn f32(self) -> F::Result { self.callback.invoke(elements::ValueType::F32) } pub fn f64(self) -> F::Result { self.callback.invoke(elements::ValueType::F64) } } pub struct ValueTypesBuilder { callback: F, value_types: Vec, } impl ValueTypesBuilder where F: Invoke>, { pub fn with_callback(callback: F) -> Self { ValueTypesBuilder { callback, value_types: Vec::new() } } pub fn i32(mut self) -> Self { self.value_types.push(elements::ValueType::I32); self } pub fn i64(mut self) -> Self { self.value_types.push(elements::ValueType::I64); self } pub fn f32(mut self) -> Self { self.value_types.push(elements::ValueType::F32); self } pub fn f64(mut self) -> Self { self.value_types.push(elements::ValueType::F64); self } pub fn build(self) -> F::Result { self.callback.invoke(self.value_types) } } parity-wasm-0.45.0/src/builder/mod.rs000064400000000000000000000012310072674642500155560ustar 00000000000000//! Various builders to generate/alter wasm components mod code; mod data; mod export; mod global; mod import; mod invoke; mod memory; mod misc; mod module; mod table; pub use self::{ code::{ function, signature, signatures, FuncBodyBuilder, FunctionBuilder, FunctionDefinition, SignatureBuilder, SignaturesBuilder, TypeRefBuilder, }, data::DataSegmentBuilder, export::{export, ExportBuilder, ExportInternalBuilder}, global::{global, GlobalBuilder}, import::{import, ImportBuilder}, invoke::Identity, memory::MemoryBuilder, module::{from_module, module, CodeLocation, ModuleBuilder}, table::{TableBuilder, TableDefinition, TableEntryDefinition}, }; parity-wasm-0.45.0/src/builder/module.rs000064400000000000000000000411500072674642500162700ustar 00000000000000use super::{ code::{self, FunctionBuilder, SignaturesBuilder}, data, export, global, import, invoke::{Identity, Invoke}, memory::{self, MemoryBuilder}, table::{self, TableBuilder}, }; use crate::elements; use alloc::vec::Vec; /// Module builder pub struct ModuleBuilder { callback: F, module: ModuleScaffold, } /// Location of the internal module function pub struct CodeLocation { /// Location (index in 'functions' section) of the signature pub signature: u32, /// Location (index in the 'code' section) of the body pub body: u32, } #[derive(Default, PartialEq)] struct ModuleScaffold { pub types: elements::TypeSection, pub import: elements::ImportSection, pub functions: elements::FunctionSection, pub table: elements::TableSection, pub memory: elements::MemorySection, pub global: elements::GlobalSection, pub export: elements::ExportSection, pub start: Option, pub element: elements::ElementSection, pub code: elements::CodeSection, pub data: elements::DataSection, pub other: Vec, } impl From for ModuleScaffold { fn from(module: elements::Module) -> Self { let mut types: Option = None; let mut import: Option = None; let mut funcs: Option = None; let mut table: Option = None; let mut memory: Option = None; let mut global: Option = None; let mut export: Option = None; let mut start: Option = None; let mut element: Option = None; let mut code: Option = None; let mut data: Option = None; let mut other = Vec::new(); let mut sections = module.into_sections(); while let Some(section) = sections.pop() { match section { elements::Section::Type(sect) => { types = Some(sect); }, elements::Section::Import(sect) => { import = Some(sect); }, elements::Section::Function(sect) => { funcs = Some(sect); }, elements::Section::Table(sect) => { table = Some(sect); }, elements::Section::Memory(sect) => { memory = Some(sect); }, elements::Section::Global(sect) => { global = Some(sect); }, elements::Section::Export(sect) => { export = Some(sect); }, elements::Section::Start(index) => { start = Some(index); }, elements::Section::Element(sect) => { element = Some(sect); }, elements::Section::Code(sect) => { code = Some(sect); }, elements::Section::Data(sect) => { data = Some(sect); }, section => other.push(section), } } ModuleScaffold { types: types.unwrap_or_default(), import: import.unwrap_or_default(), functions: funcs.unwrap_or_default(), table: table.unwrap_or_default(), memory: memory.unwrap_or_default(), global: global.unwrap_or_default(), export: export.unwrap_or_default(), start, element: element.unwrap_or_default(), code: code.unwrap_or_default(), data: data.unwrap_or_default(), other, } } } impl From for elements::Module { fn from(module: ModuleScaffold) -> Self { let mut sections = Vec::new(); let types = module.types; if !types.types().is_empty() { sections.push(elements::Section::Type(types)); } let import = module.import; if !import.entries().is_empty() { sections.push(elements::Section::Import(import)); } let functions = module.functions; if !functions.entries().is_empty() { sections.push(elements::Section::Function(functions)); } let table = module.table; if !table.entries().is_empty() { sections.push(elements::Section::Table(table)); } let memory = module.memory; if !memory.entries().is_empty() { sections.push(elements::Section::Memory(memory)); } let global = module.global; if !global.entries().is_empty() { sections.push(elements::Section::Global(global)); } let export = module.export; if !export.entries().is_empty() { sections.push(elements::Section::Export(export)); } if let Some(start) = module.start { sections.push(elements::Section::Start(start)); } let element = module.element; if !element.entries().is_empty() { sections.push(elements::Section::Element(element)); } let code = module.code; if !code.bodies().is_empty() { sections.push(elements::Section::Code(code)); } let data = module.data; if !data.entries().is_empty() { sections.push(elements::Section::Data(data)); } sections.extend(module.other); elements::Module::new(sections) } } impl ModuleBuilder { /// New empty module builder pub fn new() -> Self { ModuleBuilder::with_callback(Identity) } } impl Default for ModuleBuilder { fn default() -> Self { Self::new() } } impl ModuleBuilder where F: Invoke, { /// New module builder with bound callback pub fn with_callback(callback: F) -> Self { ModuleBuilder { callback, module: Default::default() } } /// Builder from raw module pub fn with_module(mut self, module: elements::Module) -> Self { self.module = module.into(); self } /// Fill module with sections from iterator pub fn with_sections(mut self, sections: I) -> Self where I: IntoIterator, { self.module.other.extend(sections); self } /// Add additional section pub fn with_section(mut self, section: elements::Section) -> Self { self.module.other.push(section); self } /// Binds to the type section, creates additional types when required pub fn with_signatures(mut self, bindings: code::SignatureBindings) -> Self { self.push_signatures(bindings); self } /// Push stand-alone function definition, creating sections, signature and code blocks /// in corresponding sections. /// `FunctionDefinition` can be build using `builder::function` builder pub fn push_function(&mut self, func: code::FunctionDefinition) -> CodeLocation { let signature = func.signature; let body = func.code; let type_ref = self.resolve_type_ref(signature); self.module.functions.entries_mut().push(elements::Func::new(type_ref)); let signature_index = self.module.functions.entries_mut().len() as u32 - 1; self.module.code.bodies_mut().push(body); let body_index = self.module.code.bodies_mut().len() as u32 - 1; if func.is_main { self.module.start = Some(body_index); } CodeLocation { signature: signature_index, body: body_index } } /// Push linear memory region pub fn push_memory(&mut self, mut memory: memory::MemoryDefinition) -> u32 { let entries = self.module.memory.entries_mut(); entries.push(elements::MemoryType::new(memory.min, memory.max)); let memory_index = (entries.len() - 1) as u32; for data in memory.data.drain(..) { self.module.data.entries_mut().push(elements::DataSegment::new( memory_index, Some(data.offset), data.values, )) } memory_index } /// Push table pub fn push_table(&mut self, mut table: table::TableDefinition) -> u32 { let entries = self.module.table.entries_mut(); entries.push(elements::TableType::new(table.min, table.max)); let table_index = (entries.len() - 1) as u32; for entry in table.elements.drain(..) { self.module.element.entries_mut().push(elements::ElementSegment::new( table_index, Some(entry.offset), entry.values, )) } table_index } /// Push global. pub fn push_global(&mut self, global: elements::GlobalEntry) -> u32 { let entries = self.module.global.entries_mut(); entries.push(global); entries.len() as u32 - 1 } fn resolve_type_ref(&mut self, signature: code::Signature) -> u32 { match signature { code::Signature::Inline(func_type) => { if let Some(existing_entry) = self.module.types.types().iter().enumerate().find(|(_idx, t)| { let elements::Type::Function(ref existing) = t; *existing == func_type }) { return existing_entry.0 as u32 } self.module.types.types_mut().push(elements::Type::Function(func_type)); self.module.types.types().len() as u32 - 1 }, code::Signature::TypeReference(type_ref) => type_ref, } } /// Push one function signature, returning it's calling index. /// Can create corresponding type in type section. pub fn push_signature(&mut self, signature: code::Signature) -> u32 { self.resolve_type_ref(signature) } /// Push signatures in the module, returning corresponding indices of pushed signatures pub fn push_signatures(&mut self, signatures: code::SignatureBindings) -> Vec { signatures.into_iter().map(|binding| self.resolve_type_ref(binding)).collect() } /// Push import entry to module. Note that this does not update calling indices in /// function bodies. pub fn push_import(&mut self, import: elements::ImportEntry) -> u32 { self.module.import.entries_mut().push(import); // todo: actually update calling addresses in function bodies // todo: also batch push self.module.import.entries_mut().len() as u32 - 1 } /// Push export entry to module. pub fn push_export(&mut self, export: elements::ExportEntry) -> u32 { self.module.export.entries_mut().push(export); self.module.export.entries_mut().len() as u32 - 1 } /// Add new function using dedicated builder pub fn function(self) -> FunctionBuilder { FunctionBuilder::with_callback(self) } /// Add new linear memory using dedicated builder pub fn memory(self) -> MemoryBuilder { MemoryBuilder::with_callback(self) } /// Add new table using dedicated builder pub fn table(self) -> TableBuilder { TableBuilder::with_callback(self) } /// Define functions section pub fn functions(self) -> SignaturesBuilder { SignaturesBuilder::with_callback(self) } /// With inserted export entry pub fn with_export(mut self, entry: elements::ExportEntry) -> Self { self.module.export.entries_mut().push(entry); self } /// With inserted import entry pub fn with_import(mut self, entry: elements::ImportEntry) -> Self { self.module.import.entries_mut().push(entry); self } /// Import entry builder /// # Examples /// ``` /// use parity_wasm::builder::module; /// /// let module = module() /// .import() /// .module("env") /// .field("memory") /// .external().memory(256, Some(256)) /// .build() /// .build(); /// /// assert_eq!(module.import_section().expect("import section to exist").entries().len(), 1); /// ``` pub fn import(self) -> import::ImportBuilder { import::ImportBuilder::with_callback(self) } /// With global variable pub fn with_global(mut self, global: elements::GlobalEntry) -> Self { self.module.global.entries_mut().push(global); self } /// With table pub fn with_table(mut self, table: elements::TableType) -> Self { self.module.table.entries_mut().push(table); self } /// Export entry builder /// # Examples /// ``` /// use parity_wasm::builder::module; /// use parity_wasm::elements::Instruction::*; /// /// let module = module() /// .global() /// .value_type().i32() /// .init_expr(I32Const(0)) /// .build() /// .export() /// .field("_zero") /// .internal().global(0) /// .build() /// .build(); /// /// assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1); /// ``` pub fn export(self) -> export::ExportBuilder { export::ExportBuilder::with_callback(self) } /// Glboal entry builder /// # Examples /// ``` /// use parity_wasm::builder::module; /// use parity_wasm::elements::Instruction::*; /// /// let module = module() /// .global() /// .value_type().i32() /// .init_expr(I32Const(0)) /// .build() /// .build(); /// /// assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1); /// ``` pub fn global(self) -> global::GlobalBuilder { global::GlobalBuilder::with_callback(self) } /// Add data segment to the builder pub fn with_data_segment(mut self, segment: elements::DataSegment) -> Self { self.module.data.entries_mut().push(segment); self } /// Data entry builder pub fn data(self) -> data::DataSegmentBuilder { data::DataSegmentBuilder::with_callback(self) } /// Build module (final step) pub fn build(self) -> F::Result { self.callback.invoke(self.module.into()) } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, section: elements::FunctionSection) -> Self { self.with_section(elements::Section::Function(section)) } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, bindings: code::SignatureBindings) -> Self { self.with_signatures(bindings) } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, def: code::FunctionDefinition) -> Self { let mut b = self; b.push_function(def); b } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, def: memory::MemoryDefinition) -> Self { let mut b = self; b.push_memory(def); b } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, def: table::TableDefinition) -> Self { let mut b = self; b.push_table(def); b } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, entry: elements::ImportEntry) -> Self::Result { self.with_import(entry) } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, entry: elements::ExportEntry) -> Self::Result { self.with_export(entry) } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, entry: elements::GlobalEntry) -> Self::Result { self.with_global(entry) } } impl Invoke for ModuleBuilder where F: Invoke, { type Result = Self; fn invoke(self, segment: elements::DataSegment) -> Self { self.with_data_segment(segment) } } /// Start new module builder /// # Examples /// /// ``` /// use parity_wasm::builder; /// /// let module = builder::module() /// .function() /// .signature().param().i32().build() /// .body().build() /// .build() /// .build(); /// /// assert_eq!(module.type_section().expect("type section to exist").types().len(), 1); /// assert_eq!(module.function_section().expect("function section to exist").entries().len(), 1); /// assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1); /// ``` pub fn module() -> ModuleBuilder { ModuleBuilder::new() } /// Start builder to extend existing module pub fn from_module(module: elements::Module) -> ModuleBuilder { ModuleBuilder::new().with_module(module) } #[cfg(test)] mod tests { use super::module; use crate::elements; #[test] fn smoky() { let module = module().build(); assert_eq!(module.sections().len(), 0); } #[test] fn functions() { let module = module() .function() .signature() .param() .i32() .build() .body() .build() .build() .build(); assert_eq!(module.type_section().expect("type section to exist").types().len(), 1); assert_eq!( module.function_section().expect("function section to exist").entries().len(), 1 ); assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1); } #[test] fn export() { let module = module().export().field("call").internal().func(0).build().build(); assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1); } #[test] fn global() { let module = module() .global() .value_type() .i64() .mutable() .init_expr(elements::Instruction::I64Const(5)) .build() .build(); assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1); } #[test] fn data() { let module = module() .data() .offset(elements::Instruction::I32Const(16)) .value(vec![0u8, 15, 10, 5, 25]) .build() .build(); assert_eq!(module.data_section().expect("data section to exist").entries().len(), 1); } #[test] fn reuse_types() { let module = module() .function() .signature() .param() .i32() .build() .body() .build() .build() .function() .signature() .param() .i32() .build() .body() .build() .build() .build(); assert_eq!(module.type_section().expect("type section failed").types().len(), 1); } } parity-wasm-0.45.0/src/builder/table.rs000064400000000000000000000034000072674642500160660ustar 00000000000000use super::invoke::{Identity, Invoke}; use crate::elements; use alloc::vec::Vec; /// Table definition #[derive(Debug, PartialEq, Default)] pub struct TableDefinition { /// Minimum length pub min: u32, /// Maximum length, if any pub max: Option, /// Element segments, if any pub elements: Vec, } /// Table elements entry definition #[derive(Debug, PartialEq)] pub struct TableEntryDefinition { /// Offset initialization expression pub offset: elements::InitExpr, /// Values of initialization pub values: Vec, } /// Table builder pub struct TableBuilder { callback: F, table: TableDefinition, } impl TableBuilder { /// New table builder pub fn new() -> Self { TableBuilder::with_callback(Identity) } } impl Default for TableBuilder { fn default() -> Self { Self::new() } } impl TableBuilder where F: Invoke, { /// New table builder with callback in chained context pub fn with_callback(callback: F) -> Self { TableBuilder { callback, table: Default::default() } } /// Set/override minimum length pub fn with_min(mut self, min: u32) -> Self { self.table.min = min; self } /// Set/override maximum length pub fn with_max(mut self, max: Option) -> Self { self.table.max = max; self } /// Generate initialization expression and element values on specified index pub fn with_element(mut self, index: u32, values: Vec) -> Self { self.table.elements.push(TableEntryDefinition { offset: elements::InitExpr::new(vec![ elements::Instruction::I32Const(index as i32), elements::Instruction::End, ]), values, }); self } /// Finalize current builder spawning resulting struct pub fn build(self) -> F::Result { self.callback.invoke(self.table) } } parity-wasm-0.45.0/src/elements/export_entry.rs000064400000000000000000000047430072674642500177420ustar 00000000000000use super::{Deserialize, Error, Serialize, VarUint32, VarUint7}; use crate::io; use alloc::string::String; /// Internal reference of the exported entry. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Internal { /// Function reference. Function(u32), /// Table reference. Table(u32), /// Memory reference. Memory(u32), /// Global reference. Global(u32), } impl Deserialize for Internal { type Error = Error; fn deserialize(reader: &mut R) -> Result { let kind = VarUint7::deserialize(reader)?; match kind.into() { 0x00 => Ok(Internal::Function(VarUint32::deserialize(reader)?.into())), 0x01 => Ok(Internal::Table(VarUint32::deserialize(reader)?.into())), 0x02 => Ok(Internal::Memory(VarUint32::deserialize(reader)?.into())), 0x03 => Ok(Internal::Global(VarUint32::deserialize(reader)?.into())), _ => Err(Error::UnknownInternalKind(kind.into())), } } } impl Serialize for Internal { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let (bt, arg) = match self { Internal::Function(arg) => (0x00, arg), Internal::Table(arg) => (0x01, arg), Internal::Memory(arg) => (0x02, arg), Internal::Global(arg) => (0x03, arg), }; VarUint7::from(bt).serialize(writer)?; VarUint32::from(arg).serialize(writer)?; Ok(()) } } /// Export entry. #[derive(Debug, Clone, PartialEq)] pub struct ExportEntry { field_str: String, internal: Internal, } impl ExportEntry { /// New export entry. pub fn new(field_str: String, internal: Internal) -> Self { ExportEntry { field_str, internal } } /// Public name. pub fn field(&self) -> &str { &self.field_str } /// Public name (mutable). pub fn field_mut(&mut self) -> &mut String { &mut self.field_str } /// Internal reference of the export entry. pub fn internal(&self) -> &Internal { &self.internal } /// Internal reference of the export entry (mutable). pub fn internal_mut(&mut self) -> &mut Internal { &mut self.internal } } impl Deserialize for ExportEntry { type Error = Error; fn deserialize(reader: &mut R) -> Result { let field_str = String::deserialize(reader)?; let internal = Internal::deserialize(reader)?; Ok(ExportEntry { field_str, internal }) } } impl Serialize for ExportEntry { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { self.field_str.serialize(writer)?; self.internal.serialize(writer)?; Ok(()) } } parity-wasm-0.45.0/src/elements/func.rs000064400000000000000000000077700072674642500161360ustar 00000000000000use super::{ CountedList, CountedListWriter, CountedWriter, Deserialize, Error, Instructions, Serialize, ValueType, VarUint32, }; use crate::{elements::section::SectionReader, io}; use alloc::vec::Vec; /// Function signature (type reference) #[derive(Debug, Copy, Clone, PartialEq)] pub struct Func(u32); impl Func { /// New function signature pub fn new(type_ref: u32) -> Self { Func(type_ref) } /// Function signature type reference. pub fn type_ref(&self) -> u32 { self.0 } /// Function signature type reference (mutable). pub fn type_ref_mut(&mut self) -> &mut u32 { &mut self.0 } } impl Serialize for Func { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { VarUint32::from(self.0).serialize(writer) } } impl Deserialize for Func { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(Func(VarUint32::deserialize(reader)?.into())) } } /// Local definition inside the function body. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Local { count: u32, value_type: ValueType, } impl Local { /// New local with `count` and `value_type`. pub fn new(count: u32, value_type: ValueType) -> Self { Local { count, value_type } } /// Number of locals with the shared type. pub fn count(&self) -> u32 { self.count } /// Type of the locals. pub fn value_type(&self) -> ValueType { self.value_type } } impl Deserialize for Local { type Error = Error; fn deserialize(reader: &mut R) -> Result { let count = VarUint32::deserialize(reader)?; let value_type = ValueType::deserialize(reader)?; Ok(Local { count: count.into(), value_type }) } } impl Serialize for Local { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { VarUint32::from(self.count).serialize(writer)?; self.value_type.serialize(writer)?; Ok(()) } } /// Function body definition. #[derive(Debug, Clone, PartialEq)] pub struct FuncBody { locals: Vec, instructions: Instructions, } impl FuncBody { /// New function body with given `locals` and `instructions`. pub fn new(locals: Vec, instructions: Instructions) -> Self { FuncBody { locals, instructions } } /// List of individual instructions. pub fn empty() -> Self { FuncBody { locals: Vec::new(), instructions: Instructions::empty() } } /// Locals declared in function body. pub fn locals(&self) -> &[Local] { &self.locals } /// Instruction list of the function body. Minimal instruction list /// /// is just `&[Instruction::End]` pub fn code(&self) -> &Instructions { &self.instructions } /// Locals declared in function body (mutable). pub fn locals_mut(&mut self) -> &mut Vec { &mut self.locals } /// Instruction list of the function body (mutable). pub fn code_mut(&mut self) -> &mut Instructions { &mut self.instructions } } impl Deserialize for FuncBody { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut body_reader = SectionReader::new(reader)?; let locals: Vec = CountedList::::deserialize(&mut body_reader)?.into_inner(); // The specification obliges us to count the total number of local variables while // decoding the binary format. locals .iter() .try_fold(0u32, |acc, &Local { count, .. }| acc.checked_add(count)) .ok_or(Error::TooManyLocals)?; let instructions = Instructions::deserialize(&mut body_reader)?; body_reader.close()?; Ok(FuncBody { locals, instructions }) } } impl Serialize for FuncBody { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.locals; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; let code = self.instructions; code.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } parity-wasm-0.45.0/src/elements/global_entry.rs000064400000000000000000000025000072674642500176460ustar 00000000000000use super::{Deserialize, Error, GlobalType, InitExpr, Serialize}; use crate::io; /// Global entry in the module. #[derive(Clone, Debug, PartialEq)] pub struct GlobalEntry { global_type: GlobalType, init_expr: InitExpr, } impl GlobalEntry { /// New global entry. pub fn new(global_type: GlobalType, init_expr: InitExpr) -> Self { GlobalEntry { global_type, init_expr } } /// Global type. pub fn global_type(&self) -> &GlobalType { &self.global_type } /// Initialization expression (instructions) for global. pub fn init_expr(&self) -> &InitExpr { &self.init_expr } /// Global type (mutable). pub fn global_type_mut(&mut self) -> &mut GlobalType { &mut self.global_type } /// Initialization expression (instructions) for global (mutable). pub fn init_expr_mut(&mut self) -> &mut InitExpr { &mut self.init_expr } } impl Deserialize for GlobalEntry { type Error = Error; fn deserialize(reader: &mut R) -> Result { let global_type = GlobalType::deserialize(reader)?; let init_expr = InitExpr::deserialize(reader)?; Ok(GlobalEntry { global_type, init_expr }) } } impl Serialize for GlobalEntry { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { self.global_type.serialize(writer)?; self.init_expr.serialize(writer) } } parity-wasm-0.45.0/src/elements/import_entry.rs000064400000000000000000000206550072674642500177330ustar 00000000000000use super::{ Deserialize, Error, Serialize, TableElementType, Uint8, ValueType, VarInt7, VarUint1, VarUint32, VarUint7, }; use crate::io; use alloc::string::String; const FLAG_HAS_MAX: u8 = 0x01; #[cfg(feature = "atomics")] const FLAG_SHARED: u8 = 0x02; /// Global definition struct #[derive(Debug, Copy, Clone, PartialEq)] pub struct GlobalType { content_type: ValueType, is_mutable: bool, } impl GlobalType { /// New global type pub fn new(content_type: ValueType, is_mutable: bool) -> Self { GlobalType { content_type, is_mutable } } /// Type of the global entry pub fn content_type(&self) -> ValueType { self.content_type } /// Is global entry is declared as mutable pub fn is_mutable(&self) -> bool { self.is_mutable } } impl Deserialize for GlobalType { type Error = Error; fn deserialize(reader: &mut R) -> Result { let content_type = ValueType::deserialize(reader)?; let is_mutable = VarUint1::deserialize(reader)?; Ok(GlobalType { content_type, is_mutable: is_mutable.into() }) } } impl Serialize for GlobalType { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { self.content_type.serialize(writer)?; VarUint1::from(self.is_mutable).serialize(writer)?; Ok(()) } } /// Table entry #[derive(Debug, Copy, Clone, PartialEq)] pub struct TableType { elem_type: TableElementType, limits: ResizableLimits, } impl TableType { /// New table definition pub fn new(min: u32, max: Option) -> Self { TableType { elem_type: TableElementType::AnyFunc, limits: ResizableLimits::new(min, max) } } /// Table memory specification pub fn limits(&self) -> &ResizableLimits { &self.limits } /// Table element type pub fn elem_type(&self) -> TableElementType { self.elem_type } } impl Deserialize for TableType { type Error = Error; fn deserialize(reader: &mut R) -> Result { let elem_type = TableElementType::deserialize(reader)?; let limits = ResizableLimits::deserialize(reader)?; Ok(TableType { elem_type, limits }) } } impl Serialize for TableType { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { self.elem_type.serialize(writer)?; self.limits.serialize(writer) } } /// Memory and table limits. #[derive(Debug, Copy, Clone, PartialEq)] pub struct ResizableLimits { initial: u32, maximum: Option, #[cfg(feature = "atomics")] shared: bool, } impl ResizableLimits { /// New memory limits definition. pub fn new(min: u32, max: Option) -> Self { ResizableLimits { initial: min, maximum: max, #[cfg(feature = "atomics")] shared: false, } } /// Initial size. pub fn initial(&self) -> u32 { self.initial } /// Maximum size. pub fn maximum(&self) -> Option { self.maximum } #[cfg(feature = "atomics")] /// Whether or not this is a shared array buffer. pub fn shared(&self) -> bool { self.shared } } impl Deserialize for ResizableLimits { type Error = Error; fn deserialize(reader: &mut R) -> Result { let flags: u8 = Uint8::deserialize(reader)?.into(); match flags { // Default flags are always supported. This is simply: FLAG_HAS_MAX={true, false}. 0x00 | 0x01 => {}, // Atomics proposal introduce FLAG_SHARED (0x02). Shared memories can be used only // together with FLAG_HAS_MAX (0x01), hence 0x03. #[cfg(feature = "atomics")] 0x03 => {}, _ => return Err(Error::InvalidLimitsFlags(flags)), } let initial = VarUint32::deserialize(reader)?; let maximum = if flags & FLAG_HAS_MAX != 0 { Some(VarUint32::deserialize(reader)?.into()) } else { None }; Ok(ResizableLimits { initial: initial.into(), maximum, #[cfg(feature = "atomics")] shared: flags & FLAG_SHARED != 0, }) } } impl Serialize for ResizableLimits { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut flags: u8 = 0; if self.maximum.is_some() { flags |= FLAG_HAS_MAX; } #[cfg(feature = "atomics")] { // If the atomics feature is enabled and if the shared flag is set, add logically // it to the flags. if self.shared { flags |= FLAG_SHARED; } } Uint8::from(flags).serialize(writer)?; VarUint32::from(self.initial).serialize(writer)?; if let Some(max) = self.maximum { VarUint32::from(max).serialize(writer)?; } Ok(()) } } /// Memory entry. #[derive(Debug, Copy, Clone, PartialEq)] pub struct MemoryType(ResizableLimits); impl MemoryType { /// New memory definition pub fn new(min: u32, max: Option) -> Self { let r = ResizableLimits::new(min, max); MemoryType(r) } /// Set the `shared` flag that denotes a memory that can be shared between threads. /// /// `false` by default. This is only available if the `atomics` feature is enabled. #[cfg(feature = "atomics")] pub fn set_shared(&mut self, shared: bool) { self.0.shared = shared; } /// Limits of the memory entry. pub fn limits(&self) -> &ResizableLimits { &self.0 } } impl Deserialize for MemoryType { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(MemoryType(ResizableLimits::deserialize(reader)?)) } } impl Serialize for MemoryType { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { self.0.serialize(writer) } } /// External to local binding. #[derive(Debug, Copy, Clone, PartialEq)] pub enum External { /// Binds to a function whose type is associated with the given index in the /// type section. Function(u32), /// Describes local table definition to be imported as. Table(TableType), /// Describes local memory definition to be imported as. Memory(MemoryType), /// Describes local global entry to be imported as. Global(GlobalType), } impl Deserialize for External { type Error = Error; fn deserialize(reader: &mut R) -> Result { let kind = VarUint7::deserialize(reader)?; match kind.into() { 0x00 => Ok(External::Function(VarUint32::deserialize(reader)?.into())), 0x01 => Ok(External::Table(TableType::deserialize(reader)?)), 0x02 => Ok(External::Memory(MemoryType::deserialize(reader)?)), 0x03 => Ok(External::Global(GlobalType::deserialize(reader)?)), _ => Err(Error::UnknownExternalKind(kind.into())), } } } impl Serialize for External { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { use self::External::*; match self { Function(index) => { VarUint7::from(0x00).serialize(writer)?; VarUint32::from(index).serialize(writer)?; }, Table(tt) => { VarInt7::from(0x01).serialize(writer)?; tt.serialize(writer)?; }, Memory(mt) => { VarInt7::from(0x02).serialize(writer)?; mt.serialize(writer)?; }, Global(gt) => { VarInt7::from(0x03).serialize(writer)?; gt.serialize(writer)?; }, } Ok(()) } } /// Import entry. #[derive(Debug, Clone, PartialEq)] pub struct ImportEntry { module_str: String, field_str: String, external: External, } impl ImportEntry { /// New import entry. pub fn new(module_str: String, field_str: String, external: External) -> Self { ImportEntry { module_str, field_str, external } } /// Module reference of the import entry. pub fn module(&self) -> &str { &self.module_str } /// Module reference of the import entry (mutable). pub fn module_mut(&mut self) -> &mut String { &mut self.module_str } /// Field reference of the import entry. pub fn field(&self) -> &str { &self.field_str } /// Field reference of the import entry (mutable) pub fn field_mut(&mut self) -> &mut String { &mut self.field_str } /// Local binidng of the import entry. pub fn external(&self) -> &External { &self.external } /// Local binidng of the import entry (mutable) pub fn external_mut(&mut self) -> &mut External { &mut self.external } } impl Deserialize for ImportEntry { type Error = Error; fn deserialize(reader: &mut R) -> Result { let module_str = String::deserialize(reader)?; let field_str = String::deserialize(reader)?; let external = External::deserialize(reader)?; Ok(ImportEntry { module_str, field_str, external }) } } impl Serialize for ImportEntry { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { self.module_str.serialize(writer)?; self.field_str.serialize(writer)?; self.external.serialize(writer) } } parity-wasm-0.45.0/src/elements/index_map.rs000064400000000000000000000416220072674642500171410ustar 00000000000000use crate::io; use alloc::vec::Vec; use super::{Deserialize, Error, Serialize, VarUint32}; use alloc::vec; use core::{ cmp::min, iter::{FromIterator, IntoIterator}, mem, slice, }; /// A map from non-contiguous `u32` keys to values of type `T`, which is /// serialized and deserialized ascending order of the keys. Normally used for /// relative dense maps with occasional "holes", and stored as an array. /// /// **SECURITY WARNING:** This code is currently subject to a denial of service /// attack if you create a map containing the key `u32::MAX`, which should never /// happen in normal data. It would be pretty easy to provide a safe /// deserializing mechanism which addressed this problem. #[derive(Debug, Default)] pub struct IndexMap { /// The number of non-`None` entries in this map. len: usize, /// A vector of entries. Missing entries are represented as `None`. entries: Vec>, } impl IndexMap { /// Create an empty `IndexMap`, preallocating enough space to store /// `capacity` entries without needing to reallocate the underlying memory. pub fn with_capacity(capacity: usize) -> IndexMap { IndexMap { len: 0, entries: Vec::with_capacity(capacity) } } /// Clear the map. pub fn clear(&mut self) { self.entries.clear(); self.len = 0; } /// Return the name for the specified index, if it exists. pub fn get(&self, idx: u32) -> Option<&T> { match self.entries.get(idx as usize) { Some(&Some(ref value)) => Some(value), Some(&None) | None => None, } } /// Does the map contain an entry for the specified index? pub fn contains_key(&self, idx: u32) -> bool { match self.entries.get(idx as usize) { Some(&Some(_)) => true, Some(&None) | None => false, } } /// Insert a name into our map, returning the existing value if present. /// /// Note: This API is designed for reasonably dense indices based on valid /// data. Inserting a huge `idx` will use up a lot of RAM, and this function /// will not try to protect you against that. pub fn insert(&mut self, idx: u32, value: T) -> Option { let idx = idx as usize; let result = if idx >= self.entries.len() { // We need to grow the array, and add the new element at the end. for _ in 0..(idx - self.entries.len()) { // We can't use `extend(repeat(None)).take(n)`, because that // would require `T` to implement `Clone`. self.entries.push(None); } self.entries.push(Some(value)); debug_assert_eq!(idx + 1, self.entries.len()); self.len += 1; None } else { // We're either replacing an existing element, or filling in a // missing one. let existing = self.entries[idx].take(); if existing.is_none() { self.len += 1; } self.entries[idx] = Some(value); existing }; if mem::size_of::() > 4 { debug_assert!(self.entries.len() <= (u32::max_value() as usize) + 1); } #[cfg(slow_assertions)] debug_assert_eq!(self.len, self.slow_len()); result } /// Remove an item if present and return it. pub fn remove(&mut self, idx: u32) -> Option { let result = match self.entries.get_mut(idx as usize) { Some(value @ &mut Some(_)) => { self.len -= 1; value.take() }, Some(&mut None) | None => None, }; #[cfg(slow_assertions)] debug_assert_eq!(self.len, self.slow_len()); result } /// The number of items in this map. pub fn len(&self) -> usize { #[cfg(slow_assertions)] debug_assert_eq!(self.len, self.slow_len()); self.len } /// Is this map empty? pub fn is_empty(&self) -> bool { self.len == 0 } /// This function is only compiled when `--cfg slow_assertions` is enabled. /// It computes the `len` value using a slow algorithm. /// /// WARNING: This turns a bunch of O(n) operations into O(n^2) operations. /// We may want to remove it once the code is tested, or to put it behind /// a feature flag named `slow_debug_checks`, or something like that. #[cfg(slow_assertions)] fn slow_len(&self) -> usize { self.entries.iter().filter(|entry| entry.is_some()).count() } /// Create a non-consuming iterator over this `IndexMap`'s keys and values. pub fn iter(&self) -> Iter { // Note that this does the right thing because we use `&self`. self.into_iter() } /// Custom deserialization routine. /// /// We will allocate an underlying array no larger than `max_entry_space` to /// hold the data, so the maximum index must be less than `max_entry_space`. /// This prevents mallicious *.wasm files from having a single entry with /// the index `u32::MAX`, which would consume far too much memory. /// /// The `deserialize_value` function will be passed the index of the value /// being deserialized, and must deserialize the value. pub fn deserialize_with( max_entry_space: usize, deserialize_value: &F, rdr: &mut R, ) -> Result, Error> where R: io::Read, F: Fn(u32, &mut R) -> Result, { let len: u32 = VarUint32::deserialize(rdr)?.into(); let mut map = IndexMap::with_capacity(len as usize); let mut prev_idx = None; for _ in 0..len { let idx: u32 = VarUint32::deserialize(rdr)?.into(); if idx as usize >= max_entry_space { return Err(Error::Other("index is larger than expected")) } match prev_idx { Some(prev) if prev >= idx => { // Supposedly these names must be "sorted by index", so // let's try enforcing that and seeing what happens. return Err(Error::Other("indices are out of order")) }, _ => { prev_idx = Some(idx); }, } let val = deserialize_value(idx, rdr)?; map.insert(idx, val); } Ok(map) } } impl Clone for IndexMap { fn clone(&self) -> IndexMap { IndexMap { len: self.len, entries: self.entries.clone() } } } impl PartialEq> for IndexMap { fn eq(&self, other: &IndexMap) -> bool { if self.len() != other.len() { // If the number of non-`None` entries is different, we can't match. false } else { // This is tricky, because one `Vec` might have a bunch of empty // entries at the end which we want to ignore. let smallest_len = min(self.entries.len(), other.entries.len()); self.entries[0..smallest_len].eq(&other.entries[0..smallest_len]) } } } impl Eq for IndexMap {} impl FromIterator<(u32, T)> for IndexMap { /// Create an `IndexMap` from an iterator. /// /// Note: This API is designed for reasonably dense indices based on valid /// data. Inserting a huge `idx` will use up a lot of RAM, and this function /// will not try to protect you against that. fn from_iter(iter: I) -> Self where I: IntoIterator, { let iter = iter.into_iter(); let (lower, upper_opt) = iter.size_hint(); let mut map = IndexMap::with_capacity(upper_opt.unwrap_or(lower)); for (idx, value) in iter { map.insert(idx, value); } map } } /// An iterator over an `IndexMap` which takes ownership of it. pub struct IntoIter { next_idx: u32, remaining_len: usize, iter: vec::IntoIter>, } impl Iterator for IntoIter { type Item = (u32, T); fn size_hint(&self) -> (usize, Option) { (self.remaining_len, Some(self.remaining_len)) } fn next(&mut self) -> Option { // Bail early if we know there are no more items. This also keeps us // from repeatedly calling `self.iter.next()` once it has been // exhausted, which is not guaranteed to keep returning `None`. if self.remaining_len == 0 { return None } for value_opt in &mut self.iter { let idx = self.next_idx; self.next_idx += 1; if let Some(value) = value_opt { self.remaining_len -= 1; return Some((idx, value)) } } debug_assert_eq!(self.remaining_len, 0); None } } impl IntoIterator for IndexMap { type Item = (u32, T); type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { IntoIter { next_idx: 0, remaining_len: self.len, iter: self.entries.into_iter() } } } /// An iterator over a borrowed `IndexMap`. pub struct Iter<'a, T: 'static> { next_idx: u32, remaining_len: usize, iter: slice::Iter<'a, Option>, } impl<'a, T: 'static> Iterator for Iter<'a, T> { type Item = (u32, &'a T); fn size_hint(&self) -> (usize, Option) { (self.remaining_len, Some(self.remaining_len)) } fn next(&mut self) -> Option { // Bail early if we know there are no more items. This also keeps us // from repeatedly calling `self.iter.next()` once it has been // exhausted, which is not guaranteed to keep returning `None`. if self.remaining_len == 0 { return None } for value_opt in &mut self.iter { let idx = self.next_idx; self.next_idx += 1; if let Some(ref value) = *value_opt { self.remaining_len -= 1; return Some((idx, value)) } } debug_assert_eq!(self.remaining_len, 0); None } } impl<'a, T: 'static> IntoIterator for &'a IndexMap { type Item = (u32, &'a T); type IntoIter = Iter<'a, T>; fn into_iter(self) -> Iter<'a, T> { Iter { next_idx: 0, remaining_len: self.len, iter: self.entries.iter() } } } impl Serialize for IndexMap where T: Serialize, Error: From<::Error>, { type Error = Error; fn serialize(self, wtr: &mut W) -> Result<(), Self::Error> { VarUint32::from(self.len()).serialize(wtr)?; for (idx, value) in self { VarUint32::from(idx).serialize(wtr)?; value.serialize(wtr)?; } Ok(()) } } impl IndexMap where T: Deserialize, Error: From<::Error>, { /// Deserialize a map containing simple values that support `Deserialize`. /// We will allocate an underlying array no larger than `max_entry_space` to /// hold the data, so the maximum index must be less than `max_entry_space`. pub fn deserialize(max_entry_space: usize, rdr: &mut R) -> Result { let deserialize_value: fn(u32, &mut R) -> Result = |_idx, rdr| T::deserialize(rdr).map_err(Error::from); Self::deserialize_with(max_entry_space, &deserialize_value, rdr) } } #[cfg(test)] mod tests { use super::*; use crate::io; #[test] fn default_is_empty_no_matter_how_we_look_at_it() { let map = IndexMap::::default(); assert_eq!(map.len(), 0); assert!(map.is_empty()); assert_eq!(map.iter().count(), 0); assert_eq!(map.into_iter().count(), 0); } #[test] fn with_capacity_creates_empty_map() { let map = IndexMap::::with_capacity(10); assert!(map.is_empty()); } #[test] fn clear_removes_all_values() { let mut map = IndexMap::::default(); map.insert(0, "sample value".to_string()); assert_eq!(map.len(), 1); map.clear(); assert_eq!(map.len(), 0); } #[test] fn get_returns_elements_that_are_there_but_nothing_else() { let mut map = IndexMap::::default(); map.insert(1, "sample value".to_string()); assert_eq!(map.len(), 1); assert_eq!(map.get(0), None); assert_eq!(map.get(1), Some(&"sample value".to_string())); assert_eq!(map.get(2), None); } #[test] fn contains_key_returns_true_when_a_key_is_present() { let mut map = IndexMap::::default(); map.insert(1, "sample value".to_string()); assert!(!map.contains_key(0)); assert!(map.contains_key(1)); assert!(!map.contains_key(2)); } #[test] fn insert_behaves_like_other_maps() { let mut map = IndexMap::::default(); // Insert a key which requires extending our storage. assert_eq!(map.insert(1, "val 1".to_string()), None); assert_eq!(map.len(), 1); assert!(map.contains_key(1)); // Insert a key which requires filling in a hole. assert_eq!(map.insert(0, "val 0".to_string()), None); assert_eq!(map.len(), 2); assert!(map.contains_key(0)); // Insert a key which replaces an existing key. assert_eq!(map.insert(1, "val 1.1".to_string()), Some("val 1".to_string())); assert_eq!(map.len(), 2); assert!(map.contains_key(1)); assert_eq!(map.get(1), Some(&"val 1.1".to_string())); } #[test] fn remove_behaves_like_other_maps() { let mut map = IndexMap::::default(); assert_eq!(map.insert(1, "val 1".to_string()), None); // Remove an out-of-bounds element. assert_eq!(map.remove(2), None); assert_eq!(map.len(), 1); // Remove an in-bounds but missing element. assert_eq!(map.remove(0), None); assert_eq!(map.len(), 1); // Remove an existing element. assert_eq!(map.remove(1), Some("val 1".to_string())); assert_eq!(map.len(), 0); } #[test] fn partial_eq_works_as_expected_in_simple_cases() { let mut map1 = IndexMap::::default(); let mut map2 = IndexMap::::default(); assert_eq!(map1, map2); map1.insert(1, "a".to_string()); map2.insert(1, "a".to_string()); assert_eq!(map1, map2); map1.insert(0, "b".to_string()); assert_ne!(map1, map2); map1.remove(0); assert_eq!(map1, map2); map1.insert(1, "not a".to_string()); assert_ne!(map1, map2); } #[test] fn partial_eq_is_smart_about_none_values_at_the_end() { let mut map1 = IndexMap::::default(); let mut map2 = IndexMap::::default(); map1.insert(1, "a".to_string()); map2.insert(1, "a".to_string()); // Both maps have the same (idx, value) pairs, but map2 has extra space. map2.insert(10, "b".to_string()); map2.remove(10); assert_eq!(map1, map2); // Both maps have the same (idx, value) pairs, but map1 has extra space. map1.insert(100, "b".to_string()); map1.remove(100); assert_eq!(map1, map2); // Let's be paranoid. map2.insert(1, "b".to_string()); assert_ne!(map1, map2); } #[test] fn from_iterator_builds_a_map() { let data = &[ // We support out-of-order values here! (3, "val 3"), (2, "val 2"), (5, "val 5"), ]; let iter = data.iter().map(|&(idx, val)| (idx, val.to_string())); let map = iter.collect::>(); assert_eq!(map.len(), 3); assert_eq!(map.get(2), Some(&"val 2".to_string())); assert_eq!(map.get(3), Some(&"val 3".to_string())); assert_eq!(map.get(5), Some(&"val 5".to_string())); } #[test] fn iterators_are_well_behaved() { // Create a map with reasonably complex internal structure, making // sure that we have both internal missing elements, and a bunch of // missing elements at the end. let data = &[(3, "val 3"), (2, "val 2"), (5, "val 5")]; let src_iter = data.iter().map(|&(idx, val)| (idx, val.to_string())); let mut map = src_iter.collect::>(); map.remove(5); // Make sure `size_hint` and `next` behave as we expect at each step. { let mut iter1 = map.iter(); assert_eq!(iter1.size_hint(), (2, Some(2))); assert_eq!(iter1.next(), Some((2, &"val 2".to_string()))); assert_eq!(iter1.size_hint(), (1, Some(1))); assert_eq!(iter1.next(), Some((3, &"val 3".to_string()))); assert_eq!(iter1.size_hint(), (0, Some(0))); assert_eq!(iter1.next(), None); assert_eq!(iter1.size_hint(), (0, Some(0))); assert_eq!(iter1.next(), None); assert_eq!(iter1.size_hint(), (0, Some(0))); } // Now do the same for a consuming iterator. let mut iter2 = map.into_iter(); assert_eq!(iter2.size_hint(), (2, Some(2))); assert_eq!(iter2.next(), Some((2, "val 2".to_string()))); assert_eq!(iter2.size_hint(), (1, Some(1))); assert_eq!(iter2.next(), Some((3, "val 3".to_string()))); assert_eq!(iter2.size_hint(), (0, Some(0))); assert_eq!(iter2.next(), None); assert_eq!(iter2.size_hint(), (0, Some(0))); assert_eq!(iter2.next(), None); assert_eq!(iter2.size_hint(), (0, Some(0))); } #[test] fn serialize_and_deserialize() { let mut map = IndexMap::::default(); map.insert(1, "val 1".to_string()); let mut output = vec![]; map.clone().serialize(&mut output).expect("serialize failed"); let mut input = io::Cursor::new(&output); let deserialized = IndexMap::deserialize(2, &mut input).expect("deserialize failed"); assert_eq!(deserialized, map); } #[test] fn deserialize_requires_elements_to_be_in_order() { // Build a in-order example by hand. let mut valid = vec![]; VarUint32::from(2u32).serialize(&mut valid).unwrap(); VarUint32::from(0u32).serialize(&mut valid).unwrap(); "val 0".to_string().serialize(&mut valid).unwrap(); VarUint32::from(1u32).serialize(&mut valid).unwrap(); "val 1".to_string().serialize(&mut valid).unwrap(); let map = IndexMap::::deserialize(2, &mut io::Cursor::new(valid)) .expect("unexpected error deserializing"); assert_eq!(map.len(), 2); // Build an out-of-order example by hand. let mut invalid = vec![]; VarUint32::from(2u32).serialize(&mut invalid).unwrap(); VarUint32::from(1u32).serialize(&mut invalid).unwrap(); "val 1".to_string().serialize(&mut invalid).unwrap(); VarUint32::from(0u32).serialize(&mut invalid).unwrap(); "val 0".to_string().serialize(&mut invalid).unwrap(); let res = IndexMap::::deserialize(2, &mut io::Cursor::new(invalid)); assert!(res.is_err()); } #[test] fn deserialize_enforces_max_idx() { // Build an example with an out-of-bounds index by hand. let mut invalid = vec![]; VarUint32::from(1u32).serialize(&mut invalid).unwrap(); VarUint32::from(5u32).serialize(&mut invalid).unwrap(); "val 5".to_string().serialize(&mut invalid).unwrap(); let res = IndexMap::::deserialize(1, &mut io::Cursor::new(invalid)); assert!(res.is_err()); } } parity-wasm-0.45.0/src/elements/mod.rs000064400000000000000000000275210072674642500157560ustar 00000000000000//! Elements of the WebAssembly binary format. use crate::io; use alloc::{string::String, vec::Vec}; use core::fmt; macro_rules! buffered_read { ($buffer_size: expr, $length: expr, $reader: expr) => {{ let mut vec_buf = Vec::new(); let mut total_read = 0; let mut buf = [0u8; $buffer_size]; while total_read < $length { let next_to_read = if $length - total_read > $buffer_size { $buffer_size } else { $length - total_read }; $reader.read(&mut buf[0..next_to_read])?; vec_buf.extend_from_slice(&buf[0..next_to_read]); total_read += next_to_read; } vec_buf }}; } mod export_entry; mod func; mod global_entry; mod import_entry; mod index_map; mod module; mod name_section; mod ops; mod primitives; mod reloc_section; mod section; mod segment; mod types; pub use self::{ export_entry::{ExportEntry, Internal}, global_entry::GlobalEntry, import_entry::{External, GlobalType, ImportEntry, MemoryType, ResizableLimits, TableType}, module::{peek_size, ImportCountType, Module}, ops::{opcodes, BrTableData, InitExpr, Instruction, Instructions}, primitives::{ CountedList, CountedListWriter, CountedWriter, Uint32, Uint64, Uint8, VarInt32, VarInt64, VarInt7, VarUint1, VarUint32, VarUint64, VarUint7, }, section::{ CodeSection, CustomSection, DataSection, ElementSection, ExportSection, FunctionSection, GlobalSection, ImportSection, MemorySection, Section, TableSection, TypeSection, }, types::{BlockType, FunctionType, TableElementType, Type, ValueType}, }; #[cfg(feature = "atomics")] pub use self::ops::AtomicsInstruction; #[cfg(feature = "simd")] pub use self::ops::SimdInstruction; #[cfg(feature = "sign_ext")] pub use self::ops::SignExtInstruction; #[cfg(feature = "bulk")] pub use self::ops::BulkInstruction; #[cfg(any(feature = "simd", feature = "atomics"))] pub use self::ops::MemArg; pub use self::{ func::{Func, FuncBody, Local}, index_map::IndexMap, name_section::{ FunctionNameSubsection, LocalNameSubsection, ModuleNameSubsection, NameMap, NameSection, }, reloc_section::{RelocSection, RelocationEntry}, segment::{DataSegment, ElementSegment}, }; /// Deserialization from serial i/o. pub trait Deserialize: Sized { /// Serialization error produced by deserialization routine. type Error: From; /// Deserialize type from serial i/o fn deserialize(reader: &mut R) -> Result; } /// Serialization to serial i/o. Takes self by value to consume less memory /// (parity-wasm IR is being partially freed by filling the result buffer). pub trait Serialize { /// Serialization error produced by serialization routine. type Error: From; /// Serialize type to serial i/o fn serialize(self, writer: &mut W) -> Result<(), Self::Error>; } /// Deserialization/serialization error #[derive(Debug, Clone)] pub enum Error { /// Unexpected end of input. UnexpectedEof, /// Invalid magic. InvalidMagic, /// Unsupported version. UnsupportedVersion(u32), /// Inconsistence between declared and actual length. InconsistentLength { /// Expected length of the definition. expected: usize, /// Actual length of the definition. actual: usize, }, /// Other static error. Other(&'static str), /// Other allocated error. HeapOther(String), /// Invalid/unknown value type declaration. UnknownValueType(i8), /// Invalid block type declaration. UnknownBlockType(i32), /// Invalid/unknown table element type declaration. UnknownTableElementType(i8), /// Non-utf8 string. NonUtf8String, /// Unknown external kind code. UnknownExternalKind(u8), /// Unknown internal kind code. UnknownInternalKind(u8), /// Unknown opcode encountered. UnknownOpcode(u8), #[cfg(feature = "simd")] /// Unknown SIMD opcode encountered. UnknownSimdOpcode(u32), /// Invalid VarUint1 value. InvalidVarUint1(u8), /// Invalid VarInt32 value. InvalidVarInt32, /// Invalid VarInt64 value. InvalidVarInt64, /// Invalid VarUint32 value. InvalidVarUint32, /// Invalid VarUint64 value. InvalidVarUint64, /// Inconsistent metadata. InconsistentMetadata, /// Invalid section id. InvalidSectionId(u8), /// Sections are out of order. SectionsOutOfOrder, /// Duplicated sections. DuplicatedSections(u8), /// Invalid memory reference (should be 0). InvalidMemoryReference(u8), /// Invalid table reference (should be 0). InvalidTableReference(u8), /// Invalid value used for flags in limits type. InvalidLimitsFlags(u8), /// Unknown function form (should be 0x60). UnknownFunctionForm(u8), /// Invalid varint7 (should be in -64..63 range). InvalidVarInt7(u8), /// Number of function body entries and signatures does not match. InconsistentCode, /// Only flags 0, 1, and 2 are accepted on segments. InvalidSegmentFlags(u32), /// Sum of counts of locals is greater than 2^32. TooManyLocals, /// Duplicated name subsections. DuplicatedNameSubsections(u8), /// Unknown name subsection type. UnknownNameSubsectionType(u8), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::UnexpectedEof => write!(f, "Unexpected end of input"), Error::InvalidMagic => write!(f, "Invalid magic number at start of file"), Error::UnsupportedVersion(v) => write!(f, "Unsupported wasm version {}", v), Error::InconsistentLength { expected, actual } => { write!(f, "Expected length {}, found {}", expected, actual) }, Error::Other(msg) => write!(f, "{}", msg), Error::HeapOther(ref msg) => write!(f, "{}", msg), Error::UnknownValueType(ty) => write!(f, "Invalid or unknown value type {}", ty), Error::UnknownBlockType(ty) => write!(f, "Invalid or unknown block type {}", ty), Error::UnknownTableElementType(ty) => write!(f, "Unknown table element type {}", ty), Error::NonUtf8String => write!(f, "Non-UTF-8 string"), Error::UnknownExternalKind(kind) => write!(f, "Unknown external kind {}", kind), Error::UnknownInternalKind(kind) => write!(f, "Unknown internal kind {}", kind), Error::UnknownOpcode(opcode) => write!(f, "Unknown opcode {}", opcode), #[cfg(feature = "simd")] Error::UnknownSimdOpcode(opcode) => write!(f, "Unknown SIMD opcode {}", opcode), Error::InvalidVarUint1(val) => write!(f, "Not an unsigned 1-bit integer: {}", val), Error::InvalidVarInt7(val) => write!(f, "Not a signed 7-bit integer: {}", val), Error::InvalidVarInt32 => write!(f, "Not a signed 32-bit integer"), Error::InvalidVarUint32 => write!(f, "Not an unsigned 32-bit integer"), Error::InvalidVarInt64 => write!(f, "Not a signed 64-bit integer"), Error::InvalidVarUint64 => write!(f, "Not an unsigned 64-bit integer"), Error::InconsistentMetadata => write!(f, "Inconsistent metadata"), Error::InvalidSectionId(ref id) => write!(f, "Invalid section id: {}", id), Error::SectionsOutOfOrder => write!(f, "Sections out of order"), Error::DuplicatedSections(ref id) => write!(f, "Duplicated sections ({})", id), Error::InvalidMemoryReference(ref mem_ref) => write!(f, "Invalid memory reference ({})", mem_ref), Error::InvalidTableReference(ref table_ref) => write!(f, "Invalid table reference ({})", table_ref), Error::InvalidLimitsFlags(ref flags) => write!(f, "Invalid limits flags ({})", flags), Error::UnknownFunctionForm(ref form) => write!(f, "Unknown function form ({})", form), Error::InconsistentCode => write!(f, "Number of function body entries and signatures does not match"), Error::InvalidSegmentFlags(n) => write!(f, "Invalid segment flags: {}", n), Error::TooManyLocals => write!(f, "Too many locals"), Error::DuplicatedNameSubsections(n) => write!(f, "Duplicated name subsections: {}", n), Error::UnknownNameSubsectionType(n) => write!(f, "Unknown subsection type: {}", n), } } } #[cfg(feature = "std")] impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { Error::UnexpectedEof => "Unexpected end of input", Error::InvalidMagic => "Invalid magic number at start of file", Error::UnsupportedVersion(_) => "Unsupported wasm version", Error::InconsistentLength { .. } => "Inconsistent length", Error::Other(msg) => msg, Error::HeapOther(ref msg) => &msg[..], Error::UnknownValueType(_) => "Invalid or unknown value type", Error::UnknownBlockType(_) => "Invalid or unknown block type", Error::UnknownTableElementType(_) => "Unknown table element type", Error::NonUtf8String => "Non-UTF-8 string", Error::UnknownExternalKind(_) => "Unknown external kind", Error::UnknownInternalKind(_) => "Unknown internal kind", Error::UnknownOpcode(_) => "Unknown opcode", #[cfg(feature = "simd")] Error::UnknownSimdOpcode(_) => "Unknown SIMD opcode", Error::InvalidVarUint1(_) => "Not an unsigned 1-bit integer", Error::InvalidVarInt32 => "Not a signed 32-bit integer", Error::InvalidVarInt7(_) => "Not a signed 7-bit integer", Error::InvalidVarUint32 => "Not an unsigned 32-bit integer", Error::InvalidVarInt64 => "Not a signed 64-bit integer", Error::InvalidVarUint64 => "Not an unsigned 64-bit integer", Error::InconsistentMetadata => "Inconsistent metadata", Error::InvalidSectionId(_) => "Invalid section id", Error::SectionsOutOfOrder => "Sections out of order", Error::DuplicatedSections(_) => "Duplicated section", Error::InvalidMemoryReference(_) => "Invalid memory reference", Error::InvalidTableReference(_) => "Invalid table reference", Error::InvalidLimitsFlags(_) => "Invalid limits flags", Error::UnknownFunctionForm(_) => "Unknown function form", Error::InconsistentCode => "Number of function body entries and signatures does not match", Error::InvalidSegmentFlags(_) => "Invalid segment flags", Error::TooManyLocals => "Too many locals", Error::DuplicatedNameSubsections(_) => "Duplicated name subsections", Error::UnknownNameSubsectionType(_) => "Unknown name subsections type", } } } impl From for Error { fn from(err: io::Error) -> Self { Error::HeapOther(format!("I/O Error: {:?}", err)) } } // These are emitted by section parsers, such as `parse_names` and `parse_reloc`. impl From<(Vec<(usize, Error)>, Module)> for Error { fn from(err: (Vec<(usize, Error)>, Module)) -> Self { let ret = err.0.iter().fold(String::new(), |mut acc, item| { acc.push_str(&format!("In section {}: {}\n", item.0, item.1)); acc }); Error::HeapOther(ret) } } /// Unparsed part of the module/section. pub struct Unparsed(pub Vec); impl Deserialize for Unparsed { type Error = Error; fn deserialize(reader: &mut R) -> Result { let len = VarUint32::deserialize(reader)?.into(); let mut vec = vec![0u8; len]; reader.read(&mut vec[..])?; Ok(Unparsed(vec)) } } impl From for Vec { fn from(u: Unparsed) -> Vec { u.0 } } /// Deserialize deserializable type from buffer. pub fn deserialize_buffer(contents: &[u8]) -> Result { let mut reader = io::Cursor::new(contents); let result = T::deserialize(&mut reader)?; if reader.position() != contents.len() { // It's a TrailingData, since if there is not enough data then // UnexpectedEof must have been returned earlier in T::deserialize. return Err(io::Error::TrailingData.into()) } Ok(result) } /// Create buffer with serialized value. pub fn serialize(val: T) -> Result, T::Error> { let mut buf = Vec::new(); val.serialize(&mut buf)?; Ok(buf) } /// Deserialize module from the file. #[cfg(feature = "std")] pub fn deserialize_file>(p: P) -> Result { let mut f = ::std::fs::File::open(p) .map_err(|e| Error::HeapOther(format!("Can't read from the file: {:?}", e)))?; Module::deserialize(&mut f) } /// Serialize module to the file #[cfg(feature = "std")] pub fn serialize_to_file>(p: P, module: Module) -> Result<(), Error> { let mut io = ::std::fs::File::create(p) .map_err(|e| Error::HeapOther(format!("Can't create the file: {:?}", e)))?; module.serialize(&mut io)?; Ok(()) } parity-wasm-0.45.0/src/elements/module.rs000064400000000000000000000746160072674642500164730ustar 00000000000000use crate::io; use alloc::{borrow::ToOwned, string::String, vec::Vec}; use super::{ deserialize_buffer, name_section::NameSection, reloc_section::RelocSection, section::{ CodeSection, CustomSection, DataSection, ElementSection, ExportSection, FunctionSection, GlobalSection, ImportSection, MemorySection, Section, TableSection, TypeSection, }, serialize, Deserialize, Error, External, Serialize, Uint32, }; use core::cmp; const WASM_MAGIC_NUMBER: [u8; 4] = [0x00, 0x61, 0x73, 0x6d]; /// WebAssembly module #[derive(Debug, Clone, PartialEq)] pub struct Module { magic: u32, version: u32, sections: Vec
, } #[derive(Debug, Clone, Copy, PartialEq)] /// Type of the import entry to count pub enum ImportCountType { /// Count functions Function, /// Count globals Global, /// Count tables Table, /// Count memories Memory, } impl Default for Module { fn default() -> Self { Module { magic: u32::from_le_bytes(WASM_MAGIC_NUMBER), version: 1, sections: Vec::with_capacity(16), } } } impl Module { /// New module with sections pub fn new(sections: Vec
) -> Self { Module { sections, ..Default::default() } } /// Construct a module from a slice. pub fn from_bytes>(input: T) -> Result { deserialize_buffer::(input.as_ref()) } /// Serialize a module to a vector. pub fn into_bytes(self) -> Result, Error> { serialize::(self) } /// Destructure the module, yielding sections pub fn into_sections(self) -> Vec
{ self.sections } /// Version of module. pub fn version(&self) -> u32 { self.version } /// Sections list. /// /// Each known section is optional and may appear at most once. pub fn sections(&self) -> &[Section] { &self.sections } /// Sections list (mutable). /// /// Each known section is optional and may appear at most once. pub fn sections_mut(&mut self) -> &mut Vec
{ &mut self.sections } /// Insert a section, in the correct section ordering. This will fail with an error, /// if the section can only appear once. pub fn insert_section(&mut self, section: Section) -> Result<(), Error> { let sections = self.sections_mut(); // Custom sections can be inserted anywhere. Lets always insert them last here. if section.order() == 0 { sections.push(section); return Ok(()) } // Check if the section already exists. if sections.iter().any(|s| s.order() == section.order()) { return Err(Error::DuplicatedSections(section.order())) } // Assume that the module is already well-ordered. if let Some(pos) = sections.iter().position(|s| section.order() < s.order()) { sections.insert(pos, section); } else { sections.push(section); } Ok(()) } /// Code section reference, if any. pub fn code_section(&self) -> Option<&CodeSection> { for section in self.sections() { if let Section::Code(ref code_section) = *section { return Some(code_section) } } None } /// Code section mutable reference, if any. pub fn code_section_mut(&mut self) -> Option<&mut CodeSection> { for section in self.sections_mut() { if let Section::Code(ref mut code_section) = *section { return Some(code_section) } } None } /// Types section reference, if any. pub fn type_section(&self) -> Option<&TypeSection> { for section in self.sections() { if let Section::Type(ref type_section) = *section { return Some(type_section) } } None } /// Types section mutable reference, if any. pub fn type_section_mut(&mut self) -> Option<&mut TypeSection> { for section in self.sections_mut() { if let Section::Type(ref mut type_section) = *section { return Some(type_section) } } None } /// Imports section reference, if any. pub fn import_section(&self) -> Option<&ImportSection> { for section in self.sections() { if let Section::Import(ref import_section) = *section { return Some(import_section) } } None } /// Imports section mutable reference, if any. pub fn import_section_mut(&mut self) -> Option<&mut ImportSection> { for section in self.sections_mut() { if let Section::Import(ref mut import_section) = *section { return Some(import_section) } } None } /// Globals section reference, if any. pub fn global_section(&self) -> Option<&GlobalSection> { for section in self.sections() { if let Section::Global(ref section) = *section { return Some(section) } } None } /// Globals section mutable reference, if any. pub fn global_section_mut(&mut self) -> Option<&mut GlobalSection> { for section in self.sections_mut() { if let Section::Global(ref mut section) = *section { return Some(section) } } None } /// Exports section reference, if any. pub fn export_section(&self) -> Option<&ExportSection> { for section in self.sections() { if let Section::Export(ref export_section) = *section { return Some(export_section) } } None } /// Exports section mutable reference, if any. pub fn export_section_mut(&mut self) -> Option<&mut ExportSection> { for section in self.sections_mut() { if let Section::Export(ref mut export_section) = *section { return Some(export_section) } } None } /// Table section reference, if any. pub fn table_section(&self) -> Option<&TableSection> { for section in self.sections() { if let Section::Table(ref section) = *section { return Some(section) } } None } /// Table section mutable reference, if any. pub fn table_section_mut(&mut self) -> Option<&mut TableSection> { for section in self.sections_mut() { if let Section::Table(ref mut section) = *section { return Some(section) } } None } /// Data section reference, if any. pub fn data_section(&self) -> Option<&DataSection> { for section in self.sections() { if let Section::Data(ref section) = *section { return Some(section) } } None } /// Data section mutable reference, if any. pub fn data_section_mut(&mut self) -> Option<&mut DataSection> { for section in self.sections_mut() { if let Section::Data(ref mut section) = *section { return Some(section) } } None } /// Element section reference, if any. pub fn elements_section(&self) -> Option<&ElementSection> { for section in self.sections() { if let Section::Element(ref section) = *section { return Some(section) } } None } /// Element section mutable reference, if any. pub fn elements_section_mut(&mut self) -> Option<&mut ElementSection> { for section in self.sections_mut() { if let Section::Element(ref mut section) = *section { return Some(section) } } None } /// Memory section reference, if any. pub fn memory_section(&self) -> Option<&MemorySection> { for section in self.sections() { if let Section::Memory(ref section) = *section { return Some(section) } } None } /// Memory section mutable reference, if any. pub fn memory_section_mut(&mut self) -> Option<&mut MemorySection> { for section in self.sections_mut() { if let Section::Memory(ref mut section) = *section { return Some(section) } } None } /// Functions signatures section reference, if any. pub fn function_section(&self) -> Option<&FunctionSection> { for section in self.sections() { if let Section::Function(ref sect) = *section { return Some(sect) } } None } /// Functions signatures section mutable reference, if any. pub fn function_section_mut(&mut self) -> Option<&mut FunctionSection> { for section in self.sections_mut() { if let Section::Function(ref mut sect) = *section { return Some(sect) } } None } /// Start section, if any. pub fn start_section(&self) -> Option { for section in self.sections() { if let Section::Start(sect) = *section { return Some(sect) } } None } /// Changes the module's start section. pub fn set_start_section(&mut self, new_start: u32) { for section in self.sections_mut().iter_mut() { if let Section::Start(_sect) = *section { *section = Section::Start(new_start); return } } // This should not fail, because we update the existing section above. self.insert_section(Section::Start(new_start)) .expect("insert_section should not fail"); } /// Removes the module's start section. pub fn clear_start_section(&mut self) { let sections = self.sections_mut(); let mut rmidx = sections.len(); for (index, section) in sections.iter_mut().enumerate() { if let Section::Start(_sect) = section { rmidx = index; break } } if rmidx < sections.len() { sections.remove(rmidx); } } /// Returns an iterator over the module's custom sections pub fn custom_sections(&self) -> impl Iterator { self.sections() .iter() .filter_map(|s| if let Section::Custom(s) = s { Some(s) } else { None }) } /// Sets the payload associated with the given custom section, or adds a new custom section, /// as appropriate. pub fn set_custom_section(&mut self, name: impl Into, payload: Vec) { let name: String = name.into(); for section in self.sections_mut() { if let Section::Custom(ref mut sect) = *section { if sect.name() == name { *sect = CustomSection::new(name, payload); return } } } self.sections_mut().push(Section::Custom(CustomSection::new(name, payload))); } /// Removes the given custom section, if it exists. /// Returns the removed section if it existed, or None otherwise. pub fn clear_custom_section(&mut self, name: impl AsRef) -> Option { let name: &str = name.as_ref(); let sections = self.sections_mut(); for i in 0..sections.len() { let mut remove = false; if let Section::Custom(ref sect) = sections[i] { if sect.name() == name { remove = true; } } if remove { let removed = sections.remove(i); match removed { Section::Custom(sect) => return Some(sect), _ => unreachable!(), // This is the section we just matched on, so... } } } None } /// True if a name section is present. /// /// NOTE: this can return true even if the section was not parsed, hence `names_section()` may return `None` /// even if this returns `true` pub fn has_names_section(&self) -> bool { self.sections().iter().any(|e| { match e { // The default case, when the section was not parsed Section::Custom(custom) => custom.name() == "name", // This is the case, when the section was parsed Section::Name(_) => true, _ => false, } }) } /// Functions signatures section reference, if any. /// /// NOTE: name section is not parsed by default so `names_section` could return None even if name section exists. /// Call `parse_names` to parse name section pub fn names_section(&self) -> Option<&NameSection> { for section in self.sections() { if let Section::Name(ref sect) = *section { return Some(sect) } } None } /// Functions signatures section mutable reference, if any. /// /// NOTE: name section is not parsed by default so `names_section` could return None even if name section exists. /// Call `parse_names` to parse name section pub fn names_section_mut(&mut self) -> Option<&mut NameSection> { for section in self.sections_mut() { if let Section::Name(ref mut sect) = *section { return Some(sect) } } None } /// Try to parse name section in place. /// /// Corresponding custom section with proper header will convert to name sections /// If some of them will fail to be decoded, Err variant is returned with the list of /// (index, Error) tuples of failed sections. pub fn parse_names(mut self) -> Result, Self)> { let mut parse_errors = Vec::new(); for i in 0..self.sections.len() { if let Some(name_section) = { let section = self.sections.get(i).expect("cannot fail because i in range 0..len; qed"); if let Section::Custom(ref custom) = *section { if custom.name() == "name" { let mut rdr = io::Cursor::new(custom.payload()); let name_section = match NameSection::deserialize(&self, &mut rdr) { Ok(ns) => ns, Err(e) => { parse_errors.push((i, e)); continue }, }; Some(name_section) } else { None } } else { None } } { // todo: according to the spec a Wasm binary can contain only one name section *self.sections.get_mut(i).expect("cannot fail because i in range 0..len; qed") = Section::Name(name_section); } } if !parse_errors.is_empty() { Err((parse_errors, self)) } else { Ok(self) } } /// Try to parse reloc section in place. /// /// Corresponding custom section with proper header will convert to reloc sections /// If some of them will fail to be decoded, Err variant is returned with the list of /// (index, Error) tuples of failed sections. pub fn parse_reloc(mut self) -> Result, Self)> { let mut parse_errors = Vec::new(); for (i, section) in self.sections.iter_mut().enumerate() { if let Some(relocation_section) = { if let Section::Custom(ref custom) = *section { if custom.name().starts_with("reloc.") { let mut rdr = io::Cursor::new(custom.payload()); let reloc_section = match RelocSection::deserialize(custom.name().to_owned(), &mut rdr) { Ok(reloc_section) => reloc_section, Err(e) => { parse_errors.push((i, e)); continue }, }; if rdr.position() != custom.payload().len() { parse_errors.push((i, io::Error::InvalidData.into())); continue } Some(Section::Reloc(reloc_section)) } else { None } } else { None } } { *section = relocation_section; } } if !parse_errors.is_empty() { Err((parse_errors, self)) } else { Ok(self) } } /// Count imports by provided type. pub fn import_count(&self, count_type: ImportCountType) -> usize { self.import_section() .map(|is| { is.entries() .iter() .filter(|import| { matches!( (count_type, *import.external()), (ImportCountType::Function, External::Function(_)) | (ImportCountType::Global, External::Global(_)) | (ImportCountType::Table, External::Table(_)) | (ImportCountType::Memory, External::Memory(_)) ) }) .count() }) .unwrap_or(0) } /// Query functions space. pub fn functions_space(&self) -> usize { self.import_count(ImportCountType::Function) + self.function_section().map(|fs| fs.entries().len()).unwrap_or(0) } /// Query globals space. pub fn globals_space(&self) -> usize { self.import_count(ImportCountType::Global) + self.global_section().map(|gs| gs.entries().len()).unwrap_or(0) } /// Query table space. pub fn table_space(&self) -> usize { self.import_count(ImportCountType::Table) + self.table_section().map(|ts| ts.entries().len()).unwrap_or(0) } /// Query memory space. pub fn memory_space(&self) -> usize { self.import_count(ImportCountType::Memory) + self.memory_section().map(|ms| ms.entries().len()).unwrap_or(0) } } impl Deserialize for Module { type Error = super::Error; fn deserialize(reader: &mut R) -> Result { let mut sections = Vec::new(); let mut magic = [0u8; 4]; reader.read(&mut magic)?; if magic != WASM_MAGIC_NUMBER { return Err(Error::InvalidMagic) } let version: u32 = Uint32::deserialize(reader)?.into(); if version != 1 { return Err(Error::UnsupportedVersion(version)) } let mut last_section_order = 0; loop { match Section::deserialize(reader) { Err(Error::UnexpectedEof) => break, Err(e) => return Err(e), Ok(section) => { if section.order() != 0 { match last_section_order { x if x > section.order() => return Err(Error::SectionsOutOfOrder), x if x == section.order() => return Err(Error::DuplicatedSections(last_section_order)), _ => {}, }; last_section_order = section.order(); } sections.push(section); }, } } let module = Module { magic: u32::from_le_bytes(magic), version, sections }; if module.code_section().map(|cs| cs.bodies().len()).unwrap_or(0) != module.function_section().map(|fs| fs.entries().len()).unwrap_or(0) { return Err(Error::InconsistentCode) } Ok(module) } } impl Serialize for Module { type Error = Error; fn serialize(self, w: &mut W) -> Result<(), Self::Error> { Uint32::from(self.magic).serialize(w)?; Uint32::from(self.version).serialize(w)?; for section in self.sections.into_iter() { // todo: according to the spec the name section should appear after the data section section.serialize(w)?; } Ok(()) } } #[derive(Debug, Copy, Clone, PartialEq)] struct PeekSection<'a> { cursor: usize, region: &'a [u8], } impl<'a> io::Read for PeekSection<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result<()> { let available = cmp::min(buf.len(), self.region.len() - self.cursor); if available < buf.len() { return Err(io::Error::UnexpectedEof) } let range = self.cursor..self.cursor + buf.len(); buf.copy_from_slice(&self.region[range]); self.cursor += available; Ok(()) } } /// Returns size of the module in the provided stream. pub fn peek_size(source: &[u8]) -> usize { if source.len() < 9 { return 0 } let mut cursor = 8; loop { let (new_cursor, section_id, section_len) = { let mut peek_section = PeekSection { cursor: 0, region: &source[cursor..] }; let section_id: u8 = match super::VarUint7::deserialize(&mut peek_section) { Ok(res) => res.into(), Err(_) => break, }; let section_len: u32 = match super::VarUint32::deserialize(&mut peek_section) { Ok(res) => res.into(), Err(_) => break, }; (peek_section.cursor, section_id, section_len) }; if section_id <= 11 && section_len > 0 { let next_cursor = cursor + new_cursor + section_len as usize; match next_cursor { x if x > source.len() => break, x if x == source.len() => { cursor = next_cursor; break }, _ => {}, } cursor = next_cursor; } else { break } } cursor } #[cfg(test)] mod integration_tests { use super::{ super::{ deserialize_buffer, deserialize_file, serialize, CodeSection, ExportSection, FunctionSection, Section, TypeSection, }, Module, }; #[test] fn hello() { let module = deserialize_file("./res/cases/v1/hello.wasm").expect("Should be deserialized"); assert_eq!(module.version(), 1); assert_eq!(module.sections().len(), 8); } #[test] fn serde() { let module = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); let buf = serialize(module).expect("serialization to succeed"); let module_new: Module = deserialize_buffer(&buf).expect("deserialization to succeed"); let module_old = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); assert_eq!(module_old.sections().len(), module_new.sections().len()); } #[test] fn serde_type() { let mut module = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); module.sections_mut().retain(|x| matches!(x, &Section::Type(_))); let buf = serialize(module).expect("serialization to succeed"); let module_new: Module = deserialize_buffer(&buf).expect("deserialization to succeed"); let module_old = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); assert_eq!( module_old.type_section().expect("type section exists").types().len(), module_new.type_section().expect("type section exists").types().len(), "There should be equal amount of types before and after serialization" ); } #[test] fn serde_import() { let mut module = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); module.sections_mut().retain(|x| matches!(x, &Section::Import(_))); let buf = serialize(module).expect("serialization to succeed"); let module_new: Module = deserialize_buffer(&buf).expect("deserialization to succeed"); let module_old = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); assert_eq!( module_old.import_section().expect("import section exists").entries().len(), module_new.import_section().expect("import section exists").entries().len(), "There should be equal amount of import entries before and after serialization" ); } #[test] fn serde_code() { let mut module = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); module.sections_mut().retain(|x| { if let Section::Code(_) = *x { return true } matches!(*x, Section::Function(_)) }); let buf = serialize(module).expect("serialization to succeed"); let module_new: Module = deserialize_buffer(&buf).expect("deserialization to succeed"); let module_old = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); assert_eq!( module_old.code_section().expect("code section exists").bodies().len(), module_new.code_section().expect("code section exists").bodies().len(), "There should be equal amount of function bodies before and after serialization" ); } #[test] fn const_() { use super::super::Instruction::*; let module = deserialize_file("./res/cases/v1/const.wasm").expect("Should be deserialized"); let func = &module.code_section().expect("Code section to exist").bodies()[0]; assert_eq!(func.code().elements().len(), 20); assert_eq!(I64Const(9223372036854775807), func.code().elements()[0]); assert_eq!(I64Const(-9223372036854775808), func.code().elements()[1]); assert_eq!(I64Const(-1152894205662152753), func.code().elements()[2]); assert_eq!(I64Const(-8192), func.code().elements()[3]); assert_eq!(I32Const(1024), func.code().elements()[4]); assert_eq!(I32Const(2048), func.code().elements()[5]); assert_eq!(I32Const(4096), func.code().elements()[6]); assert_eq!(I32Const(8192), func.code().elements()[7]); assert_eq!(I32Const(16384), func.code().elements()[8]); assert_eq!(I32Const(32767), func.code().elements()[9]); assert_eq!(I32Const(-1024), func.code().elements()[10]); assert_eq!(I32Const(-2048), func.code().elements()[11]); assert_eq!(I32Const(-4096), func.code().elements()[12]); assert_eq!(I32Const(-8192), func.code().elements()[13]); assert_eq!(I32Const(-16384), func.code().elements()[14]); assert_eq!(I32Const(-32768), func.code().elements()[15]); assert_eq!(I32Const(-2147483648), func.code().elements()[16]); assert_eq!(I32Const(2147483647), func.code().elements()[17]); } #[test] fn store() { use super::super::Instruction::*; let module = deserialize_file("./res/cases/v1/offset.wasm").expect("Should be deserialized"); let func = &module.code_section().expect("Code section to exist").bodies()[0]; assert_eq!(func.code().elements().len(), 5); assert_eq!(I64Store(0, 32), func.code().elements()[2]); } #[test] fn peek() { use super::peek_size; let module = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); let mut buf = serialize(module).expect("serialization to succeed"); buf.extend_from_slice(&[1, 5, 12, 17]); assert_eq!(peek_size(&buf), buf.len() - 4); } #[test] fn peek_2() { use super::peek_size; let module = deserialize_file("./res/cases/v1/offset.wasm").expect("Should be deserialized"); let mut buf = serialize(module).expect("serialization to succeed"); buf.extend_from_slice(&[0, 0, 0, 0, 0, 1, 5, 12, 17]); assert_eq!(peek_size(&buf), buf.len() - 9); } #[test] fn peek_3() { use super::peek_size; let module = deserialize_file("./res/cases/v1/peek_sample.wasm").expect("Should be deserialized"); let buf = serialize(module).expect("serialization to succeed"); assert_eq!(peek_size(&buf), buf.len()); } #[test] fn module_default_round_trip() { let module1 = Module::default(); let buf = serialize(module1).expect("Serialization should succeed"); let module2: Module = deserialize_buffer(&buf).expect("Deserialization should succeed"); assert_eq!(Module::default().magic, module2.magic); } #[test] fn names() { let module = deserialize_file("./res/cases/v1/with_names.wasm") .expect("Should be deserialized") .parse_names() .expect("Names to be parsed"); let mut found_section = false; for section in module.sections() { if let Section::Name(ref name_section) = *section { let function_name_subsection = name_section.functions().expect("function_name_subsection should be present"); assert_eq!( function_name_subsection.names().get(0).expect("Should be entry #0"), "elog" ); assert_eq!( function_name_subsection.names().get(11).expect("Should be entry #0"), "_ZN48_$LT$pwasm_token_contract..Endpoint$LT$T$GT$$GT$3new17hc3ace6dea0978cd9E" ); found_section = true; } } assert!(found_section, "Name section should be present in dedicated example"); } #[test] fn names_with_global_section() { let module = deserialize_file("./res/cases/v1/global_section.wasm") .expect("Should be deserialized") .parse_names() .expect("Names to be parsed"); let mut found_section = false; for section in module.sections() { if let Section::Name(ref name_section) = *section { let function_name_subsection = name_section.functions().expect("function_name_subsection should be present"); assert_eq!( function_name_subsection.names().get(0).expect("Should be entry #0"), "~lib/builtins/abort" ); assert_eq!( function_name_subsection.names().get(11).expect("Should be entry #0"), "~lib/typedarray/Uint8Array#__set" ); found_section = true; } } assert!(found_section, "Name section should be present in dedicated example"); } // This test fixture has FLAG_SHARED so it depends on atomics feature. #[test] fn shared_memory_flag() { let module = deserialize_file("./res/cases/v1/varuint1_1.wasm"); assert_eq!(module.is_ok(), cfg!(feature = "atomics")); } #[test] fn memory_space() { let module = deserialize_file("./res/cases/v1/two-mems.wasm").expect("failed to deserialize"); assert_eq!(module.memory_space(), 2); } #[test] fn add_custom_section() { let mut module = deserialize_file("./res/cases/v1/start_mut.wasm").expect("failed to deserialize"); assert!(module.custom_sections().next().is_none()); module.set_custom_section("mycustomsection".to_string(), vec![1, 2, 3, 4]); { let sections = module.custom_sections().collect::>(); assert_eq!(sections.len(), 1); assert_eq!(sections[0].name(), "mycustomsection"); assert_eq!(sections[0].payload(), &[1, 2, 3, 4]); } let old_section = module.clear_custom_section("mycustomsection"); assert_eq!(old_section.expect("Did not find custom section").payload(), &[1, 2, 3, 4]); assert!(module.custom_sections().next().is_none()); } #[test] fn mut_start() { let mut module = deserialize_file("./res/cases/v1/start_mut.wasm").expect("failed to deserialize"); assert_eq!(module.start_section().expect("Did not find any start section"), 1); module.set_start_section(0); assert_eq!(module.start_section().expect("Did not find any start section"), 0); module.clear_start_section(); assert_eq!(None, module.start_section()); } #[test] fn add_start() { let mut module = deserialize_file("./res/cases/v1/start_add.wasm").expect("failed to deserialize"); assert!(module.start_section().is_none()); module.set_start_section(0); assert_eq!(module.start_section().expect("Did not find any start section"), 0); let sections = module.sections().iter().map(|s| s.order()).collect::>(); assert_eq!(sections, vec![1, 2, 3, 6, 7, 8, 9, 11, 12]); } #[test] fn add_start_custom() { let mut module = deserialize_file("./res/cases/v1/start_add_custom.wasm") .expect("failed to deserialize"); let sections = module.sections().iter().map(|s| s.order()).collect::>(); assert_eq!(sections, vec![1, 2, 3, 6, 7, 9, 11, 12, 0]); assert!(module.start_section().is_none()); module.set_start_section(0); assert_eq!(module.start_section().expect("Dorder not find any start section"), 0); let sections = module.sections().iter().map(|s| s.order()).collect::>(); assert_eq!(sections, vec![1, 2, 3, 6, 7, 8, 9, 11, 12, 0]); } #[test] fn names_section_present() { let mut module = deserialize_file("./res/cases/v1/names.wasm").expect("failed to deserialize"); // Before parsing assert!(module.names_section().is_none()); assert!(module.names_section_mut().is_none()); assert!(module.has_names_section()); // After parsing let mut module = module.parse_names().expect("failed to parse names section"); assert!(module.names_section().is_some()); assert!(module.names_section_mut().is_some()); assert!(module.has_names_section()); } #[test] fn names_section_not_present() { let mut module = deserialize_file("./res/cases/v1/test.wasm").expect("failed to deserialize"); // Before parsing assert!(module.names_section().is_none()); assert!(module.names_section_mut().is_none()); assert!(!module.has_names_section()); // After parsing let mut module = module.parse_names().expect("failed to parse names section"); assert!(module.names_section().is_none()); assert!(module.names_section_mut().is_none()); assert!(!module.has_names_section()); } #[test] fn insert_sections() { let mut module = Module::default(); assert!(module .insert_section(Section::Function(FunctionSection::with_entries(vec![]))) .is_ok()); // Duplicate. assert!(module .insert_section(Section::Function(FunctionSection::with_entries(vec![]))) .is_err()); assert!(module.insert_section(Section::Type(TypeSection::with_types(vec![]))).is_ok()); // Duplicate. assert!(module.insert_section(Section::Type(TypeSection::with_types(vec![]))).is_err()); assert!(module .insert_section(Section::Export(ExportSection::with_entries(vec![]))) .is_ok()); // Duplicate. assert!(module .insert_section(Section::Export(ExportSection::with_entries(vec![]))) .is_err()); assert!(module.insert_section(Section::Code(CodeSection::with_bodies(vec![]))).is_ok()); // Duplicate. assert!(module.insert_section(Section::Code(CodeSection::with_bodies(vec![]))).is_err()); // Try serialisation roundtrip to check well-orderedness. let serialized = serialize(module).expect("serialization to succeed"); assert!(deserialize_buffer::(&serialized).is_ok()); } #[test] fn serialization_roundtrip() { let module = deserialize_file("./res/cases/v1/test.wasm").expect("failed to deserialize"); let module_copy = module.clone().into_bytes().expect("failed to serialize"); let module_copy = Module::from_bytes(&module_copy).expect("failed to deserialize"); assert_eq!(module, module_copy); } } parity-wasm-0.45.0/src/elements/name_section.rs000064400000000000000000000243140072674642500176400ustar 00000000000000use crate::io; use alloc::string::String; use super::{ index_map::IndexMap, Deserialize, Error, Module, Serialize, Type, VarUint32, VarUint7, }; const NAME_TYPE_MODULE: u8 = 0; const NAME_TYPE_FUNCTION: u8 = 1; const NAME_TYPE_LOCAL: u8 = 2; /// Debug name information. #[derive(Clone, Debug, PartialEq)] pub struct NameSection { /// Module name subsection. module: Option, /// Function name subsection. functions: Option, /// Local name subsection. locals: Option, } impl NameSection { /// Creates a new name section. pub fn new( module: Option, functions: Option, locals: Option, ) -> Self { Self { module, functions, locals } } /// Module name subsection of this section. pub fn module(&self) -> Option<&ModuleNameSubsection> { self.module.as_ref() } /// Module name subsection of this section (mutable). pub fn module_mut(&mut self) -> &mut Option { &mut self.module } /// Functions name subsection of this section. pub fn functions(&self) -> Option<&FunctionNameSubsection> { self.functions.as_ref() } /// Functions name subsection of this section (mutable). pub fn functions_mut(&mut self) -> &mut Option { &mut self.functions } /// Local name subsection of this section. pub fn locals(&self) -> Option<&LocalNameSubsection> { self.locals.as_ref() } /// Local name subsection of this section (mutable). pub fn locals_mut(&mut self) -> &mut Option { &mut self.locals } } impl NameSection { /// Deserialize a name section. pub fn deserialize(module: &Module, rdr: &mut R) -> Result { let mut module_name: Option = None; let mut function_names: Option = None; let mut local_names: Option = None; while let Ok(raw_subsection_type) = VarUint7::deserialize(rdr) { let subsection_type = raw_subsection_type.into(); // deserialize the section size let size: usize = VarUint32::deserialize(rdr)?.into(); match subsection_type { NAME_TYPE_MODULE => { if module_name.is_some() { return Err(Error::DuplicatedNameSubsections(NAME_TYPE_FUNCTION)) } module_name = Some(ModuleNameSubsection::deserialize(rdr)?); }, NAME_TYPE_FUNCTION => { if function_names.is_some() { return Err(Error::DuplicatedNameSubsections(NAME_TYPE_FUNCTION)) } function_names = Some(FunctionNameSubsection::deserialize(module, rdr)?); }, NAME_TYPE_LOCAL => { if local_names.is_some() { return Err(Error::DuplicatedNameSubsections(NAME_TYPE_LOCAL)) } local_names = Some(LocalNameSubsection::deserialize(module, rdr)?); }, _ => { // Consume the entire subsection size and drop it. This allows other sections to still be // consumed if there are any. let mut buf = vec![0; size]; rdr.read(&mut buf)?; }, }; } Ok(Self { module: module_name, functions: function_names, locals: local_names }) } } impl Serialize for NameSection { type Error = Error; fn serialize(self, wtr: &mut W) -> Result<(), Error> { fn serialize_subsection( wtr: &mut W, name_type: u8, name_payload: &[u8], ) -> Result<(), Error> { VarUint7::from(name_type).serialize(wtr)?; VarUint32::from(name_payload.len()).serialize(wtr)?; wtr.write(name_payload).map_err(Into::into) } if let Some(module_name_subsection) = self.module { let mut buffer = vec![]; module_name_subsection.serialize(&mut buffer)?; serialize_subsection(wtr, NAME_TYPE_MODULE, &buffer)?; } if let Some(function_name_subsection) = self.functions { let mut buffer = vec![]; function_name_subsection.serialize(&mut buffer)?; serialize_subsection(wtr, NAME_TYPE_FUNCTION, &buffer)?; } if let Some(local_name_subsection) = self.locals { let mut buffer = vec![]; local_name_subsection.serialize(&mut buffer)?; serialize_subsection(wtr, NAME_TYPE_LOCAL, &buffer)?; } Ok(()) } } /// The name of this module. #[derive(Clone, Debug, PartialEq)] pub struct ModuleNameSubsection { name: String, } impl ModuleNameSubsection { /// Create a new module name section with the specified name. pub fn new>(name: S) -> ModuleNameSubsection { ModuleNameSubsection { name: name.into() } } /// The name of this module. pub fn name(&self) -> &str { &self.name } /// The name of this module (mutable). pub fn name_mut(&mut self) -> &mut String { &mut self.name } } impl Serialize for ModuleNameSubsection { type Error = Error; fn serialize(self, wtr: &mut W) -> Result<(), Error> { self.name.serialize(wtr) } } impl Deserialize for ModuleNameSubsection { type Error = Error; fn deserialize(rdr: &mut R) -> Result { let name = String::deserialize(rdr)?; Ok(ModuleNameSubsection { name }) } } /// The names of the functions in this module. #[derive(Clone, Debug, Default, PartialEq)] pub struct FunctionNameSubsection { names: NameMap, } impl FunctionNameSubsection { /// A map from function indices to names. pub fn names(&self) -> &NameMap { &self.names } /// A map from function indices to names (mutable). pub fn names_mut(&mut self) -> &mut NameMap { &mut self.names } /// Deserialize names, making sure that all names correspond to functions. pub fn deserialize( module: &Module, rdr: &mut R, ) -> Result { let names = IndexMap::deserialize(module.functions_space(), rdr)?; Ok(FunctionNameSubsection { names }) } } impl Serialize for FunctionNameSubsection { type Error = Error; fn serialize(self, wtr: &mut W) -> Result<(), Error> { self.names.serialize(wtr) } } /// The names of the local variables in this module's functions. #[derive(Clone, Debug, Default, PartialEq)] pub struct LocalNameSubsection { local_names: IndexMap, } impl LocalNameSubsection { /// A map from function indices to a map from variables indices to names. pub fn local_names(&self) -> &IndexMap { &self.local_names } /// A map from function indices to a map from variables indices to names /// (mutable). pub fn local_names_mut(&mut self) -> &mut IndexMap { &mut self.local_names } /// Deserialize names, making sure that all names correspond to local /// variables. pub fn deserialize( module: &Module, rdr: &mut R, ) -> Result { let max_entry_space = module.functions_space(); let max_signature_args = module .type_section() .map(|ts| { ts.types() .iter() .map(|x| { let Type::Function(ref func) = *x; func.params().len() }) .max() .unwrap_or(0) }) .unwrap_or(0); let max_locals = module .code_section() .map(|cs| { cs.bodies() .iter() .map(|f| f.locals().iter().map(|l| l.count() as usize).sum()) .max() .unwrap_or(0) }) .unwrap_or(0); let max_space = max_signature_args + max_locals; let deserialize_locals = |_: u32, rdr: &mut R| IndexMap::deserialize(max_space, rdr); let local_names = IndexMap::deserialize_with(max_entry_space, &deserialize_locals, rdr)?; Ok(LocalNameSubsection { local_names }) } } impl Serialize for LocalNameSubsection { type Error = Error; fn serialize(self, wtr: &mut W) -> Result<(), Error> { self.local_names.serialize(wtr) } } /// A map from indices to names. pub type NameMap = IndexMap; #[cfg(test)] mod tests { use super::*; // A helper function for the tests. Serialize a section, deserialize it, // and make sure it matches the original. fn serialize_test(original: NameSection) -> Vec { let mut buffer = vec![]; original.serialize(&mut buffer).expect("serialize error"); buffer // todo: add deserialization to this test } #[test] fn serialize_module_name() { let module_name_subsection = ModuleNameSubsection::new("my_mod"); let original = NameSection::new(Some(module_name_subsection), None, None); serialize_test(original); } #[test] fn serialize_function_names() { let mut function_name_subsection = FunctionNameSubsection::default(); function_name_subsection.names_mut().insert(0, "hello_world".to_string()); let name_section = NameSection::new(None, Some(function_name_subsection), None); serialize_test(name_section); } #[test] fn serialize_local_names() { let mut local_name_subsection = LocalNameSubsection::default(); let mut locals = NameMap::default(); locals.insert(0, "msg".to_string()); local_name_subsection.local_names_mut().insert(0, locals); let name_section = NameSection::new(None, None, Some(local_name_subsection)); serialize_test(name_section); } #[test] fn serialize_all_subsections() { let module_name_subsection = ModuleNameSubsection::new("ModuleNameSubsection"); let mut function_name_subsection = FunctionNameSubsection::default(); function_name_subsection.names_mut().insert(0, "foo".to_string()); function_name_subsection.names_mut().insert(1, "bar".to_string()); let mut local_name_subsection = LocalNameSubsection::default(); let mut locals = NameMap::default(); locals.insert(0, "msg1".to_string()); locals.insert(1, "msg2".to_string()); local_name_subsection.local_names_mut().insert(0, locals); let name_section = NameSection::new( Some(module_name_subsection), Some(function_name_subsection), Some(local_name_subsection), ); serialize_test(name_section); } #[test] fn deserialize_local_names() { let module = super::super::deserialize_file("./res/cases/v1/names_with_imports.wasm") .expect("Should be deserialized") .parse_names() .expect("Names to be parsed"); let name_section = module.names_section().expect("name_section should be present"); let local_names = name_section.locals().expect("local_name_section should be present"); let locals = local_names.local_names().get(0).expect("entry #0 should be present"); assert_eq!(locals.get(0).expect("entry #0 should be present"), "abc"); let locals = local_names.local_names().get(1).expect("entry #1 should be present"); assert_eq!(locals.get(0).expect("entry #0 should be present"), "def"); } } parity-wasm-0.45.0/src/elements/ops.rs000064400000000000000000002723050072674642500160020ustar 00000000000000use super::{ BlockType, CountedList, CountedListWriter, Deserialize, Error, Serialize, Uint32, Uint64, Uint8, VarInt32, VarInt64, VarUint32, }; use crate::io; use alloc::{boxed::Box, vec::Vec}; use core::fmt; /// List of instructions (usually inside a block section). #[derive(Debug, Clone, PartialEq)] pub struct Instructions(Vec); impl Instructions { /// New list of instructions from vector of instructions. pub fn new(elements: Vec) -> Self { Instructions(elements) } /// Empty expression with only `Instruction::End` instruction. pub fn empty() -> Self { Instructions(vec![Instruction::End]) } /// List of individual instructions. pub fn elements(&self) -> &[Instruction] { &self.0 } /// Individual instructions, mutable. pub fn elements_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for Instructions { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut instructions = Vec::new(); let mut block_count = 1usize; loop { let instruction = Instruction::deserialize(reader)?; if instruction.is_terminal() { block_count -= 1; } else if instruction.is_block() { block_count = block_count.checked_add(1).ok_or(Error::Other("too many instructions"))?; } instructions.push(instruction); if block_count == 0 { break } } Ok(Instructions(instructions)) } } /// Initialization expression. #[derive(Debug, Clone, PartialEq)] pub struct InitExpr(Vec); impl InitExpr { /// New initialization expression from instruction list. /// /// `code` must end with the `Instruction::End` instruction! pub fn new(code: Vec) -> Self { InitExpr(code) } /// Empty expression with only `Instruction::End` instruction. pub fn empty() -> Self { InitExpr(vec![Instruction::End]) } /// List of instructions used in the expression. pub fn code(&self) -> &[Instruction] { &self.0 } /// List of instructions used in the expression. pub fn code_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for InitExpr { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut instructions = Vec::new(); loop { let instruction = Instruction::deserialize(reader)?; let is_terminal = instruction.is_terminal(); instructions.push(instruction); if is_terminal { break } } Ok(InitExpr(instructions)) } } /// Instruction. #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[allow(missing_docs)] pub enum Instruction { Unreachable, Nop, Block(BlockType), Loop(BlockType), If(BlockType), Else, End, Br(u32), BrIf(u32), BrTable(Box), Return, Call(u32), CallIndirect(u32, u8), Drop, Select, GetLocal(u32), SetLocal(u32), TeeLocal(u32), GetGlobal(u32), SetGlobal(u32), // All store/load instructions operate with 'memory immediates' // which represented here as (flag, offset) tuple I32Load(u32, u32), I64Load(u32, u32), F32Load(u32, u32), F64Load(u32, u32), I32Load8S(u32, u32), I32Load8U(u32, u32), I32Load16S(u32, u32), I32Load16U(u32, u32), I64Load8S(u32, u32), I64Load8U(u32, u32), I64Load16S(u32, u32), I64Load16U(u32, u32), I64Load32S(u32, u32), I64Load32U(u32, u32), I32Store(u32, u32), I64Store(u32, u32), F32Store(u32, u32), F64Store(u32, u32), I32Store8(u32, u32), I32Store16(u32, u32), I64Store8(u32, u32), I64Store16(u32, u32), I64Store32(u32, u32), CurrentMemory(u8), GrowMemory(u8), I32Const(i32), I64Const(i64), F32Const(u32), F64Const(u64), I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU, I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU, F32Eq, F32Ne, F32Lt, F32Gt, F32Le, F32Ge, F64Eq, F64Ne, F64Lt, F64Gt, F64Le, F64Ge, I32Clz, I32Ctz, I32Popcnt, I32Add, I32Sub, I32Mul, I32DivS, I32DivU, I32RemS, I32RemU, I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr, I64Clz, I64Ctz, I64Popcnt, I64Add, I64Sub, I64Mul, I64DivS, I64DivU, I64RemS, I64RemU, I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr, F32Abs, F32Neg, F32Ceil, F32Floor, F32Trunc, F32Nearest, F32Sqrt, F32Add, F32Sub, F32Mul, F32Div, F32Min, F32Max, F32Copysign, F64Abs, F64Neg, F64Ceil, F64Floor, F64Trunc, F64Nearest, F64Sqrt, F64Add, F64Sub, F64Mul, F64Div, F64Min, F64Max, F64Copysign, I32WrapI64, I32TruncSF32, I32TruncUF32, I32TruncSF64, I32TruncUF64, I64ExtendSI32, I64ExtendUI32, I64TruncSF32, I64TruncUF32, I64TruncSF64, I64TruncUF64, F32ConvertSI32, F32ConvertUI32, F32ConvertSI64, F32ConvertUI64, F32DemoteF64, F64ConvertSI32, F64ConvertUI32, F64ConvertSI64, F64ConvertUI64, F64PromoteF32, I32ReinterpretF32, I64ReinterpretF64, F32ReinterpretI32, F64ReinterpretI64, #[cfg(feature = "atomics")] Atomics(AtomicsInstruction), #[cfg(feature = "simd")] Simd(SimdInstruction), #[cfg(feature = "sign_ext")] SignExt(SignExtInstruction), #[cfg(feature = "bulk")] Bulk(BulkInstruction), } #[allow(missing_docs)] #[cfg(feature = "atomics")] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum AtomicsInstruction { AtomicWake(MemArg), I32AtomicWait(MemArg), I64AtomicWait(MemArg), I32AtomicLoad(MemArg), I64AtomicLoad(MemArg), I32AtomicLoad8u(MemArg), I32AtomicLoad16u(MemArg), I64AtomicLoad8u(MemArg), I64AtomicLoad16u(MemArg), I64AtomicLoad32u(MemArg), I32AtomicStore(MemArg), I64AtomicStore(MemArg), I32AtomicStore8u(MemArg), I32AtomicStore16u(MemArg), I64AtomicStore8u(MemArg), I64AtomicStore16u(MemArg), I64AtomicStore32u(MemArg), I32AtomicRmwAdd(MemArg), I64AtomicRmwAdd(MemArg), I32AtomicRmwAdd8u(MemArg), I32AtomicRmwAdd16u(MemArg), I64AtomicRmwAdd8u(MemArg), I64AtomicRmwAdd16u(MemArg), I64AtomicRmwAdd32u(MemArg), I32AtomicRmwSub(MemArg), I64AtomicRmwSub(MemArg), I32AtomicRmwSub8u(MemArg), I32AtomicRmwSub16u(MemArg), I64AtomicRmwSub8u(MemArg), I64AtomicRmwSub16u(MemArg), I64AtomicRmwSub32u(MemArg), I32AtomicRmwAnd(MemArg), I64AtomicRmwAnd(MemArg), I32AtomicRmwAnd8u(MemArg), I32AtomicRmwAnd16u(MemArg), I64AtomicRmwAnd8u(MemArg), I64AtomicRmwAnd16u(MemArg), I64AtomicRmwAnd32u(MemArg), I32AtomicRmwOr(MemArg), I64AtomicRmwOr(MemArg), I32AtomicRmwOr8u(MemArg), I32AtomicRmwOr16u(MemArg), I64AtomicRmwOr8u(MemArg), I64AtomicRmwOr16u(MemArg), I64AtomicRmwOr32u(MemArg), I32AtomicRmwXor(MemArg), I64AtomicRmwXor(MemArg), I32AtomicRmwXor8u(MemArg), I32AtomicRmwXor16u(MemArg), I64AtomicRmwXor8u(MemArg), I64AtomicRmwXor16u(MemArg), I64AtomicRmwXor32u(MemArg), I32AtomicRmwXchg(MemArg), I64AtomicRmwXchg(MemArg), I32AtomicRmwXchg8u(MemArg), I32AtomicRmwXchg16u(MemArg), I64AtomicRmwXchg8u(MemArg), I64AtomicRmwXchg16u(MemArg), I64AtomicRmwXchg32u(MemArg), I32AtomicRmwCmpxchg(MemArg), I64AtomicRmwCmpxchg(MemArg), I32AtomicRmwCmpxchg8u(MemArg), I32AtomicRmwCmpxchg16u(MemArg), I64AtomicRmwCmpxchg8u(MemArg), I64AtomicRmwCmpxchg16u(MemArg), I64AtomicRmwCmpxchg32u(MemArg), } #[allow(missing_docs)] #[cfg(feature = "simd")] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum SimdInstruction { V128Const(Box<[u8; 16]>), V128Load(MemArg), V128Store(MemArg), I8x16Splat, I16x8Splat, I32x4Splat, I64x2Splat, F32x4Splat, F64x2Splat, I8x16ExtractLaneS(u8), I8x16ExtractLaneU(u8), I16x8ExtractLaneS(u8), I16x8ExtractLaneU(u8), I32x4ExtractLane(u8), I64x2ExtractLane(u8), F32x4ExtractLane(u8), F64x2ExtractLane(u8), I8x16ReplaceLane(u8), I16x8ReplaceLane(u8), I32x4ReplaceLane(u8), I64x2ReplaceLane(u8), F32x4ReplaceLane(u8), F64x2ReplaceLane(u8), V8x16Shuffle(Box<[u8; 16]>), I8x16Add, I16x8Add, I32x4Add, I64x2Add, I8x16Sub, I16x8Sub, I32x4Sub, I64x2Sub, I8x16Mul, I16x8Mul, I32x4Mul, // I64x2Mul, I8x16Neg, I16x8Neg, I32x4Neg, I64x2Neg, I8x16AddSaturateS, I8x16AddSaturateU, I16x8AddSaturateS, I16x8AddSaturateU, I8x16SubSaturateS, I8x16SubSaturateU, I16x8SubSaturateS, I16x8SubSaturateU, I8x16Shl, I16x8Shl, I32x4Shl, I64x2Shl, I8x16ShrS, I8x16ShrU, I16x8ShrS, I16x8ShrU, I32x4ShrS, I32x4ShrU, I64x2ShrS, I64x2ShrU, V128And, V128Or, V128Xor, V128Not, V128Bitselect, I8x16AnyTrue, I16x8AnyTrue, I32x4AnyTrue, I64x2AnyTrue, I8x16AllTrue, I16x8AllTrue, I32x4AllTrue, I64x2AllTrue, I8x16Eq, I16x8Eq, I32x4Eq, // I64x2Eq, F32x4Eq, F64x2Eq, I8x16Ne, I16x8Ne, I32x4Ne, // I64x2Ne, F32x4Ne, F64x2Ne, I8x16LtS, I8x16LtU, I16x8LtS, I16x8LtU, I32x4LtS, I32x4LtU, // I64x2LtS, // I64x2LtU, F32x4Lt, F64x2Lt, I8x16LeS, I8x16LeU, I16x8LeS, I16x8LeU, I32x4LeS, I32x4LeU, // I64x2LeS, // I64x2LeU, F32x4Le, F64x2Le, I8x16GtS, I8x16GtU, I16x8GtS, I16x8GtU, I32x4GtS, I32x4GtU, // I64x2GtS, // I64x2GtU, F32x4Gt, F64x2Gt, I8x16GeS, I8x16GeU, I16x8GeS, I16x8GeU, I32x4GeS, I32x4GeU, // I64x2GeS, // I64x2GeU, F32x4Ge, F64x2Ge, F32x4Neg, F64x2Neg, F32x4Abs, F64x2Abs, F32x4Min, F64x2Min, F32x4Max, F64x2Max, F32x4Add, F64x2Add, F32x4Sub, F64x2Sub, F32x4Div, F64x2Div, F32x4Mul, F64x2Mul, F32x4Sqrt, F64x2Sqrt, F32x4ConvertSI32x4, F32x4ConvertUI32x4, F64x2ConvertSI64x2, F64x2ConvertUI64x2, I32x4TruncSF32x4Sat, I32x4TruncUF32x4Sat, I64x2TruncSF64x2Sat, I64x2TruncUF64x2Sat, } #[allow(missing_docs)] #[cfg(feature = "sign_ext")] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum SignExtInstruction { I32Extend8S, I32Extend16S, I64Extend8S, I64Extend16S, I64Extend32S, } #[allow(missing_docs)] #[cfg(feature = "bulk")] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum BulkInstruction { MemoryInit(u32), MemoryDrop(u32), MemoryCopy, MemoryFill, TableInit(u32), TableDrop(u32), TableCopy, } #[cfg(any(feature = "simd", feature = "atomics"))] #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[allow(missing_docs)] pub struct MemArg { pub align: u8, pub offset: u32, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[allow(missing_docs)] pub struct BrTableData { pub table: Box<[u32]>, pub default: u32, } impl Instruction { /// Is this instruction starts the new block (which should end with terminal instruction). pub fn is_block(&self) -> bool { matches!(self, &Instruction::Block(_) | &Instruction::Loop(_) | &Instruction::If(_)) } /// Is this instruction determines the termination of instruction sequence? /// /// `true` for `Instruction::End` pub fn is_terminal(&self) -> bool { matches!(self, &Instruction::End) } } #[allow(missing_docs)] pub mod opcodes { pub const UNREACHABLE: u8 = 0x00; pub const NOP: u8 = 0x01; pub const BLOCK: u8 = 0x02; pub const LOOP: u8 = 0x03; pub const IF: u8 = 0x04; pub const ELSE: u8 = 0x05; pub const END: u8 = 0x0b; pub const BR: u8 = 0x0c; pub const BRIF: u8 = 0x0d; pub const BRTABLE: u8 = 0x0e; pub const RETURN: u8 = 0x0f; pub const CALL: u8 = 0x10; pub const CALLINDIRECT: u8 = 0x11; pub const DROP: u8 = 0x1a; pub const SELECT: u8 = 0x1b; pub const GETLOCAL: u8 = 0x20; pub const SETLOCAL: u8 = 0x21; pub const TEELOCAL: u8 = 0x22; pub const GETGLOBAL: u8 = 0x23; pub const SETGLOBAL: u8 = 0x24; pub const I32LOAD: u8 = 0x28; pub const I64LOAD: u8 = 0x29; pub const F32LOAD: u8 = 0x2a; pub const F64LOAD: u8 = 0x2b; pub const I32LOAD8S: u8 = 0x2c; pub const I32LOAD8U: u8 = 0x2d; pub const I32LOAD16S: u8 = 0x2e; pub const I32LOAD16U: u8 = 0x2f; pub const I64LOAD8S: u8 = 0x30; pub const I64LOAD8U: u8 = 0x31; pub const I64LOAD16S: u8 = 0x32; pub const I64LOAD16U: u8 = 0x33; pub const I64LOAD32S: u8 = 0x34; pub const I64LOAD32U: u8 = 0x35; pub const I32STORE: u8 = 0x36; pub const I64STORE: u8 = 0x37; pub const F32STORE: u8 = 0x38; pub const F64STORE: u8 = 0x39; pub const I32STORE8: u8 = 0x3a; pub const I32STORE16: u8 = 0x3b; pub const I64STORE8: u8 = 0x3c; pub const I64STORE16: u8 = 0x3d; pub const I64STORE32: u8 = 0x3e; pub const CURRENTMEMORY: u8 = 0x3f; pub const GROWMEMORY: u8 = 0x40; pub const I32CONST: u8 = 0x41; pub const I64CONST: u8 = 0x42; pub const F32CONST: u8 = 0x43; pub const F64CONST: u8 = 0x44; pub const I32EQZ: u8 = 0x45; pub const I32EQ: u8 = 0x46; pub const I32NE: u8 = 0x47; pub const I32LTS: u8 = 0x48; pub const I32LTU: u8 = 0x49; pub const I32GTS: u8 = 0x4a; pub const I32GTU: u8 = 0x4b; pub const I32LES: u8 = 0x4c; pub const I32LEU: u8 = 0x4d; pub const I32GES: u8 = 0x4e; pub const I32GEU: u8 = 0x4f; pub const I64EQZ: u8 = 0x50; pub const I64EQ: u8 = 0x51; pub const I64NE: u8 = 0x52; pub const I64LTS: u8 = 0x53; pub const I64LTU: u8 = 0x54; pub const I64GTS: u8 = 0x55; pub const I64GTU: u8 = 0x56; pub const I64LES: u8 = 0x57; pub const I64LEU: u8 = 0x58; pub const I64GES: u8 = 0x59; pub const I64GEU: u8 = 0x5a; pub const F32EQ: u8 = 0x5b; pub const F32NE: u8 = 0x5c; pub const F32LT: u8 = 0x5d; pub const F32GT: u8 = 0x5e; pub const F32LE: u8 = 0x5f; pub const F32GE: u8 = 0x60; pub const F64EQ: u8 = 0x61; pub const F64NE: u8 = 0x62; pub const F64LT: u8 = 0x63; pub const F64GT: u8 = 0x64; pub const F64LE: u8 = 0x65; pub const F64GE: u8 = 0x66; pub const I32CLZ: u8 = 0x67; pub const I32CTZ: u8 = 0x68; pub const I32POPCNT: u8 = 0x69; pub const I32ADD: u8 = 0x6a; pub const I32SUB: u8 = 0x6b; pub const I32MUL: u8 = 0x6c; pub const I32DIVS: u8 = 0x6d; pub const I32DIVU: u8 = 0x6e; pub const I32REMS: u8 = 0x6f; pub const I32REMU: u8 = 0x70; pub const I32AND: u8 = 0x71; pub const I32OR: u8 = 0x72; pub const I32XOR: u8 = 0x73; pub const I32SHL: u8 = 0x74; pub const I32SHRS: u8 = 0x75; pub const I32SHRU: u8 = 0x76; pub const I32ROTL: u8 = 0x77; pub const I32ROTR: u8 = 0x78; pub const I64CLZ: u8 = 0x79; pub const I64CTZ: u8 = 0x7a; pub const I64POPCNT: u8 = 0x7b; pub const I64ADD: u8 = 0x7c; pub const I64SUB: u8 = 0x7d; pub const I64MUL: u8 = 0x7e; pub const I64DIVS: u8 = 0x7f; pub const I64DIVU: u8 = 0x80; pub const I64REMS: u8 = 0x81; pub const I64REMU: u8 = 0x82; pub const I64AND: u8 = 0x83; pub const I64OR: u8 = 0x84; pub const I64XOR: u8 = 0x85; pub const I64SHL: u8 = 0x86; pub const I64SHRS: u8 = 0x87; pub const I64SHRU: u8 = 0x88; pub const I64ROTL: u8 = 0x89; pub const I64ROTR: u8 = 0x8a; pub const F32ABS: u8 = 0x8b; pub const F32NEG: u8 = 0x8c; pub const F32CEIL: u8 = 0x8d; pub const F32FLOOR: u8 = 0x8e; pub const F32TRUNC: u8 = 0x8f; pub const F32NEAREST: u8 = 0x90; pub const F32SQRT: u8 = 0x91; pub const F32ADD: u8 = 0x92; pub const F32SUB: u8 = 0x93; pub const F32MUL: u8 = 0x94; pub const F32DIV: u8 = 0x95; pub const F32MIN: u8 = 0x96; pub const F32MAX: u8 = 0x97; pub const F32COPYSIGN: u8 = 0x98; pub const F64ABS: u8 = 0x99; pub const F64NEG: u8 = 0x9a; pub const F64CEIL: u8 = 0x9b; pub const F64FLOOR: u8 = 0x9c; pub const F64TRUNC: u8 = 0x9d; pub const F64NEAREST: u8 = 0x9e; pub const F64SQRT: u8 = 0x9f; pub const F64ADD: u8 = 0xa0; pub const F64SUB: u8 = 0xa1; pub const F64MUL: u8 = 0xa2; pub const F64DIV: u8 = 0xa3; pub const F64MIN: u8 = 0xa4; pub const F64MAX: u8 = 0xa5; pub const F64COPYSIGN: u8 = 0xa6; pub const I32WRAPI64: u8 = 0xa7; pub const I32TRUNCSF32: u8 = 0xa8; pub const I32TRUNCUF32: u8 = 0xa9; pub const I32TRUNCSF64: u8 = 0xaa; pub const I32TRUNCUF64: u8 = 0xab; pub const I64EXTENDSI32: u8 = 0xac; pub const I64EXTENDUI32: u8 = 0xad; pub const I64TRUNCSF32: u8 = 0xae; pub const I64TRUNCUF32: u8 = 0xaf; pub const I64TRUNCSF64: u8 = 0xb0; pub const I64TRUNCUF64: u8 = 0xb1; pub const F32CONVERTSI32: u8 = 0xb2; pub const F32CONVERTUI32: u8 = 0xb3; pub const F32CONVERTSI64: u8 = 0xb4; pub const F32CONVERTUI64: u8 = 0xb5; pub const F32DEMOTEF64: u8 = 0xb6; pub const F64CONVERTSI32: u8 = 0xb7; pub const F64CONVERTUI32: u8 = 0xb8; pub const F64CONVERTSI64: u8 = 0xb9; pub const F64CONVERTUI64: u8 = 0xba; pub const F64PROMOTEF32: u8 = 0xbb; pub const I32REINTERPRETF32: u8 = 0xbc; pub const I64REINTERPRETF64: u8 = 0xbd; pub const F32REINTERPRETI32: u8 = 0xbe; pub const F64REINTERPRETI64: u8 = 0xbf; #[cfg(feature = "sign_ext")] pub mod sign_ext { pub const I32_EXTEND8_S: u8 = 0xc0; pub const I32_EXTEND16_S: u8 = 0xc1; pub const I64_EXTEND8_S: u8 = 0xc2; pub const I64_EXTEND16_S: u8 = 0xc3; pub const I64_EXTEND32_S: u8 = 0xc4; } #[cfg(feature = "atomics")] pub mod atomics { pub const ATOMIC_PREFIX: u8 = 0xfe; pub const ATOMIC_WAKE: u8 = 0x00; pub const I32_ATOMIC_WAIT: u8 = 0x01; pub const I64_ATOMIC_WAIT: u8 = 0x02; pub const I32_ATOMIC_LOAD: u8 = 0x10; pub const I64_ATOMIC_LOAD: u8 = 0x11; pub const I32_ATOMIC_LOAD8U: u8 = 0x12; pub const I32_ATOMIC_LOAD16U: u8 = 0x13; pub const I64_ATOMIC_LOAD8U: u8 = 0x14; pub const I64_ATOMIC_LOAD16U: u8 = 0x15; pub const I64_ATOMIC_LOAD32U: u8 = 0x16; pub const I32_ATOMIC_STORE: u8 = 0x17; pub const I64_ATOMIC_STORE: u8 = 0x18; pub const I32_ATOMIC_STORE8U: u8 = 0x19; pub const I32_ATOMIC_STORE16U: u8 = 0x1a; pub const I64_ATOMIC_STORE8U: u8 = 0x1b; pub const I64_ATOMIC_STORE16U: u8 = 0x1c; pub const I64_ATOMIC_STORE32U: u8 = 0x1d; pub const I32_ATOMIC_RMW_ADD: u8 = 0x1e; pub const I64_ATOMIC_RMW_ADD: u8 = 0x1f; pub const I32_ATOMIC_RMW_ADD8U: u8 = 0x20; pub const I32_ATOMIC_RMW_ADD16U: u8 = 0x21; pub const I64_ATOMIC_RMW_ADD8U: u8 = 0x22; pub const I64_ATOMIC_RMW_ADD16U: u8 = 0x23; pub const I64_ATOMIC_RMW_ADD32U: u8 = 0x24; pub const I32_ATOMIC_RMW_SUB: u8 = 0x25; pub const I64_ATOMIC_RMW_SUB: u8 = 0x26; pub const I32_ATOMIC_RMW_SUB8U: u8 = 0x27; pub const I32_ATOMIC_RMW_SUB16U: u8 = 0x28; pub const I64_ATOMIC_RMW_SUB8U: u8 = 0x29; pub const I64_ATOMIC_RMW_SUB16U: u8 = 0x2a; pub const I64_ATOMIC_RMW_SUB32U: u8 = 0x2b; pub const I32_ATOMIC_RMW_AND: u8 = 0x2c; pub const I64_ATOMIC_RMW_AND: u8 = 0x2d; pub const I32_ATOMIC_RMW_AND8U: u8 = 0x2e; pub const I32_ATOMIC_RMW_AND16U: u8 = 0x2f; pub const I64_ATOMIC_RMW_AND8U: u8 = 0x30; pub const I64_ATOMIC_RMW_AND16U: u8 = 0x31; pub const I64_ATOMIC_RMW_AND32U: u8 = 0x32; pub const I32_ATOMIC_RMW_OR: u8 = 0x33; pub const I64_ATOMIC_RMW_OR: u8 = 0x34; pub const I32_ATOMIC_RMW_OR8U: u8 = 0x35; pub const I32_ATOMIC_RMW_OR16U: u8 = 0x36; pub const I64_ATOMIC_RMW_OR8U: u8 = 0x37; pub const I64_ATOMIC_RMW_OR16U: u8 = 0x38; pub const I64_ATOMIC_RMW_OR32U: u8 = 0x39; pub const I32_ATOMIC_RMW_XOR: u8 = 0x3a; pub const I64_ATOMIC_RMW_XOR: u8 = 0x3b; pub const I32_ATOMIC_RMW_XOR8U: u8 = 0x3c; pub const I32_ATOMIC_RMW_XOR16U: u8 = 0x3d; pub const I64_ATOMIC_RMW_XOR8U: u8 = 0x3e; pub const I64_ATOMIC_RMW_XOR16U: u8 = 0x3f; pub const I64_ATOMIC_RMW_XOR32U: u8 = 0x40; pub const I32_ATOMIC_RMW_XCHG: u8 = 0x41; pub const I64_ATOMIC_RMW_XCHG: u8 = 0x42; pub const I32_ATOMIC_RMW_XCHG8U: u8 = 0x43; pub const I32_ATOMIC_RMW_XCHG16U: u8 = 0x44; pub const I64_ATOMIC_RMW_XCHG8U: u8 = 0x45; pub const I64_ATOMIC_RMW_XCHG16U: u8 = 0x46; pub const I64_ATOMIC_RMW_XCHG32U: u8 = 0x47; pub const I32_ATOMIC_RMW_CMPXCHG: u8 = 0x48; pub const I64_ATOMIC_RMW_CMPXCHG: u8 = 0x49; pub const I32_ATOMIC_RMW_CMPXCHG8U: u8 = 0x4a; pub const I32_ATOMIC_RMW_CMPXCHG16U: u8 = 0x4b; pub const I64_ATOMIC_RMW_CMPXCHG8U: u8 = 0x4c; pub const I64_ATOMIC_RMW_CMPXCHG16U: u8 = 0x4d; pub const I64_ATOMIC_RMW_CMPXCHG32U: u8 = 0x4e; } #[cfg(feature = "simd")] pub mod simd { // https://github.com/WebAssembly/simd/blob/master/proposals/simd/BinarySIMD.md pub const SIMD_PREFIX: u8 = 0xfd; pub const V128_LOAD: u32 = 0x00; pub const V128_STORE: u32 = 0x01; pub const V128_CONST: u32 = 0x02; pub const V8X16_SHUFFLE: u32 = 0x03; pub const I8X16_SPLAT: u32 = 0x04; pub const I8X16_EXTRACT_LANE_S: u32 = 0x05; pub const I8X16_EXTRACT_LANE_U: u32 = 0x06; pub const I8X16_REPLACE_LANE: u32 = 0x07; pub const I16X8_SPLAT: u32 = 0x08; pub const I16X8_EXTRACT_LANE_S: u32 = 0x09; pub const I16X8_EXTRACT_LANE_U: u32 = 0xa; pub const I16X8_REPLACE_LANE: u32 = 0x0b; pub const I32X4_SPLAT: u32 = 0x0c; pub const I32X4_EXTRACT_LANE: u32 = 0x0d; pub const I32X4_REPLACE_LANE: u32 = 0x0e; pub const I64X2_SPLAT: u32 = 0x0f; pub const I64X2_EXTRACT_LANE: u32 = 0x10; pub const I64X2_REPLACE_LANE: u32 = 0x11; pub const F32X4_SPLAT: u32 = 0x12; pub const F32X4_EXTRACT_LANE: u32 = 0x13; pub const F32X4_REPLACE_LANE: u32 = 0x14; pub const F64X2_SPLAT: u32 = 0x15; pub const F64X2_EXTRACT_LANE: u32 = 0x16; pub const F64X2_REPLACE_LANE: u32 = 0x17; pub const I8X16_EQ: u32 = 0x18; pub const I8X16_NE: u32 = 0x19; pub const I8X16_LT_S: u32 = 0x1a; pub const I8X16_LT_U: u32 = 0x1b; pub const I8X16_GT_S: u32 = 0x1c; pub const I8X16_GT_U: u32 = 0x1d; pub const I8X16_LE_S: u32 = 0x1e; pub const I8X16_LE_U: u32 = 0x1f; pub const I8X16_GE_S: u32 = 0x20; pub const I8X16_GE_U: u32 = 0x21; pub const I16X8_EQ: u32 = 0x22; pub const I16X8_NE: u32 = 0x23; pub const I16X8_LT_S: u32 = 0x24; pub const I16X8_LT_U: u32 = 0x25; pub const I16X8_GT_S: u32 = 0x26; pub const I16X8_GT_U: u32 = 0x27; pub const I16X8_LE_S: u32 = 0x28; pub const I16X8_LE_U: u32 = 0x29; pub const I16X8_GE_S: u32 = 0x2a; pub const I16X8_GE_U: u32 = 0x2b; pub const I32X4_EQ: u32 = 0x2c; pub const I32X4_NE: u32 = 0x2d; pub const I32X4_LT_S: u32 = 0x2e; pub const I32X4_LT_U: u32 = 0x2f; pub const I32X4_GT_S: u32 = 0x30; pub const I32X4_GT_U: u32 = 0x31; pub const I32X4_LE_S: u32 = 0x32; pub const I32X4_LE_U: u32 = 0x33; pub const I32X4_GE_S: u32 = 0x34; pub const I32X4_GE_U: u32 = 0x35; pub const F32X4_EQ: u32 = 0x40; pub const F32X4_NE: u32 = 0x41; pub const F32X4_LT: u32 = 0x42; pub const F32X4_GT: u32 = 0x43; pub const F32X4_LE: u32 = 0x44; pub const F32X4_GE: u32 = 0x45; pub const F64X2_EQ: u32 = 0x46; pub const F64X2_NE: u32 = 0x47; pub const F64X2_LT: u32 = 0x48; pub const F64X2_GT: u32 = 0x49; pub const F64X2_LE: u32 = 0x4a; pub const F64X2_GE: u32 = 0x4b; pub const V128_NOT: u32 = 0x4c; pub const V128_AND: u32 = 0x4d; pub const V128_OR: u32 = 0x4e; pub const V128_XOR: u32 = 0x4f; pub const V128_BITSELECT: u32 = 0x50; pub const I8X16_NEG: u32 = 0x51; pub const I8X16_ANY_TRUE: u32 = 0x52; pub const I8X16_ALL_TRUE: u32 = 0x53; pub const I8X16_SHL: u32 = 0x54; pub const I8X16_SHR_S: u32 = 0x55; pub const I8X16_SHR_U: u32 = 0x56; pub const I8X16_ADD: u32 = 0x57; pub const I8X16_ADD_SATURATE_S: u32 = 0x58; pub const I8X16_ADD_SATURATE_U: u32 = 0x59; pub const I8X16_SUB: u32 = 0x5a; pub const I8X16_SUB_SATURATE_S: u32 = 0x5b; pub const I8X16_SUB_SATURATE_U: u32 = 0x5c; pub const I8X16_MUL: u32 = 0x5d; pub const I16X8_NEG: u32 = 0x62; pub const I16X8_ANY_TRUE: u32 = 0x63; pub const I16X8_ALL_TRUE: u32 = 0x64; pub const I16X8_SHL: u32 = 0x65; pub const I16X8_SHR_S: u32 = 0x66; pub const I16X8_SHR_U: u32 = 0x67; pub const I16X8_ADD: u32 = 0x68; pub const I16X8_ADD_SATURATE_S: u32 = 0x69; pub const I16X8_ADD_SATURATE_U: u32 = 0x6a; pub const I16X8_SUB: u32 = 0x6b; pub const I16X8_SUB_SATURATE_S: u32 = 0x6c; pub const I16X8_SUB_SATURATE_U: u32 = 0x6d; pub const I16X8_MUL: u32 = 0x6e; pub const I32X4_NEG: u32 = 0x73; pub const I32X4_ANY_TRUE: u32 = 0x74; pub const I32X4_ALL_TRUE: u32 = 0x75; pub const I32X4_SHL: u32 = 0x76; pub const I32X4_SHR_S: u32 = 0x77; pub const I32X4_SHR_U: u32 = 0x78; pub const I32X4_ADD: u32 = 0x79; pub const I32X4_ADD_SATURATE_S: u32 = 0x7a; pub const I32X4_ADD_SATURATE_U: u32 = 0x7b; pub const I32X4_SUB: u32 = 0x7c; pub const I32X4_SUB_SATURATE_S: u32 = 0x7d; pub const I32X4_SUB_SATURATE_U: u32 = 0x7e; pub const I32X4_MUL: u32 = 0x7f; pub const I64X2_NEG: u32 = 0x84; pub const I64X2_ANY_TRUE: u32 = 0x85; pub const I64X2_ALL_TRUE: u32 = 0x86; pub const I64X2_SHL: u32 = 0x87; pub const I64X2_SHR_S: u32 = 0x88; pub const I64X2_SHR_U: u32 = 0x89; pub const I64X2_ADD: u32 = 0x8a; pub const I64X2_SUB: u32 = 0x8d; pub const F32X4_ABS: u32 = 0x95; pub const F32X4_NEG: u32 = 0x96; pub const F32X4_SQRT: u32 = 0x97; pub const F32X4_ADD: u32 = 0x9a; pub const F32X4_SUB: u32 = 0x9b; pub const F32X4_MUL: u32 = 0x9c; pub const F32X4_DIV: u32 = 0x9d; pub const F32X4_MIN: u32 = 0x9e; pub const F32X4_MAX: u32 = 0x9f; pub const F64X2_ABS: u32 = 0xa0; pub const F64X2_NEG: u32 = 0xa1; pub const F64X2_SQRT: u32 = 0xa2; pub const F64X2_ADD: u32 = 0xa5; pub const F64X2_SUB: u32 = 0xa6; pub const F64X2_MUL: u32 = 0xa7; pub const F64X2_DIV: u32 = 0xa8; pub const F64X2_MIN: u32 = 0xa9; pub const F64X2_MAX: u32 = 0xaa; pub const I32X4_TRUNC_S_F32X4_SAT: u32 = 0xab; pub const I32X4_TRUNC_U_F32X4_SAT: u32 = 0xac; pub const I64X2_TRUNC_S_F64X2_SAT: u32 = 0xad; pub const I64X2_TRUNC_U_F64X2_SAT: u32 = 0xae; pub const F32X4_CONVERT_S_I32X4: u32 = 0xaf; pub const F32X4_CONVERT_U_I32X4: u32 = 0xb0; pub const F64X2_CONVERT_S_I64X2: u32 = 0xb1; pub const F64X2_CONVERT_U_I64X2: u32 = 0xb2; } #[cfg(feature = "bulk")] pub mod bulk { pub const BULK_PREFIX: u8 = 0xfc; pub const MEMORY_INIT: u8 = 0x08; pub const MEMORY_DROP: u8 = 0x09; pub const MEMORY_COPY: u8 = 0x0a; pub const MEMORY_FILL: u8 = 0x0b; pub const TABLE_INIT: u8 = 0x0c; pub const TABLE_DROP: u8 = 0x0d; pub const TABLE_COPY: u8 = 0x0e; } } impl Deserialize for Instruction { type Error = Error; fn deserialize(reader: &mut R) -> Result { use self::{opcodes::*, Instruction::*}; #[cfg(feature = "sign_ext")] use self::opcodes::sign_ext::*; let val: u8 = Uint8::deserialize(reader)?.into(); Ok(match val { UNREACHABLE => Unreachable, NOP => Nop, BLOCK => Block(BlockType::deserialize(reader)?), LOOP => Loop(BlockType::deserialize(reader)?), IF => If(BlockType::deserialize(reader)?), ELSE => Else, END => End, BR => Br(VarUint32::deserialize(reader)?.into()), BRIF => BrIf(VarUint32::deserialize(reader)?.into()), BRTABLE => { let t1: Vec = CountedList::::deserialize(reader)? .into_inner() .into_iter() .map(Into::into) .collect(); BrTable(Box::new(BrTableData { table: t1.into_boxed_slice(), default: VarUint32::deserialize(reader)?.into(), })) }, RETURN => Return, CALL => Call(VarUint32::deserialize(reader)?.into()), CALLINDIRECT => { let signature: u32 = VarUint32::deserialize(reader)?.into(); let table_ref: u8 = Uint8::deserialize(reader)?.into(); if table_ref != 0 { return Err(Error::InvalidTableReference(table_ref)) } CallIndirect(signature, table_ref) }, DROP => Drop, SELECT => Select, GETLOCAL => GetLocal(VarUint32::deserialize(reader)?.into()), SETLOCAL => SetLocal(VarUint32::deserialize(reader)?.into()), TEELOCAL => TeeLocal(VarUint32::deserialize(reader)?.into()), GETGLOBAL => GetGlobal(VarUint32::deserialize(reader)?.into()), SETGLOBAL => SetGlobal(VarUint32::deserialize(reader)?.into()), I32LOAD => I32Load( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64LOAD => I64Load( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), F32LOAD => F32Load( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), F64LOAD => F64Load( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I32LOAD8S => I32Load8S( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I32LOAD8U => I32Load8U( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I32LOAD16S => I32Load16S( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I32LOAD16U => I32Load16U( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64LOAD8S => I64Load8S( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64LOAD8U => I64Load8U( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64LOAD16S => I64Load16S( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64LOAD16U => I64Load16U( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64LOAD32S => I64Load32S( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64LOAD32U => I64Load32U( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I32STORE => I32Store( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64STORE => I64Store( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), F32STORE => F32Store( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), F64STORE => F64Store( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I32STORE8 => I32Store8( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I32STORE16 => I32Store16( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64STORE8 => I64Store8( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64STORE16 => I64Store16( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), I64STORE32 => I64Store32( VarUint32::deserialize(reader)?.into(), VarUint32::deserialize(reader)?.into(), ), CURRENTMEMORY => { let mem_ref: u8 = Uint8::deserialize(reader)?.into(); if mem_ref != 0 { return Err(Error::InvalidMemoryReference(mem_ref)) } CurrentMemory(mem_ref) }, GROWMEMORY => { let mem_ref: u8 = Uint8::deserialize(reader)?.into(); if mem_ref != 0 { return Err(Error::InvalidMemoryReference(mem_ref)) } GrowMemory(mem_ref) }, I32CONST => I32Const(VarInt32::deserialize(reader)?.into()), I64CONST => I64Const(VarInt64::deserialize(reader)?.into()), F32CONST => F32Const(Uint32::deserialize(reader)?.into()), F64CONST => F64Const(Uint64::deserialize(reader)?.into()), I32EQZ => I32Eqz, I32EQ => I32Eq, I32NE => I32Ne, I32LTS => I32LtS, I32LTU => I32LtU, I32GTS => I32GtS, I32GTU => I32GtU, I32LES => I32LeS, I32LEU => I32LeU, I32GES => I32GeS, I32GEU => I32GeU, I64EQZ => I64Eqz, I64EQ => I64Eq, I64NE => I64Ne, I64LTS => I64LtS, I64LTU => I64LtU, I64GTS => I64GtS, I64GTU => I64GtU, I64LES => I64LeS, I64LEU => I64LeU, I64GES => I64GeS, I64GEU => I64GeU, F32EQ => F32Eq, F32NE => F32Ne, F32LT => F32Lt, F32GT => F32Gt, F32LE => F32Le, F32GE => F32Ge, F64EQ => F64Eq, F64NE => F64Ne, F64LT => F64Lt, F64GT => F64Gt, F64LE => F64Le, F64GE => F64Ge, I32CLZ => I32Clz, I32CTZ => I32Ctz, I32POPCNT => I32Popcnt, I32ADD => I32Add, I32SUB => I32Sub, I32MUL => I32Mul, I32DIVS => I32DivS, I32DIVU => I32DivU, I32REMS => I32RemS, I32REMU => I32RemU, I32AND => I32And, I32OR => I32Or, I32XOR => I32Xor, I32SHL => I32Shl, I32SHRS => I32ShrS, I32SHRU => I32ShrU, I32ROTL => I32Rotl, I32ROTR => I32Rotr, I64CLZ => I64Clz, I64CTZ => I64Ctz, I64POPCNT => I64Popcnt, I64ADD => I64Add, I64SUB => I64Sub, I64MUL => I64Mul, I64DIVS => I64DivS, I64DIVU => I64DivU, I64REMS => I64RemS, I64REMU => I64RemU, I64AND => I64And, I64OR => I64Or, I64XOR => I64Xor, I64SHL => I64Shl, I64SHRS => I64ShrS, I64SHRU => I64ShrU, I64ROTL => I64Rotl, I64ROTR => I64Rotr, F32ABS => F32Abs, F32NEG => F32Neg, F32CEIL => F32Ceil, F32FLOOR => F32Floor, F32TRUNC => F32Trunc, F32NEAREST => F32Nearest, F32SQRT => F32Sqrt, F32ADD => F32Add, F32SUB => F32Sub, F32MUL => F32Mul, F32DIV => F32Div, F32MIN => F32Min, F32MAX => F32Max, F32COPYSIGN => F32Copysign, F64ABS => F64Abs, F64NEG => F64Neg, F64CEIL => F64Ceil, F64FLOOR => F64Floor, F64TRUNC => F64Trunc, F64NEAREST => F64Nearest, F64SQRT => F64Sqrt, F64ADD => F64Add, F64SUB => F64Sub, F64MUL => F64Mul, F64DIV => F64Div, F64MIN => F64Min, F64MAX => F64Max, F64COPYSIGN => F64Copysign, I32WRAPI64 => I32WrapI64, I32TRUNCSF32 => I32TruncSF32, I32TRUNCUF32 => I32TruncUF32, I32TRUNCSF64 => I32TruncSF64, I32TRUNCUF64 => I32TruncUF64, I64EXTENDSI32 => I64ExtendSI32, I64EXTENDUI32 => I64ExtendUI32, I64TRUNCSF32 => I64TruncSF32, I64TRUNCUF32 => I64TruncUF32, I64TRUNCSF64 => I64TruncSF64, I64TRUNCUF64 => I64TruncUF64, F32CONVERTSI32 => F32ConvertSI32, F32CONVERTUI32 => F32ConvertUI32, F32CONVERTSI64 => F32ConvertSI64, F32CONVERTUI64 => F32ConvertUI64, F32DEMOTEF64 => F32DemoteF64, F64CONVERTSI32 => F64ConvertSI32, F64CONVERTUI32 => F64ConvertUI32, F64CONVERTSI64 => F64ConvertSI64, F64CONVERTUI64 => F64ConvertUI64, F64PROMOTEF32 => F64PromoteF32, I32REINTERPRETF32 => I32ReinterpretF32, I64REINTERPRETF64 => I64ReinterpretF64, F32REINTERPRETI32 => F32ReinterpretI32, F64REINTERPRETI64 => F64ReinterpretI64, #[cfg(feature = "sign_ext")] I32_EXTEND8_S | I32_EXTEND16_S | I64_EXTEND8_S | I64_EXTEND16_S | I64_EXTEND32_S => match val { I32_EXTEND8_S => SignExt(SignExtInstruction::I32Extend8S), I32_EXTEND16_S => SignExt(SignExtInstruction::I32Extend16S), I64_EXTEND8_S => SignExt(SignExtInstruction::I64Extend8S), I64_EXTEND16_S => SignExt(SignExtInstruction::I64Extend16S), I64_EXTEND32_S => SignExt(SignExtInstruction::I64Extend32S), _ => return Err(Error::UnknownOpcode(val)), }, #[cfg(feature = "atomics")] atomics::ATOMIC_PREFIX => return deserialize_atomic(reader), #[cfg(feature = "simd")] simd::SIMD_PREFIX => return deserialize_simd(reader), #[cfg(feature = "bulk")] bulk::BULK_PREFIX => return deserialize_bulk(reader), _ => return Err(Error::UnknownOpcode(val)), }) } } #[cfg(feature = "atomics")] fn deserialize_atomic(reader: &mut R) -> Result { use self::{opcodes::atomics::*, AtomicsInstruction::*}; let val: u8 = Uint8::deserialize(reader)?.into(); let mem = MemArg::deserialize(reader)?; Ok(Instruction::Atomics(match val { ATOMIC_WAKE => AtomicWake(mem), I32_ATOMIC_WAIT => I32AtomicWait(mem), I64_ATOMIC_WAIT => I64AtomicWait(mem), I32_ATOMIC_LOAD => I32AtomicLoad(mem), I64_ATOMIC_LOAD => I64AtomicLoad(mem), I32_ATOMIC_LOAD8U => I32AtomicLoad8u(mem), I32_ATOMIC_LOAD16U => I32AtomicLoad16u(mem), I64_ATOMIC_LOAD8U => I64AtomicLoad8u(mem), I64_ATOMIC_LOAD16U => I64AtomicLoad16u(mem), I64_ATOMIC_LOAD32U => I64AtomicLoad32u(mem), I32_ATOMIC_STORE => I32AtomicStore(mem), I64_ATOMIC_STORE => I64AtomicStore(mem), I32_ATOMIC_STORE8U => I32AtomicStore8u(mem), I32_ATOMIC_STORE16U => I32AtomicStore16u(mem), I64_ATOMIC_STORE8U => I64AtomicStore8u(mem), I64_ATOMIC_STORE16U => I64AtomicStore16u(mem), I64_ATOMIC_STORE32U => I64AtomicStore32u(mem), I32_ATOMIC_RMW_ADD => I32AtomicRmwAdd(mem), I64_ATOMIC_RMW_ADD => I64AtomicRmwAdd(mem), I32_ATOMIC_RMW_ADD8U => I32AtomicRmwAdd8u(mem), I32_ATOMIC_RMW_ADD16U => I32AtomicRmwAdd16u(mem), I64_ATOMIC_RMW_ADD8U => I64AtomicRmwAdd8u(mem), I64_ATOMIC_RMW_ADD16U => I64AtomicRmwAdd16u(mem), I64_ATOMIC_RMW_ADD32U => I64AtomicRmwAdd32u(mem), I32_ATOMIC_RMW_SUB => I32AtomicRmwSub(mem), I64_ATOMIC_RMW_SUB => I64AtomicRmwSub(mem), I32_ATOMIC_RMW_SUB8U => I32AtomicRmwSub8u(mem), I32_ATOMIC_RMW_SUB16U => I32AtomicRmwSub16u(mem), I64_ATOMIC_RMW_SUB8U => I64AtomicRmwSub8u(mem), I64_ATOMIC_RMW_SUB16U => I64AtomicRmwSub16u(mem), I64_ATOMIC_RMW_SUB32U => I64AtomicRmwSub32u(mem), I32_ATOMIC_RMW_AND => I32AtomicRmwAnd(mem), I64_ATOMIC_RMW_AND => I64AtomicRmwAnd(mem), I32_ATOMIC_RMW_AND8U => I32AtomicRmwAnd8u(mem), I32_ATOMIC_RMW_AND16U => I32AtomicRmwAnd16u(mem), I64_ATOMIC_RMW_AND8U => I64AtomicRmwAnd8u(mem), I64_ATOMIC_RMW_AND16U => I64AtomicRmwAnd16u(mem), I64_ATOMIC_RMW_AND32U => I64AtomicRmwAnd32u(mem), I32_ATOMIC_RMW_OR => I32AtomicRmwOr(mem), I64_ATOMIC_RMW_OR => I64AtomicRmwOr(mem), I32_ATOMIC_RMW_OR8U => I32AtomicRmwOr8u(mem), I32_ATOMIC_RMW_OR16U => I32AtomicRmwOr16u(mem), I64_ATOMIC_RMW_OR8U => I64AtomicRmwOr8u(mem), I64_ATOMIC_RMW_OR16U => I64AtomicRmwOr16u(mem), I64_ATOMIC_RMW_OR32U => I64AtomicRmwOr32u(mem), I32_ATOMIC_RMW_XOR => I32AtomicRmwXor(mem), I64_ATOMIC_RMW_XOR => I64AtomicRmwXor(mem), I32_ATOMIC_RMW_XOR8U => I32AtomicRmwXor8u(mem), I32_ATOMIC_RMW_XOR16U => I32AtomicRmwXor16u(mem), I64_ATOMIC_RMW_XOR8U => I64AtomicRmwXor8u(mem), I64_ATOMIC_RMW_XOR16U => I64AtomicRmwXor16u(mem), I64_ATOMIC_RMW_XOR32U => I64AtomicRmwXor32u(mem), I32_ATOMIC_RMW_XCHG => I32AtomicRmwXchg(mem), I64_ATOMIC_RMW_XCHG => I64AtomicRmwXchg(mem), I32_ATOMIC_RMW_XCHG8U => I32AtomicRmwXchg8u(mem), I32_ATOMIC_RMW_XCHG16U => I32AtomicRmwXchg16u(mem), I64_ATOMIC_RMW_XCHG8U => I64AtomicRmwXchg8u(mem), I64_ATOMIC_RMW_XCHG16U => I64AtomicRmwXchg16u(mem), I64_ATOMIC_RMW_XCHG32U => I64AtomicRmwXchg32u(mem), I32_ATOMIC_RMW_CMPXCHG => I32AtomicRmwCmpxchg(mem), I64_ATOMIC_RMW_CMPXCHG => I64AtomicRmwCmpxchg(mem), I32_ATOMIC_RMW_CMPXCHG8U => I32AtomicRmwCmpxchg8u(mem), I32_ATOMIC_RMW_CMPXCHG16U => I32AtomicRmwCmpxchg16u(mem), I64_ATOMIC_RMW_CMPXCHG8U => I64AtomicRmwCmpxchg8u(mem), I64_ATOMIC_RMW_CMPXCHG16U => I64AtomicRmwCmpxchg16u(mem), I64_ATOMIC_RMW_CMPXCHG32U => I64AtomicRmwCmpxchg32u(mem), _ => return Err(Error::UnknownOpcode(val)), })) } #[cfg(feature = "simd")] fn deserialize_simd(reader: &mut R) -> Result { use self::{opcodes::simd::*, SimdInstruction::*}; let val = VarUint32::deserialize(reader)?.into(); Ok(Instruction::Simd(match val { V128_CONST => { let mut buf = [0; 16]; reader.read(&mut buf)?; V128Const(Box::new(buf)) }, V128_LOAD => V128Load(MemArg::deserialize(reader)?), V128_STORE => V128Store(MemArg::deserialize(reader)?), I8X16_SPLAT => I8x16Splat, I16X8_SPLAT => I16x8Splat, I32X4_SPLAT => I32x4Splat, I64X2_SPLAT => I64x2Splat, F32X4_SPLAT => F32x4Splat, F64X2_SPLAT => F64x2Splat, I8X16_EXTRACT_LANE_S => I8x16ExtractLaneS(Uint8::deserialize(reader)?.into()), I8X16_EXTRACT_LANE_U => I8x16ExtractLaneU(Uint8::deserialize(reader)?.into()), I16X8_EXTRACT_LANE_S => I16x8ExtractLaneS(Uint8::deserialize(reader)?.into()), I16X8_EXTRACT_LANE_U => I16x8ExtractLaneU(Uint8::deserialize(reader)?.into()), I32X4_EXTRACT_LANE => I32x4ExtractLane(Uint8::deserialize(reader)?.into()), I64X2_EXTRACT_LANE => I64x2ExtractLane(Uint8::deserialize(reader)?.into()), F32X4_EXTRACT_LANE => F32x4ExtractLane(Uint8::deserialize(reader)?.into()), F64X2_EXTRACT_LANE => F64x2ExtractLane(Uint8::deserialize(reader)?.into()), I8X16_REPLACE_LANE => I8x16ReplaceLane(Uint8::deserialize(reader)?.into()), I16X8_REPLACE_LANE => I16x8ReplaceLane(Uint8::deserialize(reader)?.into()), I32X4_REPLACE_LANE => I32x4ReplaceLane(Uint8::deserialize(reader)?.into()), I64X2_REPLACE_LANE => I64x2ReplaceLane(Uint8::deserialize(reader)?.into()), F32X4_REPLACE_LANE => F32x4ReplaceLane(Uint8::deserialize(reader)?.into()), F64X2_REPLACE_LANE => F64x2ReplaceLane(Uint8::deserialize(reader)?.into()), V8X16_SHUFFLE => { let mut buf = [0; 16]; reader.read(&mut buf)?; V8x16Shuffle(Box::new(buf)) }, I8X16_ADD => I8x16Add, I16X8_ADD => I16x8Add, I32X4_ADD => I32x4Add, I64X2_ADD => I64x2Add, I8X16_SUB => I8x16Sub, I16X8_SUB => I16x8Sub, I32X4_SUB => I32x4Sub, I64X2_SUB => I64x2Sub, I8X16_MUL => I8x16Mul, I16X8_MUL => I16x8Mul, I32X4_MUL => I32x4Mul, // I64X2_MUL => I64x2Mul, I8X16_NEG => I8x16Neg, I16X8_NEG => I16x8Neg, I32X4_NEG => I32x4Neg, I64X2_NEG => I64x2Neg, I8X16_ADD_SATURATE_S => I8x16AddSaturateS, I8X16_ADD_SATURATE_U => I8x16AddSaturateU, I16X8_ADD_SATURATE_S => I16x8AddSaturateS, I16X8_ADD_SATURATE_U => I16x8AddSaturateU, I8X16_SUB_SATURATE_S => I8x16SubSaturateS, I8X16_SUB_SATURATE_U => I8x16SubSaturateU, I16X8_SUB_SATURATE_S => I16x8SubSaturateS, I16X8_SUB_SATURATE_U => I16x8SubSaturateU, I8X16_SHL => I8x16Shl, I16X8_SHL => I16x8Shl, I32X4_SHL => I32x4Shl, I64X2_SHL => I64x2Shl, I8X16_SHR_S => I8x16ShrS, I8X16_SHR_U => I8x16ShrU, I16X8_SHR_S => I16x8ShrS, I16X8_SHR_U => I16x8ShrU, I32X4_SHR_S => I32x4ShrS, I32X4_SHR_U => I32x4ShrU, I64X2_SHR_S => I64x2ShrS, I64X2_SHR_U => I64x2ShrU, V128_AND => V128And, V128_OR => V128Or, V128_XOR => V128Xor, V128_NOT => V128Not, V128_BITSELECT => V128Bitselect, I8X16_ANY_TRUE => I8x16AnyTrue, I16X8_ANY_TRUE => I16x8AnyTrue, I32X4_ANY_TRUE => I32x4AnyTrue, I64X2_ANY_TRUE => I64x2AnyTrue, I8X16_ALL_TRUE => I8x16AllTrue, I16X8_ALL_TRUE => I16x8AllTrue, I32X4_ALL_TRUE => I32x4AllTrue, I64X2_ALL_TRUE => I64x2AllTrue, I8X16_EQ => I8x16Eq, I16X8_EQ => I16x8Eq, I32X4_EQ => I32x4Eq, // I64X2_EQ => I64x2Eq, F32X4_EQ => F32x4Eq, F64X2_EQ => F64x2Eq, I8X16_NE => I8x16Ne, I16X8_NE => I16x8Ne, I32X4_NE => I32x4Ne, // I64X2_NE => I64x2Ne, F32X4_NE => F32x4Ne, F64X2_NE => F64x2Ne, I8X16_LT_S => I8x16LtS, I8X16_LT_U => I8x16LtU, I16X8_LT_S => I16x8LtS, I16X8_LT_U => I16x8LtU, I32X4_LT_S => I32x4LtS, I32X4_LT_U => I32x4LtU, // I64X2_LT_S => I64x2LtS, // I64X2_LT_U => I64x2LtU, F32X4_LT => F32x4Lt, F64X2_LT => F64x2Lt, I8X16_LE_S => I8x16LeS, I8X16_LE_U => I8x16LeU, I16X8_LE_S => I16x8LeS, I16X8_LE_U => I16x8LeU, I32X4_LE_S => I32x4LeS, I32X4_LE_U => I32x4LeU, // I64X2_LE_S => I64x2LeS, // I64X2_LE_U => I64x2LeU, F32X4_LE => F32x4Le, F64X2_LE => F64x2Le, I8X16_GT_S => I8x16GtS, I8X16_GT_U => I8x16GtU, I16X8_GT_S => I16x8GtS, I16X8_GT_U => I16x8GtU, I32X4_GT_S => I32x4GtS, I32X4_GT_U => I32x4GtU, // I64X2_GT_S => I64x2GtS, // I64X2_GT_U => I64x2GtU, F32X4_GT => F32x4Gt, F64X2_GT => F64x2Gt, I8X16_GE_S => I8x16GeS, I8X16_GE_U => I8x16GeU, I16X8_GE_S => I16x8GeS, I16X8_GE_U => I16x8GeU, I32X4_GE_S => I32x4GeS, I32X4_GE_U => I32x4GeU, // I64X2_GE_S => I64x2GeS, // I64X2_GE_U => I64x2GeU, F32X4_GE => F32x4Ge, F64X2_GE => F64x2Ge, F32X4_NEG => F32x4Neg, F64X2_NEG => F64x2Neg, F32X4_ABS => F32x4Abs, F64X2_ABS => F64x2Abs, F32X4_MIN => F32x4Min, F64X2_MIN => F64x2Min, F32X4_MAX => F32x4Max, F64X2_MAX => F64x2Max, F32X4_ADD => F32x4Add, F64X2_ADD => F64x2Add, F32X4_SUB => F32x4Sub, F64X2_SUB => F64x2Sub, F32X4_DIV => F32x4Div, F64X2_DIV => F64x2Div, F32X4_MUL => F32x4Mul, F64X2_MUL => F64x2Mul, F32X4_SQRT => F32x4Sqrt, F64X2_SQRT => F64x2Sqrt, F32X4_CONVERT_S_I32X4 => F32x4ConvertSI32x4, F32X4_CONVERT_U_I32X4 => F32x4ConvertUI32x4, F64X2_CONVERT_S_I64X2 => F64x2ConvertSI64x2, F64X2_CONVERT_U_I64X2 => F64x2ConvertUI64x2, I32X4_TRUNC_S_F32X4_SAT => I32x4TruncSF32x4Sat, I32X4_TRUNC_U_F32X4_SAT => I32x4TruncUF32x4Sat, I64X2_TRUNC_S_F64X2_SAT => I64x2TruncSF64x2Sat, I64X2_TRUNC_U_F64X2_SAT => I64x2TruncUF64x2Sat, _ => return Err(Error::UnknownSimdOpcode(val)), })) } #[cfg(feature = "bulk")] fn deserialize_bulk(reader: &mut R) -> Result { use self::{opcodes::bulk::*, BulkInstruction::*}; let val: u8 = Uint8::deserialize(reader)?.into(); Ok(Instruction::Bulk(match val { MEMORY_INIT => { if u8::from(Uint8::deserialize(reader)?) != 0 { return Err(Error::UnknownOpcode(val)) } MemoryInit(VarUint32::deserialize(reader)?.into()) }, MEMORY_DROP => MemoryDrop(VarUint32::deserialize(reader)?.into()), MEMORY_FILL => { if u8::from(Uint8::deserialize(reader)?) != 0 { return Err(Error::UnknownOpcode(val)) } MemoryFill }, MEMORY_COPY => { if u8::from(Uint8::deserialize(reader)?) != 0 { return Err(Error::UnknownOpcode(val)) } MemoryCopy }, TABLE_INIT => { if u8::from(Uint8::deserialize(reader)?) != 0 { return Err(Error::UnknownOpcode(val)) } TableInit(VarUint32::deserialize(reader)?.into()) }, TABLE_DROP => TableDrop(VarUint32::deserialize(reader)?.into()), TABLE_COPY => { if u8::from(Uint8::deserialize(reader)?) != 0 { return Err(Error::UnknownOpcode(val)) } TableCopy }, _ => return Err(Error::UnknownOpcode(val)), })) } #[cfg(any(feature = "simd", feature = "atomics"))] impl Deserialize for MemArg { type Error = Error; fn deserialize(reader: &mut R) -> Result { let align = Uint8::deserialize(reader)?; let offset = VarUint32::deserialize(reader)?; Ok(MemArg { align: align.into(), offset: offset.into() }) } } macro_rules! op { ($writer: expr, $byte: expr) => {{ let b: u8 = $byte; $writer.write(&[b])?; }}; ($writer: expr, $byte: expr, $s: block) => {{ op!($writer, $byte); $s; }}; } #[cfg(feature = "atomics")] macro_rules! atomic { ($writer: expr, $byte: expr, $mem:expr) => {{ $writer.write(&[ATOMIC_PREFIX, $byte])?; MemArg::serialize($mem, $writer)?; }}; } #[cfg(feature = "simd")] macro_rules! simd { ($writer: expr, $byte: expr, $other:expr) => {{ $writer.write(&[SIMD_PREFIX])?; VarUint32::from($byte).serialize($writer)?; $other; }}; } #[cfg(feature = "bulk")] macro_rules! bulk { ($writer: expr, $byte: expr) => {{ $writer.write(&[BULK_PREFIX, $byte])?; }}; ($writer: expr, $byte: expr, $remaining:expr) => {{ bulk!($writer, $byte); $remaining; }}; } impl Serialize for Instruction { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { use self::{opcodes::*, Instruction::*}; match self { Unreachable => op!(writer, UNREACHABLE), Nop => op!(writer, NOP), Block(block_type) => op!(writer, BLOCK, { block_type.serialize(writer)?; }), Loop(block_type) => op!(writer, LOOP, { block_type.serialize(writer)?; }), If(block_type) => op!(writer, IF, { block_type.serialize(writer)?; }), Else => op!(writer, ELSE), End => op!(writer, END), Br(idx) => op!(writer, BR, { VarUint32::from(idx).serialize(writer)?; }), BrIf(idx) => op!(writer, BRIF, { VarUint32::from(idx).serialize(writer)?; }), BrTable(ref table) => op!(writer, BRTABLE, { let list_writer = CountedListWriter::( table.table.len(), table.table.iter().map(|x| VarUint32::from(*x)), ); list_writer.serialize(writer)?; VarUint32::from(table.default).serialize(writer)?; }), Return => op!(writer, RETURN), Call(index) => op!(writer, CALL, { VarUint32::from(index).serialize(writer)?; }), CallIndirect(index, reserved) => op!(writer, CALLINDIRECT, { VarUint32::from(index).serialize(writer)?; Uint8::from(reserved).serialize(writer)?; }), Drop => op!(writer, DROP), Select => op!(writer, SELECT), GetLocal(index) => op!(writer, GETLOCAL, { VarUint32::from(index).serialize(writer)?; }), SetLocal(index) => op!(writer, SETLOCAL, { VarUint32::from(index).serialize(writer)?; }), TeeLocal(index) => op!(writer, TEELOCAL, { VarUint32::from(index).serialize(writer)?; }), GetGlobal(index) => op!(writer, GETGLOBAL, { VarUint32::from(index).serialize(writer)?; }), SetGlobal(index) => op!(writer, SETGLOBAL, { VarUint32::from(index).serialize(writer)?; }), I32Load(flags, offset) => op!(writer, I32LOAD, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Load(flags, offset) => op!(writer, I64LOAD, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), F32Load(flags, offset) => op!(writer, F32LOAD, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), F64Load(flags, offset) => op!(writer, F64LOAD, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I32Load8S(flags, offset) => op!(writer, I32LOAD8S, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I32Load8U(flags, offset) => op!(writer, I32LOAD8U, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I32Load16S(flags, offset) => op!(writer, I32LOAD16S, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I32Load16U(flags, offset) => op!(writer, I32LOAD16U, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Load8S(flags, offset) => op!(writer, I64LOAD8S, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Load8U(flags, offset) => op!(writer, I64LOAD8U, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Load16S(flags, offset) => op!(writer, I64LOAD16S, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Load16U(flags, offset) => op!(writer, I64LOAD16U, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Load32S(flags, offset) => op!(writer, I64LOAD32S, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Load32U(flags, offset) => op!(writer, I64LOAD32U, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I32Store(flags, offset) => op!(writer, I32STORE, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Store(flags, offset) => op!(writer, I64STORE, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), F32Store(flags, offset) => op!(writer, F32STORE, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), F64Store(flags, offset) => op!(writer, F64STORE, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I32Store8(flags, offset) => op!(writer, I32STORE8, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I32Store16(flags, offset) => op!(writer, I32STORE16, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Store8(flags, offset) => op!(writer, I64STORE8, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Store16(flags, offset) => op!(writer, I64STORE16, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), I64Store32(flags, offset) => op!(writer, I64STORE32, { VarUint32::from(flags).serialize(writer)?; VarUint32::from(offset).serialize(writer)?; }), CurrentMemory(flag) => op!(writer, CURRENTMEMORY, { Uint8::from(flag).serialize(writer)?; }), GrowMemory(flag) => op!(writer, GROWMEMORY, { Uint8::from(flag).serialize(writer)?; }), I32Const(def) => op!(writer, I32CONST, { VarInt32::from(def).serialize(writer)?; }), I64Const(def) => op!(writer, I64CONST, { VarInt64::from(def).serialize(writer)?; }), F32Const(def) => op!(writer, F32CONST, { Uint32::from(def).serialize(writer)?; }), F64Const(def) => op!(writer, F64CONST, { Uint64::from(def).serialize(writer)?; }), I32Eqz => op!(writer, I32EQZ), I32Eq => op!(writer, I32EQ), I32Ne => op!(writer, I32NE), I32LtS => op!(writer, I32LTS), I32LtU => op!(writer, I32LTU), I32GtS => op!(writer, I32GTS), I32GtU => op!(writer, I32GTU), I32LeS => op!(writer, I32LES), I32LeU => op!(writer, I32LEU), I32GeS => op!(writer, I32GES), I32GeU => op!(writer, I32GEU), I64Eqz => op!(writer, I64EQZ), I64Eq => op!(writer, I64EQ), I64Ne => op!(writer, I64NE), I64LtS => op!(writer, I64LTS), I64LtU => op!(writer, I64LTU), I64GtS => op!(writer, I64GTS), I64GtU => op!(writer, I64GTU), I64LeS => op!(writer, I64LES), I64LeU => op!(writer, I64LEU), I64GeS => op!(writer, I64GES), I64GeU => op!(writer, I64GEU), F32Eq => op!(writer, F32EQ), F32Ne => op!(writer, F32NE), F32Lt => op!(writer, F32LT), F32Gt => op!(writer, F32GT), F32Le => op!(writer, F32LE), F32Ge => op!(writer, F32GE), F64Eq => op!(writer, F64EQ), F64Ne => op!(writer, F64NE), F64Lt => op!(writer, F64LT), F64Gt => op!(writer, F64GT), F64Le => op!(writer, F64LE), F64Ge => op!(writer, F64GE), I32Clz => op!(writer, I32CLZ), I32Ctz => op!(writer, I32CTZ), I32Popcnt => op!(writer, I32POPCNT), I32Add => op!(writer, I32ADD), I32Sub => op!(writer, I32SUB), I32Mul => op!(writer, I32MUL), I32DivS => op!(writer, I32DIVS), I32DivU => op!(writer, I32DIVU), I32RemS => op!(writer, I32REMS), I32RemU => op!(writer, I32REMU), I32And => op!(writer, I32AND), I32Or => op!(writer, I32OR), I32Xor => op!(writer, I32XOR), I32Shl => op!(writer, I32SHL), I32ShrS => op!(writer, I32SHRS), I32ShrU => op!(writer, I32SHRU), I32Rotl => op!(writer, I32ROTL), I32Rotr => op!(writer, I32ROTR), I64Clz => op!(writer, I64CLZ), I64Ctz => op!(writer, I64CTZ), I64Popcnt => op!(writer, I64POPCNT), I64Add => op!(writer, I64ADD), I64Sub => op!(writer, I64SUB), I64Mul => op!(writer, I64MUL), I64DivS => op!(writer, I64DIVS), I64DivU => op!(writer, I64DIVU), I64RemS => op!(writer, I64REMS), I64RemU => op!(writer, I64REMU), I64And => op!(writer, I64AND), I64Or => op!(writer, I64OR), I64Xor => op!(writer, I64XOR), I64Shl => op!(writer, I64SHL), I64ShrS => op!(writer, I64SHRS), I64ShrU => op!(writer, I64SHRU), I64Rotl => op!(writer, I64ROTL), I64Rotr => op!(writer, I64ROTR), F32Abs => op!(writer, F32ABS), F32Neg => op!(writer, F32NEG), F32Ceil => op!(writer, F32CEIL), F32Floor => op!(writer, F32FLOOR), F32Trunc => op!(writer, F32TRUNC), F32Nearest => op!(writer, F32NEAREST), F32Sqrt => op!(writer, F32SQRT), F32Add => op!(writer, F32ADD), F32Sub => op!(writer, F32SUB), F32Mul => op!(writer, F32MUL), F32Div => op!(writer, F32DIV), F32Min => op!(writer, F32MIN), F32Max => op!(writer, F32MAX), F32Copysign => op!(writer, F32COPYSIGN), F64Abs => op!(writer, F64ABS), F64Neg => op!(writer, F64NEG), F64Ceil => op!(writer, F64CEIL), F64Floor => op!(writer, F64FLOOR), F64Trunc => op!(writer, F64TRUNC), F64Nearest => op!(writer, F64NEAREST), F64Sqrt => op!(writer, F64SQRT), F64Add => op!(writer, F64ADD), F64Sub => op!(writer, F64SUB), F64Mul => op!(writer, F64MUL), F64Div => op!(writer, F64DIV), F64Min => op!(writer, F64MIN), F64Max => op!(writer, F64MAX), F64Copysign => op!(writer, F64COPYSIGN), I32WrapI64 => op!(writer, I32WRAPI64), I32TruncSF32 => op!(writer, I32TRUNCSF32), I32TruncUF32 => op!(writer, I32TRUNCUF32), I32TruncSF64 => op!(writer, I32TRUNCSF64), I32TruncUF64 => op!(writer, I32TRUNCUF64), I64ExtendSI32 => op!(writer, I64EXTENDSI32), I64ExtendUI32 => op!(writer, I64EXTENDUI32), I64TruncSF32 => op!(writer, I64TRUNCSF32), I64TruncUF32 => op!(writer, I64TRUNCUF32), I64TruncSF64 => op!(writer, I64TRUNCSF64), I64TruncUF64 => op!(writer, I64TRUNCUF64), F32ConvertSI32 => op!(writer, F32CONVERTSI32), F32ConvertUI32 => op!(writer, F32CONVERTUI32), F32ConvertSI64 => op!(writer, F32CONVERTSI64), F32ConvertUI64 => op!(writer, F32CONVERTUI64), F32DemoteF64 => op!(writer, F32DEMOTEF64), F64ConvertSI32 => op!(writer, F64CONVERTSI32), F64ConvertUI32 => op!(writer, F64CONVERTUI32), F64ConvertSI64 => op!(writer, F64CONVERTSI64), F64ConvertUI64 => op!(writer, F64CONVERTUI64), F64PromoteF32 => op!(writer, F64PROMOTEF32), I32ReinterpretF32 => op!(writer, I32REINTERPRETF32), I64ReinterpretF64 => op!(writer, I64REINTERPRETF64), F32ReinterpretI32 => op!(writer, F32REINTERPRETI32), F64ReinterpretI64 => op!(writer, F64REINTERPRETI64), #[cfg(feature = "sign_ext")] SignExt(ref a) => match *a { SignExtInstruction::I32Extend8S => op!(writer, sign_ext::I32_EXTEND8_S), SignExtInstruction::I32Extend16S => op!(writer, sign_ext::I32_EXTEND16_S), SignExtInstruction::I64Extend8S => op!(writer, sign_ext::I64_EXTEND8_S), SignExtInstruction::I64Extend16S => op!(writer, sign_ext::I64_EXTEND16_S), SignExtInstruction::I64Extend32S => op!(writer, sign_ext::I64_EXTEND32_S), }, #[cfg(feature = "atomics")] Atomics(a) => return a.serialize(writer), #[cfg(feature = "simd")] Simd(a) => return a.serialize(writer), #[cfg(feature = "bulk")] Bulk(a) => return a.serialize(writer), } Ok(()) } } #[cfg(feature = "atomics")] impl Serialize for AtomicsInstruction { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { use self::{opcodes::atomics::*, AtomicsInstruction::*}; match self { AtomicWake(m) => atomic!(writer, ATOMIC_WAKE, m), I32AtomicWait(m) => atomic!(writer, I32_ATOMIC_WAIT, m), I64AtomicWait(m) => atomic!(writer, I64_ATOMIC_WAIT, m), I32AtomicLoad(m) => atomic!(writer, I32_ATOMIC_LOAD, m), I64AtomicLoad(m) => atomic!(writer, I64_ATOMIC_LOAD, m), I32AtomicLoad8u(m) => atomic!(writer, I32_ATOMIC_LOAD8U, m), I32AtomicLoad16u(m) => atomic!(writer, I32_ATOMIC_LOAD16U, m), I64AtomicLoad8u(m) => atomic!(writer, I64_ATOMIC_LOAD8U, m), I64AtomicLoad16u(m) => atomic!(writer, I64_ATOMIC_LOAD16U, m), I64AtomicLoad32u(m) => atomic!(writer, I64_ATOMIC_LOAD32U, m), I32AtomicStore(m) => atomic!(writer, I32_ATOMIC_STORE, m), I64AtomicStore(m) => atomic!(writer, I64_ATOMIC_STORE, m), I32AtomicStore8u(m) => atomic!(writer, I32_ATOMIC_STORE8U, m), I32AtomicStore16u(m) => atomic!(writer, I32_ATOMIC_STORE16U, m), I64AtomicStore8u(m) => atomic!(writer, I64_ATOMIC_STORE8U, m), I64AtomicStore16u(m) => atomic!(writer, I64_ATOMIC_STORE16U, m), I64AtomicStore32u(m) => atomic!(writer, I64_ATOMIC_STORE32U, m), I32AtomicRmwAdd(m) => atomic!(writer, I32_ATOMIC_RMW_ADD, m), I64AtomicRmwAdd(m) => atomic!(writer, I64_ATOMIC_RMW_ADD, m), I32AtomicRmwAdd8u(m) => atomic!(writer, I32_ATOMIC_RMW_ADD8U, m), I32AtomicRmwAdd16u(m) => atomic!(writer, I32_ATOMIC_RMW_ADD16U, m), I64AtomicRmwAdd8u(m) => atomic!(writer, I64_ATOMIC_RMW_ADD8U, m), I64AtomicRmwAdd16u(m) => atomic!(writer, I64_ATOMIC_RMW_ADD16U, m), I64AtomicRmwAdd32u(m) => atomic!(writer, I64_ATOMIC_RMW_ADD32U, m), I32AtomicRmwSub(m) => atomic!(writer, I32_ATOMIC_RMW_SUB, m), I64AtomicRmwSub(m) => atomic!(writer, I64_ATOMIC_RMW_SUB, m), I32AtomicRmwSub8u(m) => atomic!(writer, I32_ATOMIC_RMW_SUB8U, m), I32AtomicRmwSub16u(m) => atomic!(writer, I32_ATOMIC_RMW_SUB16U, m), I64AtomicRmwSub8u(m) => atomic!(writer, I64_ATOMIC_RMW_SUB8U, m), I64AtomicRmwSub16u(m) => atomic!(writer, I64_ATOMIC_RMW_SUB16U, m), I64AtomicRmwSub32u(m) => atomic!(writer, I64_ATOMIC_RMW_SUB32U, m), I32AtomicRmwAnd(m) => atomic!(writer, I32_ATOMIC_RMW_AND, m), I64AtomicRmwAnd(m) => atomic!(writer, I64_ATOMIC_RMW_AND, m), I32AtomicRmwAnd8u(m) => atomic!(writer, I32_ATOMIC_RMW_AND8U, m), I32AtomicRmwAnd16u(m) => atomic!(writer, I32_ATOMIC_RMW_AND16U, m), I64AtomicRmwAnd8u(m) => atomic!(writer, I64_ATOMIC_RMW_AND8U, m), I64AtomicRmwAnd16u(m) => atomic!(writer, I64_ATOMIC_RMW_AND16U, m), I64AtomicRmwAnd32u(m) => atomic!(writer, I64_ATOMIC_RMW_AND32U, m), I32AtomicRmwOr(m) => atomic!(writer, I32_ATOMIC_RMW_OR, m), I64AtomicRmwOr(m) => atomic!(writer, I64_ATOMIC_RMW_OR, m), I32AtomicRmwOr8u(m) => atomic!(writer, I32_ATOMIC_RMW_OR8U, m), I32AtomicRmwOr16u(m) => atomic!(writer, I32_ATOMIC_RMW_OR16U, m), I64AtomicRmwOr8u(m) => atomic!(writer, I64_ATOMIC_RMW_OR8U, m), I64AtomicRmwOr16u(m) => atomic!(writer, I64_ATOMIC_RMW_OR16U, m), I64AtomicRmwOr32u(m) => atomic!(writer, I64_ATOMIC_RMW_OR32U, m), I32AtomicRmwXor(m) => atomic!(writer, I32_ATOMIC_RMW_XOR, m), I64AtomicRmwXor(m) => atomic!(writer, I64_ATOMIC_RMW_XOR, m), I32AtomicRmwXor8u(m) => atomic!(writer, I32_ATOMIC_RMW_XOR8U, m), I32AtomicRmwXor16u(m) => atomic!(writer, I32_ATOMIC_RMW_XOR16U, m), I64AtomicRmwXor8u(m) => atomic!(writer, I64_ATOMIC_RMW_XOR8U, m), I64AtomicRmwXor16u(m) => atomic!(writer, I64_ATOMIC_RMW_XOR16U, m), I64AtomicRmwXor32u(m) => atomic!(writer, I64_ATOMIC_RMW_XOR32U, m), I32AtomicRmwXchg(m) => atomic!(writer, I32_ATOMIC_RMW_XCHG, m), I64AtomicRmwXchg(m) => atomic!(writer, I64_ATOMIC_RMW_XCHG, m), I32AtomicRmwXchg8u(m) => atomic!(writer, I32_ATOMIC_RMW_XCHG8U, m), I32AtomicRmwXchg16u(m) => atomic!(writer, I32_ATOMIC_RMW_XCHG16U, m), I64AtomicRmwXchg8u(m) => atomic!(writer, I64_ATOMIC_RMW_XCHG8U, m), I64AtomicRmwXchg16u(m) => atomic!(writer, I64_ATOMIC_RMW_XCHG16U, m), I64AtomicRmwXchg32u(m) => atomic!(writer, I64_ATOMIC_RMW_XCHG32U, m), I32AtomicRmwCmpxchg(m) => atomic!(writer, I32_ATOMIC_RMW_CMPXCHG, m), I64AtomicRmwCmpxchg(m) => atomic!(writer, I64_ATOMIC_RMW_CMPXCHG, m), I32AtomicRmwCmpxchg8u(m) => atomic!(writer, I32_ATOMIC_RMW_CMPXCHG8U, m), I32AtomicRmwCmpxchg16u(m) => atomic!(writer, I32_ATOMIC_RMW_CMPXCHG16U, m), I64AtomicRmwCmpxchg8u(m) => atomic!(writer, I64_ATOMIC_RMW_CMPXCHG8U, m), I64AtomicRmwCmpxchg16u(m) => atomic!(writer, I64_ATOMIC_RMW_CMPXCHG16U, m), I64AtomicRmwCmpxchg32u(m) => atomic!(writer, I64_ATOMIC_RMW_CMPXCHG32U, m), } Ok(()) } } #[cfg(feature = "simd")] impl Serialize for SimdInstruction { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { use self::{opcodes::simd::*, SimdInstruction::*}; match self { V128Const(ref c) => simd!(writer, V128_CONST, writer.write(&c[..])?), V128Load(m) => simd!(writer, V128_LOAD, MemArg::serialize(m, writer)?), V128Store(m) => simd!(writer, V128_STORE, MemArg::serialize(m, writer)?), I8x16Splat => simd!(writer, I8X16_SPLAT, {}), I16x8Splat => simd!(writer, I16X8_SPLAT, {}), I32x4Splat => simd!(writer, I32X4_SPLAT, {}), I64x2Splat => simd!(writer, I64X2_SPLAT, {}), F32x4Splat => simd!(writer, F32X4_SPLAT, {}), F64x2Splat => simd!(writer, F64X2_SPLAT, {}), I8x16ExtractLaneS(i) => simd!(writer, I8X16_EXTRACT_LANE_S, writer.write(&[i])?), I8x16ExtractLaneU(i) => simd!(writer, I8X16_EXTRACT_LANE_U, writer.write(&[i])?), I16x8ExtractLaneS(i) => simd!(writer, I16X8_EXTRACT_LANE_S, writer.write(&[i])?), I16x8ExtractLaneU(i) => simd!(writer, I16X8_EXTRACT_LANE_U, writer.write(&[i])?), I32x4ExtractLane(i) => simd!(writer, I32X4_EXTRACT_LANE, writer.write(&[i])?), I64x2ExtractLane(i) => simd!(writer, I64X2_EXTRACT_LANE, writer.write(&[i])?), F32x4ExtractLane(i) => simd!(writer, F32X4_EXTRACT_LANE, writer.write(&[i])?), F64x2ExtractLane(i) => simd!(writer, F64X2_EXTRACT_LANE, writer.write(&[i])?), I8x16ReplaceLane(i) => simd!(writer, I8X16_REPLACE_LANE, writer.write(&[i])?), I16x8ReplaceLane(i) => simd!(writer, I16X8_REPLACE_LANE, writer.write(&[i])?), I32x4ReplaceLane(i) => simd!(writer, I32X4_REPLACE_LANE, writer.write(&[i])?), I64x2ReplaceLane(i) => simd!(writer, I64X2_REPLACE_LANE, writer.write(&[i])?), F32x4ReplaceLane(i) => simd!(writer, F32X4_REPLACE_LANE, writer.write(&[i])?), F64x2ReplaceLane(i) => simd!(writer, F64X2_REPLACE_LANE, writer.write(&[i])?), V8x16Shuffle(ref i) => simd!(writer, V8X16_SHUFFLE, writer.write(&i[..])?), I8x16Add => simd!(writer, I8X16_ADD, {}), I16x8Add => simd!(writer, I16X8_ADD, {}), I32x4Add => simd!(writer, I32X4_ADD, {}), I64x2Add => simd!(writer, I64X2_ADD, {}), I8x16Sub => simd!(writer, I8X16_SUB, {}), I16x8Sub => simd!(writer, I16X8_SUB, {}), I32x4Sub => simd!(writer, I32X4_SUB, {}), I64x2Sub => simd!(writer, I64X2_SUB, {}), I8x16Mul => simd!(writer, I8X16_MUL, {}), I16x8Mul => simd!(writer, I16X8_MUL, {}), I32x4Mul => simd!(writer, I32X4_MUL, {}), // I64x2Mul => simd!(writer, I64X2_MUL, {}), I8x16Neg => simd!(writer, I8X16_NEG, {}), I16x8Neg => simd!(writer, I16X8_NEG, {}), I32x4Neg => simd!(writer, I32X4_NEG, {}), I64x2Neg => simd!(writer, I64X2_NEG, {}), I8x16AddSaturateS => simd!(writer, I8X16_ADD_SATURATE_S, {}), I8x16AddSaturateU => simd!(writer, I8X16_ADD_SATURATE_U, {}), I16x8AddSaturateS => simd!(writer, I16X8_ADD_SATURATE_S, {}), I16x8AddSaturateU => simd!(writer, I16X8_ADD_SATURATE_U, {}), I8x16SubSaturateS => simd!(writer, I8X16_SUB_SATURATE_S, {}), I8x16SubSaturateU => simd!(writer, I8X16_SUB_SATURATE_U, {}), I16x8SubSaturateS => simd!(writer, I16X8_SUB_SATURATE_S, {}), I16x8SubSaturateU => simd!(writer, I16X8_SUB_SATURATE_U, {}), I8x16Shl => simd!(writer, I8X16_SHL, {}), I16x8Shl => simd!(writer, I16X8_SHL, {}), I32x4Shl => simd!(writer, I32X4_SHL, {}), I64x2Shl => simd!(writer, I64X2_SHL, {}), I8x16ShrS => simd!(writer, I8X16_SHR_S, {}), I8x16ShrU => simd!(writer, I8X16_SHR_U, {}), I16x8ShrS => simd!(writer, I16X8_SHR_S, {}), I16x8ShrU => simd!(writer, I16X8_SHR_U, {}), I32x4ShrU => simd!(writer, I32X4_SHR_U, {}), I32x4ShrS => simd!(writer, I32X4_SHR_S, {}), I64x2ShrU => simd!(writer, I64X2_SHR_U, {}), I64x2ShrS => simd!(writer, I64X2_SHR_S, {}), V128And => simd!(writer, V128_AND, {}), V128Or => simd!(writer, V128_OR, {}), V128Xor => simd!(writer, V128_XOR, {}), V128Not => simd!(writer, V128_NOT, {}), V128Bitselect => simd!(writer, V128_BITSELECT, {}), I8x16AnyTrue => simd!(writer, I8X16_ANY_TRUE, {}), I16x8AnyTrue => simd!(writer, I16X8_ANY_TRUE, {}), I32x4AnyTrue => simd!(writer, I32X4_ANY_TRUE, {}), I64x2AnyTrue => simd!(writer, I64X2_ANY_TRUE, {}), I8x16AllTrue => simd!(writer, I8X16_ALL_TRUE, {}), I16x8AllTrue => simd!(writer, I16X8_ALL_TRUE, {}), I32x4AllTrue => simd!(writer, I32X4_ALL_TRUE, {}), I64x2AllTrue => simd!(writer, I64X2_ALL_TRUE, {}), I8x16Eq => simd!(writer, I8X16_EQ, {}), I16x8Eq => simd!(writer, I16X8_EQ, {}), I32x4Eq => simd!(writer, I32X4_EQ, {}), // I64x2Eq => simd!(writer, I64X2_EQ, {}), F32x4Eq => simd!(writer, F32X4_EQ, {}), F64x2Eq => simd!(writer, F64X2_EQ, {}), I8x16Ne => simd!(writer, I8X16_NE, {}), I16x8Ne => simd!(writer, I16X8_NE, {}), I32x4Ne => simd!(writer, I32X4_NE, {}), // I64x2Ne => simd!(writer, I64X2_NE, {}), F32x4Ne => simd!(writer, F32X4_NE, {}), F64x2Ne => simd!(writer, F64X2_NE, {}), I8x16LtS => simd!(writer, I8X16_LT_S, {}), I8x16LtU => simd!(writer, I8X16_LT_U, {}), I16x8LtS => simd!(writer, I16X8_LT_S, {}), I16x8LtU => simd!(writer, I16X8_LT_U, {}), I32x4LtS => simd!(writer, I32X4_LT_S, {}), I32x4LtU => simd!(writer, I32X4_LT_U, {}), // I64x2LtS => simd!(writer, I64X2_LT_S, {}), // I64x2LtU => simd!(writer, I64X2_LT_U, {}), F32x4Lt => simd!(writer, F32X4_LT, {}), F64x2Lt => simd!(writer, F64X2_LT, {}), I8x16LeS => simd!(writer, I8X16_LE_S, {}), I8x16LeU => simd!(writer, I8X16_LE_U, {}), I16x8LeS => simd!(writer, I16X8_LE_S, {}), I16x8LeU => simd!(writer, I16X8_LE_U, {}), I32x4LeS => simd!(writer, I32X4_LE_S, {}), I32x4LeU => simd!(writer, I32X4_LE_U, {}), // I64x2LeS => simd!(writer, I64X2_LE_S, {}), // I64x2LeU => simd!(writer, I64X2_LE_U, {}), F32x4Le => simd!(writer, F32X4_LE, {}), F64x2Le => simd!(writer, F64X2_LE, {}), I8x16GtS => simd!(writer, I8X16_GT_S, {}), I8x16GtU => simd!(writer, I8X16_GT_U, {}), I16x8GtS => simd!(writer, I16X8_GT_S, {}), I16x8GtU => simd!(writer, I16X8_GT_U, {}), I32x4GtS => simd!(writer, I32X4_GT_S, {}), I32x4GtU => simd!(writer, I32X4_GT_U, {}), // I64x2GtS => simd!(writer, I64X2_GT_S, {}), // I64x2GtU => simd!(writer, I64X2_GT_U, {}), F32x4Gt => simd!(writer, F32X4_GT, {}), F64x2Gt => simd!(writer, F64X2_GT, {}), I8x16GeS => simd!(writer, I8X16_GE_S, {}), I8x16GeU => simd!(writer, I8X16_GE_U, {}), I16x8GeS => simd!(writer, I16X8_GE_S, {}), I16x8GeU => simd!(writer, I16X8_GE_U, {}), I32x4GeS => simd!(writer, I32X4_GE_S, {}), I32x4GeU => simd!(writer, I32X4_GE_U, {}), // I64x2GeS => simd!(writer, I64X2_GE_S, {}), // I64x2GeU => simd!(writer, I64X2_GE_U, {}), F32x4Ge => simd!(writer, F32X4_GE, {}), F64x2Ge => simd!(writer, F64X2_GE, {}), F32x4Neg => simd!(writer, F32X4_NEG, {}), F64x2Neg => simd!(writer, F64X2_NEG, {}), F32x4Abs => simd!(writer, F32X4_ABS, {}), F64x2Abs => simd!(writer, F64X2_ABS, {}), F32x4Min => simd!(writer, F32X4_MIN, {}), F64x2Min => simd!(writer, F64X2_MIN, {}), F32x4Max => simd!(writer, F32X4_MAX, {}), F64x2Max => simd!(writer, F64X2_MAX, {}), F32x4Add => simd!(writer, F32X4_ADD, {}), F64x2Add => simd!(writer, F64X2_ADD, {}), F32x4Sub => simd!(writer, F32X4_SUB, {}), F64x2Sub => simd!(writer, F64X2_SUB, {}), F32x4Div => simd!(writer, F32X4_DIV, {}), F64x2Div => simd!(writer, F64X2_DIV, {}), F32x4Mul => simd!(writer, F32X4_MUL, {}), F64x2Mul => simd!(writer, F64X2_MUL, {}), F32x4Sqrt => simd!(writer, F32X4_SQRT, {}), F64x2Sqrt => simd!(writer, F64X2_SQRT, {}), F32x4ConvertSI32x4 => simd!(writer, F32X4_CONVERT_S_I32X4, {}), F32x4ConvertUI32x4 => simd!(writer, F32X4_CONVERT_U_I32X4, {}), F64x2ConvertSI64x2 => simd!(writer, F64X2_CONVERT_S_I64X2, {}), F64x2ConvertUI64x2 => simd!(writer, F64X2_CONVERT_U_I64X2, {}), I32x4TruncSF32x4Sat => simd!(writer, I32X4_TRUNC_S_F32X4_SAT, {}), I32x4TruncUF32x4Sat => simd!(writer, I32X4_TRUNC_U_F32X4_SAT, {}), I64x2TruncSF64x2Sat => simd!(writer, I64X2_TRUNC_S_F64X2_SAT, {}), I64x2TruncUF64x2Sat => simd!(writer, I64X2_TRUNC_U_F64X2_SAT, {}), } Ok(()) } } #[cfg(feature = "bulk")] impl Serialize for BulkInstruction { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { use self::{opcodes::bulk::*, BulkInstruction::*}; match self { MemoryInit(seg) => bulk!(writer, MEMORY_INIT, { Uint8::from(0).serialize(writer)?; VarUint32::from(seg).serialize(writer)?; }), MemoryDrop(seg) => bulk!(writer, MEMORY_DROP, VarUint32::from(seg).serialize(writer)?), MemoryFill => bulk!(writer, MEMORY_FILL, Uint8::from(0).serialize(writer)?), MemoryCopy => bulk!(writer, MEMORY_COPY, Uint8::from(0).serialize(writer)?), TableInit(seg) => bulk!(writer, TABLE_INIT, { Uint8::from(0).serialize(writer)?; VarUint32::from(seg).serialize(writer)?; }), TableDrop(seg) => bulk!(writer, TABLE_DROP, VarUint32::from(seg).serialize(writer)?), TableCopy => bulk!(writer, TABLE_COPY, Uint8::from(0).serialize(writer)?), } Ok(()) } } #[cfg(any(feature = "simd", feature = "atomics"))] impl Serialize for MemArg { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { Uint8::from(self.align).serialize(writer)?; VarUint32::from(self.offset).serialize(writer)?; Ok(()) } } macro_rules! fmt_op { ($f: expr, $mnemonic: expr) => {{ write!($f, "{}", $mnemonic) }}; ($f: expr, $mnemonic: expr, $immediate: expr) => {{ write!($f, "{} {}", $mnemonic, $immediate) }}; ($f: expr, $mnemonic: expr, $immediate1: expr, $immediate2: expr) => {{ write!($f, "{} {} {}", $mnemonic, $immediate1, $immediate2) }}; } impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Instruction::*; match *self { Unreachable => fmt_op!(f, "unreachable"), Nop => fmt_op!(f, "nop"), Block(BlockType::NoResult) => fmt_op!(f, "block"), Block(BlockType::Value(value_type)) => fmt_op!(f, "block", value_type), #[cfg(feature = "multi_value")] Block(BlockType::TypeIndex(idx)) => write!(f, "block type_idx={}", idx), Loop(BlockType::NoResult) => fmt_op!(f, "loop"), Loop(BlockType::Value(value_type)) => fmt_op!(f, "loop", value_type), #[cfg(feature = "multi_value")] Loop(BlockType::TypeIndex(idx)) => write!(f, "loop type_idx={}", idx), If(BlockType::NoResult) => fmt_op!(f, "if"), If(BlockType::Value(value_type)) => fmt_op!(f, "if", value_type), #[cfg(feature = "multi_value")] If(BlockType::TypeIndex(idx)) => write!(f, "if type_idx={}", idx), Else => fmt_op!(f, "else"), End => fmt_op!(f, "end"), Br(idx) => fmt_op!(f, "br", idx), BrIf(idx) => fmt_op!(f, "br_if", idx), BrTable(ref table) => fmt_op!(f, "br_table", table.default), Return => fmt_op!(f, "return"), Call(index) => fmt_op!(f, "call", index), CallIndirect(index, _) => fmt_op!(f, "call_indirect", index), Drop => fmt_op!(f, "drop"), Select => fmt_op!(f, "select"), GetLocal(index) => fmt_op!(f, "get_local", index), SetLocal(index) => fmt_op!(f, "set_local", index), TeeLocal(index) => fmt_op!(f, "tee_local", index), GetGlobal(index) => fmt_op!(f, "get_global", index), SetGlobal(index) => fmt_op!(f, "set_global", index), I32Load(_, 0) => write!(f, "i32.load"), I32Load(_, offset) => write!(f, "i32.load offset={}", offset), I64Load(_, 0) => write!(f, "i64.load"), I64Load(_, offset) => write!(f, "i64.load offset={}", offset), F32Load(_, 0) => write!(f, "f32.load"), F32Load(_, offset) => write!(f, "f32.load offset={}", offset), F64Load(_, 0) => write!(f, "f64.load"), F64Load(_, offset) => write!(f, "f64.load offset={}", offset), I32Load8S(_, 0) => write!(f, "i32.load8_s"), I32Load8S(_, offset) => write!(f, "i32.load8_s offset={}", offset), I32Load8U(_, 0) => write!(f, "i32.load8_u"), I32Load8U(_, offset) => write!(f, "i32.load8_u offset={}", offset), I32Load16S(_, 0) => write!(f, "i32.load16_s"), I32Load16S(_, offset) => write!(f, "i32.load16_s offset={}", offset), I32Load16U(_, 0) => write!(f, "i32.load16_u"), I32Load16U(_, offset) => write!(f, "i32.load16_u offset={}", offset), I64Load8S(_, 0) => write!(f, "i64.load8_s"), I64Load8S(_, offset) => write!(f, "i64.load8_s offset={}", offset), I64Load8U(_, 0) => write!(f, "i64.load8_u"), I64Load8U(_, offset) => write!(f, "i64.load8_u offset={}", offset), I64Load16S(_, 0) => write!(f, "i64.load16_s"), I64Load16S(_, offset) => write!(f, "i64.load16_s offset={}", offset), I64Load16U(_, 0) => write!(f, "i64.load16_u"), I64Load16U(_, offset) => write!(f, "i64.load16_u offset={}", offset), I64Load32S(_, 0) => write!(f, "i64.load32_s"), I64Load32S(_, offset) => write!(f, "i64.load32_s offset={}", offset), I64Load32U(_, 0) => write!(f, "i64.load32_u"), I64Load32U(_, offset) => write!(f, "i64.load32_u offset={}", offset), I32Store(_, 0) => write!(f, "i32.store"), I32Store(_, offset) => write!(f, "i32.store offset={}", offset), I64Store(_, 0) => write!(f, "i64.store"), I64Store(_, offset) => write!(f, "i64.store offset={}", offset), F32Store(_, 0) => write!(f, "f32.store"), F32Store(_, offset) => write!(f, "f32.store offset={}", offset), F64Store(_, 0) => write!(f, "f64.store"), F64Store(_, offset) => write!(f, "f64.store offset={}", offset), I32Store8(_, 0) => write!(f, "i32.store8"), I32Store8(_, offset) => write!(f, "i32.store8 offset={}", offset), I32Store16(_, 0) => write!(f, "i32.store16"), I32Store16(_, offset) => write!(f, "i32.store16 offset={}", offset), I64Store8(_, 0) => write!(f, "i64.store8"), I64Store8(_, offset) => write!(f, "i64.store8 offset={}", offset), I64Store16(_, 0) => write!(f, "i64.store16"), I64Store16(_, offset) => write!(f, "i64.store16 offset={}", offset), I64Store32(_, 0) => write!(f, "i64.store32"), I64Store32(_, offset) => write!(f, "i64.store32 offset={}", offset), CurrentMemory(_) => fmt_op!(f, "current_memory"), GrowMemory(_) => fmt_op!(f, "grow_memory"), I32Const(def) => fmt_op!(f, "i32.const", def), I64Const(def) => fmt_op!(f, "i64.const", def), F32Const(def) => fmt_op!(f, "f32.const", def), F64Const(def) => fmt_op!(f, "f64.const", def), I32Eq => write!(f, "i32.eq"), I32Eqz => write!(f, "i32.eqz"), I32Ne => write!(f, "i32.ne"), I32LtS => write!(f, "i32.lt_s"), I32LtU => write!(f, "i32.lt_u"), I32GtS => write!(f, "i32.gt_s"), I32GtU => write!(f, "i32.gt_u"), I32LeS => write!(f, "i32.le_s"), I32LeU => write!(f, "i32.le_u"), I32GeS => write!(f, "i32.ge_s"), I32GeU => write!(f, "i32.ge_u"), I64Eq => write!(f, "i64.eq"), I64Eqz => write!(f, "i64.eqz"), I64Ne => write!(f, "i64.ne"), I64LtS => write!(f, "i64.lt_s"), I64LtU => write!(f, "i64.lt_u"), I64GtS => write!(f, "i64.gt_s"), I64GtU => write!(f, "i64.gt_u"), I64LeS => write!(f, "i64.le_s"), I64LeU => write!(f, "i64.le_u"), I64GeS => write!(f, "i64.ge_s"), I64GeU => write!(f, "i64.ge_u"), F32Eq => write!(f, "f32.eq"), F32Ne => write!(f, "f32.ne"), F32Lt => write!(f, "f32.lt"), F32Gt => write!(f, "f32.gt"), F32Le => write!(f, "f32.le"), F32Ge => write!(f, "f32.ge"), F64Eq => write!(f, "f64.eq"), F64Ne => write!(f, "f64.ne"), F64Lt => write!(f, "f64.lt"), F64Gt => write!(f, "f64.gt"), F64Le => write!(f, "f64.le"), F64Ge => write!(f, "f64.ge"), I32Clz => write!(f, "i32.clz"), I32Ctz => write!(f, "i32.ctz"), I32Popcnt => write!(f, "i32.popcnt"), I32Add => write!(f, "i32.add"), I32Sub => write!(f, "i32.sub"), I32Mul => write!(f, "i32.mul"), I32DivS => write!(f, "i32.div_s"), I32DivU => write!(f, "i32.div_u"), I32RemS => write!(f, "i32.rem_s"), I32RemU => write!(f, "i32.rem_u"), I32And => write!(f, "i32.and"), I32Or => write!(f, "i32.or"), I32Xor => write!(f, "i32.xor"), I32Shl => write!(f, "i32.shl"), I32ShrS => write!(f, "i32.shr_s"), I32ShrU => write!(f, "i32.shr_u"), I32Rotl => write!(f, "i32.rotl"), I32Rotr => write!(f, "i32.rotr"), I64Clz => write!(f, "i64.clz"), I64Ctz => write!(f, "i64.ctz"), I64Popcnt => write!(f, "i64.popcnt"), I64Add => write!(f, "i64.add"), I64Sub => write!(f, "i64.sub"), I64Mul => write!(f, "i64.mul"), I64DivS => write!(f, "i64.div_s"), I64DivU => write!(f, "i64.div_u"), I64RemS => write!(f, "i64.rem_s"), I64RemU => write!(f, "i64.rem_u"), I64And => write!(f, "i64.and"), I64Or => write!(f, "i64.or"), I64Xor => write!(f, "i64.xor"), I64Shl => write!(f, "i64.shl"), I64ShrS => write!(f, "i64.shr_s"), I64ShrU => write!(f, "i64.shr_u"), I64Rotl => write!(f, "i64.rotl"), I64Rotr => write!(f, "i64.rotr"), F32Abs => write!(f, "f32.abs"), F32Neg => write!(f, "f32.neg"), F32Ceil => write!(f, "f32.ceil"), F32Floor => write!(f, "f32.floor"), F32Trunc => write!(f, "f32.trunc"), F32Nearest => write!(f, "f32.nearest"), F32Sqrt => write!(f, "f32.sqrt"), F32Add => write!(f, "f32.add"), F32Sub => write!(f, "f32.sub"), F32Mul => write!(f, "f32.mul"), F32Div => write!(f, "f32.div"), F32Min => write!(f, "f32.min"), F32Max => write!(f, "f32.max"), F32Copysign => write!(f, "f32.copysign"), F64Abs => write!(f, "f64.abs"), F64Neg => write!(f, "f64.neg"), F64Ceil => write!(f, "f64.ceil"), F64Floor => write!(f, "f64.floor"), F64Trunc => write!(f, "f64.trunc"), F64Nearest => write!(f, "f64.nearest"), F64Sqrt => write!(f, "f64.sqrt"), F64Add => write!(f, "f64.add"), F64Sub => write!(f, "f64.sub"), F64Mul => write!(f, "f64.mul"), F64Div => write!(f, "f64.div"), F64Min => write!(f, "f64.min"), F64Max => write!(f, "f64.max"), F64Copysign => write!(f, "f64.copysign"), I32WrapI64 => write!(f, "i32.wrap/i64"), I32TruncSF32 => write!(f, "i32.trunc_s/f32"), I32TruncUF32 => write!(f, "i32.trunc_u/f32"), I32TruncSF64 => write!(f, "i32.trunc_s/f64"), I32TruncUF64 => write!(f, "i32.trunc_u/f64"), I64ExtendSI32 => write!(f, "i64.extend_s/i32"), I64ExtendUI32 => write!(f, "i64.extend_u/i32"), I64TruncSF32 => write!(f, "i64.trunc_s/f32"), I64TruncUF32 => write!(f, "i64.trunc_u/f32"), I64TruncSF64 => write!(f, "i64.trunc_s/f64"), I64TruncUF64 => write!(f, "i64.trunc_u/f64"), F32ConvertSI32 => write!(f, "f32.convert_s/i32"), F32ConvertUI32 => write!(f, "f32.convert_u/i32"), F32ConvertSI64 => write!(f, "f32.convert_s/i64"), F32ConvertUI64 => write!(f, "f32.convert_u/i64"), F32DemoteF64 => write!(f, "f32.demote/f64"), F64ConvertSI32 => write!(f, "f64.convert_s/i32"), F64ConvertUI32 => write!(f, "f64.convert_u/i32"), F64ConvertSI64 => write!(f, "f64.convert_s/i64"), F64ConvertUI64 => write!(f, "f64.convert_u/i64"), F64PromoteF32 => write!(f, "f64.promote/f32"), I32ReinterpretF32 => write!(f, "i32.reinterpret/f32"), I64ReinterpretF64 => write!(f, "i64.reinterpret/f64"), F32ReinterpretI32 => write!(f, "f32.reinterpret/i32"), F64ReinterpretI64 => write!(f, "f64.reinterpret/i64"), #[cfg(feature = "sign_ext")] SignExt(ref i) => match i { SignExtInstruction::I32Extend8S => write!(f, "i32.extend8_s"), SignExtInstruction::I32Extend16S => write!(f, "i32.extend16_s"), SignExtInstruction::I64Extend8S => write!(f, "i64.extend8_s"), SignExtInstruction::I64Extend16S => write!(f, "i64.extend16_s"), SignExtInstruction::I64Extend32S => write!(f, "i64.extend32_s"), }, #[cfg(feature = "atomics")] Atomics(ref i) => i.fmt(f), #[cfg(feature = "simd")] Simd(ref i) => i.fmt(f), #[cfg(feature = "bulk")] Bulk(ref i) => i.fmt(f), } } } #[cfg(feature = "atomics")] impl fmt::Display for AtomicsInstruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::AtomicsInstruction::*; match *self { AtomicWake(_) => write!(f, "atomic.wake"), I32AtomicWait(_) => write!(f, "i32.atomic.wait"), I64AtomicWait(_) => write!(f, "i64.atomic.wait"), I32AtomicLoad(_) => write!(f, "i32.atomic.load"), I64AtomicLoad(_) => write!(f, "i64.atomic.load"), I32AtomicLoad8u(_) => write!(f, "i32.atomic.load8_u"), I32AtomicLoad16u(_) => write!(f, "i32.atomic.load16_u"), I64AtomicLoad8u(_) => write!(f, "i64.atomic.load8_u"), I64AtomicLoad16u(_) => write!(f, "i64.atomic.load16_u"), I64AtomicLoad32u(_) => write!(f, "i64.atomic.load32_u"), I32AtomicStore(_) => write!(f, "i32.atomic.store"), I64AtomicStore(_) => write!(f, "i64.atomic.store"), I32AtomicStore8u(_) => write!(f, "i32.atomic.store8_u"), I32AtomicStore16u(_) => write!(f, "i32.atomic.store16_u"), I64AtomicStore8u(_) => write!(f, "i64.atomic.store8_u"), I64AtomicStore16u(_) => write!(f, "i64.atomic.store16_u"), I64AtomicStore32u(_) => write!(f, "i64.atomic.store32_u"), I32AtomicRmwAdd(_) => write!(f, "i32.atomic.rmw.add"), I64AtomicRmwAdd(_) => write!(f, "i64.atomic.rmw.add"), I32AtomicRmwAdd8u(_) => write!(f, "i32.atomic.rmw8_u.add"), I32AtomicRmwAdd16u(_) => write!(f, "i32.atomic.rmw16_u.add"), I64AtomicRmwAdd8u(_) => write!(f, "i64.atomic.rmw8_u.add"), I64AtomicRmwAdd16u(_) => write!(f, "i64.atomic.rmw16_u.add"), I64AtomicRmwAdd32u(_) => write!(f, "i64.atomic.rmw32_u.add"), I32AtomicRmwSub(_) => write!(f, "i32.atomic.rmw.sub"), I64AtomicRmwSub(_) => write!(f, "i64.atomic.rmw.sub"), I32AtomicRmwSub8u(_) => write!(f, "i32.atomic.rmw8_u.sub"), I32AtomicRmwSub16u(_) => write!(f, "i32.atomic.rmw16_u.sub"), I64AtomicRmwSub8u(_) => write!(f, "i64.atomic.rmw8_u.sub"), I64AtomicRmwSub16u(_) => write!(f, "i64.atomic.rmw16_u.sub"), I64AtomicRmwSub32u(_) => write!(f, "i64.atomic.rmw32_u.sub"), I32AtomicRmwAnd(_) => write!(f, "i32.atomic.rmw.and"), I64AtomicRmwAnd(_) => write!(f, "i64.atomic.rmw.and"), I32AtomicRmwAnd8u(_) => write!(f, "i32.atomic.rmw8_u.and"), I32AtomicRmwAnd16u(_) => write!(f, "i32.atomic.rmw16_u.and"), I64AtomicRmwAnd8u(_) => write!(f, "i64.atomic.rmw8_u.and"), I64AtomicRmwAnd16u(_) => write!(f, "i64.atomic.rmw16_u.and"), I64AtomicRmwAnd32u(_) => write!(f, "i64.atomic.rmw32_u.and"), I32AtomicRmwOr(_) => write!(f, "i32.atomic.rmw.or"), I64AtomicRmwOr(_) => write!(f, "i64.atomic.rmw.or"), I32AtomicRmwOr8u(_) => write!(f, "i32.atomic.rmw8_u.or"), I32AtomicRmwOr16u(_) => write!(f, "i32.atomic.rmw16_u.or"), I64AtomicRmwOr8u(_) => write!(f, "i64.atomic.rmw8_u.or"), I64AtomicRmwOr16u(_) => write!(f, "i64.atomic.rmw16_u.or"), I64AtomicRmwOr32u(_) => write!(f, "i64.atomic.rmw32_u.or"), I32AtomicRmwXor(_) => write!(f, "i32.atomic.rmw.xor"), I64AtomicRmwXor(_) => write!(f, "i64.atomic.rmw.xor"), I32AtomicRmwXor8u(_) => write!(f, "i32.atomic.rmw8_u.xor"), I32AtomicRmwXor16u(_) => write!(f, "i32.atomic.rmw16_u.xor"), I64AtomicRmwXor8u(_) => write!(f, "i64.atomic.rmw8_u.xor"), I64AtomicRmwXor16u(_) => write!(f, "i64.atomic.rmw16_u.xor"), I64AtomicRmwXor32u(_) => write!(f, "i64.atomic.rmw32_u.xor"), I32AtomicRmwXchg(_) => write!(f, "i32.atomic.rmw.xchg"), I64AtomicRmwXchg(_) => write!(f, "i64.atomic.rmw.xchg"), I32AtomicRmwXchg8u(_) => write!(f, "i32.atomic.rmw8_u.xchg"), I32AtomicRmwXchg16u(_) => write!(f, "i32.atomic.rmw16_u.xchg"), I64AtomicRmwXchg8u(_) => write!(f, "i64.atomic.rmw8_u.xchg"), I64AtomicRmwXchg16u(_) => write!(f, "i64.atomic.rmw16_u.xchg"), I64AtomicRmwXchg32u(_) => write!(f, "i64.atomic.rmw32_u.xchg"), I32AtomicRmwCmpxchg(_) => write!(f, "i32.atomic.rmw.cmpxchg"), I64AtomicRmwCmpxchg(_) => write!(f, "i64.atomic.rmw.cmpxchg"), I32AtomicRmwCmpxchg8u(_) => write!(f, "i32.atomic.rmw8_u.cmpxchg"), I32AtomicRmwCmpxchg16u(_) => write!(f, "i32.atomic.rmw16_u.cmpxchg"), I64AtomicRmwCmpxchg8u(_) => write!(f, "i64.atomic.rmw8_u.cmpxchg"), I64AtomicRmwCmpxchg16u(_) => write!(f, "i64.atomic.rmw16_u.cmpxchg"), I64AtomicRmwCmpxchg32u(_) => write!(f, "i64.atomic.rmw32_u.cmpxchg"), } } } #[cfg(feature = "simd")] impl fmt::Display for SimdInstruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::SimdInstruction::*; match *self { V128Const(_) => write!(f, "v128.const"), V128Load(_) => write!(f, "v128.load"), V128Store(_) => write!(f, "v128.store"), I8x16Splat => write!(f, "i8x16.splat"), I16x8Splat => write!(f, "i16x8.splat"), I32x4Splat => write!(f, "i32x4.splat"), I64x2Splat => write!(f, "i64x2.splat"), F32x4Splat => write!(f, "f32x4.splat"), F64x2Splat => write!(f, "f64x2.splat"), I8x16ExtractLaneS(_) => write!(f, "i8x16.extract_lane_s"), I8x16ExtractLaneU(_) => write!(f, "i8x16.extract_lane_u"), I16x8ExtractLaneS(_) => write!(f, "i16x8.extract_lane_s"), I16x8ExtractLaneU(_) => write!(f, "i16x8.extract_lane_u"), I32x4ExtractLane(_) => write!(f, "i32x4.extract_lane"), I64x2ExtractLane(_) => write!(f, "i64x2.extract_lane"), F32x4ExtractLane(_) => write!(f, "f32x4.extract_lane"), F64x2ExtractLane(_) => write!(f, "f64x2.extract_lane"), I8x16ReplaceLane(_) => write!(f, "i8x16.replace_lane"), I16x8ReplaceLane(_) => write!(f, "i16x8.replace_lane"), I32x4ReplaceLane(_) => write!(f, "i32x4.replace_lane"), I64x2ReplaceLane(_) => write!(f, "i64x2.replace_lane"), F32x4ReplaceLane(_) => write!(f, "f32x4.replace_lane"), F64x2ReplaceLane(_) => write!(f, "f64x2.replace_lane"), V8x16Shuffle(_) => write!(f, "v8x16.shuffle"), I8x16Add => write!(f, "i8x16.add"), I16x8Add => write!(f, "i16x8.add"), I32x4Add => write!(f, "i32x4.add"), I64x2Add => write!(f, "i64x2.add"), I8x16Sub => write!(f, "i8x16.sub"), I16x8Sub => write!(f, "i16x8.sub"), I32x4Sub => write!(f, "i32x4.sub"), I64x2Sub => write!(f, "i64x2.sub"), I8x16Mul => write!(f, "i8x16.mul"), I16x8Mul => write!(f, "i16x8.mul"), I32x4Mul => write!(f, "i32x4.mul"), // I64x2Mul => write!(f, "i64x2.mul"), I8x16Neg => write!(f, "i8x16.neg"), I16x8Neg => write!(f, "i16x8.neg"), I32x4Neg => write!(f, "i32x4.neg"), I64x2Neg => write!(f, "i64x2.neg"), I8x16AddSaturateS => write!(f, "i8x16.add_saturate_s"), I8x16AddSaturateU => write!(f, "i8x16.add_saturate_u"), I16x8AddSaturateS => write!(f, "i16x8.add_saturate_S"), I16x8AddSaturateU => write!(f, "i16x8.add_saturate_u"), I8x16SubSaturateS => write!(f, "i8x16.sub_saturate_S"), I8x16SubSaturateU => write!(f, "i8x16.sub_saturate_u"), I16x8SubSaturateS => write!(f, "i16x8.sub_saturate_S"), I16x8SubSaturateU => write!(f, "i16x8.sub_saturate_u"), I8x16Shl => write!(f, "i8x16.shl"), I16x8Shl => write!(f, "i16x8.shl"), I32x4Shl => write!(f, "i32x4.shl"), I64x2Shl => write!(f, "i64x2.shl"), I8x16ShrS => write!(f, "i8x16.shr_s"), I8x16ShrU => write!(f, "i8x16.shr_u"), I16x8ShrS => write!(f, "i16x8.shr_s"), I16x8ShrU => write!(f, "i16x8.shr_u"), I32x4ShrS => write!(f, "i32x4.shr_s"), I32x4ShrU => write!(f, "i32x4.shr_u"), I64x2ShrS => write!(f, "i64x2.shr_s"), I64x2ShrU => write!(f, "i64x2.shr_u"), V128And => write!(f, "v128.and"), V128Or => write!(f, "v128.or"), V128Xor => write!(f, "v128.xor"), V128Not => write!(f, "v128.not"), V128Bitselect => write!(f, "v128.bitselect"), I8x16AnyTrue => write!(f, "i8x16.any_true"), I16x8AnyTrue => write!(f, "i16x8.any_true"), I32x4AnyTrue => write!(f, "i32x4.any_true"), I64x2AnyTrue => write!(f, "i64x2.any_true"), I8x16AllTrue => write!(f, "i8x16.all_true"), I16x8AllTrue => write!(f, "i16x8.all_true"), I32x4AllTrue => write!(f, "i32x4.all_true"), I64x2AllTrue => write!(f, "i64x2.all_true"), I8x16Eq => write!(f, "i8x16.eq"), I16x8Eq => write!(f, "i16x8.eq"), I32x4Eq => write!(f, "i32x4.eq"), // I64x2Eq => write!(f, "i64x2.eq"), F32x4Eq => write!(f, "f32x4.eq"), F64x2Eq => write!(f, "f64x2.eq"), I8x16Ne => write!(f, "i8x16.ne"), I16x8Ne => write!(f, "i16x8.ne"), I32x4Ne => write!(f, "i32x4.ne"), // I64x2Ne => write!(f, "i64x2.ne"), F32x4Ne => write!(f, "f32x4.ne"), F64x2Ne => write!(f, "f64x2.ne"), I8x16LtS => write!(f, "i8x16.lt_s"), I8x16LtU => write!(f, "i8x16.lt_u"), I16x8LtS => write!(f, "i16x8.lt_s"), I16x8LtU => write!(f, "i16x8.lt_u"), I32x4LtS => write!(f, "i32x4.lt_s"), I32x4LtU => write!(f, "i32x4.lt_u"), // I64x2LtS => write!(f, "// I64x2.lt_s"), // I64x2LtU => write!(f, "// I64x2.lt_u"), F32x4Lt => write!(f, "f32x4.lt"), F64x2Lt => write!(f, "f64x2.lt"), I8x16LeS => write!(f, "i8x16.le_s"), I8x16LeU => write!(f, "i8x16.le_u"), I16x8LeS => write!(f, "i16x8.le_s"), I16x8LeU => write!(f, "i16x8.le_u"), I32x4LeS => write!(f, "i32x4.le_s"), I32x4LeU => write!(f, "i32x4.le_u"), // I64x2LeS => write!(f, "// I64x2.le_s"), // I64x2LeU => write!(f, "// I64x2.le_u"), F32x4Le => write!(f, "f32x4.le"), F64x2Le => write!(f, "f64x2.le"), I8x16GtS => write!(f, "i8x16.gt_s"), I8x16GtU => write!(f, "i8x16.gt_u"), I16x8GtS => write!(f, "i16x8.gt_s"), I16x8GtU => write!(f, "i16x8.gt_u"), I32x4GtS => write!(f, "i32x4.gt_s"), I32x4GtU => write!(f, "i32x4.gt_u"), // I64x2GtS => write!(f, "// I64x2.gt_s"), // I64x2GtU => write!(f, "// I64x2.gt_u"), F32x4Gt => write!(f, "f32x4.gt"), F64x2Gt => write!(f, "f64x2.gt"), I8x16GeS => write!(f, "i8x16.ge_s"), I8x16GeU => write!(f, "i8x16.ge_u"), I16x8GeS => write!(f, "i16x8.ge_s"), I16x8GeU => write!(f, "i16x8.ge_u"), I32x4GeS => write!(f, "i32x4.ge_s"), I32x4GeU => write!(f, "i32x4.ge_u"), // I64x2GeS => write!(f, "// I64x2.ge_s"), // I64x2GeU => write!(f, "// I64x2.ge_u"), F32x4Ge => write!(f, "f32x4.ge"), F64x2Ge => write!(f, "f64x2.ge"), F32x4Neg => write!(f, "f32x4.neg"), F64x2Neg => write!(f, "f64x2.neg"), F32x4Abs => write!(f, "f32x4.abs"), F64x2Abs => write!(f, "f64x2.abs"), F32x4Min => write!(f, "f32x4.min"), F64x2Min => write!(f, "f64x2.min"), F32x4Max => write!(f, "f32x4.max"), F64x2Max => write!(f, "f64x2.max"), F32x4Add => write!(f, "f32x4.add"), F64x2Add => write!(f, "f64x2.add"), F32x4Sub => write!(f, "f32x4.sub"), F64x2Sub => write!(f, "f64x2.sub"), F32x4Div => write!(f, "f32x4.div"), F64x2Div => write!(f, "f64x2.div"), F32x4Mul => write!(f, "f32x4.mul"), F64x2Mul => write!(f, "f64x2.mul"), F32x4Sqrt => write!(f, "f32x4.sqrt"), F64x2Sqrt => write!(f, "f64x2.sqrt"), F32x4ConvertSI32x4 => write!(f, "f32x4.convert_s/i32x4"), F32x4ConvertUI32x4 => write!(f, "f32x4.convert_u/i32x4"), F64x2ConvertSI64x2 => write!(f, "f64x2.convert_s/i64x2"), F64x2ConvertUI64x2 => write!(f, "f64x2.convert_u/i64x2"), I32x4TruncSF32x4Sat => write!(f, "i32x4.trunc_s/f32x4:sat"), I32x4TruncUF32x4Sat => write!(f, "i32x4.trunc_u/f32x4:sat"), I64x2TruncSF64x2Sat => write!(f, "i64x2.trunc_s/f64x2:sat"), I64x2TruncUF64x2Sat => write!(f, "i64x2.trunc_u/f64x2:sat"), } } } #[cfg(feature = "bulk")] impl fmt::Display for BulkInstruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::BulkInstruction::*; match *self { MemoryInit(_) => write!(f, "memory.init"), MemoryDrop(_) => write!(f, "memory.drop"), MemoryFill => write!(f, "memory.fill"), MemoryCopy => write!(f, "memory.copy"), TableInit(_) => write!(f, "table.init"), TableDrop(_) => write!(f, "table.drop"), TableCopy => write!(f, "table.copy"), } } } impl Serialize for Instructions { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { for op in self.0.into_iter() { op.serialize(writer)?; } Ok(()) } } impl Serialize for InitExpr { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { for op in self.0.into_iter() { op.serialize(writer)?; } Ok(()) } } #[test] fn ifelse() { // see if-else.wast/if-else.wasm let instruction_list = super::deserialize_buffer::(&[ 0x04, 0x7F, 0x41, 0x05, 0x05, 0x41, 0x07, 0x0B, 0x0B, ]) .expect("valid hex of if instruction"); let instructions = instruction_list.elements(); match instructions[0] { Instruction::If(_) => (), _ => panic!("Should be deserialized as if instruction"), } let before_else = instructions .iter() .skip(1) .take_while(|op| !matches!(**op, Instruction::Else)) .count(); let after_else = instructions .iter() .skip(1) .skip_while(|op| !matches!(**op, Instruction::Else)) .take_while(|op| !matches!(**op, Instruction::End)) .count() - 1; // minus Instruction::Else itself assert_eq!(before_else, after_else); } #[test] fn display() { let instruction = Instruction::GetLocal(0); assert_eq!("get_local 0", format!("{}", instruction)); let instruction = Instruction::F64Store(0, 24); assert_eq!("f64.store offset=24", format!("{}", instruction)); let instruction = Instruction::I64Store(0, 0); assert_eq!("i64.store", format!("{}", instruction)); } #[test] fn size_off() { assert!(::std::mem::size_of::() <= 24); } #[test] fn instructions_hashset() { use self::Instruction::{Block, Call, Drop}; use super::types::{BlockType::Value, ValueType}; let set: std::collections::HashSet = vec![Call(1), Block(Value(ValueType::I32)), Drop].into_iter().collect(); assert!(set.contains(&Drop)); } parity-wasm-0.45.0/src/elements/primitives.rs000064400000000000000000000466170072674642500174010ustar 00000000000000use super::{Deserialize, Error, Serialize}; use crate::{elements, io}; use alloc::{string::String, vec::Vec}; #[cfg(feature = "reduced-stack-buffer")] const PRIMITIVES_BUFFER_LENGTH: usize = 256; #[cfg(not(feature = "reduced-stack-buffer"))] const PRIMITIVES_BUFFER_LENGTH: usize = 1024; /// Unsigned variable-length integer, limited to 32 bits, /// represented by at most 5 bytes that may contain padding 0x80 bytes. #[derive(Debug, Copy, Clone, PartialEq)] pub struct VarUint32(u32); impl From for usize { fn from(var: VarUint32) -> usize { var.0 as usize } } impl From for u32 { fn from(var: VarUint32) -> u32 { var.0 } } impl From for VarUint32 { fn from(i: u32) -> VarUint32 { VarUint32(i) } } impl From for VarUint32 { fn from(i: usize) -> VarUint32 { assert!(i <= u32::max_value() as usize); VarUint32(i as u32) } } impl Deserialize for VarUint32 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut res = 0; let mut shift = 0; let mut u8buf = [0u8; 1]; loop { if shift > 31 { return Err(Error::InvalidVarUint32) } reader.read(&mut u8buf)?; let b = u8buf[0] as u32; res |= (b & 0x7f).checked_shl(shift).ok_or(Error::InvalidVarUint32)?; shift += 7; if (b >> 7) == 0 { if shift >= 32 && (b as u8).leading_zeros() < 4 { return Err(Error::InvalidVarInt32) } break } } Ok(VarUint32(res)) } } impl Serialize for VarUint32 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut buf = [0u8; 1]; let mut v = self.0; loop { buf[0] = (v & 0b0111_1111) as u8; v >>= 7; if v > 0 { buf[0] |= 0b1000_0000; } writer.write(&buf[..])?; if v == 0 { break } } Ok(()) } } /// Unsigned variable-length integer, limited to 64 bits, /// represented by at most 9 bytes that may contain padding 0x80 bytes. #[derive(Debug, Copy, Clone, PartialEq)] pub struct VarUint64(u64); impl From for u64 { fn from(var: VarUint64) -> u64 { var.0 } } impl Deserialize for VarUint64 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut res = 0; let mut shift = 0; let mut u8buf = [0u8; 1]; loop { if shift > 63 { return Err(Error::InvalidVarUint64) } reader.read(&mut u8buf)?; let b = u8buf[0] as u64; res |= (b & 0x7f).checked_shl(shift).ok_or(Error::InvalidVarUint64)?; shift += 7; if (b >> 7) == 0 { if shift >= 64 && (b as u8).leading_zeros() < 7 { return Err(Error::InvalidVarInt64) } break } } Ok(VarUint64(res)) } } impl Serialize for VarUint64 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut buf = [0u8; 1]; let mut v = self.0; loop { buf[0] = (v & 0b0111_1111) as u8; v >>= 7; if v > 0 { buf[0] |= 0b1000_0000; } writer.write(&buf[..])?; if v == 0 { break } } Ok(()) } } impl From for VarUint64 { fn from(u: u64) -> VarUint64 { VarUint64(u) } } /// 7-bit unsigned integer, encoded in LEB128 (always 1 byte length). #[derive(Debug, Copy, Clone, PartialEq)] pub struct VarUint7(u8); impl From for u8 { fn from(v: VarUint7) -> u8 { v.0 } } impl From for VarUint7 { fn from(v: u8) -> Self { VarUint7(v) } } impl Deserialize for VarUint7 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut u8buf = [0u8; 1]; reader.read(&mut u8buf)?; Ok(VarUint7(u8buf[0])) } } impl Serialize for VarUint7 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { // todo check range? writer.write(&[self.0])?; Ok(()) } } /// 7-bit signed integer, encoded in LEB128 (always 1 byte length) #[derive(Debug, Copy, Clone, PartialEq)] pub struct VarInt7(i8); impl From for i8 { fn from(v: VarInt7) -> i8 { v.0 } } impl From for VarInt7 { fn from(v: i8) -> VarInt7 { VarInt7(v) } } impl Deserialize for VarInt7 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut u8buf = [0u8; 1]; reader.read(&mut u8buf)?; // check if number is not continued! if u8buf[0] & 0b1000_0000 != 0 { return Err(Error::InvalidVarInt7(u8buf[0])) } // expand sign if u8buf[0] & 0b0100_0000 == 0b0100_0000 { u8buf[0] |= 0b1000_0000 } Ok(VarInt7(u8buf[0] as i8)) } } impl Serialize for VarInt7 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { // todo check range? let mut b: u8 = self.0 as u8; if self.0 < 0 { b |= 0b0100_0000; b &= 0b0111_1111; } writer.write(&[b])?; Ok(()) } } /// 8-bit unsigned integer, NOT encoded in LEB128; /// it's just a single byte. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Uint8(u8); impl From for u8 { fn from(v: Uint8) -> u8 { v.0 } } impl From for Uint8 { fn from(v: u8) -> Self { Uint8(v) } } impl Deserialize for Uint8 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut u8buf = [0u8; 1]; reader.read(&mut u8buf)?; Ok(Uint8(u8buf[0])) } } impl Serialize for Uint8 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { writer.write(&[self.0])?; Ok(()) } } /// 32-bit signed integer, encoded in LEB128 (can be 1-5 bytes length). #[derive(Debug, Copy, Clone, PartialEq)] pub struct VarInt32(i32); impl From for i32 { fn from(v: VarInt32) -> i32 { v.0 } } impl From for VarInt32 { fn from(v: i32) -> VarInt32 { VarInt32(v) } } impl Deserialize for VarInt32 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut res = 0; let mut shift = 0; let mut u8buf = [0u8; 1]; loop { if shift > 31 { return Err(Error::InvalidVarInt32) } reader.read(&mut u8buf)?; let b = u8buf[0]; res |= ((b & 0x7f) as i32).checked_shl(shift).ok_or(Error::InvalidVarInt32)?; shift += 7; if (b >> 7) == 0 { if shift < 32 && b & 0b0100_0000 == 0b0100_0000 { res |= (1i32 << shift).wrapping_neg(); } else if shift >= 32 && b & 0b0100_0000 == 0b0100_0000 { if (!(b | 0b1000_0000)).leading_zeros() < 5 { return Err(Error::InvalidVarInt32) } } else if shift >= 32 && b & 0b0100_0000 == 0 && b.leading_zeros() < 5 { return Err(Error::InvalidVarInt32) } break } } Ok(VarInt32(res)) } } impl Serialize for VarInt32 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut buf = [0u8; 1]; let mut v = self.0; let mut more = true; while more { buf[0] = (v & 0b0111_1111) as u8; v >>= 7; if (v == 0 && buf[0] & 0b0100_0000 == 0) || (v == -1 && buf[0] & 0b0100_0000 == 0b0100_0000) { more = false } else { buf[0] |= 0b1000_0000 } writer.write(&buf[..])?; } Ok(()) } } /// 64-bit signed integer, encoded in LEB128 (can be 1-9 bytes length). #[derive(Debug, Copy, Clone, PartialEq)] pub struct VarInt64(i64); impl From for i64 { fn from(v: VarInt64) -> i64 { v.0 } } impl From for VarInt64 { fn from(v: i64) -> VarInt64 { VarInt64(v) } } impl Deserialize for VarInt64 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut res = 0i64; let mut shift = 0; let mut u8buf = [0u8; 1]; loop { if shift > 63 { return Err(Error::InvalidVarInt64) } reader.read(&mut u8buf)?; let b = u8buf[0]; res |= ((b & 0x7f) as i64).checked_shl(shift).ok_or(Error::InvalidVarInt64)?; shift += 7; if (b >> 7) == 0 { if shift < 64 && b & 0b0100_0000 == 0b0100_0000 { res |= (1i64 << shift).wrapping_neg(); } else if shift >= 64 && b & 0b0100_0000 == 0b0100_0000 { if (b | 0b1000_0000) as i8 != -1 { return Err(Error::InvalidVarInt64) } } else if shift >= 64 && b != 0 { return Err(Error::InvalidVarInt64) } break } } Ok(VarInt64(res)) } } impl Serialize for VarInt64 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut buf = [0u8; 1]; let mut v = self.0; let mut more = true; while more { buf[0] = (v & 0b0111_1111) as u8; v >>= 7; if (v == 0 && buf[0] & 0b0100_0000 == 0) || (v == -1 && buf[0] & 0b0100_0000 == 0b0100_0000) { more = false } else { buf[0] |= 0b1000_0000 } writer.write(&buf[..])?; } Ok(()) } } /// 32-bit unsigned integer, encoded in little endian. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Uint32(u32); impl Deserialize for Uint32 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut buf = [0u8; 4]; reader.read(&mut buf)?; // todo check range Ok(u32::from_le_bytes(buf).into()) } } impl From for u32 { fn from(var: Uint32) -> u32 { var.0 } } impl Serialize for Uint32 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { writer.write(&self.0.to_le_bytes())?; Ok(()) } } impl From for Uint32 { fn from(u: u32) -> Self { Uint32(u) } } /// 64-bit unsigned integer, encoded in little endian. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Uint64(u64); impl Deserialize for Uint64 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut buf = [0u8; 8]; reader.read(&mut buf)?; // todo check range Ok(u64::from_le_bytes(buf).into()) } } impl Serialize for Uint64 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { writer.write(&self.0.to_le_bytes())?; Ok(()) } } impl From for Uint64 { fn from(u: u64) -> Self { Uint64(u) } } impl From for u64 { fn from(var: Uint64) -> u64 { var.0 } } /// VarUint1, 1-bit value (0/1). #[derive(Debug, Copy, Clone, PartialEq)] pub struct VarUint1(bool); impl From for bool { fn from(v: VarUint1) -> bool { v.0 } } impl From for VarUint1 { fn from(b: bool) -> Self { VarUint1(b) } } impl Deserialize for VarUint1 { type Error = Error; fn deserialize(reader: &mut R) -> Result { let mut u8buf = [0u8; 1]; reader.read(&mut u8buf)?; match u8buf[0] { 0 => Ok(VarUint1(false)), 1 => Ok(VarUint1(true)), v => Err(Error::InvalidVarUint1(v)), } } } impl Serialize for VarUint1 { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { writer.write(&[if self.0 { 1u8 } else { 0u8 }])?; Ok(()) } } impl Deserialize for String { type Error = Error; fn deserialize(reader: &mut R) -> Result { let length = u32::from(VarUint32::deserialize(reader)?) as usize; if length > 0 { String::from_utf8(buffered_read!(PRIMITIVES_BUFFER_LENGTH, length, reader)) .map_err(|_| Error::NonUtf8String) } else { Ok(String::new()) } } } impl Serialize for String { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Error> { VarUint32::from(self.len()).serialize(writer)?; writer.write(&self.into_bytes()[..])?; Ok(()) } } /// List for reading sequence of elements typed `T`, given /// they are preceded by length (serialized as VarUint32). #[derive(Debug, Clone)] pub struct CountedList(Vec); impl CountedList { /// Destroy counted list returing inner vector. pub fn into_inner(self) -> Vec { self.0 } } impl Deserialize for CountedList where T::Error: From, { type Error = T::Error; fn deserialize(reader: &mut R) -> Result { let count: usize = VarUint32::deserialize(reader)?.into(); let mut result = Vec::new(); for _ in 0..count { result.push(T::deserialize(reader)?); } Ok(CountedList(result)) } } /// Helper struct to write payload which is preceded by /// it's own length in bytes. #[derive(Debug)] pub struct CountedWriter<'a, W: 'a + io::Write> { writer: &'a mut W, data: Vec, } impl<'a, W: 'a + io::Write> CountedWriter<'a, W> { /// New counted writer on top of the given serial writer. pub fn new(writer: &'a mut W) -> Self { CountedWriter { writer, data: Vec::new() } } /// Finish counted writer routing, which writes accumulated length /// and actual payload. pub fn done(self) -> io::Result<()> { let writer = self.writer; let data = self.data; VarUint32::from(data.len()) .serialize(writer) .map_err(|_| io::Error::InvalidData)?; writer.write(&data[..])?; Ok(()) } } impl<'a, W: 'a + io::Write> io::Write for CountedWriter<'a, W> { fn write(&mut self, buf: &[u8]) -> io::Result<()> { self.data.extend_from_slice(buf); Ok(()) } } /// Helper struct to write series of `T` preceded by the length of the sequence /// serialized as VarUint32. #[derive(Debug, Clone)] pub struct CountedListWriter, T: IntoIterator>( pub usize, pub T, ); impl, T: IntoIterator> Serialize for CountedListWriter { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let len_us = self.0; let data = self.1; let len: VarUint32 = len_us.into(); len.serialize(writer)?; for data_element in data { data_element.serialize(writer)? } Ok(()) } } #[cfg(test)] mod tests { use super::{ super::{deserialize_buffer, Serialize}, CountedList, VarInt32, VarInt64, VarInt7, VarUint32, VarUint64, }; use crate::elements::Error; fn varuint32_ser_test(val: u32, expected: Vec) { let mut buf = Vec::new(); let v1: VarUint32 = val.into(); v1.serialize(&mut buf).expect("to be serialized ok"); assert_eq!(expected, buf); } fn varuint32_de_test(dt: Vec, expected: u32) { let val: VarUint32 = super::super::deserialize_buffer(&dt).expect("buf to be serialized"); assert_eq!(expected, val.into()); } fn varuint32_serde_test(dt: Vec, val: u32) { varuint32_de_test(dt.clone(), val); varuint32_ser_test(val, dt); } fn varint32_ser_test(val: i32, expected: Vec) { let mut buf = Vec::new(); let v1: VarInt32 = val.into(); v1.serialize(&mut buf).expect("to be serialized ok"); assert_eq!(expected, buf); } fn varint32_de_test(dt: Vec, expected: i32) { let val: VarInt32 = super::super::deserialize_buffer(&dt).expect("buf to be serialized"); assert_eq!(expected, val.into()); } fn varint32_serde_test(dt: Vec, val: i32) { varint32_de_test(dt.clone(), val); varint32_ser_test(val, dt); } fn varuint64_ser_test(val: u64, expected: Vec) { let mut buf = Vec::new(); let v1: VarUint64 = val.into(); v1.serialize(&mut buf).expect("to be serialized ok"); assert_eq!(expected, buf); } fn varuint64_de_test(dt: Vec, expected: u64) { let val: VarUint64 = super::super::deserialize_buffer(&dt).expect("buf to be serialized"); assert_eq!(expected, val.into()); } fn varuint64_serde_test(dt: Vec, val: u64) { varuint64_de_test(dt.clone(), val); varuint64_ser_test(val, dt); } fn varint64_ser_test(val: i64, expected: Vec) { let mut buf = Vec::new(); let v1: VarInt64 = val.into(); v1.serialize(&mut buf).expect("to be serialized ok"); assert_eq!(expected, buf); } fn varint64_de_test(dt: Vec, expected: i64) { let val: VarInt64 = super::super::deserialize_buffer(&dt).expect("buf to be serialized"); assert_eq!(expected, val.into()); } fn varint64_serde_test(dt: Vec, val: i64) { varint64_de_test(dt.clone(), val); varint64_ser_test(val, dt); } #[test] fn varuint32_0() { varuint32_serde_test(vec![0u8; 1], 0); } #[test] fn varuint32_1() { varuint32_serde_test(vec![1u8; 1], 1); } #[test] fn varuint32_135() { varuint32_serde_test(vec![135u8, 0x01], 135); } #[test] fn varuint32_8192() { varuint32_serde_test(vec![0x80, 0x40], 8192); } #[test] fn varint32_8192() { varint32_serde_test(vec![0x80, 0xc0, 0x00], 8192); } #[test] fn varint32_neg_8192() { varint32_serde_test(vec![0x80, 0x40], -8192); } #[test] fn varuint64_0() { varuint64_serde_test(vec![0u8; 1], 0); } #[test] fn varuint64_1() { varuint64_serde_test(vec![1u8; 1], 1); } #[test] fn varuint64_135() { varuint64_serde_test(vec![135u8, 0x01], 135); } #[test] fn varuint64_8192() { varuint64_serde_test(vec![0x80, 0x40], 8192); } #[test] fn varint64_8192() { varint64_serde_test(vec![0x80, 0xc0, 0x00], 8192); } #[test] fn varint64_neg_8192() { varint64_serde_test(vec![0x80, 0x40], -8192); } #[test] fn varint64_min() { varint64_serde_test( vec![0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f], -9223372036854775808, ); } #[test] fn varint64_bad_extended() { let res = deserialize_buffer::( &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x6f][..], ); assert!(res.is_err()); } #[test] fn varint32_bad_extended() { let res = deserialize_buffer::(&[0x80, 0x80, 0x80, 0x80, 0x6f][..]); assert!(res.is_err()); } #[test] fn varint32_bad_extended2() { let res = deserialize_buffer::(&[0x80, 0x80, 0x80, 0x80, 0x41][..]); assert!(res.is_err()); } #[test] fn varint64_max() { varint64_serde_test( vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00], 9223372036854775807, ); } #[test] fn varint64_too_long() { assert!(deserialize_buffer::( &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..], ) .is_err()); } #[test] fn varint32_too_long() { assert!(deserialize_buffer::(&[0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..],).is_err()); } #[test] fn varuint64_too_long() { assert!(deserialize_buffer::( &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..], ) .is_err()); } #[test] fn varuint32_too_long() { assert!( deserialize_buffer::(&[0xff, 0xff, 0xff, 0xff, 0xff, 0x00][..],).is_err() ); } #[test] fn varuint32_too_long_trailing() { assert!(deserialize_buffer::(&[0xff, 0xff, 0xff, 0xff, 0x7f][..],).is_err()); } #[test] fn varuint64_too_long_trailing() { assert!(deserialize_buffer::( &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04][..], ) .is_err()); } #[test] fn varint32_min() { varint32_serde_test(vec![0x80, 0x80, 0x80, 0x80, 0x78], -2147483648); } #[test] fn varint7_invalid() { match deserialize_buffer::(&[240]) { Err(Error::InvalidVarInt7(_)) => {}, _ => panic!("Should be invalid varint7 error!"), } } #[test] fn varint7_neg() { assert_eq!(-0x10i8, deserialize_buffer::(&[0x70]).expect("fail").into()); } #[test] fn varuint32_too_long_nulled() { match deserialize_buffer::(&[ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x78, ]) { Err(Error::InvalidVarUint32) => {}, _ => panic!("Should be invalid varuint32"), } } #[test] fn varint32_max() { varint32_serde_test(vec![0xff, 0xff, 0xff, 0xff, 0x07], 2147483647); } #[test] fn counted_list() { let payload = [ 133u8, //(128+5), length is 5 0x80, 0x80, 0x80, 0x0, // padding 0x01, 0x7d, 0x05, 0x07, 0x09, ]; let list: CountedList = deserialize_buffer(&payload).expect("type_section be deserialized"); let vars = list.into_inner(); assert_eq!(5, vars.len()); let v3: i8 = (*vars.get(1).unwrap()).into(); assert_eq!(-0x03i8, v3); } } parity-wasm-0.45.0/src/elements/reloc_section.rs000064400000000000000000000215650072674642500200310ustar 00000000000000use crate::io; use alloc::{string::String, vec::Vec}; use super::{ CountedList, CountedListWriter, CountedWriter, Deserialize, Error, Serialize, VarInt32, VarUint32, VarUint7, }; const FUNCTION_INDEX_LEB: u8 = 0; const TABLE_INDEX_SLEB: u8 = 1; const TABLE_INDEX_I32: u8 = 2; const MEMORY_ADDR_LEB: u8 = 3; const MEMORY_ADDR_SLEB: u8 = 4; const MEMORY_ADDR_I32: u8 = 5; const TYPE_INDEX_LEB: u8 = 6; const GLOBAL_INDEX_LEB: u8 = 7; /// Relocation information. #[derive(Clone, Debug, PartialEq)] pub struct RelocSection { /// Name of this section. name: String, /// ID of the section containing the relocations described in this section. section_id: u32, /// Name of the section containing the relocations described in this section. Only set if section_id is 0. relocation_section_name: Option, /// Relocation entries. entries: Vec, } impl RelocSection { /// Name of this section. pub fn name(&self) -> &str { &self.name } /// Name of this section (mutable). pub fn name_mut(&mut self) -> &mut String { &mut self.name } /// ID of the section containing the relocations described in this section. pub fn section_id(&self) -> u32 { self.section_id } /// ID of the section containing the relocations described in this section (mutable). pub fn section_id_mut(&mut self) -> &mut u32 { &mut self.section_id } /// Name of the section containing the relocations described in this section. pub fn relocation_section_name(&self) -> Option<&str> { self.relocation_section_name.as_deref() } /// Name of the section containing the relocations described in this section (mutable). pub fn relocation_section_name_mut(&mut self) -> &mut Option { &mut self.relocation_section_name } /// List of relocation entries. pub fn entries(&self) -> &[RelocationEntry] { &self.entries } /// List of relocation entries (mutable). pub fn entries_mut(&mut self) -> &mut Vec { &mut self.entries } } impl RelocSection { /// Deserialize a reloc section. pub fn deserialize(name: String, rdr: &mut R) -> Result { let section_id = VarUint32::deserialize(rdr)?.into(); let relocation_section_name = if section_id == 0 { Some(String::deserialize(rdr)?) } else { None }; let entries = CountedList::deserialize(rdr)?.into_inner(); Ok(RelocSection { name, section_id, relocation_section_name, entries }) } } impl Serialize for RelocSection { type Error = Error; fn serialize(self, wtr: &mut W) -> Result<(), Error> { let mut counted_writer = CountedWriter::new(wtr); self.name.serialize(&mut counted_writer)?; VarUint32::from(self.section_id).serialize(&mut counted_writer)?; if let Some(relocation_section_name) = self.relocation_section_name { relocation_section_name.serialize(&mut counted_writer)?; } let counted_list = CountedListWriter(self.entries.len(), self.entries.into_iter()); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Relocation entry. #[derive(Clone, Copy, Debug, PartialEq)] pub enum RelocationEntry { /// Function index. FunctionIndexLeb { /// Offset of the value to rewrite. offset: u32, /// Index of the function symbol in the symbol table. index: u32, }, /// Function table index. TableIndexSleb { /// Offset of the value to rewrite. offset: u32, /// Index of the function symbol in the symbol table. index: u32, }, /// Function table index. TableIndexI32 { /// Offset of the value to rewrite. offset: u32, /// Index of the function symbol in the symbol table. index: u32, }, /// Linear memory index. MemoryAddressLeb { /// Offset of the value to rewrite. offset: u32, /// Index of the data symbol in the symbol table. index: u32, /// Addend to add to the address. addend: i32, }, /// Linear memory index. MemoryAddressSleb { /// Offset of the value to rewrite. offset: u32, /// Index of the data symbol in the symbol table. index: u32, /// Addend to add to the address. addend: i32, }, /// Linear memory index. MemoryAddressI32 { /// Offset of the value to rewrite. offset: u32, /// Index of the data symbol in the symbol table. index: u32, /// Addend to add to the address. addend: i32, }, /// Type table index. TypeIndexLeb { /// Offset of the value to rewrite. offset: u32, /// Index of the type used. index: u32, }, /// Global index. GlobalIndexLeb { /// Offset of the value to rewrite. offset: u32, /// Index of the global symbol in the symbol table. index: u32, }, } impl Deserialize for RelocationEntry { type Error = Error; fn deserialize(rdr: &mut R) -> Result { match VarUint7::deserialize(rdr)?.into() { FUNCTION_INDEX_LEB => Ok(RelocationEntry::FunctionIndexLeb { offset: VarUint32::deserialize(rdr)?.into(), index: VarUint32::deserialize(rdr)?.into(), }), TABLE_INDEX_SLEB => Ok(RelocationEntry::TableIndexSleb { offset: VarUint32::deserialize(rdr)?.into(), index: VarUint32::deserialize(rdr)?.into(), }), TABLE_INDEX_I32 => Ok(RelocationEntry::TableIndexI32 { offset: VarUint32::deserialize(rdr)?.into(), index: VarUint32::deserialize(rdr)?.into(), }), MEMORY_ADDR_LEB => Ok(RelocationEntry::MemoryAddressLeb { offset: VarUint32::deserialize(rdr)?.into(), index: VarUint32::deserialize(rdr)?.into(), addend: VarInt32::deserialize(rdr)?.into(), }), MEMORY_ADDR_SLEB => Ok(RelocationEntry::MemoryAddressSleb { offset: VarUint32::deserialize(rdr)?.into(), index: VarUint32::deserialize(rdr)?.into(), addend: VarInt32::deserialize(rdr)?.into(), }), MEMORY_ADDR_I32 => Ok(RelocationEntry::MemoryAddressI32 { offset: VarUint32::deserialize(rdr)?.into(), index: VarUint32::deserialize(rdr)?.into(), addend: VarInt32::deserialize(rdr)?.into(), }), TYPE_INDEX_LEB => Ok(RelocationEntry::TypeIndexLeb { offset: VarUint32::deserialize(rdr)?.into(), index: VarUint32::deserialize(rdr)?.into(), }), GLOBAL_INDEX_LEB => Ok(RelocationEntry::GlobalIndexLeb { offset: VarUint32::deserialize(rdr)?.into(), index: VarUint32::deserialize(rdr)?.into(), }), entry_type => Err(Error::UnknownValueType(entry_type as i8)), } } } impl Serialize for RelocationEntry { type Error = Error; fn serialize(self, wtr: &mut W) -> Result<(), Error> { match self { RelocationEntry::FunctionIndexLeb { offset, index } => { VarUint7::from(FUNCTION_INDEX_LEB).serialize(wtr)?; VarUint32::from(offset).serialize(wtr)?; VarUint32::from(index).serialize(wtr)?; }, RelocationEntry::TableIndexSleb { offset, index } => { VarUint7::from(TABLE_INDEX_SLEB).serialize(wtr)?; VarUint32::from(offset).serialize(wtr)?; VarUint32::from(index).serialize(wtr)?; }, RelocationEntry::TableIndexI32 { offset, index } => { VarUint7::from(TABLE_INDEX_I32).serialize(wtr)?; VarUint32::from(offset).serialize(wtr)?; VarUint32::from(index).serialize(wtr)?; }, RelocationEntry::MemoryAddressLeb { offset, index, addend } => { VarUint7::from(MEMORY_ADDR_LEB).serialize(wtr)?; VarUint32::from(offset).serialize(wtr)?; VarUint32::from(index).serialize(wtr)?; VarInt32::from(addend).serialize(wtr)?; }, RelocationEntry::MemoryAddressSleb { offset, index, addend } => { VarUint7::from(MEMORY_ADDR_SLEB).serialize(wtr)?; VarUint32::from(offset).serialize(wtr)?; VarUint32::from(index).serialize(wtr)?; VarInt32::from(addend).serialize(wtr)?; }, RelocationEntry::MemoryAddressI32 { offset, index, addend } => { VarUint7::from(MEMORY_ADDR_I32).serialize(wtr)?; VarUint32::from(offset).serialize(wtr)?; VarUint32::from(index).serialize(wtr)?; VarInt32::from(addend).serialize(wtr)?; }, RelocationEntry::TypeIndexLeb { offset, index } => { VarUint7::from(TYPE_INDEX_LEB).serialize(wtr)?; VarUint32::from(offset).serialize(wtr)?; VarUint32::from(index).serialize(wtr)?; }, RelocationEntry::GlobalIndexLeb { offset, index } => { VarUint7::from(GLOBAL_INDEX_LEB).serialize(wtr)?; VarUint32::from(offset).serialize(wtr)?; VarUint32::from(index).serialize(wtr)?; }, } Ok(()) } } #[cfg(test)] mod tests { use super::{ super::{deserialize_file, Section}, RelocationEntry, }; #[test] fn reloc_section() { let module = deserialize_file("./res/cases/v1/relocatable.wasm") .expect("Module should be deserialized") .parse_reloc() .expect("Reloc section should be deserialized"); let mut found = false; for section in module.sections() { if let Section::Reloc(ref reloc_section) = *section { assert_eq!( vec![ RelocationEntry::MemoryAddressSleb { offset: 4, index: 0, addend: 0 }, RelocationEntry::FunctionIndexLeb { offset: 12, index: 0 }, ], reloc_section.entries() ); found = true } } assert!(found, "There should be a reloc section in relocatable.wasm"); } } parity-wasm-0.45.0/src/elements/section.rs000064400000000000000000000661030072674642500166420ustar 00000000000000use super::{ serialize, CountedList, CountedListWriter, CountedWriter, DataSegment, Deserialize, ElementSegment, Error, ExportEntry, External, Func, FuncBody, GlobalEntry, ImportEntry, MemoryType, Serialize, TableType, VarUint32, VarUint7, }; use crate::{elements, io}; use alloc::{borrow::ToOwned, string::String, vec::Vec}; use super::{name_section::NameSection, reloc_section::RelocSection, types::Type}; #[cfg(feature = "reduced-stack-buffer")] const ENTRIES_BUFFER_LENGTH: usize = 256; #[cfg(not(feature = "reduced-stack-buffer"))] const ENTRIES_BUFFER_LENGTH: usize = 16384; /// Section in the WebAssembly module. #[derive(Debug, Clone, PartialEq)] pub enum Section { /// Section is unparsed. Unparsed { /// id of the unparsed section. id: u8, /// raw bytes of the unparsed section. payload: Vec, }, /// Custom section (`id=0`). Custom(CustomSection), /// Types section. Type(TypeSection), /// Import section. Import(ImportSection), /// Function signatures section. Function(FunctionSection), /// Table definition section. Table(TableSection), /// Memory definition section. Memory(MemorySection), /// Global entries section. Global(GlobalSection), /// Export definitions. Export(ExportSection), /// Entry reference of the module. Start(u32), /// Elements section. Element(ElementSection), /// Number of passive data entries in the data section DataCount(u32), /// Function bodies section. Code(CodeSection), /// Data definition section. Data(DataSection), /// Name section. /// /// Note that initially it is not parsed until `parse_names` is called explicitly. Name(NameSection), /// Relocation section. /// /// Note that initially it is not parsed until `parse_reloc` is called explicitly. /// Also note that currently there are serialization (but not de-serialization) /// issues with this section (#198). Reloc(RelocSection), } impl Deserialize for Section { type Error = Error; fn deserialize(reader: &mut R) -> Result { let id = match VarUint7::deserialize(reader) { // todo: be more selective detecting no more section Err(_) => return Err(Error::UnexpectedEof), Ok(id) => id, }; Ok(match id.into() { 0 => Section::Custom(CustomSection::deserialize(reader)?), 1 => Section::Type(TypeSection::deserialize(reader)?), 2 => Section::Import(ImportSection::deserialize(reader)?), 3 => Section::Function(FunctionSection::deserialize(reader)?), 4 => Section::Table(TableSection::deserialize(reader)?), 5 => Section::Memory(MemorySection::deserialize(reader)?), 6 => Section::Global(GlobalSection::deserialize(reader)?), 7 => Section::Export(ExportSection::deserialize(reader)?), 8 => { let mut section_reader = SectionReader::new(reader)?; let start_idx = VarUint32::deserialize(&mut section_reader)?; section_reader.close()?; Section::Start(start_idx.into()) }, 9 => Section::Element(ElementSection::deserialize(reader)?), 10 => Section::Code(CodeSection::deserialize(reader)?), 11 => Section::Data(DataSection::deserialize(reader)?), 12 => { let mut section_reader = SectionReader::new(reader)?; let count = VarUint32::deserialize(&mut section_reader)?; section_reader.close()?; Section::DataCount(count.into()) }, invalid_id => return Err(Error::InvalidSectionId(invalid_id)), }) } } impl Serialize for Section { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { match self { Section::Custom(custom_section) => { VarUint7::from(0x00).serialize(writer)?; custom_section.serialize(writer)?; }, Section::Unparsed { id, payload } => { VarUint7::from(id).serialize(writer)?; writer.write(&payload[..])?; }, Section::Type(type_section) => { VarUint7::from(0x01).serialize(writer)?; type_section.serialize(writer)?; }, Section::Import(import_section) => { VarUint7::from(0x02).serialize(writer)?; import_section.serialize(writer)?; }, Section::Function(function_section) => { VarUint7::from(0x03).serialize(writer)?; function_section.serialize(writer)?; }, Section::Table(table_section) => { VarUint7::from(0x04).serialize(writer)?; table_section.serialize(writer)?; }, Section::Memory(memory_section) => { VarUint7::from(0x05).serialize(writer)?; memory_section.serialize(writer)?; }, Section::Global(global_section) => { VarUint7::from(0x06).serialize(writer)?; global_section.serialize(writer)?; }, Section::Export(export_section) => { VarUint7::from(0x07).serialize(writer)?; export_section.serialize(writer)?; }, Section::Start(index) => { VarUint7::from(0x08).serialize(writer)?; let mut counted_writer = CountedWriter::new(writer); VarUint32::from(index).serialize(&mut counted_writer)?; counted_writer.done()?; }, Section::DataCount(count) => { VarUint7::from(0x0c).serialize(writer)?; let mut counted_writer = CountedWriter::new(writer); VarUint32::from(count).serialize(&mut counted_writer)?; counted_writer.done()?; }, Section::Element(element_section) => { VarUint7::from(0x09).serialize(writer)?; element_section.serialize(writer)?; }, Section::Code(code_section) => { VarUint7::from(0x0a).serialize(writer)?; code_section.serialize(writer)?; }, Section::Data(data_section) => { VarUint7::from(0x0b).serialize(writer)?; data_section.serialize(writer)?; }, Section::Name(name_section) => { VarUint7::from(0x00).serialize(writer)?; let custom = CustomSection { name: "name".to_owned(), payload: serialize(name_section)? }; custom.serialize(writer)?; }, Section::Reloc(reloc_section) => { VarUint7::from(0x00).serialize(writer)?; reloc_section.serialize(writer)?; }, } Ok(()) } } impl Section { pub(crate) fn order(&self) -> u8 { match *self { Section::Custom(_) => 0x00, Section::Unparsed { .. } => 0x00, Section::Type(_) => 0x1, Section::Import(_) => 0x2, Section::Function(_) => 0x3, Section::Table(_) => 0x4, Section::Memory(_) => 0x5, Section::Global(_) => 0x6, Section::Export(_) => 0x7, Section::Start(_) => 0x8, Section::Element(_) => 0x9, Section::DataCount(_) => 0x0a, Section::Code(_) => 0x0b, Section::Data(_) => 0x0c, Section::Name(_) => 0x00, Section::Reloc(_) => 0x00, } } } pub(crate) struct SectionReader { cursor: io::Cursor>, declared_length: usize, } impl SectionReader { pub fn new(reader: &mut R) -> Result { let length = u32::from(VarUint32::deserialize(reader)?) as usize; let inner_buffer = buffered_read!(ENTRIES_BUFFER_LENGTH, length, reader); let declared_length = inner_buffer.len(); let cursor = io::Cursor::new(inner_buffer); Ok(SectionReader { cursor, declared_length }) } pub fn close(self) -> Result<(), io::Error> { let cursor = self.cursor; let buf_length = self.declared_length; if cursor.position() != buf_length { Err(io::Error::InvalidData) } else { Ok(()) } } } impl io::Read for SectionReader { fn read(&mut self, buf: &mut [u8]) -> io::Result<()> { self.cursor.read(buf)?; Ok(()) } } fn read_entries>( reader: &mut R, ) -> Result, elements::Error> { let mut section_reader = SectionReader::new(reader)?; let result = CountedList::::deserialize(&mut section_reader)?.into_inner(); section_reader.close()?; Ok(result) } /// Custom section. #[derive(Debug, Default, Clone, PartialEq)] pub struct CustomSection { name: String, payload: Vec, } impl CustomSection { /// Creates a new custom section with the given name and payload. pub fn new(name: String, payload: Vec) -> CustomSection { CustomSection { name, payload } } /// Name of the custom section. pub fn name(&self) -> &str { &self.name } /// Payload of the custom section. pub fn payload(&self) -> &[u8] { &self.payload } /// Name of the custom section (mutable). pub fn name_mut(&mut self) -> &mut String { &mut self.name } /// Payload of the custom section (mutable). pub fn payload_mut(&mut self) -> &mut Vec { &mut self.payload } } impl Deserialize for CustomSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { let section_length: usize = u32::from(VarUint32::deserialize(reader)?) as usize; let buf = buffered_read!(ENTRIES_BUFFER_LENGTH, section_length, reader); let mut cursor = io::Cursor::new(&buf[..]); let name = String::deserialize(&mut cursor)?; let payload = buf[cursor.position() as usize..].to_vec(); Ok(CustomSection { name, payload }) } } impl Serialize for CustomSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { use io::Write; let mut counted_writer = CountedWriter::new(writer); self.name.serialize(&mut counted_writer)?; counted_writer.write(&self.payload[..])?; counted_writer.done()?; Ok(()) } } /// Section with type declarations. #[derive(Debug, Default, Clone, PartialEq)] pub struct TypeSection(Vec); impl TypeSection { /// New type section with provided types. pub fn with_types(types: Vec) -> Self { TypeSection(types) } /// List of type declarations. pub fn types(&self) -> &[Type] { &self.0 } /// List of type declarations (mutable). pub fn types_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for TypeSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(TypeSection(read_entries(reader)?)) } } impl Serialize for TypeSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Section of the imports definition. #[derive(Debug, Default, Clone, PartialEq)] pub struct ImportSection(Vec); impl ImportSection { /// New import section with provided types. pub fn with_entries(entries: Vec) -> Self { ImportSection(entries) } /// List of import entries. pub fn entries(&self) -> &[ImportEntry] { &self.0 } /// List of import entries (mutable). pub fn entries_mut(&mut self) -> &mut Vec { &mut self.0 } /// Returns number of functions. pub fn functions(&self) -> usize { self.0 .iter() .filter(|entry| matches!(*entry.external(), External::Function(_))) .count() } /// Returns number of globals pub fn globals(&self) -> usize { self.0 .iter() .filter(|entry| matches!(entry.external(), &External::Global(_))) .count() } } impl Deserialize for ImportSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(ImportSection(read_entries(reader)?)) } } impl Serialize for ImportSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Section with function signatures definition. #[derive(Default, Debug, Clone, PartialEq)] pub struct FunctionSection(Vec); impl FunctionSection { /// New function signatures section with provided entries. pub fn with_entries(entries: Vec) -> Self { FunctionSection(entries) } /// List of all functions in the section, mutable. pub fn entries_mut(&mut self) -> &mut Vec { &mut self.0 } /// List of all functions in the section. pub fn entries(&self) -> &[Func] { &self.0 } } impl Deserialize for FunctionSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(FunctionSection(read_entries(reader)?)) } } impl Serialize for FunctionSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::( data.len(), data.into_iter().map(|func| func.type_ref().into()), ); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Section with table definition (currently only one is allowed). #[derive(Default, Debug, Clone, PartialEq)] pub struct TableSection(Vec); impl TableSection { /// Table entries. pub fn entries(&self) -> &[TableType] { &self.0 } /// New table section with provided table entries. pub fn with_entries(entries: Vec) -> Self { TableSection(entries) } /// Mutable table entries. pub fn entries_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for TableSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(TableSection(read_entries(reader)?)) } } impl Serialize for TableSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Section with table definition (currently only one entry is allowed). #[derive(Default, Debug, Clone, PartialEq)] pub struct MemorySection(Vec); impl MemorySection { /// List of all memory entries in the section pub fn entries(&self) -> &[MemoryType] { &self.0 } /// New memory section with memory types. pub fn with_entries(entries: Vec) -> Self { MemorySection(entries) } /// Mutable list of all memory entries in the section. pub fn entries_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for MemorySection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(MemorySection(read_entries(reader)?)) } } impl Serialize for MemorySection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Globals definition section. #[derive(Default, Debug, Clone, PartialEq)] pub struct GlobalSection(Vec); impl GlobalSection { /// List of all global entries in the section. pub fn entries(&self) -> &[GlobalEntry] { &self.0 } /// New global section from list of global entries. pub fn with_entries(entries: Vec) -> Self { GlobalSection(entries) } /// List of all global entries in the section (mutable). pub fn entries_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for GlobalSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(GlobalSection(read_entries(reader)?)) } } impl Serialize for GlobalSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// List of exports definition. #[derive(Debug, Default, Clone, PartialEq)] pub struct ExportSection(Vec); impl ExportSection { /// List of all export entries in the section. pub fn entries(&self) -> &[ExportEntry] { &self.0 } /// New export section from list of export entries. pub fn with_entries(entries: Vec) -> Self { ExportSection(entries) } /// List of all export entries in the section (mutable). pub fn entries_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for ExportSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(ExportSection(read_entries(reader)?)) } } impl Serialize for ExportSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Section with function bodies of the module. #[derive(Default, Debug, Clone, PartialEq)] pub struct CodeSection(Vec); impl CodeSection { /// New code section with specified function bodies. pub fn with_bodies(bodies: Vec) -> Self { CodeSection(bodies) } /// All function bodies in the section. pub fn bodies(&self) -> &[FuncBody] { &self.0 } /// All function bodies in the section, mutable. pub fn bodies_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for CodeSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(CodeSection(read_entries(reader)?)) } } impl Serialize for CodeSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Element entries section. #[derive(Default, Debug, Clone, PartialEq)] pub struct ElementSection(Vec); impl ElementSection { /// New elements section. pub fn with_entries(entries: Vec) -> Self { ElementSection(entries) } /// New elements entries in the section. pub fn entries(&self) -> &[ElementSegment] { &self.0 } /// List of all data entries in the section (mutable). pub fn entries_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for ElementSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(ElementSection(read_entries(reader)?)) } } impl Serialize for ElementSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } /// Data entries definitions. #[derive(Default, Debug, Clone, PartialEq)] pub struct DataSection(Vec); impl DataSection { /// New data section. pub fn with_entries(entries: Vec) -> Self { DataSection(entries) } /// List of all data entries in the section. pub fn entries(&self) -> &[DataSegment] { &self.0 } /// List of all data entries in the section (mutable). pub fn entries_mut(&mut self) -> &mut Vec { &mut self.0 } } impl Deserialize for DataSection { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(DataSection(read_entries(reader)?)) } } impl Serialize for DataSection { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let mut counted_writer = CountedWriter::new(writer); let data = self.0; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(&mut counted_writer)?; counted_writer.done()?; Ok(()) } } #[cfg(test)] mod tests { use super::{ super::{ deserialize_buffer, deserialize_file, serialize, BlockType, DataSegment, ElementSegment, FuncBody, InitExpr, Instructions, Local, ValueType, }, CodeSection, DataSection, ElementSection, Section, Type, TypeSection, }; #[test] fn import_section() { let module = deserialize_file("./res/cases/v1/test5.wasm").expect("Should be deserialized"); let mut found = false; for section in module.sections() { if let Section::Import(ref import_section) = *section { assert_eq!(25, import_section.entries().len()); found = true } } assert!(found, "There should be import section in test5.wasm"); } fn functions_test_payload() -> &'static [u8] { &[ // functions section id 0x03u8, // functions section length 0x87, 0x80, 0x80, 0x80, 0x0, // number of functions 0x04, // type reference 1 0x01, // type reference 2 0x86, 0x80, 0x00, // type reference 3 0x09, // type reference 4 0x33, ] } #[test] fn fn_section_detect() { let section: Section = deserialize_buffer(functions_test_payload()).expect("section to be deserialized"); match section { Section::Function(_) => {}, _ => { panic!("Payload should be recognized as functions section") }, } } #[test] fn fn_section_number() { let section: Section = deserialize_buffer(functions_test_payload()).expect("section to be deserialized"); if let Section::Function(fn_section) = section { assert_eq!(4, fn_section.entries().len(), "There should be 4 functions total"); } } #[test] fn fn_section_ref() { let section: Section = deserialize_buffer(functions_test_payload()).expect("section to be deserialized"); if let Section::Function(fn_section) = section { assert_eq!(6, fn_section.entries()[1].type_ref()); } } fn types_test_payload() -> &'static [u8] { &[ // section length 11, // 2 functions 2, // func 1, form =1 0x60, // param_count=1 1, // first param 0x7e, // i64 // no return params 0x00, // func 2, form=1 0x60, // param_count=2 2, // first param 0x7e, // second param 0x7d, // return param (is_present, param_type) 0x01, 0x7e, ] } #[test] fn type_section_len() { let type_section: TypeSection = deserialize_buffer(types_test_payload()).expect("type_section be deserialized"); assert_eq!(type_section.types().len(), 2); } #[test] fn type_section_infer() { let type_section: TypeSection = deserialize_buffer(types_test_payload()).expect("type_section be deserialized"); let Type::Function(ref t1) = type_section.types()[1]; assert_eq!(vec![ValueType::I64], t1.results()); assert_eq!(2, t1.params().len()); } fn export_payload() -> &'static [u8] { &[ // section id 0x07, // section length 28, // 6 entries 6, // func "A", index 6 // [name_len(1-5 bytes), name_bytes(name_len, internal_kind(1byte), internal_index(1-5 bytes)]) 0x01, 0x41, 0x01, 0x86, 0x80, 0x00, // func "B", index 8 0x01, 0x42, 0x01, 0x86, 0x00, // func "C", index 7 0x01, 0x43, 0x01, 0x07, // memory "D", index 0 0x01, 0x44, 0x02, 0x00, // func "E", index 1 0x01, 0x45, 0x01, 0x01, // func "F", index 2 0x01, 0x46, 0x01, 0x02, ] } #[test] fn export_detect() { let section: Section = deserialize_buffer(export_payload()).expect("section to be deserialized"); match section { Section::Export(_) => {}, _ => { panic!("Payload should be recognized as export section") }, } } fn code_payload() -> &'static [u8] { &[ // sectionid 0x0Au8, // section length, 32 0x20, // body count 0x01, // body 1, length 30 0x1E, 0x01, 0x01, 0x7F, // local i32 (one collection of length one of type i32) 0x02, 0x7F, // block i32 0x23, 0x00, // get_global 0 0x21, 0x01, // set_local 1 0x23, 0x00, // get_global 0 0x20, 0x00, // get_local 0 0x6A, // i32.add 0x24, 0x00, // set_global 0 0x23, 0x00, // get_global 0 0x41, 0x0F, // i32.const 15 0x6A, // i32.add 0x41, 0x70, // i32.const -16 0x71, // i32.and 0x24, 0x00, // set_global 0 0x20, 0x01, // get_local 1 0x0B, 0x0B, ] } #[test] fn code_detect() { let section: Section = deserialize_buffer(code_payload()).expect("section to be deserialized"); match section { Section::Code(_) => {}, _ => { panic!("Payload should be recognized as a code section") }, } } fn data_payload() -> &'static [u8] { &[ 0x0bu8, // section id 20, // 20 bytes overall 0x01, // number of segments 0x00, // index 0x0b, // just `end` op 0x10, // 16x 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] } #[test] fn data_section_ser() { let data_section = DataSection::with_entries(vec![DataSegment::new( 0u32, Some(InitExpr::empty()), vec![0u8; 16], )]); let buf = serialize(data_section).expect("Data section to be serialized"); assert_eq!( buf, vec![ 20u8, // 19 bytes overall 0x01, // number of segments 0x00, // index 0x0b, // just `end` op 16, // value of length 16 0x00, 0x00, 0x00, 0x00, // 16x 0x00 as in initialization 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] ); } #[test] fn data_section_detect() { let section: Section = deserialize_buffer(data_payload()).expect("section to be deserialized"); match section { Section::Data(_) => {}, _ => { panic!("Payload should be recognized as a data section") }, } } #[test] fn element_section_ser() { let element_section = ElementSection::with_entries(vec![ElementSegment::new( 0u32, Some(InitExpr::empty()), vec![0u32; 4], )]); let buf = serialize(element_section).expect("Element section to be serialized"); assert_eq!( buf, vec![ 8u8, // 8 bytes overall 0x01, // number of segments 0x00, // index 0x0b, // just `end` op 0x04, // 4 elements 0x00, 0x00, 0x00, 0x00 // 4x 0x00 as in initialization ] ); } #[test] fn code_section_ser() { use super::super::Instruction::*; let code_section = CodeSection::with_bodies(vec![FuncBody::new( vec![Local::new(1, ValueType::I32)], Instructions::new(vec![ Block(BlockType::Value(ValueType::I32)), GetGlobal(0), End, End, ]), )]); let buf = serialize(code_section).expect("Code section to be serialized"); assert_eq!( buf, vec![ 11u8, // 11 bytes total section size 0x01, // 1 function 9, // function #1 total code size 1, // 1 local variable declaration 1, // amount of variables 0x7f, // type of variable (7-bit, -0x01), negative 0x02, // block 0x7f, // block return type (7-bit, -0x01), negative 0x23, 0x00, // get_global(0) 0x0b, // block end 0x0b, // function end ] ); } #[test] fn start_section() { let section: Section = deserialize_buffer(&[8u8, 1u8, 0u8]).expect("Start section to deserialize"); if let Section::Start(_) = section { } else { panic!("Payload should be a start section"); } let serialized = serialize(section).expect("Start section to successfully serializen"); assert_eq!(serialized, vec![8u8, 1u8, 0u8]); } } parity-wasm-0.45.0/src/elements/segment.rs000064400000000000000000000171140072674642500166360ustar 00000000000000use super::{CountedList, CountedListWriter, Deserialize, Error, InitExpr, Serialize, VarUint32}; use crate::io; use alloc::vec::Vec; #[cfg(feature = "bulk")] const FLAG_MEMZERO: u32 = 0; #[cfg(feature = "bulk")] const FLAG_PASSIVE: u32 = 1; #[cfg(feature = "bulk")] const FLAG_MEM_NONZERO: u32 = 2; #[cfg(feature = "reduced-stack-buffer")] const VALUES_BUFFER_LENGTH: usize = 256; #[cfg(not(feature = "reduced-stack-buffer"))] const VALUES_BUFFER_LENGTH: usize = 16384; /// Entry in the element section. #[derive(Debug, Clone, PartialEq)] pub struct ElementSegment { index: u32, offset: Option, members: Vec, #[cfg(feature = "bulk")] passive: bool, } impl ElementSegment { /// New element segment. pub fn new(index: u32, offset: Option, members: Vec) -> Self { ElementSegment { index, offset, members, #[cfg(feature = "bulk")] passive: false, } } /// Sequence of function indices. pub fn members(&self) -> &[u32] { &self.members } /// Sequence of function indices (mutable) pub fn members_mut(&mut self) -> &mut Vec { &mut self.members } /// Table index (currently valid only value of `0`) pub fn index(&self) -> u32 { self.index } /// An i32 initializer expression that computes the offset at which to place the elements. /// /// Note that this return `None` if the segment is `passive`. pub fn offset(&self) -> &Option { &self.offset } /// An i32 initializer expression that computes the offset at which to place the elements (mutable) /// /// Note that this return `None` if the segment is `passive`. pub fn offset_mut(&mut self) -> &mut Option { &mut self.offset } } #[cfg(feature = "bulk")] impl ElementSegment { /// Whether or not this table segment is "passive" pub fn passive(&self) -> bool { self.passive } /// Whether or not this table segment is "passive" pub fn passive_mut(&mut self) -> &mut bool { &mut self.passive } /// Set whether or not this table segment is "passive" pub fn set_passive(&mut self, passive: bool) { self.passive = passive; } } impl Deserialize for ElementSegment { type Error = Error; #[cfg(not(feature = "bulk"))] fn deserialize(reader: &mut R) -> Result { let index: u32 = VarUint32::deserialize(reader)?.into(); let offset = InitExpr::deserialize(reader)?; let members: Vec = CountedList::::deserialize(reader)? .into_inner() .into_iter() .map(Into::into) .collect(); Ok(ElementSegment { index, offset: Some(offset), members }) } #[cfg(feature = "bulk")] fn deserialize(reader: &mut R) -> Result { // This piece of data was treated as `index` [of the table], but was repurposed // for flags in bulk-memory operations proposal. let flags: u32 = VarUint32::deserialize(reader)?.into(); let index = if flags == FLAG_MEMZERO || flags == FLAG_PASSIVE { 0u32 } else if flags == FLAG_MEM_NONZERO { VarUint32::deserialize(reader)?.into() } else { return Err(Error::InvalidSegmentFlags(flags)) }; let offset = if flags == FLAG_PASSIVE { None } else { Some(InitExpr::deserialize(reader)?) }; let members: Vec = CountedList::::deserialize(reader)? .into_inner() .into_iter() .map(Into::into) .collect(); Ok(ElementSegment { index, offset, members, passive: flags == FLAG_PASSIVE }) } } impl Serialize for ElementSegment { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { #[cfg(feature = "bulk")] { if self.passive { VarUint32::from(FLAG_PASSIVE).serialize(writer)?; } else if self.index != 0 { VarUint32::from(FLAG_MEM_NONZERO).serialize(writer)?; VarUint32::from(self.index).serialize(writer)?; } else { VarUint32::from(FLAG_MEMZERO).serialize(writer)?; } } #[cfg(not(feature = "bulk"))] VarUint32::from(self.index).serialize(writer)?; if let Some(offset) = self.offset { offset.serialize(writer)?; } let data = self.members; let counted_list = CountedListWriter::(data.len(), data.into_iter().map(Into::into)); counted_list.serialize(writer)?; Ok(()) } } /// Data segment definition. #[derive(Clone, Debug, PartialEq)] pub struct DataSegment { index: u32, offset: Option, value: Vec, #[cfg(feature = "bulk")] passive: bool, } impl DataSegment { /// New data segments. pub fn new(index: u32, offset: Option, value: Vec) -> Self { DataSegment { index, offset, value, #[cfg(feature = "bulk")] passive: false, } } /// Linear memory index (currently the only valid value is `0`). pub fn index(&self) -> u32 { self.index } /// An i32 initializer expression that computes the offset at which to place the data. /// /// Note that this return `None` if the segment is `passive`. pub fn offset(&self) -> &Option { &self.offset } /// An i32 initializer expression that computes the offset at which to place the data (mutable) /// /// Note that this return `None` if the segment is `passive`. pub fn offset_mut(&mut self) -> &mut Option { &mut self.offset } /// Initial value of the data segment. pub fn value(&self) -> &[u8] { &self.value } /// Initial value of the data segment (mutable). pub fn value_mut(&mut self) -> &mut Vec { &mut self.value } } #[cfg(feature = "bulk")] impl DataSegment { /// Whether or not this data segment is "passive". pub fn passive(&self) -> bool { self.passive } /// Whether or not this data segment is "passive" (mutable). pub fn passive_mut(&mut self) -> &mut bool { &mut self.passive } /// Set whether or not this table segment is "passive" pub fn set_passive(&mut self, passive: bool) { self.passive = passive; } } impl Deserialize for DataSegment { type Error = Error; #[cfg(not(feature = "bulk"))] fn deserialize(reader: &mut R) -> Result { let index = VarUint32::deserialize(reader)?; let offset = InitExpr::deserialize(reader)?; let value_len = u32::from(VarUint32::deserialize(reader)?) as usize; let value = buffered_read!(VALUES_BUFFER_LENGTH, value_len, reader); Ok(DataSegment { index: index.into(), offset: Some(offset), value }) } #[cfg(feature = "bulk")] fn deserialize(reader: &mut R) -> Result { let flags: u32 = VarUint32::deserialize(reader)?.into(); let index = if flags == FLAG_MEMZERO || flags == FLAG_PASSIVE { 0u32 } else if flags == FLAG_MEM_NONZERO { VarUint32::deserialize(reader)?.into() } else { return Err(Error::InvalidSegmentFlags(flags)) }; let offset = if flags == FLAG_PASSIVE { None } else { Some(InitExpr::deserialize(reader)?) }; let value_len = u32::from(VarUint32::deserialize(reader)?) as usize; let value = buffered_read!(VALUES_BUFFER_LENGTH, value_len, reader); Ok(DataSegment { index, offset, value, passive: flags == FLAG_PASSIVE }) } } impl Serialize for DataSegment { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { #[cfg(feature = "bulk")] { if self.passive { VarUint32::from(FLAG_PASSIVE).serialize(writer)?; } else if self.index != 0 { VarUint32::from(FLAG_MEM_NONZERO).serialize(writer)?; VarUint32::from(self.index).serialize(writer)?; } else { VarUint32::from(FLAG_MEMZERO).serialize(writer)?; } } #[cfg(not(feature = "bulk"))] VarUint32::from(self.index).serialize(writer)?; if let Some(offset) = self.offset { offset.serialize(writer)?; } let value = self.value; VarUint32::from(value.len()).serialize(writer)?; writer.write(&value[..])?; Ok(()) } } parity-wasm-0.45.0/src/elements/types.rs000064400000000000000000000154210072674642500163370ustar 00000000000000use super::{ CountedList, CountedListWriter, Deserialize, Error, Serialize, VarInt32, VarInt7, VarUint7, }; use crate::io; use alloc::vec::Vec; use core::fmt; /// Type definition in types section. Currently can be only of the function type. #[derive(Debug, Clone, PartialEq, Hash, Eq)] pub enum Type { /// Function type. Function(FunctionType), } impl Deserialize for Type { type Error = Error; fn deserialize(reader: &mut R) -> Result { Ok(Type::Function(FunctionType::deserialize(reader)?)) } } impl Serialize for Type { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { match self { Type::Function(fn_type) => fn_type.serialize(writer), } } } /// Value type. #[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] pub enum ValueType { /// 32-bit signed integer I32, /// 64-bit signed integer I64, /// 32-bit float F32, /// 64-bit float F64, #[cfg(feature = "simd")] /// 128-bit SIMD register V128, } impl Deserialize for ValueType { type Error = Error; fn deserialize(reader: &mut R) -> Result { let val = VarInt7::deserialize(reader)?; match val.into() { -0x01 => Ok(ValueType::I32), -0x02 => Ok(ValueType::I64), -0x03 => Ok(ValueType::F32), -0x04 => Ok(ValueType::F64), #[cfg(feature = "simd")] -0x05 => Ok(ValueType::V128), _ => Err(Error::UnknownValueType(val.into())), } } } impl Serialize for ValueType { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let val: VarInt7 = match self { ValueType::I32 => -0x01, ValueType::I64 => -0x02, ValueType::F32 => -0x03, ValueType::F64 => -0x04, #[cfg(feature = "simd")] ValueType::V128 => -0x05, } .into(); val.serialize(writer)?; Ok(()) } } impl fmt::Display for ValueType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ValueType::I32 => write!(f, "i32"), ValueType::I64 => write!(f, "i64"), ValueType::F32 => write!(f, "f32"), ValueType::F64 => write!(f, "f64"), #[cfg(feature = "simd")] ValueType::V128 => write!(f, "v128"), } } } /// Block type which is basically `ValueType` + NoResult (to define blocks that have no return type) #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum BlockType { /// No specified block type NoResult, /// Inline value type. Value(ValueType), /// Reference to a signature. #[cfg(feature = "multi_value")] TypeIndex(u32), } impl Deserialize for BlockType { type Error = Error; fn deserialize(reader: &mut R) -> Result { let val = VarInt32::deserialize(reader)?; match val.into() { -0x40 => Ok(BlockType::NoResult), -0x01 => Ok(BlockType::Value(ValueType::I32)), -0x02 => Ok(BlockType::Value(ValueType::I64)), -0x03 => Ok(BlockType::Value(ValueType::F32)), -0x04 => Ok(BlockType::Value(ValueType::F64)), #[cfg(feature = "simd")] -0x05 => Ok(BlockType::Value(ValueType::V128)), #[cfg(feature = "multi_value")] idx => { let idx = idx.try_into().map_err(|_| Error::UnknownBlockType(idx))?; Ok(BlockType::TypeIndex(idx)) }, #[cfg(not(feature = "multi_value"))] _ => Err(Error::UnknownBlockType(val.into())), } } } impl Serialize for BlockType { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let val: VarInt32 = match self { BlockType::NoResult => -0x40, BlockType::Value(ValueType::I32) => -0x01, BlockType::Value(ValueType::I64) => -0x02, BlockType::Value(ValueType::F32) => -0x03, BlockType::Value(ValueType::F64) => -0x04, #[cfg(feature = "simd")] BlockType::Value(ValueType::V128) => -0x05, #[cfg(feature = "multi_value")] BlockType::TypeIndex(idx) => idx as i32, } .into(); val.serialize(writer)?; Ok(()) } } /// Function signature type. #[derive(Debug, Clone, PartialEq, Hash, Eq)] pub struct FunctionType { form: u8, params: Vec, results: Vec, } impl Default for FunctionType { fn default() -> Self { FunctionType { form: 0x60, params: Vec::new(), results: Vec::new() } } } impl FunctionType { /// New function type given the params and results as vectors pub fn new(params: Vec, results: Vec) -> Self { FunctionType { form: 0x60, params, results } } /// Function form (currently only valid value is `0x60`) pub fn form(&self) -> u8 { self.form } /// Parameters in the function signature. pub fn params(&self) -> &[ValueType] { &self.params } /// Mutable parameters in the function signature. pub fn params_mut(&mut self) -> &mut Vec { &mut self.params } /// Results in the function signature, if any. pub fn results(&self) -> &[ValueType] { &self.results } /// Mutable type in the function signature, if any. pub fn results_mut(&mut self) -> &mut Vec { &mut self.results } } impl Deserialize for FunctionType { type Error = Error; fn deserialize(reader: &mut R) -> Result { let form: u8 = VarUint7::deserialize(reader)?.into(); if form != 0x60 { return Err(Error::UnknownFunctionForm(form)) } let params: Vec = CountedList::deserialize(reader)?.into_inner(); let results: Vec = CountedList::deserialize(reader)?.into_inner(); #[cfg(not(feature = "multi_value"))] if results.len() > 1 { return Err(Error::Other( "Enable the multi_value feature to deserialize more than one function result", )) } Ok(FunctionType { form, params, results }) } } impl Serialize for FunctionType { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { VarUint7::from(self.form).serialize(writer)?; let params_counted_list = CountedListWriter::( self.params.len(), self.params.into_iter().map(Into::into), ); params_counted_list.serialize(writer)?; let results_counted_list = CountedListWriter::( self.results.len(), self.results.into_iter().map(Into::into), ); results_counted_list.serialize(writer)?; Ok(()) } } /// Table element type. #[derive(Clone, Copy, Debug, PartialEq)] pub enum TableElementType { /// A reference to a function with any signature. AnyFunc, } impl Deserialize for TableElementType { type Error = Error; fn deserialize(reader: &mut R) -> Result { let val = VarInt7::deserialize(reader)?; match val.into() { -0x10 => Ok(TableElementType::AnyFunc), _ => Err(Error::UnknownTableElementType(val.into())), } } } impl Serialize for TableElementType { type Error = Error; fn serialize(self, writer: &mut W) -> Result<(), Self::Error> { let val: VarInt7 = match self { TableElementType::AnyFunc => -0x10, } .into(); val.serialize(writer)?; Ok(()) } } parity-wasm-0.45.0/src/io.rs000064400000000000000000000047560072674642500137770ustar 00000000000000//! Simple abstractions for the IO operations. //! //! Basically it just a replacement for the std::io that is usable from //! the `no_std` environment. #[cfg(feature = "std")] use std::io; /// IO specific error. #[derive(Debug)] pub enum Error { /// Some unexpected data left in the buffer after reading all data. TrailingData, /// Unexpected End-Of-File UnexpectedEof, /// Invalid data is encountered. InvalidData, #[cfg(feature = "std")] Io(std::io::Error), } /// IO specific Result. pub type Result = core::result::Result; pub trait Write { /// Write a buffer of data into this write. /// /// All data is written at once. fn write(&mut self, buf: &[u8]) -> Result<()>; } pub trait Read { /// Read a data from this read to a buffer. /// /// If there is not enough data in this read then `UnexpectedEof` will be returned. fn read(&mut self, buf: &mut [u8]) -> Result<()>; } /// Reader that saves the last position. pub struct Cursor { inner: T, pos: usize, } impl Cursor { pub fn new(inner: T) -> Cursor { Cursor { inner, pos: 0 } } pub fn position(&self) -> usize { self.pos } } impl> Read for Cursor { fn read(&mut self, buf: &mut [u8]) -> Result<()> { let slice = self.inner.as_ref(); let remainder = slice.len() - self.pos; let requested = buf.len(); if requested > remainder { return Err(Error::UnexpectedEof) } buf.copy_from_slice(&slice[self.pos..(self.pos + requested)]); self.pos += requested; Ok(()) } } #[cfg(not(feature = "std"))] impl Write for alloc::vec::Vec { fn write(&mut self, buf: &[u8]) -> Result<()> { self.extend(buf); Ok(()) } } #[cfg(feature = "std")] impl Read for T { fn read(&mut self, buf: &mut [u8]) -> Result<()> { self.read_exact(buf).map_err(Error::Io) } } #[cfg(feature = "std")] impl Write for T { fn write(&mut self, buf: &[u8]) -> Result<()> { self.write_all(buf).map_err(Error::Io) } } #[cfg(test)] mod tests { use super::*; #[test] fn cursor() { let mut cursor = Cursor::new(vec![0xFFu8, 0x7Fu8]); assert_eq!(cursor.position(), 0); let mut buf = [0u8]; assert!(cursor.read(&mut buf[..]).is_ok()); assert_eq!(cursor.position(), 1); assert_eq!(buf[0], 0xFFu8); assert!(cursor.read(&mut buf[..]).is_ok()); assert_eq!(buf[0], 0x7Fu8); assert_eq!(cursor.position(), 2); } #[test] fn overflow_in_cursor() { let mut cursor = Cursor::new(vec![0u8]); let mut buf = [0, 1, 2]; assert!(cursor.read(&mut buf[..]).is_err()); } } parity-wasm-0.45.0/src/lib.rs000064400000000000000000000005350072674642500141250ustar 00000000000000//! WebAssembly format library #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #[macro_use] extern crate alloc; pub mod builder; pub mod elements; mod io; pub use elements::{deserialize_buffer, peek_size, serialize, Error as SerializationError}; #[cfg(feature = "std")] pub use elements::{deserialize_file, serialize_to_file};