From e666be3222b3b78b1134b07cc06bcae391c54940 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 23 Oct 2026 15:25:55 -0700 Subject: [PATCH 1/2] Plumb wasm features through reencoding/printing I've resisted doing this in the past in the hopes that it's unnecessary complexity, but it's again gotten to the point where this is becoming more painful than necessary. Upcoming proposal such as multibyte-array-access and acquire-release-atomics are reinterpreting previously invalid modules with a new meaning. This means that preexisting spec tests often fail when these proposals are implemented unless the old behavior can be preserved. This has historically been fixed for the `validate` step but the extra testing the `wast` subcommand does, such as reencoding and round-tripping through prints, does not yet support this. The goal of this commit is to close these gaps and to plumb `WasmFeatures` through more steps that `wasm-tools wast` handles. This ensures that so long as behavior is dynamically gated in `BinaryReader` and validation, which it easily is, it should be pretty easy to avoid breaking preexisting tests when new proposals are implemented. --- Cargo.toml | 2 +- crates/wasm-encoder/src/reencode.rs | 4 +- crates/wasmparser/src/parser.rs | 29 +++- crates/wasmprinter/Cargo.toml | 3 +- crates/wasmprinter/src/lib.rs | 17 +- crates/wasmprinter/src/operand_stack.rs | 6 +- .../wasmprinter/src/operand_stack_disabled.rs | 2 +- src/bin/wasm-tools/print.rs | 6 + src/bin/wasm-tools/validate.rs | 143 +---------------- src/bin/wasm-tools/wast.rs | 58 ++++--- src/features.rs | 151 ++++++++++++++++++ src/lib.rs | 4 + tests/cli/help-print-short.wat.stdout | 3 + tests/cli/help-print.wat.stdout | 20 +++ tests/cli/help-validate-short.wat.stdout | 4 +- tests/cli/help-validate.wat.stdout | 4 +- 16 files changed, 275 insertions(+), 181 deletions(-) create mode 100644 src/features.rs diff --git a/Cargo.toml b/Cargo.toml index d97eda6d9d..ccce2ba94c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -275,7 +275,7 @@ validate = [ 'wasmparser/validate', 'wasmparser/features', ] -print = [] +print = ['dep:bitflags', 'wasmparser/features'] parse = [] smith = ['wasm-smith', 'arbitrary', 'dep:serde', 'dep:serde_derive', 'dep:serde_json'] shrink = ['wasm-shrink', 'is_executable'] diff --git a/crates/wasm-encoder/src/reencode.rs b/crates/wasm-encoder/src/reencode.rs index d6812f1393..6e34f7b98b 100644 --- a/crates/wasm-encoder/src/reencode.rs +++ b/crates/wasm-encoder/src/reencode.rs @@ -693,7 +693,7 @@ pub mod utils { let data_range = start as usize..end; data.get(data_range).ok_or(Error::InvalidCodeSectionSize) }; - for section in parser.parse_all(data) { + for section in parser.clone().parse_all(data) { match section? { wasmparser::Payload::Version { encoding: wasmparser::Encoding::Module, @@ -849,7 +849,7 @@ pub mod utils { // that users overriding `parse_code_section` always get // that function called. let section = get_original_section(unchecked_range.clone())?; - let reader = wasmparser::BinaryReader::new(section, unchecked_range.start); + let reader = parser.binary_reader(section, unchecked_range.start); let section = wasmparser::CodeSectionReader::new(reader)?; reencoder.parse_code_section(&mut codes, section)?; module.section(&codes); diff --git a/crates/wasmparser/src/parser.rs b/crates/wasmparser/src/parser.rs index 6ae027a798..da28e9f94c 100644 --- a/crates/wasmparser/src/parser.rs +++ b/crates/wasmparser/src/parser.rs @@ -474,6 +474,29 @@ impl Parser { self.features = features; } + /// Creates a new [`BinaryReader`] for `data` which uses the same set of + /// wasm features as this parser. + /// + /// This is useful when parsing a range of the input, such as a section's + /// contents, outside of this parser while still respecting the features + /// configured with [`Parser::set_features`]. + /// + /// For more information see [`BinaryReader::new`]. + /// + /// # Panics + /// + /// Panics in the same situations as [`BinaryReader::new`]. + pub fn binary_reader<'a>(&self, data: &'a [u8], original_offset: u64) -> BinaryReader<'a> { + #[cfg(feature = "features")] + { + BinaryReader::new_features(data, original_offset, self.features) + } + #[cfg(not(feature = "features"))] + { + BinaryReader::new(data, original_offset) + } + } + /// Returns the original offset that this parser is currently at. pub fn offset(&self) -> u64 { self.offset @@ -640,11 +663,7 @@ impl Parser { } else { (data, eof) }; - let mut reader = BinaryReader::new(data, self.offset); - #[cfg(feature = "features")] - { - reader.set_features(self.features); - } + let mut reader = self.binary_reader(data, self.offset); match self.parse_reader(&mut reader, eof) { Ok(payload) => { // Be sure to update our offset with how far we got in the reader diff --git a/crates/wasmprinter/Cargo.toml b/crates/wasmprinter/Cargo.toml index a1136d2c93..a9017ff96f 100644 --- a/crates/wasmprinter/Cargo.toml +++ b/crates/wasmprinter/Cargo.toml @@ -25,4 +25,5 @@ wat = { path = "../wat" } [features] default = ['component-model', 'validate'] component-model = ['wasmparser/component-model'] -validate = ['wasmparser/validate', 'wasmparser/features'] +validate = ['wasmparser/validate', 'features'] +features = ['wasmparser/features'] diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index de13090f7e..76a52866ec 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -65,6 +65,8 @@ pub struct Config { fold_instructions: bool, indent_text: String, print_operand_stack: bool, + #[cfg(feature = "features")] + features: wasmparser::WasmFeatures, } impl Default for Config { @@ -76,6 +78,8 @@ impl Default for Config { fold_instructions: false, indent_text: " ".to_string(), print_operand_stack: false, + #[cfg(feature = "features")] + features: wasmparser::WasmFeatures::all(), } } } @@ -285,6 +289,15 @@ impl Config { self } + /// Select the set of WebAssembly features used when decoding the input. + /// + /// The default setting is [`WasmFeatures::all`](wasmparser::WasmFeatures::all). + #[cfg(feature = "features")] + pub fn features(&mut self, features: wasmparser::WasmFeatures) -> &mut Self { + self.features = features; + self + } + /// Print a WebAssembly binary. /// /// This function takes an entire `wasm` binary blob and prints it to the @@ -441,11 +454,13 @@ impl Printer<'_, '_> { let mut expected = None; let mut states: Vec = Vec::new(); let mut parser = Parser::new(0); + #[cfg(feature = "features")] + parser.set_features(self.config.features); #[cfg(feature = "component-model")] let mut parsers = Vec::new(); let mut validator = if self.config.print_operand_stack { - operand_stack::Validator::new() + operand_stack::Validator::new(self.config) } else { None }; diff --git a/crates/wasmprinter/src/operand_stack.rs b/crates/wasmprinter/src/operand_stack.rs index 924b483a25..1aa2865804 100644 --- a/crates/wasmprinter/src/operand_stack.rs +++ b/crates/wasmprinter/src/operand_stack.rs @@ -1,7 +1,7 @@ use crate::{Config, PrintTermcolor, Printer}; use wasmparser::{ BinaryReader, Frame, OperatorsReader, Payload, Result, ValType, ValidPayload, - ValidatorResources, WasmFeatures, + ValidatorResources, }; pub struct Validator { @@ -10,9 +10,9 @@ pub struct Validator { } impl Validator { - pub fn new() -> Option { + pub fn new(config: &Config) -> Option { Some(Validator { - validator: wasmparser::Validator::new_with_features(WasmFeatures::all()), + validator: wasmparser::Validator::new_with_features(config.features), last_function: None, }) } diff --git a/crates/wasmprinter/src/operand_stack_disabled.rs b/crates/wasmprinter/src/operand_stack_disabled.rs index 85c75d2141..f334eb0ae6 100644 --- a/crates/wasmprinter/src/operand_stack_disabled.rs +++ b/crates/wasmprinter/src/operand_stack_disabled.rs @@ -3,7 +3,7 @@ use wasmparser::{BinaryReader, OperatorsReader, Payload, Result}; pub enum Validator {} impl Validator { - pub fn new() -> Option { + pub fn new(_config: &crate::Config) -> Option { None } diff --git a/src/bin/wasm-tools/print.rs b/src/bin/wasm-tools/print.rs index 50cf345723..7001465944 100644 --- a/src/bin/wasm-tools/print.rs +++ b/src/bin/wasm-tools/print.rs @@ -49,10 +49,15 @@ Exit status: 0 on success, nonzero if the input file fails to parse. ")] +// `-f` is already taken by `--fold-instructions`, so `--features` is long-only. +#[clap(mut_arg("features", |a| a.short(None)))] pub struct Opts { #[clap(flatten)] generate_dwarf: wasm_tools::GenerateDwarfArg, + #[clap(flatten)] + features: wasm_tools::CliFeatures, + #[clap(flatten)] io: wasm_tools::InputOutput, @@ -102,6 +107,7 @@ impl Opts { let wasm = self.io.get_input_wasm(Some(&self.generate_dwarf))?; let mut config = wasmprinter::Config::new(); + config.features(self.features.features_from(wasmparser::WasmFeatures::all())); config.print_offsets(self.print_offsets); config.print_skeleton(self.skeleton); config.name_unnamed(self.name_unnamed); diff --git a/src/bin/wasm-tools/validate.rs b/src/bin/wasm-tools/validate.rs index 108db1c073..4268e0c429 100644 --- a/src/bin/wasm-tools/validate.rs +++ b/src/bin/wasm-tools/validate.rs @@ -1,10 +1,9 @@ use addr2line::LookupResult; -use anyhow::{Result, bail}; -use bitflags::Flags; +use anyhow::Result; use std::fmt::Write; use std::time::Instant; use wasm_tools::addr2line::Addr2lineModules; -use wasmparser::WasmFeatures; +use wasm_tools::CliFeatures; /// Validate a WebAssembly binary /// @@ -56,38 +55,6 @@ pub struct Opts { validate_custom: bool, } -// Helper structure extracted used to parse the feature flags for `validate`. -#[derive(clap::Parser)] -pub struct CliFeatures { - /// Comma-separated list of WebAssembly features to enable during - /// validation. - /// - /// If a "-" character is present in front of a feature it will disable that - /// feature. For example "-simd" will disable the simd proposal. - /// - /// The placeholder "all" can be used to enable all wasm features and the - /// term "-all" can be used to disable all features. - /// - /// The default set of features enabled are all WebAssembly proposals that - /// are at phase 4 or after. This means that the default set of features - /// accepted are relatively bleeding edge. Versions of the WebAssembly - /// specification can also be selected. The "wasm1" or "mvp" feature can - /// select the original WebAssembly specification and "wasm2" can be used to - /// select the 2.0 version. - /// - /// Available feature options can be found in the wasmparser crate: - /// - #[clap(long, short = 'f', value_parser = parse_features)] - features: Vec>, -} - -#[derive(Clone)] -enum FeatureAction { - Reset(WasmFeatures), - Enable(WasmFeatures), - Disable(WasmFeatures), -} - impl Opts { pub fn general_opts(&self) -> &wasm_tools::GeneralOpts { &self.general @@ -170,109 +137,3 @@ impl Opts { } } -impl CliFeatures { - pub fn features(&self) -> WasmFeatures { - let mut ret = WasmFeatures::default(); - - for action in self.features.iter().flat_map(|v| v) { - match action { - FeatureAction::Enable(features) => { - ret |= *features; - } - FeatureAction::Disable(features) => { - ret &= !*features; - } - FeatureAction::Reset(features) => { - ret = *features; - } - } - } - - ret - } -} - -fn parse_features(arg: &str) -> Result> { - let mut ret = Vec::new(); - - const GROUPS: &[(&str, WasmFeatures)] = &[ - ("mvp", WasmFeatures::MVP), - ("wasm1", WasmFeatures::WASM1), - ("wasm2", WasmFeatures::WASM2), - ("wasm3", WasmFeatures::WASM3), - ("lime1", WasmFeatures::LIME1), - ]; - - enum Action { - ChangeAll, - Group(WasmFeatures), - Modify(WasmFeatures), - } - - fn actions() -> impl Iterator { - WasmFeatures::FLAGS - .iter() - .map(|f| (f.name(), Action::Modify(*f.value()))) - .chain( - GROUPS - .iter() - .map(|(name, features)| (*name, Action::Group(*features))), - ) - .chain([("all", Action::ChangeAll)]) - } - - fn flag_name(name: &str) -> String { - name.to_lowercase().replace('_', "-") - } - - 'outer: for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) { - let (enable, part) = if let Some(part) = part.strip_prefix("-") { - (false, part) - } else { - (true, part) - }; - for (name, action) in actions() { - if part != flag_name(name) { - continue; - } - match action { - Action::ChangeAll => { - ret.push(if enable { - FeatureAction::Enable(WasmFeatures::all()) - } else { - FeatureAction::Disable(WasmFeatures::all()) - }); - } - Action::Modify(feature) => { - ret.push(if enable { - FeatureAction::Enable(feature) - } else { - FeatureAction::Disable(feature) - }); - } - Action::Group(features) => { - if !enable { - bail!("cannot disable `{part}`, it can only be enabled"); - } - ret.push(FeatureAction::Reset(features)); - } - } - continue 'outer; - } - - let mut error = format!("unknown feature `{part}`\n"); - error.push_str("Valid features: "); - let mut first = true; - for (name, _) in actions() { - if first { - first = false; - } else { - error.push_str(", "); - } - error.push_str(&flag_name(name)); - } - bail!("{error}") - } - - Ok(ret) -} diff --git a/src/bin/wasm-tools/wast.rs b/src/bin/wasm-tools/wast.rs index 0724cbfe2d..d095094a42 100644 --- a/src/bin/wasm-tools/wast.rs +++ b/src/bin/wasm-tools/wast.rs @@ -1,4 +1,4 @@ -use crate::validate::CliFeatures; +use wasm_tools::CliFeatures; use anyhow::{Context, Result, bail}; use clap::Parser; use rayon::prelude::*; @@ -203,8 +203,7 @@ impl Opts { return Ok(()); } }; - let mut parser = wasmparser::Parser::new(0); - parser.set_features(self.features.features()); + let parser = self.parser(); if let Err(e) = parse_binary_wasm(parser, &bytes, is_custom) { self.assert_error_matches(test, &format!("{e:?}"), message)?; @@ -284,8 +283,7 @@ impl Opts { } => { // ensure module parses successfully let binary_wasm = module.encode()?; - let mut parser = wasmparser::Parser::new(0); - parser.set_features(self.features.features()); + let parser = self.parser(); // For `assert_invalid` tests it should be the case that // everything parses. If a wasmparser-specific @@ -394,7 +392,7 @@ impl Opts { enable_snapshot: bool, ) -> Result<()> { // Test that we can print these bytes. - let text1 = wasmprinter::print_bytes(contents).context("failed to print wasm")?; + let text1 = self.print(contents, false)?; if enable_snapshot && self.assert(Assert::SnapshotPrint) { self.snapshot("print", test, &text1) .context("failed to validate the `print` snapshot")?; @@ -403,11 +401,7 @@ impl Opts { // Test that we can print these bytes with instructions in folded form. let mut text1_folded = String::new(); if self.assert(Assert::TestFolded) { - let mut folding_printer = wasmprinter::Config::new(); - folding_printer.fold_instructions(true); - folding_printer - .print(contents, &mut PrintFmtWrite(&mut text1_folded)) - .context("failed to print wasm in folded form")?; + text1_folded = self.print(contents, true)?; if enable_snapshot && self.assert(Assert::SnapshotFolded) { self.snapshot("print-folded", test, &text1_folded) @@ -422,7 +416,7 @@ impl Opts { // Convert the text versions back to binary and compare to original let binary2 = wat::parse_str(&text1).context("failed to parse `wat` from `wasmprinter`")?; - let text2 = wasmprinter::print_bytes(&binary2).context("failed to print wasm")?; + let text2 = self.print(&binary2, false)?; if roundtrip_binary { self.binary_compare(&binary2, contents) .context("failed to compare original binary with roundtrip through wasmparser->wasmprinter->wat")?; @@ -436,18 +430,12 @@ impl Opts { if self.assert(Assert::TestFolded) { let binary2_from_folded = wat::parse_str(&text1_folded) .context("failed to parse folded `wat` from `wasmprinter`")?; - let mut text2_folded = String::new(); - let mut folding_printer = wasmprinter::Config::new(); - folding_printer.fold_instructions(true); - folding_printer - .print(contents, &mut PrintFmtWrite(&mut text2_folded)) - .context("failed to print wasm in folded form")?; + let text2_folded = self.print(contents, true)?; if roundtrip_binary { self.binary_compare(&binary2_from_folded, contents) .context("failed to compare original `wat` with roundtrip folded `wat`")?; } - let text3 = - wasmprinter::print_bytes(binary2_from_folded).context("failed to print wasm")?; + let text3 = self.print(&binary2_from_folded, false)?; if text1_folded != text2_folded { anyhow::bail!( "original folded print does not match roundtrip folded print\n{}", @@ -466,14 +454,14 @@ impl Opts { if wasmparser::Parser::is_component(contents) { let mut reencode = Default::default(); RoundtripReencoder - .parse_component(&mut reencode, wasmparser::Parser::new(0), contents) + .parse_component(&mut reencode, self.parser(), contents) .context("failed to reencode module")?; self.binary_compare(&reencode.finish(), contents) .context("failed to compare reencoded module with original encoding")?; } else { let mut reencode = Default::default(); RoundtripReencoder - .parse_core_module(&mut reencode, wasmparser::Parser::new(0), contents) + .parse_core_module(&mut reencode, self.parser(), contents) .context("failed to reencode module")?; self.binary_compare(&reencode.finish(), contents) .context("failed to compare reencoded module with original encoding")?; @@ -482,6 +470,32 @@ impl Opts { Ok(()) } + /// Prints `contents` with `wasmprinter`, decoding it with respect to + /// `self.features`. + fn print(&self, contents: &[u8], fold_instructions: bool) -> Result { + let mut config = wasmprinter::Config::new(); + config.features(self.features.features()); + config.fold_instructions(fold_instructions); + let mut text = String::new(); + config + .print(contents, &mut PrintFmtWrite(&mut text)) + .with_context(|| { + if fold_instructions { + "failed to print wasm in folded form" + } else { + "failed to print wasm" + } + })?; + Ok(text) + } + + /// Creates a parser which decodes with respect to `self.features`. + fn parser(&self) -> wasmparser::Parser { + let mut parser = wasmparser::Parser::new(0); + parser.set_features(self.features.features()); + parser + } + /// Tests that `contents` is valid wasm binary with respect to /// `self.features`. fn test_wasm_valid(&self, _test: &Path, contents: &[u8], validate_custom: bool) -> Result<()> { diff --git a/src/features.rs b/src/features.rs new file mode 100644 index 0000000000..31b7a447de --- /dev/null +++ b/src/features.rs @@ -0,0 +1,151 @@ +//! Shared parsing of the `--features` flag for subcommands. + +use anyhow::{Result, bail}; +use bitflags::Flags; +use wasmparser::WasmFeatures; + +/// Helper structure used to parse the `--features` flag of subcommands. +#[derive(clap::Parser)] +pub struct CliFeatures { + /// Comma-separated list of WebAssembly features to enable during + /// parsing and validation. + /// + /// If a "-" character is present in front of a feature it will disable that + /// feature. For example "-simd" will disable the simd proposal. + /// + /// The placeholder "all" can be used to enable all wasm features and the + /// term "-all" can be used to disable all features. + /// + /// The default set of features enabled are all WebAssembly proposals that + /// are at phase 4 or after. This means that the default set of features + /// accepted are relatively bleeding edge. Versions of the WebAssembly + /// specification can also be selected. The "wasm1" or "mvp" feature can + /// select the original WebAssembly specification and "wasm2" can be used to + /// select the 2.0 version. + /// + /// Available feature options can be found in the wasmparser crate: + /// + #[clap(long, short = 'f', value_parser = parse_features)] + features: Vec>, +} + +#[derive(Clone)] +enum FeatureAction { + Reset(WasmFeatures), + Enable(WasmFeatures), + Disable(WasmFeatures), +} + +impl CliFeatures { + /// Returns the set of features configured, starting from + /// [`WasmFeatures::default`]. + pub fn features(&self) -> WasmFeatures { + self.features_from(WasmFeatures::default()) + } + + /// Returns the set of features configured, starting from `base`. + pub fn features_from(&self, base: WasmFeatures) -> WasmFeatures { + let mut ret = base; + + for action in self.features.iter().flat_map(|v| v) { + match action { + FeatureAction::Enable(features) => { + ret |= *features; + } + FeatureAction::Disable(features) => { + ret &= !*features; + } + FeatureAction::Reset(features) => { + ret = *features; + } + } + } + + ret + } +} + +fn parse_features(arg: &str) -> Result> { + let mut ret = Vec::new(); + + const GROUPS: &[(&str, WasmFeatures)] = &[ + ("mvp", WasmFeatures::MVP), + ("wasm1", WasmFeatures::WASM1), + ("wasm2", WasmFeatures::WASM2), + ("wasm3", WasmFeatures::WASM3), + ("lime1", WasmFeatures::LIME1), + ]; + + enum Action { + ChangeAll, + Group(WasmFeatures), + Modify(WasmFeatures), + } + + fn actions() -> impl Iterator { + WasmFeatures::FLAGS + .iter() + .map(|f| (f.name(), Action::Modify(*f.value()))) + .chain( + GROUPS + .iter() + .map(|(name, features)| (*name, Action::Group(*features))), + ) + .chain([("all", Action::ChangeAll)]) + } + + fn flag_name(name: &str) -> String { + name.to_lowercase().replace('_', "-") + } + + 'outer: for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) { + let (enable, part) = if let Some(part) = part.strip_prefix("-") { + (false, part) + } else { + (true, part) + }; + for (name, action) in actions() { + if part != flag_name(name) { + continue; + } + match action { + Action::ChangeAll => { + ret.push(if enable { + FeatureAction::Enable(WasmFeatures::all()) + } else { + FeatureAction::Disable(WasmFeatures::all()) + }); + } + Action::Modify(feature) => { + ret.push(if enable { + FeatureAction::Enable(feature) + } else { + FeatureAction::Disable(feature) + }); + } + Action::Group(features) => { + if !enable { + bail!("cannot disable `{part}`, it can only be enabled"); + } + ret.push(FeatureAction::Reset(features)); + } + } + continue 'outer; + } + + let mut error = format!("unknown feature `{part}`\n"); + error.push_str("Valid features: "); + let mut first = true; + for (name, _) in actions() { + if first { + first = false; + } else { + error.push_str(", "); + } + error.push_str(&flag_name(name)); + } + bail!("{error}") + } + + Ok(ret) +} diff --git a/src/lib.rs b/src/lib.rs index 85aa3eff4e..3e4402991e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,10 @@ use termcolor::{Ansi, ColorChoice, NoColor, StandardStream, WriteColor}; #[cfg(any(feature = "addr2line", feature = "validate"))] pub mod addr2line; +#[cfg(any(feature = "validate", feature = "print"))] +mod features; +#[cfg(any(feature = "validate", feature = "print"))] +pub use features::CliFeatures; #[cfg(any(feature = "component", feature = "wit-dylib"))] pub mod wit; diff --git a/tests/cli/help-print-short.wat.stdout b/tests/cli/help-print-short.wat.stdout index 68f1f84126..662e15ac89 100644 --- a/tests/cli/help-print-short.wat.stdout +++ b/tests/cli/help-print-short.wat.stdout @@ -11,6 +11,9 @@ Options: files -g Shorthand for `--generate-dwarf full` + --features + Comma-separated list of WebAssembly features to enable during parsing + and validation -o, --output Where to place output -v, --verbose... diff --git a/tests/cli/help-print.wat.stdout b/tests/cli/help-print.wat.stdout index b273ee6438..b4b1592f1e 100644 --- a/tests/cli/help-print.wat.stdout +++ b/tests/cli/help-print.wat.stdout @@ -25,6 +25,26 @@ Options: -g Shorthand for `--generate-dwarf full` + --features + Comma-separated list of WebAssembly features to enable during parsing + and validation. + + If a "-" character is present in front of a feature it will disable + that feature. For example "-simd" will disable the simd proposal. + + The placeholder "all" can be used to enable all wasm features and the + term "-all" can be used to disable all features. + + The default set of features enabled are all WebAssembly proposals that + are at phase 4 or after. This means that the default set of features + accepted are relatively bleeding edge. Versions of the WebAssembly + specification can also be selected. The "wasm1" or "mvp" feature can + select the original WebAssembly specification and "wasm2" can be used + to select the 2.0 version. + + Available feature options can be found in the wasmparser crate: + + -o, --output Where to place output. diff --git a/tests/cli/help-validate-short.wat.stdout b/tests/cli/help-validate-short.wat.stdout index edc9350349..b19cd7e910 100644 --- a/tests/cli/help-validate-short.wat.stdout +++ b/tests/cli/help-validate-short.wat.stdout @@ -12,8 +12,8 @@ Options: -g Shorthand for `--generate-dwarf full` -f, --features - Comma-separated list of WebAssembly features to enable during - validation + Comma-separated list of WebAssembly features to enable during parsing + and validation -v, --verbose... Use verbose output (-v info, -vv debug, -vvv trace) --color diff --git a/tests/cli/help-validate.wat.stdout b/tests/cli/help-validate.wat.stdout index 05a248213e..6bf1f8fece 100644 --- a/tests/cli/help-validate.wat.stdout +++ b/tests/cli/help-validate.wat.stdout @@ -35,8 +35,8 @@ Options: Shorthand for `--generate-dwarf full` -f, --features - Comma-separated list of WebAssembly features to enable during - validation. + Comma-separated list of WebAssembly features to enable during parsing + and validation. If a "-" character is present in front of a feature it will disable that feature. For example "-simd" will disable the simd proposal. From ab89258d58c9147370a9c0047574050f71905b22 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 23 Oct 2026 15:38:12 -0700 Subject: [PATCH 2/2] rustfmt --- src/bin/wasm-tools/validate.rs | 3 +-- src/bin/wasm-tools/wast.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bin/wasm-tools/validate.rs b/src/bin/wasm-tools/validate.rs index 4268e0c429..17f2e8ba2a 100644 --- a/src/bin/wasm-tools/validate.rs +++ b/src/bin/wasm-tools/validate.rs @@ -2,8 +2,8 @@ use addr2line::LookupResult; use anyhow::Result; use std::fmt::Write; use std::time::Instant; -use wasm_tools::addr2line::Addr2lineModules; use wasm_tools::CliFeatures; +use wasm_tools::addr2line::Addr2lineModules; /// Validate a WebAssembly binary /// @@ -136,4 +136,3 @@ impl Opts { } } } - diff --git a/src/bin/wasm-tools/wast.rs b/src/bin/wasm-tools/wast.rs index d095094a42..a42eee18e8 100644 --- a/src/bin/wasm-tools/wast.rs +++ b/src/bin/wasm-tools/wast.rs @@ -1,4 +1,3 @@ -use wasm_tools::CliFeatures; use anyhow::{Context, Result, bail}; use clap::Parser; use rayon::prelude::*; @@ -9,6 +8,7 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::str; use wasm_encoder::reencode::{Reencode, ReencodeComponent, RoundtripReencoder}; +use wasm_tools::CliFeatures; use wasm_tools::parse_binary_wasm; use wasmprinter::PrintFmtWrite; use wast::component::{Component, ComponentKind};