Browse Source

List exports of an instance in linking error (#3456)

When there is a linking error caused by an undefined instance, list all
the instances exports in the error message. This will clarify errors for
undefined two-level imports that get desugared to one-level instance
imports under the module-linking proposal.
pull/3466/head
Adam Bratschi-Kaye 3 years ago
committed by GitHub
parent
commit
afd10646c9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      crates/wasmtime/src/linker.rs
  2. 28
      tests/all/linker.rs

37
crates/wasmtime/src/linker.rs

@ -5,7 +5,7 @@ use crate::{
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
IntoFunc, Module, StoreContextMut, Trap, Val, ValRaw,
};
use anyhow::{anyhow, bail, Context, Error, Result};
use anyhow::{anyhow, bail, Context, Result};
use log::warn;
use std::collections::hash_map::{Entry, HashMap};
#[cfg(feature = "async")]
@ -1040,22 +1040,11 @@ impl<T> Linker<T> {
let store = store.as_context_mut().0;
let imports = module
.imports()
.map(|import| {
self._get_by_import(&import)
.ok_or_else(|| self.link_error(&import))
})
.map(|import| self._get_by_import(&import))
.collect::<Result<_>>()?;
unsafe { InstancePre::new(store, module, imports) }
}
fn link_error(&self, import: &ImportType) -> Error {
let desc = match import.name() {
Some(name) => format!("{}::{}", import.module(), name),
None => import.module().to_string(),
};
anyhow!("unknown import: `{}` has not been defined", desc)
}
/// Returns an iterator over all items defined in this `Linker`, in
/// arbitrary order.
///
@ -1133,16 +1122,20 @@ impl<T> Linker<T> {
) -> Option<Extern> {
let store = store.as_context_mut().0;
// Should be safe since `T` is connecting the linker and store
Some(unsafe { self._get_by_import(import)?.to_extern(store) })
Some(unsafe { self._get_by_import(import).ok()?.to_extern(store) })
}
fn _get_by_import(&self, import: &ImportType) -> Option<Definition> {
fn _get_by_import(&self, import: &ImportType) -> anyhow::Result<Definition> {
fn undef_err(missing_import: &str) -> anyhow::Error {
anyhow!("unknown import: `{}` has not been defined", missing_import)
}
if let Some(item) = self._get(import.module(), import.name()) {
return Some(item.clone());
return Ok(item.clone());
}
if import.name().is_some() {
return None;
if let Some(name) = import.name() {
return Err(undef_err(&format!("{}::{}", import.module(), name)));
}
if let ExternType::Instance(t) = import.ty() {
@ -1163,13 +1156,15 @@ impl<T> Linker<T> {
// suffice.
let mut map = indexmap::IndexMap::new();
for export in t.exports() {
let item = self._get(import.module(), Some(export.name()))?;
let item = self
._get(import.module(), Some(export.name()))
.ok_or_else(|| undef_err(&format!("{}::{}", import.module(), export.name())))?;
map.insert(export.name().to_string(), item.clone());
}
return Some(Definition::Instance(Arc::new(map)));
return Ok(Definition::Instance(Arc::new(map)));
}
None
Err(undef_err(&import.module()))
}
/// Returns the "default export" of a module.

28
tests/all/linker.rs

@ -23,6 +23,34 @@ fn link_undefined() -> Result<()> {
Ok(())
}
#[test]
fn undefined_error_message_with_linking() {
let mut config = Config::new();
config.wasm_module_linking(true);
let engine = Engine::new(&config).unwrap();
let module = Module::new(
&engine,
r#"
(module
(import "foo" "bar" (func))
)
"#,
)
.unwrap();
let linker = Linker::new(&engine);
let mut store = Store::new(&engine, ());
assert!(linker
.instantiate(&mut store, &module)
.unwrap_err()
.to_string()
.contains("foo"));
assert!(linker
.instantiate(&mut store, &module)
.unwrap_err()
.to_string()
.contains("bar"));
}
#[test]
fn link_twice_bad() -> Result<()> {
let mut store = Store::<()>::default();

Loading…
Cancel
Save