Repository: bytecodealliance/wasm-tools
Pull request #1474 (2024-03-27 17:28:10)

Add additional error context to ComponentEncoder::encode
This would have helped me when investigating an issue. Changes to `wasm-parser`'s `BinaryReaderError` would be needed to make the errors here even clearer.  

Before:

```
error: failed to encode a component from module

Caused by:
    unexpected end-of-file (at offset 0xc)
```

After: 
```
error: failed to encode a component from module

Caused by:
    0: failed to decode world from module
    1: module was not valid
    2: unexpected end-of-file (at offset 0xc)
```

fitzgen: Test failure in CI: https://github.com/bytecodealliance/wasm-tools/actions/runs/8456061199/job/23164960594?pr=1474#step:4:924

```
 ---- component_encoding_via_flags stdout ----
testing error-empty-module-import
thread 'component_encoding_via_flags' panicked at crates/wit-component/tests/components.rs:241:17:
assertion failed: `(left == right)`: failed baseline comparison (tests/components/error-empty-module-import/error.txt)

Diff < left / right > :
<no top-level imported function `foo` specified
>failed to decode world from module
>
>Caused by:
>    0: module was not valid
>    1: no top-level imported function `foo` specified
```

Patch:
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index b5bb6c63..fa959527 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -2078,7 +2078,7 @@ impl ComponentEncoder {
             bail!("a module is required when encoding a component");
         }
 
