Repository: bytecodealliance/wasm-tools
Pull request #1882 (2024-10-23 22:52:35)

Support multiple `-f` flags in `validate`
This commit adds support for multiple `--feature` or `-f` flags in the `wasm-tools validate` subcommand. This behaves the same way as supplying multiple values separated by commas, but it can additionally be used through passing multiple arguments now.


Patch:
diff --git a/src/bin/wasm-tools/validate.rs b/src/bin/wasm-tools/validate.rs
index 1ef778b9..c5a149e8 100644
--- a/src/bin/wasm-tools/validate.rs
+++ b/src/bin/wasm-tools/validate.rs
@@ -54,12 +54,19 @@ pub struct Opts {
     /// Available feature options can be found in the wasmparser crate:
     /// <https://github.com/bytecodealliance/wasm-tools/blob/main/crates/wasmparser/src/features.rs>
     #[clap(long, short = 'f', value_parser = parse_features)]
-    features: Option<WasmFeatures>,
+    features: Vec<Vec<FeatureAction>>,
 
     #[clap(flatten)]
     io: wasm_tools::InputOutput,
 }
 
+#[derive(Clone)]
+enum FeatureAction {
+    Reset(WasmFeatures),
+    Enable(WasmFeatures),
+    Disable(WasmFeatures),
+}
+
 impl Opts {
     pub fn general_opts(&self) -> &wasm_tools::GeneralOpts {
         self.io.general_opts()
@@ -92,6 +99,26 @@ impl Opts {
         }
     }
 
+    fn features(&self) -> Result<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;
+                }
+            }
+        }
+
+        Ok(ret)
+    }
+
     fn validate(&self, wasm: &[u8]) -> Result<()> {
         // Note that here we're copying the contents of
         // `Validator::validate_all`, but the end is followed up with a parallel
@@ -103,7 +130,7 @@ impl Opts {
         // `Validator` we're using as we navigate nested modules (the module
         // linking proposal) and any functions found are deferred to get
         // validated later.
-        let mut validator = Validator::new_with_features(self.features.unwrap_or_default());
+        let mut validator = Validator::new_with_features(self.features()?);
         let mut functions_to_validate = Vec::new();
 
         let start = Instant::now();
@@ -182,8 +209,8 @@ impl Opts {
     }
 }
 
-fn parse_features(arg: &str) -> Result<WasmFeatures> {
-    let mut ret = WasmFeatures::default();
+fn parse_features(arg: &str) -> Result<Vec<FeatureAction>> {
+    let mut ret = Vec::new();
 
     const GROUPS: &[(&str, WasmFeatures)] = &[
         ("mvp", WasmFeatures::WASM1),
@@ -226,18 +253,24 @@ fn parse_features(arg: &str) -> Result<WasmFeatures> {
             }
             match action {
                 Action::ChangeAll => {
-                    for flag in WasmFeatures::FLAGS.iter() {
-                        ret.set(*flag.value(), enable);
-                    }
+                    ret.push(if enable {
+                        FeatureAction::Enable(WasmFeatures::all())
+                    } else {
+                        FeatureAction::Disable(WasmFeatures::all())
+                    });
                 }
                 Action::Modify(feature) => {
-                    ret.set(feature, enable);
+                    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 = features;
+                    ret.push(FeatureAction::Reset(features));
                 }
             }
             continue 'outer;


Test patch:
diff --git a/tests/cli/validate-multiple-flags.wat b/tests/cli/validate-multiple-flags.wat
new file mode 100644
index 00000000..bbcf18a3
--- /dev/null
+++ b/tests/cli/validate-multiple-flags.wat
@@ -0,0 +1,5 @@
+;; RUN: validate % -f wasm1 -f simd
+
+(module
+  (import "" "" (global v128))
+)