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