-        let world = ComponentWorld::new(self)?;
+        let world = ComponentWorld::new(self).context("failed to decode world from module")?;
         let mut state = EncodingState {
             component: ComponentBuilder::default(),
             module_index: None,
diff --git a/crates/wit-component/src/encoding/world.rs b/crates/wit-component/src/encoding/world.rs
index 66a61a39..1545decd 100644
--- a/crates/wit-component/src/encoding/world.rs
+++ b/crates/wit-component/src/encoding/world.rs
@@ -76,7 +76,8 @@ impl<'a> ComponentWorld<'a> {
             &encoder.metadata,
             &encoder.main_module_exports,
             &adapters,
-        )?;
+        )
+        .context("module was not valid")?;
 
         let mut ret = ComponentWorld {
             encoder,
diff --git a/crates/wit-component/src/metadata.rs b/crates/wit-component/src/metadata.rs
index 2409b02e..4c7fcf81 100644
--- a/crates/wit-component/src/metadata.rs
+++ b/crates/wit-component/src/metadata.rs
@@ -50,7 +50,7 @@ use wasm_encoder::{
     ComponentBuilder, ComponentExportKind, ComponentType, ComponentTypeRef, CustomSection,
 };
 use wasm_metadata::Producers;
-use wasmparser::{BinaryReader, Parser, Payload};
+use wasmparser::{BinaryReader, Encoding, Parser, Payload};
 use wit_parser::{Package, PackageName, Resolve, World, WorldId, WorldItem};
 
 const CURRENT_VERSION: u8 = 0x04;
@@ -127,6 +127,8 @@ pub struct ModuleMetadata {
 /// The extraction here provides the metadata necessary to continue the process
 /// later on.
 ///
+/// 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)> {
@@ -142,6 +144,9 @@ pub fn decode(wasm: &[u8]) -> Result<(Vec<u8>, Bindgen)> {
                 ret.merge(data)
                     .with_context(|| format!("updating metadata for section {}", cs.name()))?;
             }
+            wasmparser::Payload::Version { encoding, .. } if encoding != Encoding::Module => {
+                bail!("decoding a component is not supported")
+            }
             _ => {
                 if let Some((id, range)) = payload.as_section() {
                     new_module.section(&wasm_encoder::RawSection {
diff --git a/crates/wit-smith/src/generate.rs b/crates/wit-smith/src/generate.rs
index bbcbc302..298979af 100644
--- a/crates/wit-smith/src/generate.rs
+++ b/crates/wit-smith/src/generate.rs
@@ -318,16 +318,14 @@ impl Generator {
 
 impl<'a> InterfaceGenerator<'a> {
     fn new(gen: &'a Generator, file: &'a mut File) -> InterfaceGenerator<'a> {
-        // Claim the name `memory` to avoid conflicting with the canonical ABI
-        // always using a linear memory named `memory`.
-        let mut unique_names = HashSet::new();
-        unique_names.insert("memory".to_string());
         InterfaceGenerator {
             gen,
             file,
             config: &gen.config,
             types_in_interface: Vec::new(),
-            unique_names,
+            // Claim the name `memory` to avoid conflicting with the canonical
+            // ABI always using a linear memory named `memory`.
+            unique_names: HashSet::from_iter(["memory".to_string()]),
         }
     }
 
@@ -413,7 +411,10 @@ impl<'a> InterfaceGenerator<'a> {
         let mut parts = Vec::new();
         let mut imported_interfaces = HashSet::new();
         let mut exported_interfaces = HashSet::new();
-        let mut export_names = HashSet::new();
+
+        // Claim the name `memory` to avoid conflicting with the canonical
+        // ABI always using a linear memory named `memory`.
+        let mut export_names = HashSet::from_iter(["memory".to_string()]);
 
         while parts.len() < self.config.max_world_items && !u.is_empty() && u.arbitrary()? {
             let kind = u.arbitrary::<ItemKind>()?;


Test patch:
diff --git a/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt
index b7a8e003..904d1aef 100644
--- a/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt
+++ b/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt
@@ -1,1 +1,5 @@
-type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`
\ No newline at end of file
+failed to decode world from module
+
+Caused by:
+    0: module was not valid
+    1: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-empty-module-import/error.txt b/crates/wit-component/tests/components/error-empty-module-import/error.txt
index 3fc2443a..75f7337b 100644
--- a/crates/wit-component/tests/components/error-empty-module-import/error.txt
+++ b/crates/wit-component/tests/components/error-empty-module-import/error.txt
@@ -1,1 +1,5 @@
-no top-level imported function `foo` specified
\ No newline at end of file
+failed to decode world from module
+
+Caused by:
+    0: module was not valid
+    1: no top-level imported function `foo` specified
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt
index d2226ac3..0588ebcf 100644
--- a/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt
+++ b/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt
@@ -1,4 +1,6 @@
-failed to validate exported interface `foo`
+failed to decode world from module
 
 Caused by:
-    type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`
\ No newline at end of file
+    0: module was not valid
+    1: failed to validate exported interface `foo`
+    2: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-import-resource-rep/error.txt b/crates/wit-component/tests/components/error-import-resource-rep/error.txt
index 5a9a5e18..f9f291d9 100644
--- a/crates/wit-component/tests/components/error-import-resource-rep/error.txt
+++ b/crates/wit-component/tests/components/error-import-resource-rep/error.txt
@@ -1,1 +1,5 @@
-no top-level imported function `[resource-rep]a` specified
\ No newline at end of file
+failed to decode world from module
+
+Caused by:
+    0: module was not valid
+    1: no top-level imported function `[resource-rep]a` specified
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt b/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt
index 205e848e..e76b70fe 100644
--- a/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt
+++ b/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt
@@ -1,1 +1,5 @@
-type mismatch for function `[resource-drop]a`: expected `[I32] -> []` but found `[I32] -> [I32]`
\ No newline at end of file
+failed to decode world from module
+
+Caused by:
+    0: module was not valid
+    1: type mismatch for function `[resource-drop]a`: expected `[I32] -> []` but found `[I32] -> [I32]`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt
index 7a6fd7d4..57dae399 100644
--- a/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt
+++ b/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt
@@ -1,4 +1,6 @@
-failed to validate import interface `foo`
+failed to decode world from module
 
 Caused by:
-    type mismatch for function `bar`: expected `[I32, I32] -> []` but found `[] -> []`
\ No newline at end of file
+    0: module was not valid
+    1: failed to validate import interface `foo`
+    2: type mismatch for function `bar`: expected `[I32, I32] -> []` but found `[] -> []`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-invalid-module-import/error.txt b/crates/wit-component/tests/components/error-invalid-module-import/error.txt
index 9d9f35c0..4a54c821 100644
--- a/crates/wit-component/tests/components/error-invalid-module-import/error.txt
+++ b/crates/wit-component/tests/components/error-invalid-module-import/error.txt
@@ -1,1 +1,5 @@
-module is only allowed to import functions
\ No newline at end of file
+failed to decode world from module
+
+Caused by:
+    0: module was not valid
+    1: module is only allowed to import functions
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-default-export/error.txt b/crates/wit-component/tests/components/error-missing-default-export/error.txt
index 8368ada3..1dc1b01f 100644
--- a/crates/wit-component/tests/components/error-missing-default-export/error.txt
+++ b/crates/wit-component/tests/components/error-missing-default-export/error.txt
@@ -1,1 +1,5 @@
-module does not export required function `a`
\ No newline at end of file
+failed to decode world from module
+
+Caused by:
+    0: module was not valid
+    1: module does not export required function `a`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-export/error.txt b/crates/wit-component/tests/components/error-missing-export/error.txt
index afcfb4e2..6d4148bd 100644
--- a/crates/wit-component/tests/components/error-missing-export/error.txt
+++ b/crates/wit-component/tests/components/error-missing-export/error.txt
@@ -1,4 +1,6 @@
-failed to validate exported interface `foo`
+failed to decode world from module
 
 Caused by:
-    module does not export required function `foo#a`
\ No newline at end of file
+    0: module was not valid
+    1: failed to validate exported interface `foo`
+    2: module does not export required function `foo#a`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-import-func/error.txt b/crates/wit-component/tests/components/error-missing-import-func/error.txt
index b4d2327f..388a6087 100644
--- a/crates/wit-component/tests/components/error-missing-import-func/error.txt
+++ b/crates/wit-component/tests/components/error-missing-import-func/error.txt
@@ -1,1 +1,5 @@
-module requires an import interface named `foo`
\ No newline at end of file
+failed to decode world from module
+
+Caused by:
+    0: module was not valid
+    1: module requires an import interface named `foo`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-import/error.txt b/crates/wit-component/tests/components/error-missing-import/error.txt
index b4d2327f..388a6087 100644
--- a/crates/wit-component/tests/components/error-missing-import/error.txt
+++ b/crates/wit-component/tests/components/error-missing-import/error.txt
@@ -1,1 +1,5 @@
-module requires an import interface named `foo`
\ No newline at end of file
+failed to decode world from module
+
+Caused by:
+    0: module was not valid
+    1: module requires an import interface named `foo`
\ No newline at end of file
diff --git a/tests/cli/component-new-existing-component.wat b/tests/cli/component-new-existing-component.wat
new file mode 100644
index 00000000..2251e2ef
--- /dev/null
+++ b/tests/cli/component-new-existing-component.wat
@@ -0,0 +1,2 @@
+;; FAIL: component new % -o component.wasm
+(component)
diff --git a/tests/cli/component-new-existing-component.wat.stderr b/tests/cli/component-new-existing-component.wat.stderr
new file mode 100644
index 00000000..bb6a3987
--- /dev/null
+++ b/tests/cli/component-new-existing-component.wat.stderr
@@ -0,0 +1,1 @@
+error: decoding a component is not supported