Add wasmtime::UnknownImportError (#5509)
This adds a new error type `UnknownImportError` which will be returned
(wrapped in an `anyhow::Error`) by `Linker::instantiate{,_async,_pre}`
if a module has an unresolvable import.
This error type is also used by `Linker::define_unknown_imports_as_traps`;
any resulting traps will also downcast to `UnknownImportError`.
Closes #5416
This commit is contained in:
@@ -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, Val, ValRaw,
|
IntoFunc, Module, StoreContextMut, Val, ValRaw,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use 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")]
|
||||||
@@ -263,15 +263,10 @@ impl<T> Linker<T> {
|
|||||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||||
pub fn define_unknown_imports_as_traps(&mut self, module: &Module) -> anyhow::Result<()> {
|
pub fn define_unknown_imports_as_traps(&mut self, module: &Module) -> anyhow::Result<()> {
|
||||||
for import in module.imports() {
|
for import in module.imports() {
|
||||||
if self._get_by_import(&import).is_err() {
|
if let Err(import_err) = self._get_by_import(&import) {
|
||||||
if let ExternType::Func(func_ty) = import.ty() {
|
if let ExternType::Func(func_ty) = import_err.ty() {
|
||||||
let err_msg = format!(
|
|
||||||
"unknown import: `{}::{}` has not been defined",
|
|
||||||
import.module(),
|
|
||||||
import.name(),
|
|
||||||
);
|
|
||||||
self.func_new(import.module(), import.name(), func_ty, move |_, _, _| {
|
self.func_new(import.module(), import.name(), func_ty, move |_, _, _| {
|
||||||
bail!("{err_msg}")
|
bail!(import_err.clone());
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -973,7 +968,9 @@ impl<T> Linker<T> {
|
|||||||
///
|
///
|
||||||
/// This method can fail because an import may not be found, or because
|
/// This method can fail because an import may not be found, or because
|
||||||
/// instantiation itself may fail. For information on instantiation
|
/// instantiation itself may fail. For information on instantiation
|
||||||
/// failures see [`Instance::new`].
|
/// failures see [`Instance::new`]. If an import is not found, the error
|
||||||
|
/// may be downcast to an [`UnknownImportError`].
|
||||||
|
///
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@@ -1035,6 +1032,11 @@ impl<T> Linker<T> {
|
|||||||
/// returned [`InstancePre`] represents a ready-to-be-instantiated module,
|
/// returned [`InstancePre`] represents a ready-to-be-instantiated module,
|
||||||
/// which can also be instantiated multiple times if desired.
|
/// which can also be instantiated multiple times if desired.
|
||||||
///
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error which may be downcast to an [`UnknownImportError`] if
|
||||||
|
/// the module has any unresolvable imports.
|
||||||
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This method will panic if any item defined in this linker used by
|
/// This method will panic if any item defined in this linker used by
|
||||||
@@ -1085,7 +1087,7 @@ impl<T> Linker<T> {
|
|||||||
let imports = module
|
let imports = module
|
||||||
.imports()
|
.imports()
|
||||||
.map(|import| self._get_by_import(&import))
|
.map(|import| self._get_by_import(&import))
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
unsafe { InstancePre::new(store, module, imports) }
|
unsafe { InstancePre::new(store, module, imports) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1166,20 +1168,11 @@ impl<T> Linker<T> {
|
|||||||
Some(unsafe { self._get_by_import(import).ok()?.to_extern(store) })
|
Some(unsafe { self._get_by_import(import).ok()?.to_extern(store) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _get_by_import(&self, import: &ImportType) -> anyhow::Result<Definition> {
|
fn _get_by_import(&self, import: &ImportType) -> Result<Definition, UnknownImportError> {
|
||||||
fn undef_err(missing_import: &str) -> anyhow::Error {
|
match self._get(import.module(), import.name()) {
|
||||||
anyhow!("unknown import: `{}` has not been defined", missing_import)
|
Some(item) => Ok(item.clone()),
|
||||||
|
None => Err(UnknownImportError::new(import)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(item) = self._get(import.module(), import.name()) {
|
|
||||||
return Ok(item.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(undef_err(&format!(
|
|
||||||
"{}::{}",
|
|
||||||
import.module(),
|
|
||||||
import.name()
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the "default export" of a module.
|
/// Returns the "default export" of a module.
|
||||||
@@ -1294,3 +1287,51 @@ impl ModuleKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error for an unresolvable import.
|
||||||
|
///
|
||||||
|
/// Returned - wrapped in an [`anyhow::Error`] - by [`Linker::instantiate`] and
|
||||||
|
/// related methods for modules with unresolvable imports.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct UnknownImportError {
|
||||||
|
module: String,
|
||||||
|
name: String,
|
||||||
|
ty: ExternType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnknownImportError {
|
||||||
|
fn new(import: &ImportType) -> Self {
|
||||||
|
Self {
|
||||||
|
module: import.module().to_string(),
|
||||||
|
name: import.name().to_string(),
|
||||||
|
ty: import.ty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the module name that the unknown import was expected to come from.
|
||||||
|
pub fn module(&self) -> &str {
|
||||||
|
&self.module
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the field name of the module that the unknown import was expected to come from.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the type of the unknown import.
|
||||||
|
pub fn ty(&self) -> ExternType {
|
||||||
|
self.ty.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for UnknownImportError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"unknown import: `{}::{}` has not been defined",
|
||||||
|
self.module, self.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for UnknownImportError {}
|
||||||
|
|||||||
@@ -23,6 +23,24 @@ fn link_undefined() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unknown_import_error() -> Result<()> {
|
||||||
|
let mut store = Store::<()>::default();
|
||||||
|
let linker = Linker::new(store.engine());
|
||||||
|
let module = Module::new(
|
||||||
|
store.engine(),
|
||||||
|
r#"(module (import "unknown-module" "unknown-name" (func)))"#,
|
||||||
|
)?;
|
||||||
|
let err = linker
|
||||||
|
.instantiate(&mut store, &module)
|
||||||
|
.expect_err("should fail");
|
||||||
|
let unknown_import: UnknownImportError = err.downcast()?;
|
||||||
|
assert_eq!(unknown_import.module(), "unknown-module");
|
||||||
|
assert_eq!(unknown_import.name(), "unknown-name");
|
||||||
|
unknown_import.ty().unwrap_func();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn link_twice_bad() -> Result<()> {
|
fn link_twice_bad() -> Result<()> {
|
||||||
let mut store = Store::<()>::default();
|
let mut store = Store::<()>::default();
|
||||||
@@ -366,7 +384,8 @@ fn test_trapping_unknown_import() -> Result<()> {
|
|||||||
.get_func(&mut store, "run")
|
.get_func(&mut store, "run")
|
||||||
.expect("expected a run func in the module");
|
.expect("expected a run func in the module");
|
||||||
|
|
||||||
assert!(run_func.call(&mut store, &[], &mut []).is_err());
|
let err = run_func.call(&mut store, &[], &mut []).unwrap_err();
|
||||||
|
assert!(err.is::<UnknownImportError>());
|
||||||
|
|
||||||
// "other" does not call the import function, so it should not trap
|
// "other" does not call the import function, so it should not trap
|
||||||
let other_func = instance
|
let other_func = instance
|
||||||
|
|||||||
Reference in New Issue
Block a user