├── .github
└── workflows
│ └── rust.yml
├── .gitignore
├── .gitmodules
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE
├── README.md
├── benches
├── bench.rs
└── fixture.wasm
├── derive
├── Cargo.toml
└── src
│ └── lib.rs
├── examples
└── dump.rs
├── fuzz
├── .gitignore
├── Cargo.toml
└── fuzz_targets
│ ├── decode.rs
│ └── roundtrip.rs
├── src
├── builtins
│ ├── blob.rs
│ ├── boolean.rs
│ ├── collections.rs
│ ├── floats.rs
│ ├── integers.rs
│ ├── lazy.rs
│ ├── mod.rs
│ └── strings.rs
├── indices.rs
├── instructions
│ ├── misc.rs
│ ├── mod.rs
│ ├── simd.rs
│ └── threads.rs
├── io.rs
├── lib.rs
├── module.rs
├── sections.rs
├── types.rs
└── visit.rs
└── tests
└── spec.rs
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on: [push, pull_request]
4 |
5 | env:
6 | CARGO_TERM_COLOR: always
7 |
8 | jobs:
9 | test:
10 |
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Check out the repo
15 | uses: actions/checkout@v2
16 | with:
17 | submodules: true
18 | - name: Run tests without proposals
19 | run: cargo test -- -q
20 | - name: Run tests with proposals enabled
21 | run: cargo test --features=proposals -- -q
22 | - name: Install and switch to nightly Rust
23 | uses: actions-rs/toolchain@v1
24 | with:
25 | profile: minimal
26 | components: clippy
27 | toolchain: nightly
28 | override: true
29 | - name: Run `clippy check`
30 | uses: actions-rs/clippy-check@v1
31 | with:
32 | token: ${{ secrets.GITHUB_TOKEN }}
33 | args: --all-features --all-targets
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | **/*.rs.bk
3 | Cargo.lock
4 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "tests/testsuite"]
2 | path = tests/testsuite
3 | url = https://github.com/WebAssembly/testsuite.git
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google.com/conduct/).
29 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "wasmbin"
3 | version = "0.3.1"
4 | authors = ["Ingvar Stepanyan "]
5 | edition = "2018"
6 | license = "Apache-2.0"
7 | description = "A self-generating WebAssembly parser and serializer"
8 | repository = "https://github.com/GoogleChromeLabs/wasmbin"
9 | categories = ["wasm", "parser-implementations"]
10 | keywords = ["webassembly", "wasm", "parser", "serializer"]
11 |
12 | exclude = [
13 | "tests/testsuite",
14 | "benches/fixture.wasm",
15 | ]
16 |
17 | [dependencies]
18 | leb128 = "0.2.4"
19 | thiserror = "1.0.25"
20 | wasmbin-derive = { version = "0.1.0", path = "derive" }
21 | custom_debug = "0.5.0"
22 | once_cell = "1.8.0"
23 | arbitrary = { version = "1.0.1", features = ["derive"] }
24 |
25 | [features]
26 | default = []
27 | nightly = ["criterion/real_blackbox", "wasmbin-derive/nightly"]
28 | proposals = ["tail-call", "simd", "threads"]
29 | tail-call = []
30 | simd = []
31 | threads = []
32 |
33 | [dev-dependencies]
34 | criterion = "0.3.4"
35 | libtest-mimic = "0.3.0"
36 | wast = "36.0.0"
37 | fehler = "1.0.0"
38 | anyhow = "1.0.41"
39 | tempfile = "3.2.0"
40 | structopt = "0.3.21"
41 |
42 | [[bench]]
43 | name = "bench"
44 | harness = false
45 |
46 | [profile.bench]
47 | debug = true
48 |
49 | [[test]]
50 | name = "spec"
51 | harness = false
52 |
53 | [workspace]
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This repository is archived as the author no longer works at Google.
2 |
3 | The continued development of this project has moved to [a personal fork](https://github.com/RReverser/wasmbin).
4 |
--------------------------------------------------------------------------------
/benches/bench.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use criterion::{black_box, criterion_group, criterion_main, Criterion};
16 | use std::fs::File;
17 | use tempfile::tempfile;
18 | use wasmbin::io::DecodeError;
19 | use wasmbin::visit::{Visit, VisitError};
20 | use wasmbin::Module;
21 |
22 | fn deep_module() -> Module {
23 | use wasmbin::builtins::Blob;
24 | use wasmbin::instructions::{Expression, Instruction};
25 | use wasmbin::sections::FuncBody;
26 | use wasmbin::types::BlockType;
27 |
28 | let mut expr = Expression::default();
29 | for _ in 0..100_000 {
30 | expr.push(Instruction::BlockStart(BlockType::Empty));
31 | }
32 | for _ in 0..100_000 {
33 | expr.push(Instruction::End);
34 | }
35 | Module {
36 | sections: vec![vec![Blob::from(FuncBody {
37 | locals: Default::default(),
38 | expr,
39 | })]
40 | .into()],
41 | ..Default::default()
42 | }
43 | }
44 |
45 | fn unlazify(wasm: T) -> Result {
46 | match wasm.visit(|()| {}) {
47 | Ok(()) => Ok(wasm),
48 | Err(err) => match err {
49 | VisitError::LazyDecode(err) => Err(err),
50 | VisitError::Custom(err) => match err {},
51 | },
52 | }
53 | }
54 |
55 | fn bench_parse(c: &mut Criterion) {
56 | c.bench_function(concat!(stringify!($name), "::bench_parse"), |b| {
57 | b.iter(|| {
58 | let f = File::open("benches/fixture.wasm").unwrap();
59 | unlazify(Module::decode_from(f).unwrap())
60 | })
61 | });
62 | }
63 |
64 | fn bench_parse_buf(c: &mut Criterion) {
65 | c.bench_function(concat!(stringify!($name), "::bench_parse_buf"), |b| {
66 | b.iter(|| {
67 | let f = File::open("benches/fixture.wasm").unwrap();
68 | let f = std::io::BufReader::new(f);
69 | unlazify(Module::decode_from(f).unwrap())
70 | })
71 | });
72 | }
73 |
74 | fn bench_parse_vec(c: &mut Criterion) {
75 | c.bench_function(concat!(stringify!($name), "::bench_parse_vec"), |b| {
76 | let f = std::fs::read("benches/fixture.wasm").unwrap();
77 | b.iter(|| {
78 | let f = black_box(f.as_slice());
79 | unlazify(Module::decode_from(f).unwrap())
80 | })
81 | });
82 | }
83 |
84 | fn bench_parse_deep_module(c: &mut Criterion) {
85 | c.bench_function(
86 | concat!(stringify!($name), "::bench_parse_deep_module"),
87 | |b| {
88 | let f = deep_module().encode_into(Vec::new()).unwrap();
89 | b.iter(|| {
90 | let f = black_box(f.as_slice());
91 | unlazify(Module::decode_from(f).unwrap())
92 | })
93 | },
94 | );
95 | }
96 |
97 | fn read_module() -> Module {
98 | let f = std::fs::read("benches/fixture.wasm").unwrap();
99 | unlazify(Module::decode_from(f.as_slice()).unwrap()).unwrap()
100 | }
101 |
102 | fn bench_write(c: &mut Criterion) {
103 | c.bench_function(concat!(stringify!($name), "::bench_write"), |b| {
104 | let m = read_module();
105 | b.iter(|| {
106 | let f = tempfile().unwrap();
107 | black_box(&m).encode_into(f).unwrap()
108 | })
109 | });
110 | }
111 |
112 | fn bench_write_buf(c: &mut Criterion) {
113 | c.bench_function(concat!(stringify!($name), "::bench_write_buf"), |b| {
114 | let m = read_module();
115 | b.iter(|| {
116 | let f = tempfile().unwrap();
117 | let f = std::io::BufWriter::new(f);
118 | black_box(&m).encode_into(f).unwrap()
119 | })
120 | });
121 | }
122 |
123 | fn bench_write_vec(c: &mut Criterion) {
124 | c.bench_function(concat!(stringify!($name), "::bench_write_vec"), |b| {
125 | let m = read_module();
126 | b.iter(|| black_box(&m).encode_into(Vec::new()).unwrap())
127 | });
128 | }
129 |
130 | fn bench_write_deep_module(c: &mut Criterion) {
131 | c.bench_function(
132 | concat!(stringify!($name), "::bench_write_deep_module"),
133 | |b| {
134 | let m = deep_module();
135 | b.iter(|| black_box(&m).encode_into(Vec::new()).unwrap())
136 | },
137 | );
138 | }
139 |
140 | criterion_group! {
141 | name = benches;
142 | config = Criterion::default().sample_size(20);
143 | targets =
144 | bench_parse,
145 | bench_parse_buf,
146 | bench_parse_vec,
147 | bench_parse_deep_module,
148 | bench_write,
149 | bench_write_buf,
150 | bench_write_vec,
151 | bench_write_deep_module,
152 | }
153 |
154 | criterion_main!(benches);
155 |
--------------------------------------------------------------------------------
/benches/fixture.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/wasmbin/1c5322117c74fdbbd9ac9649b62cef70d14252a7/benches/fixture.wasm
--------------------------------------------------------------------------------
/derive/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "wasmbin-derive"
3 | version = "0.1.0"
4 | authors = ["Ingvar Stepanyan "]
5 | edition = "2018"
6 | license = "Apache-2.0"
7 | description = "Derive crate for the wasmbin library"
8 | repository = "https://github.com/GoogleChromeLabs/wasmbin"
9 |
10 | [lib]
11 | proc-macro = true
12 |
13 | [dependencies]
14 | synstructure = "0.12.4"
15 | quote = "1.0.9"
16 | proc-macro2 = "1.0.27"
17 | syn = "1.0.73"
18 | thiserror = "1.0.25"
19 |
20 | [features]
21 | nightly = []
22 |
--------------------------------------------------------------------------------
/derive/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | extern crate proc_macro;
16 |
17 | use quote::{quote, ToTokens};
18 | use std::borrow::Cow;
19 | use synstructure::{decl_derive, Structure, VariantInfo};
20 |
21 | macro_rules! syn_throw {
22 | ($err:expr) => {
23 | return syn::Error::to_compile_error(&$err);
24 | };
25 | }
26 |
27 | macro_rules! syn_try {
28 | ($expr:expr) => {
29 | match $expr {
30 | Ok(expr) => expr,
31 | Err(err) => syn_throw!(err),
32 | }
33 | };
34 | }
35 |
36 | fn discriminant<'v>(v: &VariantInfo<'v>) -> syn::Result>> {
37 | v.ast()
38 | .discriminant
39 | .iter()
40 | .map(|(_, discriminant)| Ok(Cow::Borrowed(discriminant)))
41 | .chain(v.ast().attrs.iter().filter_map(|attr| match attr {
42 | syn::Attribute {
43 | style: syn::AttrStyle::Outer,
44 | path,
45 | ..
46 | } if path.is_ident("wasmbin") => {
47 | syn::custom_keyword!(discriminant);
48 |
49 | Some(
50 | attr.parse_args_with(|parser: syn::parse::ParseStream| {
51 | parser.parse::()?;
52 | parser.parse::()?;
53 | parser.parse()
54 | })
55 | .map(Cow::Owned),
56 | )
57 | }
58 | _ => None,
59 | }))
60 | .try_fold(None, |prev, discriminant| {
61 | let discriminant = discriminant?;
62 | if let Some(prev) = prev {
63 | let mut err = syn::Error::new_spanned(
64 | discriminant,
65 | "#[derive(Wasmbin)]: duplicate discriminant",
66 | );
67 | err.combine(syn::Error::new_spanned(
68 | prev,
69 | "#[derive(Wasmbin)]: previous discriminant here",
70 | ));
71 | return Err(err);
72 | }
73 | Ok(Some(discriminant))
74 | })
75 | }
76 |
77 | fn gen_encode_discriminant(repr: &syn::Type, discriminant: &syn::Expr) -> proc_macro2::TokenStream {
78 | quote!(<#repr as Encode>::encode(discriminant, w)?)
79 | }
80 |
81 | fn is_newtype_like(v: &VariantInfo) -> bool {
82 | matches!(v.ast().fields, fields @ syn::Fields::Unnamed(_) if fields.len() == 1)
83 | }
84 |
85 | fn track_err_in_field(
86 | mut res: proc_macro2::TokenStream,
87 | v: &VariantInfo,
88 | field: &syn::Field,
89 | index: usize,
90 | ) -> proc_macro2::TokenStream {
91 | if !is_newtype_like(v) {
92 | let field_name = match &field.ident {
93 | Some(ident) => ident.to_string(),
94 | None => index.to_string(),
95 | };
96 | res = quote!(#res.map_err(|err| err.in_path(PathItem::Name(#field_name))));
97 | }
98 | res
99 | }
100 |
101 | fn track_err_in_variant(
102 | res: proc_macro2::TokenStream,
103 | v: &VariantInfo,
104 | ) -> proc_macro2::TokenStream {
105 | use std::fmt::Write;
106 |
107 | let mut variant_name = String::new();
108 | if let Some(prefix) = v.prefix {
109 | write!(variant_name, "{}::", prefix).unwrap();
110 | }
111 | write!(variant_name, "{}", v.ast().ident).unwrap();
112 |
113 | quote!(#res.map_err(|err| err.in_path(PathItem::Variant(#variant_name))))
114 | }
115 |
116 | fn catch_expr(
117 | res: proc_macro2::TokenStream,
118 | err: proc_macro2::TokenStream,
119 | ) -> proc_macro2::TokenStream {
120 | quote!(
121 | (move || -> Result<_, #err> {
122 | Ok({ #res })
123 | })()
124 | )
125 | }
126 |
127 | fn gen_decode(v: &VariantInfo) -> proc_macro2::TokenStream {
128 | let mut res = v.construct(|field, index| {
129 | let res = track_err_in_field(quote!(Decode::decode(r)), v, field, index);
130 | quote!(#res?)
131 | });
132 | res = catch_expr(res, quote!(DecodeError));
133 | res = track_err_in_variant(res, v);
134 | res
135 | }
136 |
137 | fn parse_repr(s: &Structure) -> syn::Result {
138 | s.ast()
139 | .attrs
140 | .iter()
141 | .find(|attr| attr.path.is_ident("repr"))
142 | .ok_or_else(|| {
143 | syn::Error::new_spanned(
144 | &s.ast().ident,
145 | "Wasmbin enums must have a #[repr(type)] attribute",
146 | )
147 | })?
148 | .parse_args()
149 | }
150 |
151 | fn wasmbin_derive(s: Structure) -> proc_macro2::TokenStream {
152 | let (encode_discriminant, decode) = match s.ast().data {
153 | syn::Data::Enum(_) => {
154 | let repr = syn_try!(parse_repr(&s));
155 |
156 | let mut encode_discriminant = quote!();
157 |
158 | let mut decoders = quote!();
159 | let mut decode_other = quote!({ return Ok(None) });
160 |
161 | for v in s.variants() {
162 | let discriminant = syn_try!(discriminant(v));
163 |
164 | match discriminant {
165 | Some(discriminant) => {
166 | let pat = v.pat();
167 |
168 | let encode = gen_encode_discriminant(&repr, &discriminant);
169 | (quote!(#pat => #encode,)).to_tokens(&mut encode_discriminant);
170 |
171 | let decode = gen_decode(v);
172 | (quote!(
173 | #discriminant => #decode?,
174 | ))
175 | .to_tokens(&mut decoders);
176 | }
177 | None => {
178 | let fields = v.ast().fields;
179 | if fields.len() != 1 {
180 | syn_throw!(syn::Error::new_spanned(
181 | fields,
182 | "Catch-all variants without discriminant must have a single field."
183 | ));
184 | }
185 | let field = fields.iter().next().unwrap();
186 | let construct = match &field.ident {
187 | Some(ident) => quote!({ #ident: res }),
188 | None => quote!((res)),
189 | };
190 | let variant_name = v.ast().ident;
191 | decode_other = quote! {
192 | if let Some(res) = DecodeWithDiscriminant::maybe_decode_with_discriminant(discriminant, r)? {
193 | Self::#variant_name #construct
194 | } else #decode_other
195 | };
196 | }
197 | }
198 | }
199 |
200 | let name = s.ast().ident.to_string();
201 |
202 | (
203 | quote! {
204 | match *self {
205 | #encode_discriminant
206 | _ => {}
207 | }
208 | },
209 | quote! {
210 | gen impl DecodeWithDiscriminant for @Self {
211 | const NAME: &'static str = #name;
212 | type Discriminant = #repr;
213 |
214 | fn maybe_decode_with_discriminant(discriminant: #repr, r: &mut impl std::io::Read) -> Result, DecodeError> {
215 | Ok(Some(match discriminant {
216 | #decoders
217 | _ => #decode_other
218 | }))
219 | }
220 | }
221 |
222 | gen impl Decode for @Self {
223 | fn decode(r: &mut impl std::io::Read) -> Result {
224 | DecodeWithDiscriminant::decode_without_discriminant(r)
225 | }
226 | }
227 | },
228 | )
229 | }
230 | _ => {
231 | let variants = s.variants();
232 | assert_eq!(variants.len(), 1);
233 | let v = &variants[0];
234 | let decode = gen_decode(v);
235 | match syn_try!(discriminant(v)) {
236 | Some(discriminant) => {
237 | let name = s.ast().ident.to_string();
238 | (
239 | gen_encode_discriminant(&syn::parse_quote!(u8), &discriminant),
240 | quote! {
241 | gen impl DecodeWithDiscriminant for @Self {
242 | const NAME: &'static str = #name;
243 | type Discriminant = u8;
244 |
245 | fn maybe_decode_with_discriminant(discriminant: u8, r: &mut impl std::io::Read) -> Result, DecodeError> {
246 | match discriminant {
247 | #discriminant => #decode.map(Some),
248 | _ => Ok(None),
249 | }
250 | }
251 | }
252 |
253 | gen impl Decode for @Self {
254 | fn decode(r: &mut impl std::io::Read) -> Result {
255 | DecodeWithDiscriminant::decode_without_discriminant(r)
256 | }
257 | }
258 | },
259 | )
260 | }
261 | None => (
262 | quote! {},
263 | quote! {
264 | gen impl Decode for @Self {
265 | fn decode(r: &mut impl std::io::Read) -> Result {
266 | #decode
267 | }
268 | }
269 | },
270 | ),
271 | }
272 | }
273 | };
274 |
275 | let encode_body = s.each(|bi| {
276 | quote! {
277 | Encode::encode(#bi, w)?
278 | }
279 | });
280 |
281 | s.gen_impl(quote! {
282 | use crate::io::{Encode, Decode, DecodeWithDiscriminant, DecodeError, PathItem};
283 |
284 | gen impl Encode for @Self {
285 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
286 | #encode_discriminant;
287 | match *self { #encode_body }
288 | Ok(())
289 | }
290 | }
291 |
292 | #decode
293 | })
294 | }
295 |
296 | fn wasmbin_countable_derive(s: Structure) -> proc_macro2::TokenStream {
297 | s.gen_impl(quote! {
298 | gen impl crate::builtins::WasmbinCountable for @Self {}
299 | })
300 | }
301 |
302 | fn wasmbin_visit_derive(mut s: Structure) -> proc_macro2::TokenStream {
303 | s.bind_with(|_| synstructure::BindStyle::Move);
304 |
305 | fn generate_visit_body(s: &Structure, method: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
306 | let body = s.each_variant(|v| {
307 | let res = v.bindings().iter().enumerate().map(|(i, bi)| {
308 | let res = quote!(Visit::#method(#bi, f));
309 | track_err_in_field(res, v, bi.ast(), i)
310 | });
311 | let mut res = quote!(#(#res?;)*);
312 | res = catch_expr(res, quote!(VisitError));
313 | res = track_err_in_variant(res, v);
314 | quote!(#res?)
315 | });
316 | quote!(
317 | match self { #body }
318 | Ok(())
319 | )
320 | }
321 |
322 | let visit_children_body = generate_visit_body(&s, quote!(visit_child));
323 |
324 | let visit_children_mut_body = generate_visit_body(&s, quote!(visit_child_mut));
325 |
326 | s.gen_impl(quote! {
327 | use crate::visit::{Visit, VisitError};
328 | use crate::io::PathItem;
329 |
330 | gen impl Visit for @Self where Self: 'static {
331 | fn visit_children<'a, VisitT: 'static, VisitE, VisitF: FnMut(&'a VisitT) -> Result<(), VisitE>>(&'a self, f: &mut VisitF) -> Result<(), VisitError> {
332 | #visit_children_body
333 | }
334 |
335 | fn visit_children_mut Result<(), VisitE>>(&mut self, f: &mut VisitF) -> Result<(), VisitError> {
336 | #visit_children_mut_body
337 | }
338 | }
339 | })
340 | }
341 |
342 | #[proc_macro_attribute]
343 | pub fn wasmbin_discriminants(
344 | _attr: proc_macro::TokenStream,
345 | input: proc_macro::TokenStream,
346 | ) -> proc_macro::TokenStream {
347 | let mut input: syn::DeriveInput = syn::parse(input).unwrap();
348 | let e = match &mut input.data {
349 | syn::Data::Enum(e) => e,
350 | _ => panic!("This attribute can only be used on enums"),
351 | };
352 | let mut seen_non_units = false;
353 | for v in &mut e.variants {
354 | match v.fields {
355 | syn::Fields::Unit => {}
356 | _ => seen_non_units = true,
357 | }
358 | #[cfg(not(feature = "nightly"))]
359 | {
360 | if let Some((_, discriminant)) = v.discriminant.take() {
361 | v.attrs
362 | .push(syn::parse_quote!(#[wasmbin(discriminant = #discriminant)]));
363 | }
364 | }
365 | }
366 | assert!(
367 | seen_non_units,
368 | "Attribute shouldn't be used on C-like enums"
369 | );
370 | input.into_token_stream().into()
371 | }
372 |
373 | decl_derive!([Wasmbin, attributes(wasmbin)] => wasmbin_derive);
374 | decl_derive!([WasmbinCountable] => wasmbin_countable_derive);
375 | decl_derive!([Visit] => wasmbin_visit_derive);
376 |
--------------------------------------------------------------------------------
/examples/dump.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use std::fs::File;
16 | use std::io::{BufReader, Seek, SeekFrom};
17 | use structopt::StructOpt;
18 | use wasmbin::io::DecodeError;
19 | use wasmbin::sections::{Kind, Section};
20 | use wasmbin::visit::{Visit, VisitError};
21 | use wasmbin::Module;
22 |
23 | #[derive(StructOpt)]
24 | enum DumpSection {
25 | All,
26 | Custom { name: String },
27 | Type,
28 | Import,
29 | Function,
30 | Table,
31 | Memory,
32 | Global,
33 | Export,
34 | Start,
35 | Element,
36 | DataCount,
37 | Code,
38 | Data,
39 | }
40 |
41 | #[derive(StructOpt)]
42 | struct DumpOpts {
43 | filename: String,
44 | #[structopt(long)]
45 | include_raw: bool,
46 | #[structopt(flatten)]
47 | section: DumpSection,
48 | }
49 |
50 | fn unlazify_with_opt(wasm: &mut T, include_raw: bool) -> Result<(), DecodeError> {
51 | let res = if include_raw {
52 | wasm.visit(|()| {})
53 | } else {
54 | wasm.visit_mut(|()| {})
55 | };
56 | match res {
57 | Ok(()) => Ok(()),
58 | Err(err) => match err {
59 | VisitError::LazyDecode(err) => Err(err),
60 | VisitError::Custom(err) => match err {},
61 | },
62 | }
63 | }
64 |
65 | fn main() {
66 | let opts = DumpOpts::from_args();
67 | let f = File::open(opts.filename).unwrap();
68 | let mut f = BufReader::new(f);
69 | let mut m = Module::decode_from(&mut f).unwrap_or_else(|err| {
70 | panic!(
71 | "Parsing error at offset 0x{:08X}: {}",
72 | f.seek(SeekFrom::Current(0)).unwrap(),
73 | err
74 | )
75 | });
76 | let filter: Box bool> = match opts.section {
77 | DumpSection::All => Box::new(|_s: &Section| true) as _,
78 | DumpSection::Custom { name } => Box::new(move |s: &Section| {
79 | let other_name = match s {
80 | Section::Custom(s) => match s.try_contents() {
81 | Ok(section) => Some(section.name()),
82 | Err(err) => {
83 | eprintln!("Warning: could not parse a custom section. {}", err);
84 | None
85 | }
86 | },
87 | _ => None,
88 | };
89 | Some(name.as_str()) == other_name
90 | }),
91 | DumpSection::Type => Box::new(|s| s.kind() == Kind::Type),
92 | DumpSection::Import => Box::new(|s| s.kind() == Kind::Import),
93 | DumpSection::Function => Box::new(|s| s.kind() == Kind::Function),
94 | DumpSection::Table => Box::new(|s| s.kind() == Kind::Table),
95 | DumpSection::Memory => Box::new(|s| s.kind() == Kind::Memory),
96 | DumpSection::Global => Box::new(|s| s.kind() == Kind::Global),
97 | DumpSection::Export => Box::new(|s| s.kind() == Kind::Export),
98 | DumpSection::Start => Box::new(|s| s.kind() == Kind::Start),
99 | DumpSection::Element => Box::new(|s| s.kind() == Kind::Element),
100 | DumpSection::DataCount => Box::new(|s| s.kind() == Kind::DataCount),
101 | DumpSection::Code => Box::new(|s| s.kind() == Kind::Code),
102 | DumpSection::Data => Box::new(|s| s.kind() == Kind::Data),
103 | };
104 | let mut count = 0;
105 | for s in m.sections.iter_mut().filter(|s| filter(s)) {
106 | count += 1;
107 | unlazify_with_opt(s, opts.include_raw).unwrap();
108 | println!("{:#?}", s);
109 | }
110 | println!("Found {} sections.", count);
111 | }
112 |
--------------------------------------------------------------------------------
/fuzz/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | target
3 | corpus
4 | artifacts
5 |
--------------------------------------------------------------------------------
/fuzz/Cargo.toml:
--------------------------------------------------------------------------------
1 |
2 | [package]
3 | name = "wasmbin-fuzz"
4 | version = "0.0.0"
5 | authors = ["Automatically generated"]
6 | publish = false
7 | edition = "2018"
8 |
9 | [package.metadata]
10 | cargo-fuzz = true
11 |
12 | [dependencies]
13 | libfuzzer-sys = "0.4.2"
14 |
15 | [dependencies.wasmbin]
16 | path = ".."
17 | default-features = false
18 |
19 | # Prevent this from interfering with workspaces
20 | [workspace]
21 | members = ["."]
22 |
23 | [[bin]]
24 | name = "decode"
25 | path = "fuzz_targets/decode.rs"
26 |
27 | [[bin]]
28 | name = "roundtrip"
29 | path = "fuzz_targets/roundtrip.rs"
30 |
--------------------------------------------------------------------------------
/fuzz/fuzz_targets/decode.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #![no_main]
16 | use libfuzzer_sys::fuzz_target;
17 |
18 | use wasmbin::Module;
19 |
20 | fuzz_target!(|data: &[u8]| {
21 | // Just check that we don't crash anywhere trying to read the data.
22 | let _ = Module::decode_from(data);
23 | });
24 |
--------------------------------------------------------------------------------
/fuzz/fuzz_targets/roundtrip.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #![no_main]
16 | use libfuzzer_sys::fuzz_target;
17 |
18 | use wasmbin::io::DecodeError;
19 | use wasmbin::visit::{Visit, VisitError};
20 | use wasmbin::Module;
21 |
22 | fn unlazify(wasm: T) -> Result {
23 | match wasm.visit(|()| {}) {
24 | Ok(()) => Ok(wasm),
25 | Err(err) => match err {
26 | VisitError::LazyDecode(err) => Err(err),
27 | VisitError::Custom(err) => match err {},
28 | },
29 | }
30 | }
31 |
32 | fuzz_target!(|module: Module| {
33 | // We're using Vec as I/O destination, so this should never fail, except
34 | // if the module itself is malformed.
35 | // In that case, bail out.
36 | let encoded = match module.encode_into(Vec::new()) {
37 | Ok(encoded) => encoded,
38 | Err(_) => return,
39 | };
40 | // Check that we can re-decoded encoded data back.
41 | let decoded = Module::decode_from(encoded.as_slice())
42 | .and_then(unlazify)
43 | .unwrap();
44 | // Ensure that re-decoded module is equivalent to the original.
45 | assert_eq!(module, decoded);
46 | // Check that encoding again results in a deterministic output.
47 | let encoded2 = decoded.encode_into(Vec::new()).unwrap();
48 | assert_eq!(encoded, encoded2);
49 | });
50 |
--------------------------------------------------------------------------------
/src/builtins/blob.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::builtins::{Lazy, WasmbinCountable};
16 | use crate::io::{Decode, DecodeError, DecodeErrorKind, Encode};
17 | use crate::visit::Visit;
18 | use arbitrary::Arbitrary;
19 |
20 | #[derive(Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
21 | pub struct RawBlob> {
22 | pub contents: T,
23 | }
24 |
25 | impl> Encode for RawBlob {
26 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
27 | let bytes = self.contents.as_ref();
28 | bytes.len().encode(w)?;
29 | bytes.encode(w)
30 | }
31 | }
32 |
33 | impl Decode for RawBlob {
34 | fn decode(r: &mut impl std::io::Read) -> Result {
35 | let size = u32::decode(r)?;
36 | let mut taken = std::io::Read::take(r, size.into());
37 | let contents = T::decode(&mut taken)?;
38 | if taken.limit() != 0 {
39 | return Err(DecodeErrorKind::UnrecognizedData.into());
40 | }
41 | Ok(RawBlob { contents })
42 | }
43 | }
44 |
45 | impl> AsRef<[u8]> for RawBlob {
46 | fn as_ref(&self) -> &[u8] {
47 | self.contents.as_ref()
48 | }
49 | }
50 |
51 | #[derive(Default, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
52 | pub struct Blob {
53 | contents: Lazy,
54 | }
55 |
56 | impl Blob {
57 | pub fn try_contents(&self) -> Result<&T, DecodeError> {
58 | self.contents.try_contents()
59 | }
60 |
61 | pub fn try_contents_mut(&mut self) -> Result<&mut T, DecodeError> {
62 | self.contents.try_contents_mut()
63 | }
64 |
65 | pub fn try_into_contents(self) -> Result {
66 | self.contents.try_into_contents()
67 | }
68 | }
69 |
70 | impl std::fmt::Debug for Blob {
71 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
72 | f.write_str("Blob(")?;
73 | self.contents.fmt(f)?;
74 | f.write_str(")")
75 | }
76 | }
77 |
78 | impl Encode for Blob {
79 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
80 | let value = match self.contents.try_as_raw() {
81 | Ok(raw) => return RawBlob { contents: raw }.encode(w),
82 | Err(value) => value,
83 | };
84 | let mut buf;
85 | buf = Vec::new();
86 | value.encode(&mut buf)?;
87 | RawBlob { contents: buf }.encode(w)
88 | }
89 | }
90 |
91 | impl Decode for Blob {
92 | fn decode(r: &mut impl std::io::Read) -> Result {
93 | let contents: Lazy = RawBlob::decode(r)?.contents;
94 | Ok(Self { contents })
95 | }
96 | }
97 |
98 | impl WasmbinCountable for Blob {}
99 |
100 | impl From for Blob {
101 | fn from(value: T) -> Self {
102 | Blob {
103 | contents: value.into(),
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/builtins/boolean.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::io::DecodeErrorKind;
16 | use crate::visit::Visit;
17 |
18 | encode_decode_as!(bool, {
19 | false <=> 0_u8,
20 | true <=> 1_u8,
21 | }, |discriminant| {
22 | Err(DecodeErrorKind::UnsupportedDiscriminant { ty: "bool", discriminant: discriminant.into() }.into())
23 | });
24 |
25 | impl Visit for bool {}
26 |
--------------------------------------------------------------------------------
/src/builtins/collections.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::io::{Decode, DecodeError, Encode, PathItem};
16 |
17 | pub use wasmbin_derive::WasmbinCountable;
18 | pub trait WasmbinCountable {}
19 |
20 | impl Encode for [T] {
21 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
22 | self.len().encode(w)?;
23 | for item in self {
24 | item.encode(w)?;
25 | }
26 | Ok(())
27 | }
28 | }
29 |
30 | impl Encode for Vec
31 | where
32 | [T]: Encode,
33 | {
34 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
35 | self.as_slice().encode(w)
36 | }
37 | }
38 |
39 | impl Decode for Vec {
40 | fn decode(r: &mut impl std::io::Read) -> Result {
41 | let count = usize::decode(r)?;
42 | (0..count)
43 | .map(|i| T::decode(r).map_err(move |err| err.in_path(PathItem::Index(i))))
44 | .collect()
45 | }
46 | }
47 |
48 | impl_visit_for_iter!(Vec);
49 |
50 | impl crate::visit::Visit for Option {
51 | fn visit_children<'a, VisitT: 'static, E, F: FnMut(&'a VisitT) -> Result<(), E>>(
52 | &'a self,
53 | f: &mut F,
54 | ) -> Result<(), crate::visit::VisitError> {
55 | if let Some(v) = self {
56 | v.visit_child(f)?;
57 | }
58 | Ok(())
59 | }
60 |
61 | fn visit_children_mut Result<(), E>>(
62 | &mut self,
63 | f: &mut F,
64 | ) -> Result<(), crate::visit::VisitError> {
65 | if let Some(v) = self {
66 | v.visit_child_mut(f)?;
67 | }
68 | Ok(())
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/builtins/floats.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::io::{Decode, DecodeError, Encode, Wasmbin};
16 | use crate::visit::Visit;
17 | use arbitrary::Arbitrary;
18 |
19 | /// A wrapper around floats that treats `NaN`s as equal.
20 | ///
21 | /// This is useful in instruction context, where we don't care
22 | /// about general floating number rules.
23 | #[derive(Wasmbin, Debug, Arbitrary, Clone, Visit)]
24 | pub struct FloatConst {
25 | pub value: F,
26 | }
27 |
28 | impl Eq for FloatConst where Self: PartialEq {}
29 |
30 | macro_rules! def_float {
31 | ($ty:ident) => {
32 | impl Encode for $ty {
33 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
34 | self.to_le_bytes().encode(w)
35 | }
36 | }
37 |
38 | impl Decode for $ty {
39 | fn decode(r: &mut impl std::io::Read) -> Result {
40 | Decode::decode(r).map($ty::from_le_bytes)
41 | }
42 | }
43 |
44 | impl Visit for $ty {}
45 |
46 | impl PartialEq for FloatConst<$ty> {
47 | fn eq(&self, other: &Self) -> bool {
48 | self.value == other.value || self.value.is_nan() && other.value.is_nan()
49 | }
50 | }
51 |
52 | impl std::hash::Hash for FloatConst<$ty> {
53 | fn hash(&self, h: &mut H) {
54 | h.write(&self.value.to_ne_bytes())
55 | }
56 | }
57 | };
58 | }
59 |
60 | def_float!(f32);
61 | def_float!(f64);
62 |
--------------------------------------------------------------------------------
/src/builtins/integers.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::io::{Decode, DecodeError, DecodeErrorKind, Encode};
16 | use crate::visit::Visit;
17 | use std::convert::TryFrom;
18 |
19 | macro_rules! def_byte_array {
20 | ($count:literal) => {
21 | impl Encode for [u8; $count] {
22 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
23 | w.write_all(self)
24 | }
25 | }
26 |
27 | impl Decode for [u8; $count] {
28 | fn decode(r: &mut impl std::io::Read) -> Result {
29 | let mut dest = [0_u8; $count];
30 | r.read_exact(&mut dest)?;
31 | Ok(dest)
32 | }
33 | }
34 | };
35 | }
36 |
37 | def_byte_array!(4);
38 | def_byte_array!(8);
39 | def_byte_array!(16);
40 |
41 | impl Encode for u8 {
42 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
43 | std::slice::from_ref(self).encode(w)
44 | }
45 | }
46 |
47 | impl Encode for [u8] {
48 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
49 | w.write_all(self)
50 | }
51 | }
52 |
53 | impl Decode for u8 {
54 | fn decode(r: &mut impl std::io::Read) -> Result {
55 | let mut dest = 0;
56 | r.read_exact(std::slice::from_mut(&mut dest))?;
57 | Ok(dest)
58 | }
59 | }
60 |
61 | impl Decode for Option {
62 | fn decode(r: &mut impl std::io::Read) -> Result {
63 | let mut dest = 0;
64 | loop {
65 | return match r.read(std::slice::from_mut(&mut dest)) {
66 | Ok(0) => Ok(None),
67 | Ok(_) => Ok(Some(dest)),
68 | Err(err) if err.kind() == std::io::ErrorKind::Interrupted => continue,
69 | Err(err) => Err(DecodeErrorKind::Io(err).into()),
70 | };
71 | }
72 | }
73 | }
74 |
75 | impl Decode for Vec {
76 | fn decode(r: &mut impl std::io::Read) -> Result {
77 | let mut dest = Vec::new();
78 | r.read_to_end(&mut dest)?;
79 | Ok(dest)
80 | }
81 | }
82 |
83 | impl Visit for u8 {}
84 |
85 | macro_rules! def_integer {
86 | ($ty:ident, $leb128_method:ident) => {
87 | impl Encode for $ty {
88 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
89 | leb128::write::$leb128_method(w, (*self).into()).map(|_| ())
90 | }
91 | }
92 |
93 | impl Decode for $ty {
94 | fn decode(r: &mut impl std::io::Read) -> Result {
95 | const LIMIT: u64 = (std::mem::size_of::<$ty>() * 8 / 7) as u64 + 1;
96 |
97 | let mut r = std::io::Read::take(r, LIMIT);
98 | let as_64 = leb128::read::$leb128_method(&mut r)?;
99 | let res = Self::try_from(as_64)?;
100 |
101 | Ok(res)
102 | }
103 | }
104 |
105 | impl Visit for $ty {}
106 | };
107 | }
108 |
109 | def_integer!(u32, unsigned);
110 | def_integer!(i32, signed);
111 | def_integer!(u64, unsigned);
112 | def_integer!(i64, signed);
113 |
114 | impl Encode for usize {
115 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
116 | match u32::try_from(*self) {
117 | Ok(v) => v.encode(w),
118 | Err(err) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, err)),
119 | }
120 | }
121 | }
122 |
123 | impl Decode for usize {
124 | fn decode(r: &mut impl std::io::Read) -> Result {
125 | Ok(usize::try_from(u32::decode(r)?)?)
126 | }
127 | }
128 |
129 | impl Visit for usize {}
130 |
--------------------------------------------------------------------------------
/src/builtins/lazy.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::builtins::WasmbinCountable;
16 | use crate::io::{Decode, DecodeError, DecodeErrorKind, Encode};
17 | use crate::visit::{Visit, VisitError};
18 | use arbitrary::Arbitrary;
19 | use custom_debug::Debug as CustomDebug;
20 | use once_cell::sync::OnceCell;
21 | use std::hash::Hash;
22 |
23 | #[derive(CustomDebug, Clone)]
24 | enum LazyStatus {
25 | FromInput {
26 | #[debug(with = "custom_debug::hexbuf_str")]
27 | raw: Vec,
28 | parsed: OnceCell,
29 | },
30 | Output {
31 | value: T,
32 | },
33 | }
34 |
35 | #[derive(Clone)]
36 | pub struct Lazy {
37 | status: LazyStatus,
38 | }
39 |
40 | impl Lazy {
41 | pub fn from_raw(raw: Vec) -> Self {
42 | Lazy {
43 | status: LazyStatus::FromInput {
44 | raw,
45 | parsed: OnceCell::new(),
46 | },
47 | }
48 | }
49 |
50 | pub fn try_as_raw(&self) -> Result<&[u8], &T> {
51 | match &self.status {
52 | LazyStatus::FromInput { raw, .. } => Ok(raw),
53 | LazyStatus::Output { value } => Err(value),
54 | }
55 | }
56 | }
57 |
58 | impl From for Lazy {
59 | fn from(value: T) -> Self {
60 | Lazy {
61 | status: LazyStatus::Output { value },
62 | }
63 | }
64 | }
65 |
66 | impl Default for Lazy {
67 | fn default() -> Self {
68 | T::default().into()
69 | }
70 | }
71 |
72 | impl Encode for Lazy {
73 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
74 | match &self.status {
75 | LazyStatus::FromInput { raw, .. } => raw.encode(w),
76 | LazyStatus::Output { value } => value.encode(w),
77 | }
78 | }
79 | }
80 |
81 | impl Decode for Lazy {
82 | fn decode(r: &mut impl std::io::Read) -> Result {
83 | Vec::decode(r).map(Self::from_raw)
84 | }
85 | }
86 |
87 | impl std::fmt::Debug for Lazy {
88 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 | self.status.fmt(f)
90 | }
91 | }
92 |
93 | fn decode_raw(mut raw: &[u8]) -> Result {
94 | let value = T::decode(&mut raw)?;
95 | if !raw.is_empty() {
96 | return Err(DecodeErrorKind::UnrecognizedData.into());
97 | }
98 | Ok(value)
99 | }
100 |
101 | impl Lazy {
102 | pub fn try_contents(&self) -> Result<&T, DecodeError> {
103 | match &self.status {
104 | LazyStatus::FromInput { raw, parsed } => parsed.get_or_try_init(|| decode_raw(raw)),
105 | LazyStatus::Output { value } => Ok(value),
106 | }
107 | }
108 |
109 | pub fn try_contents_mut(&mut self) -> Result<&mut T, DecodeError> {
110 | if let LazyStatus::FromInput { raw, parsed } = &mut self.status {
111 | // We can't trust input and output to match once we obtained a mutable reference,
112 | // so get the value and change the status to just Output.
113 | let parsed = std::mem::replace(parsed, OnceCell::new());
114 | self.status = LazyStatus::Output {
115 | value: match parsed.into_inner() {
116 | Some(value) => value,
117 | None => decode_raw(raw)?,
118 | },
119 | };
120 | }
121 | if let LazyStatus::Output { value } = &mut self.status {
122 | return Ok(value);
123 | }
124 | unsafe { std::hint::unreachable_unchecked() }
125 | }
126 |
127 | pub fn try_into_contents(self) -> Result {
128 | match self.status {
129 | LazyStatus::FromInput { raw, parsed } => match parsed.into_inner() {
130 | Some(value) => Ok(value),
131 | None => decode_raw(&raw),
132 | },
133 | LazyStatus::Output { value } => Ok(value),
134 | }
135 | }
136 | }
137 |
138 | impl PartialEq for Lazy {
139 | fn eq(&self, other: &Self) -> bool {
140 | if let (LazyStatus::FromInput { raw: raw1, .. }, LazyStatus::FromInput { raw: raw2, .. }) =
141 | (&self.status, &other.status)
142 | {
143 | if raw1 == raw2 {
144 | return true;
145 | }
146 | }
147 | if let (Ok(value1), Ok(value2)) = (self.try_contents(), other.try_contents()) {
148 | return value1 == value2;
149 | }
150 | false
151 | }
152 | }
153 |
154 | impl Eq for Lazy {}
155 |
156 | impl Hash for Lazy {
157 | fn hash(&self, state: &mut H) {
158 | self.try_contents().ok().hash(state);
159 | }
160 | }
161 |
162 | impl<'a, T: Arbitrary<'a>> Arbitrary<'a> for Lazy {
163 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result {
164 | T::arbitrary(u).map(Self::from)
165 | }
166 |
167 | fn arbitrary_take_rest(u: arbitrary::Unstructured<'a>) -> arbitrary::Result {
168 | T::arbitrary_take_rest(u).map(Self::from)
169 | }
170 |
171 | fn size_hint(depth: usize) -> (usize, Option) {
172 | T::size_hint(depth)
173 | }
174 | }
175 |
176 | impl WasmbinCountable for Lazy {}
177 |
178 | impl Visit for Lazy {
179 | fn visit_children<'a, VisitT: 'static, E, F: FnMut(&'a VisitT) -> Result<(), E>>(
180 | &'a self,
181 | f: &mut F,
182 | ) -> Result<(), VisitError> {
183 | match self.try_contents() {
184 | Ok(contents) => contents.visit_child(f),
185 | Err(err) => Err(VisitError::LazyDecode(err)),
186 | }
187 | }
188 |
189 | fn visit_children_mut Result<(), E>>(
190 | &mut self,
191 | f: &mut F,
192 | ) -> Result<(), VisitError> {
193 | match self.try_contents_mut() {
194 | Ok(contents) => contents.visit_child_mut(f),
195 | Err(err) => Err(VisitError::LazyDecode(err)),
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/builtins/mod.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | mod blob;
16 | mod boolean;
17 | mod collections;
18 | mod floats;
19 | mod integers;
20 | mod lazy;
21 | mod strings;
22 |
23 | pub use blob::{Blob, RawBlob};
24 | pub use collections::WasmbinCountable;
25 | pub use floats::FloatConst;
26 | pub use lazy::Lazy;
27 |
--------------------------------------------------------------------------------
/src/builtins/strings.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use super::RawBlob;
16 | use crate::io::{Decode, DecodeError, Encode};
17 | use crate::visit::Visit;
18 |
19 | impl Encode for str {
20 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
21 | RawBlob { contents: self }.encode(w)
22 | }
23 | }
24 |
25 | impl Encode for String {
26 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
27 | self.as_str().encode(w)
28 | }
29 | }
30 |
31 | impl Decode for String {
32 | fn decode(r: &mut impl std::io::Read) -> Result {
33 | Ok(String::from_utf8(RawBlob::decode(r)?.contents)?)
34 | }
35 | }
36 |
37 | impl Visit for String {}
38 |
--------------------------------------------------------------------------------
/src/indices.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::builtins::WasmbinCountable;
16 | use crate::io::Wasmbin;
17 | use crate::visit::Visit;
18 | use arbitrary::Arbitrary;
19 |
20 | macro_rules! newtype_id {
21 | ($name:ident) => {
22 | #[derive(PartialEq, Eq, Clone, Copy, Wasmbin, WasmbinCountable, Arbitrary, Hash, Visit)]
23 | #[repr(transparent)]
24 | pub struct $name {
25 | pub index: u32,
26 | }
27 |
28 | impl From for $name {
29 | fn from(index: u32) -> Self {
30 | Self { index }
31 | }
32 | }
33 |
34 | impl From<$name> for u32 {
35 | fn from(id: $name) -> u32 {
36 | id.index
37 | }
38 | }
39 |
40 | impl std::fmt::Debug for $name {
41 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
42 | write!(
43 | f,
44 | "{}#{}",
45 | &stringify!($name)[..stringify!($name).len() - "Id".len()],
46 | self.index
47 | )
48 | }
49 | }
50 | };
51 | }
52 |
53 | newtype_id!(DataId);
54 | newtype_id!(ElemId);
55 | newtype_id!(FuncId);
56 | newtype_id!(GlobalId);
57 | newtype_id!(LabelId);
58 | newtype_id!(LocalId);
59 | newtype_id!(MemId);
60 | newtype_id!(TableId);
61 | newtype_id!(TypeId);
62 |
--------------------------------------------------------------------------------
/src/instructions/misc.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::indices::{DataId, ElemId, MemId, TableId};
16 | use crate::io::Wasmbin;
17 | use crate::visit::Visit;
18 | use arbitrary::Arbitrary;
19 |
20 | #[crate::wasmbin_discriminants]
21 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
22 | #[repr(u32)]
23 | pub enum Misc {
24 | I32TruncSatF32S = 0x00,
25 | I32TruncSatF32U = 0x01,
26 | I32TruncSatF64S = 0x02,
27 | I32TruncSatF64U = 0x03,
28 | I64TruncSatF32S = 0x04,
29 | I64TruncSatF32U = 0x05,
30 | I64TruncSatF64S = 0x06,
31 | I64TruncSatF64U = 0x07,
32 | MemoryInit { data: DataId, mem: MemId } = 0x08,
33 | DataDrop(DataId) = 0x09,
34 | MemoryCopy { dest: MemId, src: MemId } = 0x0A,
35 | MemoryFill(MemId) = 0x0B,
36 | TableInit { elem: ElemId, table: TableId } = 0x0C,
37 | ElemDrop(ElemId) = 0x0D,
38 | TableCopy { dest: TableId, src: TableId } = 0x0E,
39 | TableGrow(TableId) = 0x0F,
40 | TableSize(TableId) = 0x10,
41 | TableFill(TableId) = 0x11,
42 | }
43 |
--------------------------------------------------------------------------------
/src/instructions/mod.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::builtins::FloatConst;
16 | use crate::indices::{FuncId, GlobalId, LabelId, LocalId, MemId, TableId, TypeId};
17 | use crate::io::{Decode, DecodeError, DecodeWithDiscriminant, Encode, PathItem, Wasmbin};
18 | use crate::types::{BlockType, RefType, ValueType};
19 | use crate::visit::Visit;
20 | use crate::wasmbin_discriminants;
21 | use arbitrary::Arbitrary;
22 | use thiserror::Error;
23 |
24 | const OP_CODE_BLOCK_START: u8 = 0x02;
25 | const OP_CODE_LOOP_START: u8 = 0x03;
26 | const OP_CODE_IF_START: u8 = 0x04;
27 | const OP_CODE_END: u8 = 0x0B;
28 |
29 | #[derive(Debug, Error)]
30 | #[error("Mismatched block depth")]
31 | struct DepthError;
32 |
33 | impl From for std::io::Error {
34 | fn from(err: DepthError) -> Self {
35 | Self::new(std::io::ErrorKind::InvalidData, err)
36 | }
37 | }
38 |
39 | #[derive(Default)]
40 | struct DepthTracker {
41 | depth: u32,
42 | }
43 |
44 | impl DepthTracker {
45 | pub fn inc(&mut self) {
46 | self.depth += 1;
47 | }
48 |
49 | // Returns a bool indicating whether to continue, or an error state.
50 | pub fn try_dec(&mut self) -> Result<(), DepthError> {
51 | self.depth = self.depth.checked_sub(1).ok_or(DepthError)?;
52 | Ok(())
53 | }
54 |
55 | pub fn assert_end(self) -> Result<(), DepthError> {
56 | match self.depth {
57 | 0 => Ok(()),
58 | _ => Err(DepthError),
59 | }
60 | }
61 | }
62 |
63 | impl Encode for [Instruction] {
64 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
65 | let mut depth_tracker = DepthTracker::default();
66 | for instr in self {
67 | match instr {
68 | Instruction::BlockStart(_)
69 | | Instruction::LoopStart(_)
70 | | Instruction::IfStart(_) => {
71 | depth_tracker.inc();
72 | }
73 | Instruction::End => {
74 | depth_tracker.try_dec()?;
75 | }
76 | _ => {}
77 | }
78 | instr.encode(w)?;
79 | }
80 | depth_tracker.assert_end()?;
81 | OP_CODE_END.encode(w)
82 | }
83 | }
84 |
85 | impl Decode for Vec {
86 | fn decode(r: &mut impl std::io::Read) -> Result {
87 | let mut res = Vec::new();
88 | let mut depth_tracker = DepthTracker::default();
89 | loop {
90 | let op_code = u8::decode(r)?;
91 | match op_code {
92 | OP_CODE_BLOCK_START | OP_CODE_LOOP_START | OP_CODE_IF_START => {
93 | depth_tracker.inc();
94 | }
95 | OP_CODE_END => {
96 | if depth_tracker.try_dec().is_err() {
97 | break;
98 | }
99 | }
100 | _ => {}
101 | }
102 | let i = res.len();
103 | res.push(
104 | Instruction::decode_with_discriminant(op_code, r)
105 | .map_err(move |err| err.in_path(PathItem::Index(i)))?,
106 | );
107 | }
108 | Ok(res)
109 | }
110 | }
111 |
112 | pub type Expression = Vec;
113 |
114 | impl crate::builtins::WasmbinCountable for Expression {}
115 |
116 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
117 | pub struct MemArg {
118 | pub align: u32,
119 | pub offset: u32,
120 | }
121 |
122 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
123 | pub struct CallIndirect {
124 | ty: TypeId,
125 | table: TableId,
126 | }
127 |
128 | #[wasmbin_discriminants]
129 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
130 | #[repr(u8)]
131 | pub enum Instruction {
132 | Unreachable = 0x00,
133 | Nop = 0x01,
134 | BlockStart(BlockType) = OP_CODE_BLOCK_START,
135 | LoopStart(BlockType) = OP_CODE_LOOP_START,
136 | IfStart(BlockType) = OP_CODE_IF_START,
137 | IfElse = 0x05,
138 | End = OP_CODE_END,
139 | Br(LabelId) = 0x0C,
140 | BrIf(LabelId) = 0x0D,
141 | BrTable {
142 | branches: Vec,
143 | otherwise: LabelId,
144 | } = 0x0E,
145 | Return = 0x0F,
146 | Call(FuncId) = 0x10,
147 | CallIndirect(CallIndirect) = 0x11,
148 | #[cfg(feature = "tail-call")]
149 | ReturnCall(FuncId) = 0x12,
150 | #[cfg(feature = "tail-call")]
151 | ReturnCallIndirect(CallIndirect) = 0x13,
152 | Drop = 0x1A,
153 | Select = 0x1B,
154 | SelectWithTypes(Vec) = 0x1C,
155 | LocalGet(LocalId) = 0x20,
156 | LocalSet(LocalId) = 0x21,
157 | LocalTee(LocalId) = 0x22,
158 | GlobalGet(GlobalId) = 0x23,
159 | GlobalSet(GlobalId) = 0x24,
160 | TableGet(TableId) = 0x25,
161 | TableSet(TableId) = 0x26,
162 | I32Load(MemArg) = 0x28,
163 | I64Load(MemArg) = 0x29,
164 | F32Load(MemArg) = 0x2A,
165 | F64Load(MemArg) = 0x2B,
166 | I32Load8S(MemArg) = 0x2C,
167 | I32Load8U(MemArg) = 0x2D,
168 | I32Load16S(MemArg) = 0x2E,
169 | I32Load16U(MemArg) = 0x2F,
170 | I64Load8S(MemArg) = 0x30,
171 | I64Load8U(MemArg) = 0x31,
172 | I64Load16S(MemArg) = 0x32,
173 | I64Load16U(MemArg) = 0x33,
174 | I64Load32S(MemArg) = 0x34,
175 | I64Load32U(MemArg) = 0x35,
176 | I32Store(MemArg) = 0x36,
177 | I64Store(MemArg) = 0x37,
178 | F32Store(MemArg) = 0x38,
179 | F64Store(MemArg) = 0x39,
180 | I32Store8(MemArg) = 0x3A,
181 | I32Store16(MemArg) = 0x3B,
182 | I64Store8(MemArg) = 0x3C,
183 | I64Store16(MemArg) = 0x3D,
184 | I64Store32(MemArg) = 0x3E,
185 | MemorySize(MemId) = 0x3F,
186 | MemoryGrow(MemId) = 0x40,
187 | I32Const(i32) = 0x41,
188 | I64Const(i64) = 0x42,
189 | F32Const(FloatConst) = 0x43,
190 | F64Const(FloatConst) = 0x44,
191 | I32Eqz = 0x45,
192 | I32Eq = 0x46,
193 | I32Ne = 0x47,
194 | I32LtS = 0x48,
195 | I32LtU = 0x49,
196 | I32GtS = 0x4A,
197 | I32GtU = 0x4B,
198 | I32LeS = 0x4C,
199 | I32LeU = 0x4D,
200 | I32GeS = 0x4E,
201 | I32GeU = 0x4F,
202 | I64Eqz = 0x50,
203 | I64Eq = 0x51,
204 | I64Ne = 0x52,
205 | I64LtS = 0x53,
206 | I64LtU = 0x54,
207 | I64GtS = 0x55,
208 | I64GtU = 0x56,
209 | I64LeS = 0x57,
210 | I64LeU = 0x58,
211 | I64GeS = 0x59,
212 | I64GeU = 0x5A,
213 | F32Eq = 0x5B,
214 | F32Ne = 0x5C,
215 | F32Lt = 0x5D,
216 | F32Gt = 0x5E,
217 | F32Le = 0x5F,
218 | F32Ge = 0x60,
219 | F64Eq = 0x61,
220 | F64Ne = 0x62,
221 | F64Lt = 0x63,
222 | F64Gt = 0x64,
223 | F64Le = 0x65,
224 | F64Ge = 0x66,
225 | I32Clz = 0x67,
226 | I32Ctz = 0x68,
227 | I32PopCnt = 0x69,
228 | I32Add = 0x6A,
229 | I32Sub = 0x6B,
230 | I32Mul = 0x6C,
231 | I32DivS = 0x6D,
232 | I32DivU = 0x6E,
233 | I32RemS = 0x6F,
234 | I32RemU = 0x70,
235 | I32And = 0x71,
236 | I32Or = 0x72,
237 | I32Xor = 0x73,
238 | I32Shl = 0x74,
239 | I32ShrS = 0x75,
240 | I32ShrU = 0x76,
241 | I32RotL = 0x77,
242 | I32RotR = 0x78,
243 | I64Clz = 0x79,
244 | I64Ctz = 0x7A,
245 | I64PopCnt = 0x7B,
246 | I64Add = 0x7C,
247 | I64Sub = 0x7D,
248 | I64Mul = 0x7E,
249 | I64DivS = 0x7F,
250 | I64DivU = 0x80,
251 | I64RemS = 0x81,
252 | I64RemU = 0x82,
253 | I64And = 0x83,
254 | I64Or = 0x84,
255 | I64Xor = 0x85,
256 | I64Shl = 0x86,
257 | I64ShrS = 0x87,
258 | I64ShrU = 0x88,
259 | I64RotL = 0x89,
260 | I64RotR = 0x8A,
261 | F32Abs = 0x8B,
262 | F32Neg = 0x8C,
263 | F32Ceil = 0x8D,
264 | F32Floor = 0x8E,
265 | F32Trunc = 0x8F,
266 | F32Nearest = 0x90,
267 | F32Sqrt = 0x91,
268 | F32Add = 0x92,
269 | F32Sub = 0x93,
270 | F32Mul = 0x94,
271 | F32Div = 0x95,
272 | F32Min = 0x96,
273 | F32Max = 0x97,
274 | F32CopySign = 0x98,
275 | F64Abs = 0x99,
276 | F64Neg = 0x9A,
277 | F64Ceil = 0x9B,
278 | F64Floor = 0x9C,
279 | F64Trunc = 0x9D,
280 | F64Nearest = 0x9E,
281 | F64Sqrt = 0x9F,
282 | F64Add = 0xA0,
283 | F64Sub = 0xA1,
284 | F64Mul = 0xA2,
285 | F64Div = 0xA3,
286 | F64Min = 0xA4,
287 | F64Max = 0xA5,
288 | F64CopySign = 0xA6,
289 | I32WrapI64 = 0xA7,
290 | I32TruncF32S = 0xA8,
291 | I32TruncF332U = 0xA9,
292 | I32TruncF64S = 0xAA,
293 | I32TruncF64U = 0xAB,
294 | I64ExtendI32S = 0xAC,
295 | I64ExtendI32U = 0xAD,
296 | I64TruncF32S = 0xAE,
297 | I64TruncF32U = 0xAF,
298 | I64TruncF64S = 0xB0,
299 | I64TruncF64U = 0xB1,
300 | F32ConvertI32S = 0xB2,
301 | F32ConvertI32U = 0xB3,
302 | F32ConvertI64S = 0xB4,
303 | F32ConvertI64U = 0xB5,
304 | F32DemoteF64 = 0xB6,
305 | F64ConvertI32S = 0xB7,
306 | F64ConvertI32U = 0xB8,
307 | F64ConvertI64S = 0xB9,
308 | F64ConvertI64U = 0xBA,
309 | F64PromoteF32 = 0xBB,
310 | I32ReinterpretF32 = 0xBC,
311 | I64ReinterpretF64 = 0xBD,
312 | F32ReinterpretI32 = 0xBE,
313 | F64ReinterpretI64 = 0xBF,
314 | I32Extend8S = 0xC0,
315 | I32Extend16S = 0xC1,
316 | I64Extend8S = 0xC2,
317 | I64Extend16S = 0xC3,
318 | I64Extend32S = 0xC4,
319 | RefNull(RefType) = 0xD0,
320 | RefIsNull = 0xD1,
321 | RefFunc(FuncId) = 0xD2,
322 | Misc(Misc) = 0xFC,
323 | #[cfg(feature = "simd")]
324 | SIMD(SIMD) = 0xFD,
325 | #[cfg(feature = "threads")]
326 | Atomic(Atomic) = 0xFE,
327 | }
328 |
329 | mod misc;
330 |
331 | pub use misc::Misc;
332 |
333 | #[cfg(feature = "simd")]
334 | pub mod simd;
335 |
336 | #[cfg(feature = "simd")]
337 | pub use simd::SIMD;
338 |
339 | #[cfg(feature = "threads")]
340 | pub mod threads;
341 |
342 | #[cfg(feature = "threads")]
343 | pub use threads::Atomic;
344 |
--------------------------------------------------------------------------------
/src/instructions/simd.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use super::MemArg;
16 | use crate::io::{Decode, DecodeError, DecodeErrorKind, Encode, Wasmbin};
17 | use crate::visit::Visit;
18 | use crate::wasmbin_discriminants;
19 | use arbitrary::Arbitrary;
20 |
21 | macro_rules! def_lane_idx {
22 | ($name:ident, $num:literal) => {
23 | #[derive(Debug, PartialEq, Eq, Hash, Clone, Visit)]
24 | #[repr(transparent)]
25 | pub struct $name(u8);
26 |
27 | impl Encode for $name {
28 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
29 | self.0.encode(w)
30 | }
31 | }
32 |
33 | impl Decode for $name {
34 | fn decode(r: &mut impl std::io::Read) -> Result {
35 | let value = u8::decode(r)?;
36 | if value >= $num {
37 | return Err(DecodeErrorKind::UnsupportedDiscriminant {
38 | ty: stringify!($name),
39 | discriminant: value.into(),
40 | }
41 | .into());
42 | }
43 | Ok(Self(value))
44 | }
45 | }
46 |
47 | impl<'a> Arbitrary<'a> for $name {
48 | #[allow(clippy::range_minus_one)]
49 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result {
50 | u.int_in_range(0..=($num - 1)).map(Self)
51 | }
52 | }
53 | };
54 | }
55 |
56 | def_lane_idx!(LaneIdx2, 2);
57 | def_lane_idx!(LaneIdx4, 4);
58 | def_lane_idx!(LaneIdx8, 8);
59 | def_lane_idx!(LaneIdx16, 16);
60 | def_lane_idx!(LaneIdx32, 32);
61 |
62 | impl Encode for [LaneIdx32; 16] {
63 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
64 | unsafe { &*(self as *const [LaneIdx32; 16] as *const [u8; 16]) }.encode(w)
65 | }
66 | }
67 |
68 | impl Decode for [LaneIdx32; 16] {
69 | fn decode(r: &mut impl std::io::Read) -> Result {
70 | let bytes = <[u8; 16]>::decode(r)?;
71 | for &b in &bytes {
72 | if b >= 32 {
73 | return Err(DecodeErrorKind::UnsupportedDiscriminant {
74 | ty: "LaneIdx32",
75 | discriminant: b.into(),
76 | }
77 | .into());
78 | }
79 | }
80 | Ok(unsafe { std::mem::transmute::<[u8; 16], [LaneIdx32; 16]>(bytes) })
81 | }
82 | }
83 |
84 | impl_visit_for_iter!([u8; 16]);
85 | impl_visit_for_iter!([LaneIdx32; 16]);
86 |
87 | #[wasmbin_discriminants]
88 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
89 | #[repr(u32)]
90 | pub enum SIMD {
91 | V128Load(MemArg) = 0x00,
92 | V128Load8x8S(MemArg) = 0x01,
93 | V128Load8x8U(MemArg) = 0x02,
94 | V128Load16x4S(MemArg) = 0x03,
95 | V128Load16x4U(MemArg) = 0x04,
96 | V128Load32x2S(MemArg) = 0x05,
97 | V128Load32x2U(MemArg) = 0x06,
98 | V128Load8Splat(MemArg) = 0x07,
99 | V128Load16Splat(MemArg) = 0x08,
100 | V128Load32Splat(MemArg) = 0x09,
101 | V128Load64Splat(MemArg) = 0x0A,
102 | V128Store(MemArg) = 0x0B,
103 | V128Const([u8; 16]) = 0x0C,
104 | I8x16Shuffle([LaneIdx32; 16]) = 0x0D,
105 | I8x16Swizzle = 0x0E,
106 | I8x16Splat = 0x0F,
107 | I16x8Splat = 0x10,
108 | I32x4Splat = 0x11,
109 | I64x2Splat = 0x12,
110 | F32x4Splat = 0x13,
111 | F64x2Splat = 0x14,
112 | I8x16ExtractLaneS(LaneIdx16) = 0x15,
113 | I8x16ExtractLaneU(LaneIdx16) = 0x16,
114 | I8x16ReplaceLane(LaneIdx16) = 0x17,
115 | I16x8ExtractLaneS(LaneIdx8) = 0x18,
116 | I16x8ExtractLaneU(LaneIdx8) = 0x19,
117 | I16x8ReplaceLane(LaneIdx8) = 0x1A,
118 | I32x4ExtractLane(LaneIdx4) = 0x1B,
119 | I32x4ReplaceLane(LaneIdx4) = 0x1C,
120 | I64x2ExtractLane(LaneIdx2) = 0x1D,
121 | I64x2ReplaceLane(LaneIdx2) = 0x1E,
122 | F32x4ExtractLane(LaneIdx4) = 0x1F,
123 | F32x4ReplaceLane(LaneIdx4) = 0x20,
124 | F64x2ExtractLane(LaneIdx2) = 0x21,
125 | F64x2ReplaceLane(LaneIdx2) = 0x22,
126 | I8x16Eq = 0x23,
127 | I8x16Ne = 0x24,
128 | I8x16LtS = 0x25,
129 | I8x16LtU = 0x26,
130 | I8x16GtS = 0x27,
131 | I8x16GtU = 0x28,
132 | I8x16LeS = 0x29,
133 | I8x16LeU = 0x2A,
134 | I8x16GeS = 0x2B,
135 | I8x16GeU = 0x2C,
136 | I16x8Eq = 0x2D,
137 | I16x8Ne = 0x2E,
138 | I16x8LtS = 0x2F,
139 | I16x8LtU = 0x30,
140 | I16x8GtS = 0x31,
141 | I16x8GtU = 0x32,
142 | I16x8LeS = 0x33,
143 | I16x8LeU = 0x34,
144 | I16x8GeS = 0x35,
145 | I16x8GeU = 0x36,
146 | I32x4Eq = 0x37,
147 | I32x4Ne = 0x38,
148 | I32x4LtS = 0x39,
149 | I32x4LtU = 0x3A,
150 | I32x4GtS = 0x3B,
151 | I32x4GtU = 0x3C,
152 | I32x4LeS = 0x3D,
153 | I32x4LeU = 0x3E,
154 | I32x4GeS = 0x3F,
155 | I32x4GeU = 0x40,
156 | F32x4Eq = 0x41,
157 | F32x4Ne = 0x42,
158 | F32x4Lt = 0x43,
159 | F32x4Gt = 0x44,
160 | F32x4Le = 0x45,
161 | F32x4Ge = 0x46,
162 | F64x2Eq = 0x47,
163 | F64x2Ne = 0x48,
164 | F64x2Lt = 0x49,
165 | F64x2Gt = 0x4A,
166 | F64x2Le = 0x4B,
167 | F64x2Ge = 0x4C,
168 | V128Not = 0x4D,
169 | V128And = 0x4E,
170 | V128Andnot = 0x4F,
171 | V128Or = 0x50,
172 | V128Xor = 0x51,
173 | V128Bitselect = 0x52,
174 | I8x16Abs = 0x60,
175 | I8x16Neg = 0x61,
176 | I8x16AllTrue = 0x63,
177 | I8x16Bitmask = 0x64,
178 | I8x16NarrowI16x8S = 0x65,
179 | I8x16NarrowI16x8U = 0x66,
180 | I8x16Shl = 0x6B,
181 | I8x16ShrS = 0x6C,
182 | I8x16ShrU = 0x6D,
183 | I8x16Add = 0x6E,
184 | I8x16AddSatS = 0x6F,
185 | I8x16AddSatU = 0x70,
186 | I8x16Sub = 0x71,
187 | I8x16SubSatS = 0x72,
188 | I8x16SubSatU = 0x73,
189 | I8x16MinS = 0x76,
190 | I8x16MinU = 0x77,
191 | I8x16MaxS = 0x78,
192 | I8x16MaxU = 0x79,
193 | I8x16AvgrU = 0x7B,
194 | I16x8Abs = 0x80,
195 | I16x8Neg = 0x81,
196 | I16x8AllTrue = 0x83,
197 | I16x8Bitmask = 0x84,
198 | I16x8NarrowI32x4S = 0x85,
199 | I16x8NarrowI32x4U = 0x86,
200 | I16x8ExtendLowI8x16S = 0x87,
201 | I16x8ExtendHighI8x16S = 0x88,
202 | I16x8ExtendLowI8x16U = 0x89,
203 | I16x8ExtendHighI8x16U = 0x8A,
204 | I16x8Shl = 0x8B,
205 | I16x8ShrS = 0x8C,
206 | I16x8ShrU = 0x8D,
207 | I16x8Add = 0x8E,
208 | I16x8AddSatS = 0x8F,
209 | I16x8AddSatU = 0x90,
210 | I16x8Sub = 0x91,
211 | I16x8SubSatS = 0x92,
212 | I16x8SubSatU = 0x93,
213 | I16x8Mul = 0x95,
214 | I16x8MinS = 0x96,
215 | I16x8MinU = 0x97,
216 | I16x8MaxS = 0x98,
217 | I16x8MaxU = 0x99,
218 | I16x8AvgrU = 0x9B,
219 | I32x4Abs = 0xA0,
220 | I32x4Neg = 0xA1,
221 | I32x4AllTrue = 0xA3,
222 | I32x4Bitmask = 0xA4,
223 | I32x4ExtendLowI16x8S = 0xA7,
224 | I32x4ExtendHighI16x8S = 0xA8,
225 | I32x4ExtendLowI16x8U = 0xA9,
226 | I32x4ExtendHighI16x8U = 0xAA,
227 | I32x4Shl = 0xAB,
228 | I32x4ShrS = 0xAC,
229 | I32x4ShrU = 0xAD,
230 | I32x4Add = 0xAE,
231 | I32x4Sub = 0xB1,
232 | I32x4Mul = 0xB5,
233 | I32x4MinS = 0xB6,
234 | I32x4MinU = 0xB7,
235 | I32x4MaxS = 0xB8,
236 | I32x4MaxU = 0xB9,
237 | I32x4DotI16x8S = 0xBA,
238 | I64x2Abs = 0xC0,
239 | I64x2Neg = 0xC1,
240 | I64x2Bitmask = 0xC4,
241 | I64x2ExtendLowI32x4S = 0xC7,
242 | I64x2ExtendHighI32x4S = 0xC8,
243 | I64x2ExtendLowI32x4U = 0xC9,
244 | I64x2ExtendHighI32x4U = 0xCA,
245 | I64x2Shl = 0xCB,
246 | I64x2ShrS = 0xCC,
247 | I64x2ShrU = 0xCD,
248 | I64x2Add = 0xCE,
249 | I64x2Sub = 0xD1,
250 | I64x2Mul = 0xD5,
251 | F32x4Ceil = 0x67,
252 | F32x4Floor = 0x68,
253 | F32x4Trunc = 0x69,
254 | F32x4Nearest = 0x6A,
255 | F64x2Ceil = 0x74,
256 | F64x2Floor = 0x75,
257 | F64x2Trunc = 0x7A,
258 | F64x2Nearest = 0x94,
259 | F32x4Abs = 0xE0,
260 | F32x4Neg = 0xE1,
261 | F32x4Sqrt = 0xE3,
262 | F32x4Add = 0xE4,
263 | F32x4Sub = 0xE5,
264 | F32x4Mul = 0xE6,
265 | F32x4Div = 0xE7,
266 | F32x4Min = 0xE8,
267 | F32x4Max = 0xE9,
268 | F32x4Pmin = 0xEA,
269 | F32x4Pmax = 0xEB,
270 | F64x2Abs = 0xEC,
271 | F64x2Neg = 0xED,
272 | F64x2Sqrt = 0xEF,
273 | F64x2Add = 0xF0,
274 | F64x2Sub = 0xF1,
275 | F64x2Mul = 0xF2,
276 | F64x2Div = 0xF3,
277 | F64x2Min = 0xF4,
278 | F64x2Max = 0xF5,
279 | F64x2Pmin = 0xF6,
280 | F64x2Pmax = 0xF7,
281 | I32x4TruncSatF32x4S = 0xF8,
282 | I32x4TruncSatF32x4U = 0xF9,
283 | F32x4ConvertI32x4S = 0xFA,
284 | F32x4ConvertI32x4U = 0xFB,
285 | V128Load32Zero(MemArg) = 0x5C,
286 | V128Load64Zero(MemArg) = 0x5D,
287 | I16x8ExtmulLowI8x16S = 0x9C,
288 | I16x8ExtmulHighI8x16S = 0x9D,
289 | I16x8ExtmulLowI8x16U = 0x9E,
290 | I16x8ExtmulHighI8x16U = 0x9F,
291 | I32x4ExtmulLowI16x8S = 0xBC,
292 | I32x4ExtmulHighI16x8S = 0xBD,
293 | I32x4ExtmulLowI16x8U = 0xBE,
294 | I32x4ExtmulHighI16x8U = 0xBF,
295 | I64x2ExtmulLowI32x4S = 0xDC,
296 | I64x2ExtmulHighI32x4S = 0xDD,
297 | I64x2ExtmulLowI32x4U = 0xDE,
298 | I64x2ExtmulHighI32x4U = 0xDF,
299 | I16x8Q15mulrSatS = 0x82,
300 | V128AnyTrue = 0x53,
301 | V128Load8Lane(MemArg, LaneIdx16) = 0x54,
302 | V128Load16Lane(MemArg, LaneIdx8) = 0x55,
303 | V128Load32Lane(MemArg, LaneIdx4) = 0x56,
304 | V128Load64Lane(MemArg, LaneIdx2) = 0x57,
305 | V128Store8Lane(MemArg, LaneIdx16) = 0x58,
306 | V128Store16Lane(MemArg, LaneIdx8) = 0x59,
307 | V128Store32Lane(MemArg, LaneIdx4) = 0x5A,
308 | V128Store64Lane(MemArg, LaneIdx2) = 0x5B,
309 | I64x2Eq = 0xD6,
310 | I64x2Ne = 0xD7,
311 | I64x2LtS = 0xD8,
312 | I64x2GtS = 0xD9,
313 | I64x2LeS = 0xDA,
314 | I64x2GeS = 0xDB,
315 | I64x2AllTrue = 0xC3,
316 | F64x2ConvertLowI32x4S = 0xFE,
317 | F64x2ConvertLowI32x4U = 0xFF,
318 | I32x4TruncSatF64x2SZero = 0xFC,
319 | I32x4TruncSatF64x2UZero = 0xFD,
320 | F32x4DemoteF64x2Zero = 0x5E,
321 | F64x2PromoteLowF32x4 = 0x5F,
322 | I8x16Popcnt = 0x62,
323 | I16x8ExtaddPairwiseI8x16S = 0x7C,
324 | I16x8ExtaddPairwiseI8x16U = 0x7D,
325 | I32x4ExtaddPairwiseI16x8S = 0x7E,
326 | I32x4ExtaddPairwiseI16x8U = 0x7F,
327 | }
328 |
--------------------------------------------------------------------------------
/src/instructions/threads.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use super::MemArg;
16 | use crate::io::{DecodeErrorKind, Wasmbin};
17 | use crate::visit::Visit;
18 | use crate::wasmbin_discriminants;
19 | use arbitrary::Arbitrary;
20 |
21 | macro_rules! def_mem_arg {
22 | ($name:ident, $num:literal) => {
23 | #[derive(Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
24 | #[repr(transparent)]
25 | pub struct $name {
26 | pub offset: u32,
27 | }
28 |
29 | impl $name {
30 | pub const ALIGN: u32 = $num;
31 | }
32 |
33 | impl From<$name> for MemArg {
34 | fn from(arg: $name) -> MemArg {
35 | MemArg {
36 | align: $num,
37 | offset: arg.offset,
38 | }
39 | }
40 | }
41 |
42 | encode_decode_as!($name, {
43 | ($name { offset }) <=> (MemArg { align: $num, offset }),
44 | }, |arg| {
45 | Err(DecodeErrorKind::UnsupportedDiscriminant {
46 | ty: stringify!($name),
47 | discriminant: arg.offset.into(),
48 | }.into())
49 | });
50 | };
51 | }
52 |
53 | def_mem_arg!(MemArg8, 0x00);
54 | def_mem_arg!(MemArg16, 0x01);
55 | def_mem_arg!(MemArg32, 0x02);
56 | def_mem_arg!(MemArg64, 0x03);
57 |
58 | #[wasmbin_discriminants]
59 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
60 | #[repr(u8)]
61 | pub enum Atomic {
62 | Wake(MemArg32) = 0x00,
63 | I32Wait(MemArg32) = 0x01,
64 | I64Wait(MemArg64) = 0x02,
65 | I32Load(MemArg32) = 0x10,
66 | I64Load(MemArg64) = 0x11,
67 | I32Load8U(MemArg8) = 0x12,
68 | I32Load16U(MemArg16) = 0x13,
69 | I64Load8U(MemArg8) = 0x14,
70 | I64Load16U(MemArg16) = 0x15,
71 | I64Load32U(MemArg32) = 0x16,
72 | I32Store(MemArg32) = 0x17,
73 | I64Store(MemArg64) = 0x18,
74 | I32Store8(MemArg8) = 0x19,
75 | I32Store16(MemArg16) = 0x1A,
76 | I64Store8(MemArg8) = 0x1B,
77 | I64Store16(MemArg16) = 0x1C,
78 | I64Store32(MemArg32) = 0x1D,
79 | I32RmwAdd(MemArg32) = 0x1E,
80 | I64RmwAdd(MemArg64) = 0x1F,
81 | I32Rmw8AddU(MemArg8) = 0x20,
82 | I32Rmw16AddU(MemArg16) = 0x21,
83 | I64Rmw8AddU(MemArg8) = 0x22,
84 | I64Rmw16AddU(MemArg16) = 0x23,
85 | I64Rmw32AddU(MemArg32) = 0x24,
86 | I32RmwSub(MemArg32) = 0x25,
87 | I64RmwSub(MemArg64) = 0x26,
88 | I32Rmw8SubU(MemArg8) = 0x27,
89 | I32Rmw16SubU(MemArg16) = 0x28,
90 | I64Rmw8SubU(MemArg8) = 0x29,
91 | I64Rmw16SubU(MemArg16) = 0x2A,
92 | I64Rmw32SubU(MemArg32) = 0x2B,
93 | I32RmwAnd(MemArg32) = 0x2C,
94 | I64RmwAnd(MemArg64) = 0x2D,
95 | I32Rmw8AndU(MemArg8) = 0x2E,
96 | I32Rmw16AndU(MemArg16) = 0x2F,
97 | I64Rmw8AndU(MemArg8) = 0x30,
98 | I64Rmw16AndU(MemArg16) = 0x31,
99 | I64Rmw32AndU(MemArg32) = 0x32,
100 | I32RmwOr(MemArg32) = 0x33,
101 | I64RmwOr(MemArg64) = 0x34,
102 | I32Rmw8OrU(MemArg8) = 0x35,
103 | I32Rmw16OrU(MemArg16) = 0x36,
104 | I64Rmw8OrU(MemArg8) = 0x37,
105 | I64Rmw16OrU(MemArg16) = 0x38,
106 | I64Rmw32OrU(MemArg32) = 0x39,
107 | I32RmwXor(MemArg32) = 0x3A,
108 | I64RmwXor(MemArg64) = 0x3B,
109 | I32Rmw8XorU(MemArg8) = 0x3C,
110 | I32Rmw16XorU(MemArg16) = 0x3D,
111 | I64Rmw8XorU(MemArg8) = 0x3E,
112 | I64Rmw16XorU(MemArg16) = 0x3F,
113 | I64Rmw32XorU(MemArg32) = 0x40,
114 | I32RmwXchg(MemArg32) = 0x41,
115 | I64RmwXchg(MemArg64) = 0x42,
116 | I32Rmw8XchgU(MemArg8) = 0x43,
117 | I32Rmw16XchgU(MemArg16) = 0x44,
118 | I64Rmw8XchgU(MemArg8) = 0x45,
119 | I64Rmw16XchgU(MemArg16) = 0x46,
120 | I64Rmw32XchgU(MemArg32) = 0x47,
121 | I32RmwCmpXchg(MemArg32) = 0x48,
122 | I64RmwCmpXchg(MemArg64) = 0x49,
123 | I32Rmw8CmpXchgU(MemArg8) = 0x4A,
124 | I32Rmw16CmpXchgU(MemArg16) = 0x4B,
125 | I64Rmw8CmpXchgU(MemArg8) = 0x4C,
126 | I64Rmw16CmpXchgU(MemArg16) = 0x4D,
127 | I64Rmw32CmpXchgU(MemArg32) = 0x4E,
128 | }
129 |
--------------------------------------------------------------------------------
/src/io.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::sections::SectionOrderError;
16 | use thiserror::Error;
17 | pub use wasmbin_derive::Wasmbin;
18 |
19 | #[derive(Error, Debug)]
20 | pub enum DecodeErrorKind {
21 | #[error("{0}")]
22 | Io(#[from] std::io::Error),
23 |
24 | #[error("{0}")]
25 | Leb128(#[from] leb128::read::Error),
26 |
27 | #[error("{0}")]
28 | Utf8(#[from] std::string::FromUtf8Error),
29 |
30 | #[error("Could not recognise discriminant 0x{discriminant:X} for type {ty}")]
31 | UnsupportedDiscriminant {
32 | ty: &'static str,
33 | discriminant: i128,
34 | },
35 |
36 | #[error("Invalid module magic signature [{actual:02X?}]")]
37 | InvalidMagic { actual: [u8; 8] },
38 |
39 | #[error("Unrecognized data")]
40 | UnrecognizedData,
41 |
42 | #[error("{0}")]
43 | SectionOutOfOrder(#[from] SectionOrderError),
44 | }
45 |
46 | #[derive(Debug)]
47 | pub(crate) enum PathItem {
48 | Name(&'static str),
49 | Index(usize),
50 | Variant(&'static str),
51 | }
52 |
53 | #[derive(Error, Debug)]
54 | pub struct DecodeError {
55 | path: Vec,
56 | #[source]
57 | pub kind: DecodeErrorKind,
58 | }
59 |
60 | impl DecodeError {
61 | pub(crate) fn in_path(mut self, item: PathItem) -> Self {
62 | self.path.push(item);
63 | self
64 | }
65 | }
66 |
67 | impl> From for DecodeError {
68 | fn from(err: E) -> DecodeError {
69 | DecodeError {
70 | path: vec![],
71 | kind: err.into(),
72 | }
73 | }
74 | }
75 |
76 | impl std::fmt::Display for DecodeError {
77 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
78 | f.write_str("(root)")?;
79 | for item in self.path.iter().rev() {
80 | match *item {
81 | PathItem::Name(name) => write!(f, ".{}", name),
82 | PathItem::Index(index) => write!(f, "[{}]", index),
83 | PathItem::Variant(variant) => write!(f, ":<{}>", variant),
84 | }?;
85 | }
86 | write!(f, ": {}", self.kind)
87 | }
88 | }
89 |
90 | impl From for DecodeErrorKind {
91 | fn from(_err: std::num::TryFromIntError) -> Self {
92 | DecodeErrorKind::Leb128(leb128::read::Error::Overflow)
93 | }
94 | }
95 |
96 | impl From for DecodeErrorKind {
97 | fn from(err: std::convert::Infallible) -> Self {
98 | match err {}
99 | }
100 | }
101 |
102 | pub trait Encode {
103 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()>;
104 | }
105 |
106 | pub trait Decode: Sized {
107 | fn decode(r: &mut impl std::io::Read) -> Result;
108 | }
109 |
110 | macro_rules! encode_decode_as {
111 | ($ty:ty, {
112 | $($lhs:tt <=> $rhs:tt,)*
113 | } $(, |$other:pat| $other_handler:expr)?) => {
114 | impl crate::io::Encode for $ty {
115 | #[allow(unused_parens)]
116 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
117 | match *self {
118 | $($lhs => $rhs,)*
119 | }.encode(w)
120 | }
121 | }
122 |
123 | impl crate::io::Decode for $ty {
124 | #[allow(unused_parens)]
125 | fn decode(r: &mut impl std::io::Read) -> Result {
126 | Ok(match crate::io::Decode::decode(r)? {
127 | $($rhs => $lhs,)*
128 | $($other => return $other_handler)?
129 | })
130 | }
131 | }
132 | };
133 | }
134 |
135 | pub trait DecodeWithDiscriminant: Decode {
136 | const NAME: &'static str;
137 | type Discriminant: Decode + Copy + Into;
138 |
139 | fn maybe_decode_with_discriminant(
140 | discriminant: Self::Discriminant,
141 | r: &mut impl std::io::Read,
142 | ) -> Result, DecodeError>;
143 |
144 | fn decode_with_discriminant(
145 | discriminant: Self::Discriminant,
146 | r: &mut impl std::io::Read,
147 | ) -> Result {
148 | Self::maybe_decode_with_discriminant(discriminant, r)?.ok_or_else(|| {
149 | DecodeErrorKind::UnsupportedDiscriminant {
150 | ty: Self::NAME,
151 | discriminant: discriminant.into(),
152 | }
153 | .into()
154 | })
155 | }
156 |
157 | fn decode_without_discriminant(r: &mut impl std::io::Read) -> Result {
158 | Self::decode_with_discriminant(Self::Discriminant::decode(r)?, r)
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #![cfg_attr(feature = "nightly", feature(arbitrary_enum_discriminant, never_type))]
16 | #![warn(clippy::all, clippy::pedantic)]
17 | #![allow(
18 | clippy::missing_errors_doc,
19 | clippy::match_bool,
20 | clippy::must_use_candidate,
21 | clippy::module_name_repetitions
22 | )]
23 |
24 | use wasmbin_derive::wasmbin_discriminants;
25 |
26 | #[macro_use]
27 | pub mod io;
28 | #[macro_use]
29 | pub mod visit;
30 |
31 | pub mod builtins;
32 | pub mod indices;
33 | pub mod instructions;
34 | pub mod module;
35 | pub mod sections;
36 | pub mod types;
37 |
38 | pub use module::Module;
39 |
--------------------------------------------------------------------------------
/src/module.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::builtins::Blob;
16 | use crate::io::{Decode, DecodeError, DecodeErrorKind, Encode, Wasmbin};
17 | use crate::sections::{Section, StdPayload};
18 | use crate::visit::Visit;
19 | use arbitrary::Arbitrary;
20 | use std::cmp::Ordering;
21 |
22 | const MAGIC_AND_VERSION: [u8; 8] = [b'\0', b'a', b's', b'm', 0x01, 0x00, 0x00, 0x00];
23 |
24 | #[derive(Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
25 | pub struct MagicAndVersion;
26 |
27 | encode_decode_as!(MagicAndVersion, {
28 | MagicAndVersion <=> MAGIC_AND_VERSION,
29 | }, |actual| {
30 | Err(DecodeErrorKind::InvalidMagic { actual }.into())
31 | });
32 |
33 | #[derive(Wasmbin, Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
34 | pub struct Module {
35 | #[doc(hidden)]
36 | pub magic_and_version: MagicAndVersion,
37 | pub sections: Vec,
38 | }
39 |
40 | impl Module {
41 | pub fn decode_from(mut r: impl std::io::Read) -> Result {
42 | Self::decode(&mut r)
43 | }
44 |
45 | pub fn encode_into(&self, mut w: W) -> std::io::Result {
46 | self.encode(&mut w)?;
47 | Ok(w)
48 | }
49 |
50 | pub fn find_std_section(&self) -> Option<&Blob> {
51 | self.sections.iter().find_map(Section::try_as)
52 | }
53 |
54 | pub fn find_std_section_mut(&mut self) -> Option<&mut Blob> {
55 | self.sections.iter_mut().find_map(Section::try_as_mut)
56 | }
57 |
58 | pub fn find_or_insert_std_section(
59 | &mut self,
60 | insert_callback: impl FnOnce() -> T,
61 | ) -> &mut Blob {
62 | let mut index = self.sections.len();
63 | let mut insert = true;
64 | for (i, section) in self.sections.iter_mut().enumerate() {
65 | match section.kind().cmp(&T::KIND) {
66 | Ordering::Less => continue,
67 | Ordering::Equal => {
68 | // We can't just `return` here due to a bug in rustc:
69 | // https://github.com/rust-lang/rust/issues/70255
70 | insert = false;
71 | }
72 | Ordering::Greater => {}
73 | }
74 | index = i;
75 | break;
76 | }
77 | if insert {
78 | self.sections.insert(index, insert_callback().into());
79 | }
80 | unsafe { self.sections.get_unchecked_mut(index) }
81 | .try_as_mut()
82 | .unwrap_or_else(|| unsafe { std::hint::unreachable_unchecked() })
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/sections.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::builtins::Lazy;
16 | use crate::builtins::WasmbinCountable;
17 | use crate::builtins::{Blob, RawBlob};
18 | use crate::indices::{FuncId, GlobalId, LocalId, MemId, TableId, TypeId};
19 | use crate::instructions::Expression;
20 | use crate::io::{
21 | Decode, DecodeError, DecodeWithDiscriminant, Encode, PathItem, Wasmbin,
22 | };
23 | use crate::types::{FuncType, GlobalType, MemType, RefType, TableType, ValueType};
24 | use crate::visit::Visit;
25 | use crate::wasmbin_discriminants;
26 | use arbitrary::Arbitrary;
27 | use custom_debug::Debug as CustomDebug;
28 | use std::convert::TryFrom;
29 | use thiserror::Error;
30 |
31 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
32 | pub struct ModuleNameSubSection {
33 | pub name: String,
34 | }
35 |
36 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
37 | pub struct NameAssoc {
38 | pub index: I,
39 | pub value: V,
40 | }
41 |
42 | impl WasmbinCountable for NameAssoc {}
43 |
44 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
45 | pub struct NameMap {
46 | pub items: Vec>,
47 | }
48 |
49 | #[wasmbin_discriminants]
50 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
51 | #[repr(u8)]
52 | pub enum NameSubSection {
53 | Module(Blob) = 0,
54 | Func(Blob>) = 1,
55 | Local(Blob>>) = 2,
56 | }
57 |
58 | impl Encode for [NameSubSection] {
59 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
60 | for sub in self {
61 | sub.encode(w)?;
62 | }
63 | Ok(())
64 | }
65 | }
66 |
67 | impl Decode for Vec {
68 | fn decode(r: &mut impl std::io::Read) -> Result {
69 | let mut sub = Vec::new();
70 | while let Some(disc) = Option::decode(r)? {
71 | let i = sub.len();
72 | sub.push(
73 | NameSubSection::decode_with_discriminant(disc, r)
74 | .map_err(move |err| err.in_path(PathItem::Index(i)))?,
75 | );
76 | }
77 | Ok(sub)
78 | }
79 | }
80 |
81 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
82 | pub struct ProducerField {
83 | pub name: String,
84 | pub values: Vec,
85 | }
86 |
87 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
88 | pub struct ProducerVersionedName {
89 | pub name: String,
90 | pub version: String,
91 | }
92 |
93 | #[derive(Wasmbin, CustomDebug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
94 | pub struct RawCustomSection {
95 | pub name: String,
96 |
97 | #[debug(with = "custom_debug::hexbuf_str")]
98 | pub data: Vec,
99 | }
100 |
101 | macro_rules! define_custom_sections {
102 | ($($name:ident($ty:ty) = $disc:literal,)*) => {
103 | #[derive(Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
104 | pub enum CustomSection {
105 | $($name(Lazy<$ty>),)*
106 | Other(RawCustomSection),
107 | }
108 |
109 | impl CustomSection {
110 | pub fn name(&self) -> &str {
111 | match self {
112 | $(Self::$name(_) => $disc,)*
113 | Self::Other(raw) => raw.name.as_str(),
114 | }
115 | }
116 | }
117 |
118 | impl Encode for CustomSection {
119 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
120 | match self {
121 | $(CustomSection::$name(data) => {
122 | $disc.encode(w)?;
123 | data.encode(w)
124 | })*
125 | CustomSection::Other(raw) => raw.encode(w)
126 | }
127 | }
128 | }
129 |
130 | impl Decode for CustomSection {
131 | fn decode(r: &mut impl std::io::Read) -> Result {
132 | let raw = RawCustomSection::decode(r)?;
133 | Ok(match raw.name.as_str() {
134 | $($disc => CustomSection::$name(Lazy::from_raw(raw.data)),)*
135 | _ => CustomSection::Other(raw)
136 | })
137 | }
138 | }
139 | };
140 | }
141 |
142 | define_custom_sections! {
143 | Name(Vec) = "name",
144 | Producers(Vec) = "producers",
145 | // https://github.com/WebAssembly/tool-conventions/blob/08bacbed/Debugging.md#external-dwarf
146 | ExternalDebugInfo(String) = "external_debug_info",
147 | // https://github.com/WebAssembly/tool-conventions/blob/08bacbed/Debugging.md#source-maps
148 | SourceMappingUrl(String) = "sourceMappingURL",
149 | }
150 |
151 | #[wasmbin_discriminants]
152 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
153 | #[repr(u8)]
154 | pub enum ImportDesc {
155 | Func(TypeId) = 0x00,
156 | Table(TableType) = 0x01,
157 | Mem(MemType) = 0x02,
158 | Global(GlobalType) = 0x03,
159 | }
160 |
161 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
162 | pub struct ImportPath {
163 | pub module: String,
164 | pub name: String,
165 | }
166 |
167 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
168 | pub struct Import {
169 | pub path: ImportPath,
170 | pub desc: ImportDesc,
171 | }
172 |
173 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
174 | pub struct Global {
175 | pub ty: GlobalType,
176 | pub init: Expression,
177 | }
178 |
179 | #[wasmbin_discriminants]
180 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
181 | #[repr(u8)]
182 | pub enum ExportDesc {
183 | Func(FuncId) = 0x00,
184 | Table(TableId) = 0x01,
185 | Mem(MemId) = 0x02,
186 | Global(GlobalId) = 0x03,
187 | }
188 |
189 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
190 | pub struct Export {
191 | pub name: String,
192 | pub desc: ExportDesc,
193 | }
194 |
195 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
196 | #[repr(u8)]
197 | pub enum ElemKind {
198 | FuncRef = 0x00,
199 | }
200 |
201 | #[wasmbin_discriminants]
202 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
203 | #[repr(u8)]
204 | pub enum Element {
205 | ActiveWithFuncs {
206 | offset: Expression,
207 | funcs: Vec,
208 | } = 0x00,
209 | PassiveWithFuncs {
210 | kind: ElemKind,
211 | funcs: Vec,
212 | } = 0x01,
213 | ActiveWithTableAndFuncs {
214 | table: TableId,
215 | offset: Expression,
216 | kind: ElemKind,
217 | funcs: Vec,
218 | } = 0x02,
219 | DeclarativeWithFuncs {
220 | kind: ElemKind,
221 | funcs: Vec,
222 | } = 0x03,
223 | ActiveWithExprs {
224 | offset: Expression,
225 | exprs: Vec,
226 | } = 0x04,
227 | PassiveWithExprs {
228 | ty: RefType,
229 | exprs: Vec,
230 | } = 0x05,
231 | ActiveWithTableAndExprs {
232 | table: TableId,
233 | offset: Expression,
234 | ty: RefType,
235 | exprs: Vec,
236 | } = 0x06,
237 | DeclarativeWithExprs {
238 | ty: RefType,
239 | exprs: Vec,
240 | } = 0x07,
241 | }
242 |
243 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
244 | pub struct Locals {
245 | pub repeat: u32,
246 | pub ty: ValueType,
247 | }
248 |
249 | #[derive(
250 | Wasmbin, WasmbinCountable, Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone, Visit,
251 | )]
252 | pub struct FuncBody {
253 | pub locals: Vec,
254 | pub expr: Expression,
255 | }
256 |
257 | #[wasmbin_discriminants]
258 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
259 | #[repr(u8)]
260 | pub enum DataInit {
261 | Active { offset: Expression } = 0x00,
262 | Passive = 0x01,
263 | ActiveWithMemory { memory: MemId, offset: Expression } = 0x02,
264 | }
265 |
266 | #[derive(Wasmbin, WasmbinCountable, CustomDebug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
267 | pub struct Data {
268 | pub init: DataInit,
269 | #[debug(with = "custom_debug::hexbuf_str")]
270 | pub blob: RawBlob,
271 | }
272 |
273 | pub trait Payload: Encode + Decode + Into {
274 | const KIND: Kind;
275 |
276 | fn try_from_ref(section: &Section) -> Option<&Blob>;
277 | fn try_from_mut(section: &mut Section) -> Option<&mut Blob>;
278 | fn try_from(section: Section) -> Result, Section>;
279 | }
280 |
281 | pub trait StdPayload: Payload {}
282 |
283 | macro_rules! define_sections {
284 | ($($(# $attr:tt)? $name:ident($ty:ty) = $disc:literal,)*) => {
285 | pub mod payload {
286 | $($(# $attr)? pub type $name = $ty;)*
287 | }
288 |
289 | #[wasmbin_discriminants]
290 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
291 | #[repr(u8)]
292 | pub enum Section {
293 | $($(# $attr)? $name(Blob) = $disc,)*
294 | }
295 |
296 | #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
297 | #[repr(u8)]
298 | pub enum Kind {
299 | $($(# $attr)? $name = $disc,)*
300 | }
301 |
302 | impl TryFrom for Kind {
303 | type Error = u8;
304 |
305 | fn try_from(discriminant: u8) -> Result {
306 | Ok(match discriminant {
307 | $($(# $attr)? $disc => Kind::$name,)*
308 | _ => return Err(discriminant),
309 | })
310 | }
311 | }
312 |
313 | impl Ord for Kind {
314 | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
315 | // Some new sections might have larger discriminants,
316 | // but be ordered logically between those will smaller
317 | // discriminants.
318 | //
319 | // To compare their Kinds in a defined order, we need an
320 | // intermediate enum without discriminants.
321 | #[derive(PartialEq, Eq, PartialOrd, Ord)]
322 | #[repr(u8)]
323 | enum OrderedRepr {
324 | $($(# $attr)? $name,)*
325 | }
326 |
327 | impl From for OrderedRepr {
328 | fn from(kind: Kind) -> Self {
329 | match kind {
330 | $($(# $attr)? Kind::$name => Self::$name,)*
331 | }
332 | }
333 | }
334 |
335 | OrderedRepr::from(*self).cmp(&OrderedRepr::from(*other))
336 | }
337 | }
338 |
339 | impl PartialOrd for Kind {
340 | fn partial_cmp(&self, other: &Self) -> Option {
341 | Some(self.cmp(other))
342 | }
343 | }
344 |
345 | $($(# $attr)? const _: () = {
346 | impl From> for Section {
347 | fn from(value: Blob) -> Self {
348 | Section::$name(value)
349 | }
350 | }
351 |
352 | impl From for Section {
353 | fn from(value: payload::$name) -> Self {
354 | Section::$name(Blob::from(value))
355 | }
356 | }
357 |
358 | impl Payload for payload::$name {
359 | const KIND: Kind = Kind::$name;
360 |
361 | fn try_from_ref(section: &Section) -> Option<&Blob> {
362 | match section {
363 | Section::$name(res) => Some(res),
364 | _ => None,
365 | }
366 | }
367 |
368 | fn try_from_mut(section: &mut Section) -> Option<&mut Blob> {
369 | match section {
370 | Section::$name(res) => Some(res),
371 | _ => None,
372 | }
373 | }
374 |
375 | fn try_from(section: Section) -> Result, Section> {
376 | match section {
377 | Section::$name(res) => Ok(res),
378 | _ => Err(section),
379 | }
380 | }
381 | }
382 | };)*
383 |
384 | impl Section {
385 | pub fn kind(&self) -> Kind {
386 | match self {
387 | $($(# $attr)? Section::$name(_) => Kind::$name,)*
388 | }
389 | }
390 |
391 | pub fn try_as(&self) -> Option<&Blob> {
392 | T::try_from_ref(self)
393 | }
394 |
395 | pub fn try_as_mut(&mut self) -> Option<&mut Blob> {
396 | T::try_from_mut(self)
397 | }
398 | }
399 |
400 | define_sections!(@std $($(# $attr)? $name)*);
401 | };
402 |
403 | (@std $ignore_custom:ident $($(# $attr:tt)? $name:ident)*) => {
404 | $($(# $attr)? impl StdPayload for payload::$name {})*
405 | };
406 | }
407 |
408 | define_sections! {
409 | Custom(super::CustomSection) = 0,
410 | Type(Vec) = 1,
411 | Import(Vec) = 2,
412 | Function(Vec) = 3,
413 | Table(Vec) = 4,
414 | Memory(Vec) = 5,
415 | Global(Vec) = 6,
416 | Export(Vec) = 7,
417 | Start(super::FuncId) = 8,
418 | Element(Vec) = 9,
419 | DataCount(u32) = 12,
420 | Code(Vec>) = 10,
421 | Data(Vec) = 11,
422 | }
423 |
424 | #[derive(Debug, Error)]
425 | #[error("Section out of order: {current:?} after {prev:?}")]
426 | pub struct SectionOrderError {
427 | pub current: Kind,
428 | pub prev: Kind,
429 | }
430 |
431 | impl From for std::io::Error {
432 | fn from(err: SectionOrderError) -> Self {
433 | Self::new(std::io::ErrorKind::InvalidData, err)
434 | }
435 | }
436 |
437 | struct SectionOrderTracker {
438 | last_kind: Kind,
439 | }
440 |
441 | impl Default for SectionOrderTracker {
442 | fn default() -> Self {
443 | Self {
444 | last_kind: Kind::Custom,
445 | }
446 | }
447 | }
448 |
449 | impl SectionOrderTracker {
450 | pub fn try_add(&mut self, section: &Section) -> Result<(), SectionOrderError> {
451 | match section.kind() {
452 | Kind::Custom => {}
453 | kind if kind > self.last_kind => {
454 | self.last_kind = kind;
455 | }
456 | kind => {
457 | return Err(SectionOrderError {
458 | prev: self.last_kind,
459 | current: kind,
460 | });
461 | }
462 | }
463 | Ok(())
464 | }
465 | }
466 |
467 | impl Encode for [Section] {
468 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
469 | let mut section_order_tracker = SectionOrderTracker::default();
470 | for section in self {
471 | section_order_tracker.try_add(section)?;
472 | section.encode(w)?;
473 | }
474 | Ok(())
475 | }
476 | }
477 |
478 | impl Decode for Vec {
479 | fn decode(r: &mut impl std::io::Read) -> Result {
480 | let mut sections = Vec::new();
481 | let mut section_order_tracker = SectionOrderTracker::default();
482 | while let Some(disc) = Option::decode(r)? {
483 | let i = sections.len();
484 | (|| -> Result<(), DecodeError> {
485 | let section = Section::decode_with_discriminant(disc, r)?;
486 | section_order_tracker.try_add(§ion)?;
487 | sections.push(section);
488 | Ok(())
489 | })()
490 | .map_err(move |err| err.in_path(PathItem::Index(i)))?;
491 | }
492 | Ok(sections)
493 | }
494 | }
495 |
--------------------------------------------------------------------------------
/src/types.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::builtins::WasmbinCountable;
16 | use crate::indices::TypeId;
17 | use crate::io::{Decode, DecodeError, DecodeWithDiscriminant, Encode, PathItem, Wasmbin};
18 | use crate::visit::Visit;
19 | use crate::wasmbin_discriminants;
20 | use arbitrary::Arbitrary;
21 | use std::convert::TryFrom;
22 | use std::fmt::{self, Debug, Formatter};
23 |
24 | const OP_CODE_EMPTY_BLOCK: u8 = 0x40;
25 |
26 | #[wasmbin_discriminants]
27 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
28 | #[repr(u8)]
29 | pub enum ValueType {
30 | #[cfg(feature = "simd")]
31 | V128 = 0x7B,
32 | F64 = 0x7C,
33 | F32 = 0x7D,
34 | I64 = 0x7E,
35 | I32 = 0x7F,
36 | Ref(RefType),
37 | }
38 |
39 | #[derive(Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
40 | #[repr(u8)]
41 | pub enum BlockType {
42 | Empty,
43 | Value(ValueType),
44 | MultiValue(TypeId),
45 | }
46 |
47 | impl Encode for BlockType {
48 | fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
49 | match self {
50 | BlockType::Empty => OP_CODE_EMPTY_BLOCK.encode(w),
51 | BlockType::Value(ty) => ty.encode(w),
52 | BlockType::MultiValue(id) => i64::from(id.index).encode(w),
53 | }
54 | }
55 | }
56 |
57 | impl Decode for BlockType {
58 | fn decode(r: &mut impl std::io::Read) -> Result {
59 | let discriminant = u8::decode(r)?;
60 | if discriminant == OP_CODE_EMPTY_BLOCK {
61 | return Ok(BlockType::Empty);
62 | }
63 | if let Some(ty) = ValueType::maybe_decode_with_discriminant(discriminant, r)
64 | .map_err(|err| err.in_path(PathItem::Variant("BlockType::Value")))?
65 | {
66 | return Ok(BlockType::Value(ty));
67 | }
68 | let index = (move || -> Result<_, DecodeError> {
69 | // We have already read one byte that could've been either a
70 | // discriminant or a part of an s33 LEB128 specially used for
71 | // type indices.
72 | //
73 | // To recover the LEB128 sequence, we need to chain it back.
74 | let buf = [discriminant];
75 | let mut r = std::io::Read::chain(&buf[..], r);
76 | let as_i64 = i64::decode(&mut r)?;
77 | // These indices are encoded as positive signed integers.
78 | // Convert them to unsigned integers and error out if they're out of range.
79 | let index = u32::try_from(as_i64)?;
80 | Ok(index)
81 | })()
82 | .map_err(|err| err.in_path(PathItem::Variant("BlockType::MultiValue")))?;
83 | Ok(BlockType::MultiValue(TypeId { index }))
84 | }
85 | }
86 |
87 | #[derive(Wasmbin, WasmbinCountable, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
88 | #[wasmbin(discriminant = 0x60)]
89 | pub struct FuncType {
90 | pub params: Vec,
91 | pub results: Vec,
92 | }
93 |
94 | impl Debug for FuncType {
95 | fn fmt(&self, f: &mut Formatter) -> fmt::Result {
96 | fn encode_types(types: &[ValueType], f: &mut Formatter) -> fmt::Result {
97 | f.write_str("(")?;
98 | for (i, ty) in types.iter().enumerate() {
99 | if i != 0 {
100 | f.write_str(", ")?;
101 | }
102 | ty.fmt(f)?;
103 | }
104 | f.write_str(")")
105 | }
106 |
107 | encode_types(&self.params, f)?;
108 | f.write_str(" -> ")?;
109 | encode_types(&self.results, f)
110 | }
111 | }
112 |
113 | #[derive(Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
114 | pub struct Limits {
115 | pub min: u32,
116 | pub max: Option,
117 | }
118 |
119 | impl Debug for Limits {
120 | fn fmt(&self, f: &mut Formatter) -> fmt::Result {
121 | write!(f, "{}..", self.min)?;
122 | if let Some(max) = self.max {
123 | write!(f, "={}", max)?;
124 | }
125 | Ok(())
126 | }
127 | }
128 |
129 | #[wasmbin_discriminants]
130 | #[derive(Wasmbin)]
131 | #[repr(u8)]
132 | enum LimitsRepr {
133 | Min { min: u32 } = 0x00,
134 | MinMax { min: u32, max: u32 } = 0x01,
135 | }
136 |
137 | encode_decode_as!(Limits, {
138 | (Limits { min, max: None }) <=> (LimitsRepr::Min { min }),
139 | (Limits { min, max: Some(max) }) <=> (LimitsRepr::MinMax { min, max }),
140 | });
141 |
142 | #[cfg(feature = "threads")]
143 | #[wasmbin_discriminants]
144 | #[derive(Wasmbin)]
145 | #[repr(u8)]
146 | enum MemTypeRepr {
147 | Unshared(LimitsRepr),
148 | SharedMin { min: u32 } = 0x02,
149 | SharedMinMax { min: u32, max: u32 } = 0x03,
150 | }
151 |
152 | #[cfg_attr(not(feature = "threads"), derive(Wasmbin))]
153 | #[derive(WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
154 | pub struct MemType {
155 | #[cfg(feature = "threads")]
156 | pub is_shared: bool,
157 | pub limits: Limits,
158 | }
159 |
160 | #[cfg(feature = "threads")]
161 | encode_decode_as!(MemType, {
162 | (MemType { is_shared: false, limits: Limits { min, max: None } }) <=> (MemTypeRepr::Unshared(LimitsRepr::Min { min })),
163 | (MemType { is_shared: false, limits: Limits { min, max: Some(max) } }) <=> (MemTypeRepr::Unshared(LimitsRepr::MinMax { min, max })),
164 | (MemType { is_shared: true, limits: Limits { min, max: None } }) <=> (MemTypeRepr::SharedMin { min }),
165 | (MemType { is_shared: true, limits: Limits { min, max: Some(max) } }) <=> (MemTypeRepr::SharedMinMax { min, max }),
166 | });
167 |
168 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
169 | #[repr(u8)]
170 | pub enum RefType {
171 | Func = 0x70,
172 | Extern = 0x6F,
173 | }
174 |
175 | #[derive(Wasmbin, WasmbinCountable, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
176 | pub struct TableType {
177 | pub elem_type: RefType,
178 | pub limits: Limits,
179 | }
180 |
181 | #[derive(Wasmbin, Debug, Arbitrary, PartialEq, Eq, Hash, Clone, Visit)]
182 | pub struct GlobalType {
183 | pub value_type: ValueType,
184 | pub mutable: bool,
185 | }
186 |
--------------------------------------------------------------------------------
/src/visit.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use crate::io::{DecodeError, PathItem};
16 |
17 | pub enum VisitError {
18 | LazyDecode(DecodeError),
19 | Custom(E),
20 | }
21 |
22 | impl std::fmt::Display for VisitError {
23 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 | match self {
25 | VisitError::LazyDecode(err) => err.fmt(f),
26 | VisitError::Custom(err) => err.fmt(f),
27 | }
28 | }
29 | }
30 |
31 | impl std::fmt::Debug for VisitError {
32 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 | match self {
34 | VisitError::LazyDecode(err) => err.fmt(f),
35 | VisitError::Custom(err) => err.fmt(f),
36 | }
37 | }
38 | }
39 |
40 | impl std::error::Error for VisitError {}
41 |
42 | impl VisitError {
43 | pub(crate) fn in_path(self, item: PathItem) -> Self {
44 | #[allow(clippy::match_wildcard_for_single_variants)]
45 | match self {
46 | VisitError::LazyDecode(err) => VisitError::LazyDecode(err.in_path(item)),
47 | err => err,
48 | }
49 | }
50 | }
51 |
52 | #[cfg(feature = "nightly")]
53 | pub type NeverError = !;
54 |
55 | #[cfg(not(feature = "nightly"))]
56 | #[allow(clippy::empty_enum)]
57 | #[derive(Debug, thiserror::Error)]
58 | pub enum NeverError {}
59 |
60 | impl From> for DecodeError {
61 | fn from(err: VisitError) -> Self {
62 | match err {
63 | VisitError::Custom(err) => match err {},
64 | VisitError::LazyDecode(err) => err,
65 | }
66 | }
67 | }
68 |
69 | pub trait VisitResult {
70 | type Error;
71 |
72 | fn as_result(self) -> Result<(), Self::Error>;
73 | }
74 |
75 | impl VisitResult for () {
76 | type Error = NeverError;
77 |
78 | fn as_result(self) -> Result<(), Self::Error> {
79 | Ok(())
80 | }
81 | }
82 |
83 | impl VisitResult for bool {
84 | type Error = ();
85 |
86 | fn as_result(self) -> Result<(), Self::Error> {
87 | match self {
88 | true => Ok(()),
89 | false => Err(()),
90 | }
91 | }
92 | }
93 |
94 | impl VisitResult for Result<(), E> {
95 | type Error = E;
96 |
97 | fn as_result(self) -> Result<(), Self::Error> {
98 | self
99 | }
100 | }
101 |
102 | pub use wasmbin_derive::Visit;
103 | pub trait Visit: 'static + Sized {
104 | fn visit<'a, T: 'static, R: VisitResult, F: FnMut(&'a T) -> R>(
105 | &'a self,
106 | mut f: F,
107 | ) -> Result<(), VisitError> {
108 | self.visit_child(&mut move |item| f(item).as_result())
109 | }
110 |
111 | fn visit_mut R>(
112 | &mut self,
113 | mut f: F,
114 | ) -> Result<(), VisitError> {
115 | self.visit_child_mut(&mut move |item| f(item).as_result())
116 | }
117 |
118 | fn visit_child<'a, T: 'static, E, F: FnMut(&'a T) -> Result<(), E>>(
119 | &'a self,
120 | f: &mut F,
121 | ) -> Result<(), VisitError> {
122 | if let Some(v) = ::downcast_ref(self) {
123 | f(v).map_err(VisitError::Custom)?;
124 | }
125 | self.visit_children(f)
126 | }
127 |
128 | fn visit_child_mut Result<(), E>>(
129 | &mut self,
130 | f: &mut F,
131 | ) -> Result<(), VisitError> {
132 | if let Some(v) = ::downcast_mut(self) {
133 | f(v).map_err(VisitError::Custom)?;
134 | }
135 | self.visit_children_mut(f)
136 | }
137 |
138 | fn visit_children<'a, T: 'static, E, F: FnMut(&'a T) -> Result<(), E>>(
139 | &'a self,
140 | _f: &mut F,
141 | ) -> Result<(), VisitError> {
142 | Ok(())
143 | }
144 |
145 | fn visit_children_mut Result<(), E>>(
146 | &mut self,
147 | _f: &mut F,
148 | ) -> Result<(), VisitError> {
149 | Ok(())
150 | }
151 | }
152 |
153 | macro_rules! impl_visit_for_iter {
154 | ($ty:tt $(<$param:ident>)?) => {
155 | impl$(<$param: crate::visit::Visit>)? crate::visit::Visit for $ty $(<$param>)? {
156 | fn visit_children<'a, VisitT: 'static, E, F: FnMut(&'a VisitT) -> Result<(), E>>(
157 | &'a self,
158 | f: &mut F,
159 | ) -> Result<(), crate::visit::VisitError> {
160 | for (i, v) in self.iter().enumerate() {
161 | v.visit_child(f).map_err(move |err| err.in_path(crate::io::PathItem::Index(i)))?;
162 | }
163 | Ok(())
164 | }
165 |
166 | fn visit_children_mut Result<(), E>>(
167 | &mut self,
168 | f: &mut F,
169 | ) -> Result<(), crate::visit::VisitError> {
170 | for (i, v) in self.iter_mut().enumerate() {
171 | v.visit_child_mut(f).map_err(move |err| err.in_path(crate::io::PathItem::Index(i)))?;
172 | }
173 | Ok(())
174 | }
175 | }
176 | };
177 | }
178 |
--------------------------------------------------------------------------------
/tests/spec.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use anyhow::{bail, Context, Error};
16 | use fehler::throws;
17 | use libtest_mimic::{run_tests, Arguments, Outcome, Test};
18 | use std::fs::{read_dir, read_to_string};
19 | use std::path::Path;
20 | use wasmbin::{
21 | io::DecodeError,
22 | visit::{Visit, VisitError},
23 | Module,
24 | };
25 | use wast::parser::{parse, ParseBuffer};
26 | use wast::Wast;
27 |
28 | const IGNORED_ERRORS: &[&str] = &[
29 | // We don't perform cross-section analysis.
30 | "function and code section have inconsistent lengths",
31 | "data count section required",
32 | "data count and data section have inconsistent lengths",
33 | // We allow non-zero table and memory IDs already.
34 | "zero flag expected",
35 | // We don't perform full function analysis either.
36 | "too many locals",
37 | ];
38 |
39 | const IGNORED_MODULES: &[&[u8]] = &[
40 | // These are outdated tests in WebAssembly/testsuite itself.
41 | // It needs updating, but see https://github.com/WebAssembly/testsuite/pull/39#issuecomment-863496809.
42 | &[
43 | 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x00, 0x0B, 0x07,
44 | 0x01, 0x80, 0x00, 0x41, 0x00, 0x0B, 0x00,
45 | ],
46 | &[
47 | 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x04, 0x04, 0x01, 0x70, 0x00, 0x00, 0x09,
48 | 0x07, 0x01, 0x80, 0x00, 0x41, 0x00, 0x0B, 0x00,
49 | ],
50 | &[
51 | 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x00, 0x0B, 0x06,
52 | 0x01, 0x01, 0x41, 0x00, 0x0B, 0x00,
53 | ],
54 | &[
55 | 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x06, 0x01, 0x01, 0x41, 0x00, 0x0B,
56 | 0x00,
57 | ],
58 | // Upstream malformed test that becomes well-formed if threads are enabled.
59 | #[cfg(feature = "threads")]
60 | &[
61 | 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x02, 0x00,
62 | ],
63 | ];
64 |
65 | struct WasmTest {
66 | module: Vec,
67 | expect_result: Result<(), String>,
68 | }
69 |
70 | #[throws]
71 | fn read_tests_from_file(path: &Path, dest: &mut Vec>) {
72 | let src = read_to_string(path)?;
73 | let set_err_path_text = |mut err: wast::Error| {
74 | err.set_path(path);
75 | err.set_text(&src);
76 | err
77 | };
78 | let buf = ParseBuffer::new(&src).map_err(set_err_path_text)?;
79 | let wast = parse::(&buf).map_err(set_err_path_text)?;
80 | for directive in wast.directives {
81 | let (span, mut module, expect_result) = match directive {
82 | // Expect errors for assert_malformed on binary or AST modules.
83 | wast::WastDirective::AssertMalformed {
84 | span,
85 | module: wast::QuoteModule::Module(module),
86 | message,
87 | }
88 | // Unlike other AssertInvalid, this is actually something we
89 | // check at the parsing time, because it's part of our
90 | // typesystem and doesn't require cross-function or
91 | // cross-section checks.
92 | //
93 | // See https://github.com/WebAssembly/simd/issues/256 for accepted
94 | // but pending suggestion to change proposal to match this as well.
95 | | wast::WastDirective::AssertInvalid {
96 | span,
97 | module,
98 | message: message @ "invalid lane index",
99 | } => (span, module, Err(message.to_owned())),
100 | // Expect successful parsing for regular AST modules.
101 | wast::WastDirective::Module(module) => (module.span, module, Ok(())),
102 | // Counter-intuitively, expect successful parsing for modules that are supposed
103 | // to error out at runtime or linking stage, too.
104 | wast::WastDirective::AssertInvalid { span, module, .. }
105 | | wast::WastDirective::AssertUnlinkable { span, module, .. } => (span, module, Ok(())),
106 | _ => {
107 | // Skipping interpreted
108 | continue;
109 | }
110 | };
111 | let (line, col) = span.linecol_in(&src);
112 | let module = module.encode()?;
113 | dest.push(Test {
114 | name: format!("{}:{}:{}", path.display(), line + 1, col + 1),
115 | kind: String::default(),
116 | is_ignored: IGNORED_MODULES.contains(&module.as_slice())
117 | || match &expect_result {
118 | Ok(()) => false,
119 | Err(err) => IGNORED_ERRORS.contains(&err.as_str()),
120 | },
121 | is_bench: false,
122 | data: WasmTest {
123 | module,
124 | expect_result,
125 | },
126 | });
127 | }
128 | }
129 |
130 | #[throws]
131 | fn read_tests_from_dir(path: &Path, dest: &mut Vec>) {
132 | for file in read_dir(path)? {
133 | let path = file?.path();
134 | if path.extension().map_or(false, |ext| ext == "wast") {
135 | read_tests_from_file(&path, dest)?;
136 | }
137 | }
138 | }
139 |
140 | #[throws]
141 | fn read_all_tests(path: &Path) -> Vec> {
142 | let mut tests = Vec::new();
143 | let proposals_dir = path.join("proposals");
144 |
145 | macro_rules! read_proposal_tests {
146 | ($name:literal) => {
147 | if cfg!(feature = $name) {
148 | read_tests_from_dir(&proposals_dir.join($name), &mut tests).context($name)?
149 | }
150 | };
151 | }
152 |
153 | read_proposal_tests!("tail-call");
154 | read_proposal_tests!("simd");
155 | read_proposal_tests!("threads");
156 |
157 | read_tests_from_dir(path, &mut tests)?;
158 |
159 | if tests.is_empty() {
160 | bail!("Couldn't find any tests. Did you run `git submodule update --init`?");
161 | }
162 |
163 | tests
164 | }
165 |
166 | fn unlazify(mut wasm: T) -> Result {
167 | match wasm.visit_mut(|()| {}) {
168 | Ok(()) => Ok(wasm),
169 | Err(err) => match err {
170 | VisitError::LazyDecode(err) => Err(err),
171 | VisitError::Custom(err) => match err {},
172 | },
173 | }
174 | }
175 |
176 | #[throws]
177 | fn run_test(test: &WasmTest) {
178 | let module = match (Module::decode_from(&mut test.module.as_slice()).and_then(unlazify), &test.expect_result) {
179 | (Ok(ref module), Err(err)) => bail!("Expected an invalid module definition with an error: {}\nParsed part: {:02X?}\nGot module: {:02X?}", err, test.module, module),
180 | (Err(err), Ok(())) => bail!(
181 | "Expected a valid module definition, but got an error\nModule: {:02X?}\nError: {:#}",
182 | test.module,
183 | err
184 | ),
185 | (Ok(module), Ok(())) => module,
186 | (Err(_), Err(_)) => return,
187 | };
188 | let out = module.encode_into(Vec::new())?;
189 | if out != test.module {
190 | // In the rare case that binary representation doesn't match, it
191 | // might be because the test uses longer LEB128 form than
192 | // required. Verify that at least decoding it back produces the
193 | // same module.
194 | let module2 = Module::decode_from(out.as_slice())?;
195 | if module != module2 {
196 | bail!(
197 | "Roundtrip mismatch. Old: {:#?}\nNew: {:#?}",
198 | module,
199 | module2
200 | );
201 | }
202 | }
203 | }
204 |
205 | #[throws]
206 | fn main() {
207 | let tests = read_all_tests(&Path::new("tests").join("testsuite"))?;
208 |
209 | run_tests(&Arguments::from_args(), tests, |test| {
210 | match run_test(&test.data) {
211 | Ok(()) => Outcome::Passed,
212 | Err(err) => Outcome::Failed {
213 | msg: Some(err.to_string()),
214 | },
215 | }
216 | })
217 | .exit_if_failed();
218 | }
219 |
--------------------------------------------------------------------------------