Repository: bytecodealliance/wasm-tools (rust)

Issue:
`wit-parser::decoding::decode` should error when given a core module
`wit-parser::decoding::decode` requires a WebAssembly component (either as a normal component or as a WIT-package-encoded-as-binary). However, if you pass in a core module, everything "works" (i.e., no error is returned), but you end up with an empty resolve. I imagine the right thing to do here would be to error specifying that a component must be passed. 

Let me know if this is the case, and I can submit a fix. 

Pull request description:
Test `wasm-tools component wit` on core modules
Also cause it to return an error if the input is a core wasm module with zero `component-type` custom sections.

Closes #1673

Patch:
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index 40c50786..7c44f2cf 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -1922,6 +1922,7 @@ impl ComponentEncoder {
     /// core module.
     pub fn module(mut self, module: &[u8]) -> Result<Self> {
         let (wasm, metadata) = metadata::decode(module)?;
+        let wasm = wasm.as_deref().unwrap_or(module);
         let world = self
             .metadata
             .merge(metadata)
@@ -1931,7 +1932,7 @@ impl ComponentEncoder {
         self.module = if let Some(producers) = &self.metadata.producers {
             producers.add_to_wasm(&wasm)?
         } else {
-            wasm
+            wasm.to_vec()
         };
         Ok(self)
     }
@@ -1986,6 +1987,7 @@ impl ComponentEncoder {
         library_info: Option<LibraryInfo>,
     ) -> Result<Self> {
         let (wasm, metadata) = metadata::decode(bytes)?;
+        let wasm = wasm.as_deref().unwrap_or(bytes);
         // Merge the adapter's document into our own document to have one large
         // document, and then afterwards merge worlds as well.
         //
@@ -2041,7 +2043,7 @@ impl ComponentEncoder {
         self.adapters.insert(
             name.to_string(),
             Adapter {
-                wasm,
+                wasm: wasm.to_vec(),
                 metadata: metadata.metadata,
                 required_exports: exports,
                 library_info,
diff --git a/crates/wit-component/src/metadata.rs b/crates/wit-component/src/metadata.rs
index 982b11f7..f436794c 100644
--- a/crates/wit-component/src/metadata.rs
+++ b/crates/wit-component/src/metadata.rs
@@ -119,7 +119,7 @@ pub struct ModuleMetadata {
     pub export_encodings: IndexMap<String, StringEncoding>,
 }
 
-/// This function will parse the `wasm` binary given as input and return a
+/// This function will parse the core `wasm` binary given as input and return a
 /// [`Bindgen`] which extracts the custom sections describing component-level
 /// types from within the binary itself.
 ///
@@ -130,12 +130,14 @@ pub struct ModuleMetadata {
 ///
 /// This will return an error if `wasm` is not a valid WebAssembly module.
 ///
-/// Note that a "stripped" binary where `component-type` sections are removed
-/// is returned as well to embed within a component.
-pub fn decode(wasm: &[u8]) -> Result<(Vec<u8>, Bindgen)> {
+/// If a `component-type` custom section was found then a new binary is
+/// optionally returned with the custom sections stripped out. If no
+/// `component-type` custom sections are found then `None` is returned.
+pub fn decode(wasm: &[u8]) -> Result<(Option<Vec<u8>>, Bindgen)> {
     let mut ret = Bindgen::default();
     let mut new_module = wasm_encoder::Module::new();
 
+    let mut found_custom = false;
     for payload in wasmparser::Parser::new(0).parse_all(wasm) {
         let payload = payload.context("decoding item in module")?;
         match payload {
@@ -144,6 +146,7 @@ pub fn decode(wasm: &[u8]) -> Result<(Vec<u8>, Bindgen)> {
                     .with_context(|| format!("decoding custom section {}", cs.name()))?;
                 ret.merge(data)
                     .with_context(|| format!("updating metadata for section {}", cs.name()))?;
+                found_custom = true;
             }
             wasmparser::Payload::Version { encoding, .. } if encoding != Encoding::Module => {
                 bail!("decoding a component is not supported")
@@ -159,7 +162,11 @@ pub fn decode(wasm: &[u8]) -> Result<(Vec<u8>, Bindgen)> {
         }
     }
 
-    Ok((new_module.finish(), ret))
+    if found_custom {
+        Ok((Some(new_module.finish()), ret))
+    } else {
+        Ok((None, ret))
+    }
 }
 
 /// Creates a `component-type*` custom section to be decoded by `decode` above.
diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs
index a5c01c6c..b7dbafdd 100644
--- a/src/bin/wasm-tools/component.rs
+++ b/src/bin/wasm-tools/component.rs
@@ -574,7 +574,15 @@ impl WitOpts {
                 if wasmparser::Parser::is_component(&input) {
                     wit_component::decode(&input)
                 } else {
-                    let (_wasm, bindgen) = wit_component::metadata::decode(&input)?;
+                    let (wasm, bindgen) = wit_component::metadata::decode(&input)?;
+                    if wasm.is_none() {
+                        bail!(
+                            "input is a core wasm module with no `component-type*` \
+                             custom sections meaning that there is not WIT information; \
+                             is the information not embedded or is this supposed \
+                             to be a component?"
+                        )
+                    }
                     Ok(DecodedWasm::Component(bindgen.resolve, bindgen.world))
                 }
             }


Test patch:
diff --git a/tests/cli/component-wit-empty-core-module.wat b/tests/cli/component-wit-empty-core-module.wat
new file mode 100644
index 00000000..809a84b0
--- /dev/null
+++ b/tests/cli/component-wit-empty-core-module.wat
@@ -0,0 +1,3 @@
+;; FAIL: component wit %
+
+(module)
diff --git a/tests/cli/component-wit-empty-core-module.wat.stderr b/tests/cli/component-wit-empty-core-module.wat.stderr
new file mode 100644
index 00000000..a9d473a9
--- /dev/null
+++ b/tests/cli/component-wit-empty-core-module.wat.stderr
@@ -0,0 +1,1 @@
+error: input is a core wasm module with no `component-type*` custom sections meaning that there is not WIT information; is the information not embedded or is this supposed to be a component?
diff --git a/tests/cli/component-wit-of-core-module.wit b/tests/cli/component-wit-of-core-module.wit
new file mode 100644
index 00000000..c4f7bf59
--- /dev/null
+++ b/tests/cli/component-wit-of-core-module.wit
@@ -0,0 +1,8 @@
+// RUN: component embed --dummy % | component wit
+
+package a:b;
+
+world foo {
+  import x: func();
+  export y: func();
+}
diff --git a/tests/cli/component-wit-of-core-module.wit.stdout b/tests/cli/component-wit-of-core-module.wit.stdout
new file mode 100644
index 00000000..6321454f
--- /dev/null
+++ b/tests/cli/component-wit-of-core-module.wit.stdout
@@ -0,0 +1,14 @@
+package root:root;
+
+world root {
+  import x: func();
+
+  export y: func();
+}
+package a:b {
+  world foo {
+    import x: func();
+
+    export y: func();
+  }
+}