Skip to main content

wit_smith/
lib.rs

1//! A small crate to generate arbitrary WIT documents.
2//!
3//! This crate is modeled after the `wasm-smith` crate but is used to generate
4//! WIT documents instead of WebAssembly modules. This crate is intended to
5//! generate "interesting" WIT package structures in addition to interesting
6//! type structures.
7
8use arbitrary::{Result, Unstructured};
9use wit_parser::{Resolve, ResolveErrorKind};
10
11mod config;
12pub use self::config::Config;
13mod generate;
14
15/// Generates an arbitrary WIT document encoded as a WebAssembly binary.
16///
17/// The `config` guides the generation of the document and the `u` bytes are
18/// used as input to construct the document.
19pub fn smith(config: &Config, u: &mut Unstructured<'_>) -> Result<Vec<u8>> {
20    let pkgs = generate::Generator::new(config.clone()).generate(u)?;
21    let mut resolve = Resolve::default();
22    resolve.all_features = true;
23    let mut last = None;
24    for pkg in pkgs {
25        log::trace!("appending package {:?}", pkg.name);
26        let group = pkg.sources.parse().unwrap();
27        let id = match resolve.push_group(group) {
28            Ok(id) => id,
29            Err(e) => match e.kind() {
30                ResolveErrorKind::InvalidTransitiveDependency { .. } => {
31                    return Err(arbitrary::Error::IncorrectFormat);
32                }
33                ResolveErrorKind::ItemShadowing { .. }
34                | ResolveErrorKind::StabilityMismatch { .. } => {
35                    log::error!("{e}");
36                    return Err(arbitrary::Error::IncorrectFormat);
37                }
38                _ => {
39                    panic!("bad wit parse: {e:?}")
40                }
41            },
42        };
43        last = Some(id);
44    }
45    let pkg = last.unwrap();
46
47    let wasm = wit_component::encode(&resolve, pkg).expect("failed to encode WIT document");
48
49    // Handle disallowing `stream<char>` here vs not generating it to start
50    // with as it's a bit easier to handle.
51    if let Err(e) = wasmparser::Validator::new_with_features(wasmparser::WasmFeatures::all())
52        .validate_all(&wasm)
53    {
54        if e.to_string()
55            .contains("`stream<char>` is not valid at this time")
56        {
57            return Err(arbitrary::Error::IncorrectFormat);
58        }
59    }
60    Ok(wasm)
61}