wasmtime: Option to return default values for unknown imports (#6010)
Similar to the `--trap-unknown-imports` option, which defines unknown function imports with functions that trap when called, this new `--default-values-unknown-imports` option defines unknown function imports with a function that returns the default values for the result types (either zero or null depending on the value type).
This commit is contained in:
@@ -3,7 +3,7 @@ use crate::instance::InstancePre;
|
|||||||
use crate::store::StoreOpaque;
|
use crate::store::StoreOpaque;
|
||||||
use crate::{
|
use crate::{
|
||||||
AsContext, AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType,
|
AsContext, AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType,
|
||||||
Instance, IntoFunc, Module, StoreContextMut, Val, ValRaw,
|
Instance, IntoFunc, Module, StoreContextMut, Val, ValRaw, ValType,
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
@@ -289,6 +289,62 @@ impl<T> Linker<T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implement any function imports of the [`Module`] with a function that
|
||||||
|
/// ignores its arguments and returns default values.
|
||||||
|
///
|
||||||
|
/// Default values are either zero or null, depending on the value type.
|
||||||
|
///
|
||||||
|
/// This method can be used to allow unknown imports from command modules.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use wasmtime::*;
|
||||||
|
/// # fn main() -> anyhow::Result<()> {
|
||||||
|
/// # let engine = Engine::default();
|
||||||
|
/// # let module = Module::new(&engine, "(module (import \"unknown\" \"import\" (func)))")?;
|
||||||
|
/// # let mut store = Store::new(&engine, ());
|
||||||
|
/// let mut linker = Linker::new(&engine);
|
||||||
|
/// linker.define_unknown_imports_as_default_values(&module)?;
|
||||||
|
/// linker.instantiate(&mut store, &module)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[cfg(compiler)]
|
||||||
|
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
|
||||||
|
pub fn define_unknown_imports_as_default_values(
|
||||||
|
&mut self,
|
||||||
|
module: &Module,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
for import in module.imports() {
|
||||||
|
if let Err(import_err) = self._get_by_import(&import) {
|
||||||
|
if let ExternType::Func(func_ty) = import_err.ty() {
|
||||||
|
let result_tys: Vec<_> = func_ty.results().collect();
|
||||||
|
self.func_new(
|
||||||
|
import.module(),
|
||||||
|
import.name(),
|
||||||
|
func_ty,
|
||||||
|
move |_caller, _args, results| {
|
||||||
|
for (result, ty) in results.iter_mut().zip(&result_tys) {
|
||||||
|
*result = match ty {
|
||||||
|
ValType::I32 => Val::I32(0),
|
||||||
|
ValType::I64 => Val::I64(0),
|
||||||
|
ValType::F32 => Val::F32(0.0_f32.to_bits()),
|
||||||
|
ValType::F64 => Val::F64(0.0_f64.to_bits()),
|
||||||
|
ValType::V128 => Val::V128(0),
|
||||||
|
ValType::FuncRef => Val::FuncRef(None),
|
||||||
|
ValType::ExternRef => Val::ExternRef(None),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Defines a new item in this [`Linker`].
|
/// Defines a new item in this [`Linker`].
|
||||||
///
|
///
|
||||||
/// This method will add a new definition, by name, to this instance of
|
/// This method will add a new definition, by name, to this instance of
|
||||||
|
|||||||
@@ -90,6 +90,11 @@ pub struct RunCommand {
|
|||||||
#[clap(long = "trap-unknown-imports")]
|
#[clap(long = "trap-unknown-imports")]
|
||||||
trap_unknown_imports: bool,
|
trap_unknown_imports: bool,
|
||||||
|
|
||||||
|
/// Allow the main module to import unknown functions, using an
|
||||||
|
/// implementation that returns default values, when running commands.
|
||||||
|
#[clap(long = "default-values-unknown-imports")]
|
||||||
|
default_values_unknown_imports: bool,
|
||||||
|
|
||||||
/// Allow executing precompiled WebAssembly modules as `*.cwasm` files.
|
/// Allow executing precompiled WebAssembly modules as `*.cwasm` files.
|
||||||
///
|
///
|
||||||
/// Note that this option is not safe to pass if the module being passed in
|
/// Note that this option is not safe to pass if the module being passed in
|
||||||
@@ -322,6 +327,12 @@ impl RunCommand {
|
|||||||
if self.trap_unknown_imports {
|
if self.trap_unknown_imports {
|
||||||
linker.define_unknown_imports_as_traps(&module)?;
|
linker.define_unknown_imports_as_traps(&module)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ...or as default values.
|
||||||
|
if self.default_values_unknown_imports {
|
||||||
|
linker.define_unknown_imports_as_default_values(&module)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Use "" as a default module name.
|
// Use "" as a default module name.
|
||||||
linker
|
linker
|
||||||
.module(&mut *store, "", &module)
|
.module(&mut *store, "", &module)
|
||||||
|
|||||||
@@ -390,3 +390,37 @@ fn test_trapping_unknown_import() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default_value_unknown_import() -> Result<()> {
|
||||||
|
const WAT: &str = r#"
|
||||||
|
(module
|
||||||
|
(import "unknown" "func" (func $unknown_func (result i64 f32 externref)))
|
||||||
|
(func (export "run") (result i64 f32 externref)
|
||||||
|
call $unknown_func
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let mut store = Store::<()>::default();
|
||||||
|
let module = Module::new(store.engine(), WAT).expect("failed to create module");
|
||||||
|
let mut linker = Linker::new(store.engine());
|
||||||
|
|
||||||
|
linker.define_unknown_imports_as_default_values(&module)?;
|
||||||
|
let instance = linker.instantiate(&mut store, &module)?;
|
||||||
|
|
||||||
|
// "run" calls an import function which will not be defined, so it should
|
||||||
|
// return default values.
|
||||||
|
let run_func = instance
|
||||||
|
.get_func(&mut store, "run")
|
||||||
|
.expect("expected a run func in the module");
|
||||||
|
|
||||||
|
let mut results = vec![Val::I32(1), Val::I32(2), Val::I32(3)];
|
||||||
|
run_func.call(&mut store, &[], &mut results)?;
|
||||||
|
|
||||||
|
assert_eq!(results[0].i64(), Some(0));
|
||||||
|
assert_eq!(results[1].f32(), Some(0.0));
|
||||||
|
assert!(results[2].externref().unwrap().is_none());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user