Skip to content

Commit f0cb97d

Browse files
authored
wasm-smith: Add --module-shape option. (#2255)
1 parent cef5a0c commit f0cb97d

4 files changed

Lines changed: 523 additions & 8 deletions

File tree

crates/wasm-smith/src/config.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,47 @@ macro_rules! define_config {
9999
/// ```
100100
pub exports: Option<Vec<u8>>,
101101

102+
/// If provided, the generated module will have imports and exports
103+
/// with exactly the same names and types as those in the provided
104+
/// WebAssembly module.
105+
///
106+
/// Defaults to `None` which means arbitrary imports and exports will be
107+
/// generated.
108+
///
109+
/// Note that [`Self::available_imports`] and [`Self::exports`] are
110+
/// ignored when `module_shape` is enabled.
111+
///
112+
/// The provided value must be a valid binary encoding of a
113+
/// WebAssembly module. `wasm-smith` will panic if the module cannot
114+
/// be parsed.
115+
///
116+
/// # Module Limits
117+
///
118+
/// All types, functions, globals, memories, tables, tags, imports, and exports
119+
/// that are needed to provide the required imports and exports will be generated,
120+
/// even if it causes the resulting module to exceed the limits defined in
121+
/// [`Self::max_type_size`], [`Self::max_types`], [`Self::max_funcs`],
122+
/// [`Self::max_globals`], [`Self::max_memories`], [`Self::max_tables`],
123+
/// [`Self::max_tags`], [`Self::max_imports`], or [`Self::max_exports`].
124+
///
125+
/// # Example
126+
///
127+
/// As for [`Self::available_imports`] and [`Self::exports`], the
128+
/// `wat` crate can be used to provide a human-readable description of the
129+
/// module shape:
130+
///
131+
/// ```rust
132+
/// Some(wat::parse_str(r#"
133+
/// (module
134+
/// (import "env" "ping" (func (param i32)))
135+
/// (import "env" "memory" (memory 1))
136+
/// (func (export "foo") (param anyref) (result structref) unreachable)
137+
/// (global (export "bar") arrayref (ref.null array))
138+
/// )
139+
/// "#));
140+
/// ```
141+
pub module_shape: Option<Vec<u8>>,
142+
102143
$(
103144
$(#[$field_attr])*
104145
pub $field: $field_ty,
@@ -110,6 +151,7 @@ macro_rules! define_config {
110151
Config {
111152
available_imports: None,
112153
exports: None,
154+
module_shape: None,
113155

114156
$(
115157
$field: $default,
@@ -173,6 +215,31 @@ macro_rules! define_config {
173215
#[cfg_attr(feature = "clap", clap(long))]
174216
exports: Option<std::path::PathBuf>,
175217

218+
/// If provided, the generated module will have imports and exports
219+
/// with exactly the same names and types as those in the provided
220+
/// WebAssembly module.
221+
///
222+
/// Defaults to `None` which means arbitrary imports and exports will be
223+
/// generated.
224+
///
225+
/// Note that [`Self::available_imports`] and [`Self::exports`] are
226+
/// ignored when `module_shape` is enabled.
227+
///
228+
/// The provided value must be a valid binary encoding of a
229+
/// WebAssembly module. `wasm-smith` will panic if the module cannot
230+
/// be parsed.
231+
///
232+
/// # Module Limits
233+
///
234+
/// All types, functions, globals, memories, tables, tags, imports, and exports
235+
/// that are needed to provide the required imports and exports will be generated,
236+
/// even if it causes the resulting module to exceed the limits defined in
237+
/// [`Self::max_type_size`], [`Self::max_types`], [`Self::max_funcs`],
238+
/// [`Self::max_globals`], [`Self::max_memories`], [`Self::max_tables`],
239+
/// [`Self::max_tags`], [`Self::max_imports`], or [`Self::max_exports`].
240+
#[cfg_attr(feature = "clap", clap(long))]
241+
module_shape: Option<std::path::PathBuf>,
242+
176243
$(
177244
$(#[$field_attr])*
178245
#[cfg_attr(feature = "clap", clap(long))]
@@ -185,6 +252,7 @@ macro_rules! define_config {
185252
Self {
186253
available_imports: self.available_imports.or(other.available_imports),
187254
exports: self.exports.or(other.exports),
255+
module_shape: self.module_shape.or(other.module_shape),
188256

189257
$(
190258
$field: self.$field.or(other.$field),
@@ -213,6 +281,13 @@ macro_rules! define_config {
213281
} else {
214282
None
215283
},
284+
module_shape: if let Some(file) = config
285+
.module_shape
286+
.as_ref() {
287+
Some(wat::parse_file(file)?)
288+
} else {
289+
None
290+
},
216291

217292
$(
218293
$field: config.$field.unwrap_or(default.$field),
@@ -230,9 +305,13 @@ macro_rules! define_config {
230305
if config.exports.is_some() {
231306
bail!("cannot serialize configuration with `exports`");
232307
}
308+
if config.module_shape.is_some() {
309+
bail!("cannot serialize configuration with `module_shape`");
310+
}
233311
Ok(InternalOptionalConfig {
234312
available_imports: None,
235313
exports: None,
314+
module_shape: None,
236315
$( $field: Some(config.$field.clone()), )*
237316
})
238317
}
@@ -788,6 +867,7 @@ impl<'a> Arbitrary<'a> for Config {
788867
canonicalize_nans: false,
789868
available_imports: None,
790869
exports: None,
870+
module_shape: None,
791871
export_everything: false,
792872
generate_custom_sections: false,
793873
allow_invalid_funcs: false,
@@ -841,6 +921,12 @@ impl Config {
841921
if !self.threads_enabled {
842922
self.shared_everything_threads_enabled = false;
843923
}
924+
925+
// If module_shape is present then disable available_imports and exports.
926+
if self.module_shape.is_some() {
927+
self.available_imports = None;
928+
self.exports = None;
929+
}
844930
}
845931

846932
/// Returns the set of features that are necessary for validating against

0 commit comments

Comments
 (0)