Browse Source

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
pull/5510/head
Lann 2 years ago
committed by GitHub
parent
commit
69b7ecf90e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 89
      crates/wasmtime/src/linker.rs
  2. 21
      tests/all/linker.rs

89
crates/wasmtime/src/linker.rs

@ -5,7 +5,7 @@ use crate::{
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
IntoFunc, Module, StoreContextMut, Val, ValRaw,
};
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{bail, Context, Result};
use log::warn;
use std::collections::hash_map::{Entry, HashMap};
#[cfg(feature = "async")]
@ -263,15 +263,10 @@ impl<T> Linker<T> {
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn define_unknown_imports_as_traps(&mut self, module: &Module) -> anyhow::Result<()> {
for import in module.imports() {
if self._get_by_import(&import).is_err() {
if let ExternType::Func(func_ty) = import.ty() {
let err_msg = format!(
"unknown import: `{}::{}` has not been defined",
import.module(),
import.name(),
);
if let Err(import_err) = self._get_by_import(&import) {
if let ExternType::Func(func_ty) = import_err.ty() {
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
/// 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
///
@ -1035,6 +1032,11 @@ impl<T> Linker<T> {
/// returned [`InstancePre`] represents a ready-to-be-instantiated module,
/// 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
///
/// This method will panic if any item defined in this linker used by
@ -1085,7 +1087,7 @@ impl<T> Linker<T> {
let imports = module
.imports()
.map(|import| self._get_by_import(&import))
.collect::<Result<_>>()?;
.collect::<Result<_, _>>()?;
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) })
}
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 Ok(item.clone());
fn _get_by_import(&self, import: &ImportType) -> Result<Definition, UnknownImportError> {
match self._get(import.module(), import.name()) {
Some(item) => Ok(item.clone()),
None => Err(UnknownImportError::new(import)),
}
Err(undef_err(&format!(
"{}::{}",
import.module(),
import.name()
)))
}
/// 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 {}

21
tests/all/linker.rs

@ -23,6 +23,24 @@ fn link_undefined() -> Result<()> {
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]
fn link_twice_bad() -> Result<()> {
let mut store = Store::<()>::default();
@ -366,7 +384,8 @@ fn test_trapping_unknown_import() -> Result<()> {
.get_func(&mut store, "run")
.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
let other_func = instance

Loading…
Cancel
Save