Implement RFC 11: Redesigning Wasmtime's APIs (#2897)
Implement Wasmtime's new API as designed by RFC 11. This is quite a large commit which has had lots of discussion externally, so for more information it's best to read the RFC thread and the PR thread.
This commit is contained in:
@@ -1,89 +1,91 @@
|
||||
use anyhow::Result;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
use std::sync::Arc;
|
||||
use wasmtime::*;
|
||||
|
||||
#[test]
|
||||
fn link_undefined() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let linker = Linker::new(&store);
|
||||
let mut store = Store::<()>::default();
|
||||
let linker = Linker::new(store.engine());
|
||||
let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?;
|
||||
assert!(linker.instantiate(&module).is_err());
|
||||
assert!(linker.instantiate(&mut store, &module).is_err());
|
||||
let module = Module::new(store.engine(), r#"(module (import "" "" (global i32)))"#)?;
|
||||
assert!(linker.instantiate(&module).is_err());
|
||||
assert!(linker.instantiate(&mut store, &module).is_err());
|
||||
let module = Module::new(store.engine(), r#"(module (import "" "" (memory 1)))"#)?;
|
||||
assert!(linker.instantiate(&module).is_err());
|
||||
assert!(linker.instantiate(&mut store, &module).is_err());
|
||||
let module = Module::new(
|
||||
store.engine(),
|
||||
r#"(module (import "" "" (table 1 funcref)))"#,
|
||||
)?;
|
||||
assert!(linker.instantiate(&module).is_err());
|
||||
assert!(linker.instantiate(&mut store, &module).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn link_twice_bad() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let mut linker = Linker::new(&store);
|
||||
let mut store = Store::<()>::default();
|
||||
let mut linker = Linker::<()>::new(store.engine());
|
||||
|
||||
// functions
|
||||
linker.func("f", "", || {})?;
|
||||
assert!(linker.func("f", "", || {}).is_err());
|
||||
linker.func_wrap("f", "", || {})?;
|
||||
assert!(linker.func_wrap("f", "", || {}).is_err());
|
||||
assert!(linker
|
||||
.func("f", "", || -> Result<(), Trap> { loop {} })
|
||||
.func_wrap("f", "", || -> Result<(), Trap> { loop {} })
|
||||
.is_err());
|
||||
|
||||
// globals
|
||||
let ty = GlobalType::new(ValType::I32, Mutability::Const);
|
||||
let global = Global::new(&store, ty, Val::I32(0))?;
|
||||
let global = Global::new(&mut store, ty, Val::I32(0))?;
|
||||
linker.define("g", "1", global.clone())?;
|
||||
assert!(linker.define("g", "1", global.clone()).is_err());
|
||||
|
||||
let ty = GlobalType::new(ValType::I32, Mutability::Var);
|
||||
let global = Global::new(&store, ty, Val::I32(0))?;
|
||||
let global = Global::new(&mut store, ty, Val::I32(0))?;
|
||||
linker.define("g", "2", global.clone())?;
|
||||
assert!(linker.define("g", "2", global.clone()).is_err());
|
||||
|
||||
let ty = GlobalType::new(ValType::I64, Mutability::Const);
|
||||
let global = Global::new(&store, ty, Val::I64(0))?;
|
||||
let global = Global::new(&mut store, ty, Val::I64(0))?;
|
||||
linker.define("g", "3", global.clone())?;
|
||||
assert!(linker.define("g", "3", global.clone()).is_err());
|
||||
|
||||
// memories
|
||||
let ty = MemoryType::new(Limits::new(1, None));
|
||||
let memory = Memory::new(&store, ty)?;
|
||||
let memory = Memory::new(&mut store, ty)?;
|
||||
linker.define("m", "", memory.clone())?;
|
||||
assert!(linker.define("m", "", memory.clone()).is_err());
|
||||
let ty = MemoryType::new(Limits::new(2, None));
|
||||
let memory = Memory::new(&store, ty)?;
|
||||
let memory = Memory::new(&mut store, ty)?;
|
||||
assert!(linker.define("m", "", memory.clone()).is_err());
|
||||
|
||||
// tables
|
||||
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
|
||||
let table = Table::new(&store, ty, Val::FuncRef(None))?;
|
||||
let table = Table::new(&mut store, ty, Val::FuncRef(None))?;
|
||||
linker.define("t", "", table.clone())?;
|
||||
assert!(linker.define("t", "", table.clone()).is_err());
|
||||
let ty = TableType::new(ValType::FuncRef, Limits::new(2, None));
|
||||
let table = Table::new(&store, ty, Val::FuncRef(None))?;
|
||||
let table = Table::new(&mut store, ty, Val::FuncRef(None))?;
|
||||
assert!(linker.define("t", "", table.clone()).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_interposition() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let mut linker = Linker::new(&store);
|
||||
let mut store = Store::<()>::default();
|
||||
let mut linker = Linker::new(store.engine());
|
||||
linker.allow_shadowing(true);
|
||||
let mut module = Module::new(
|
||||
store.engine(),
|
||||
r#"(module (func (export "green") (result i32) (i32.const 7)))"#,
|
||||
)?;
|
||||
for _ in 0..4 {
|
||||
let instance = linker.instantiate(&module)?;
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
linker.define(
|
||||
"red",
|
||||
"green",
|
||||
instance.get_export("green").unwrap().clone(),
|
||||
instance.get_export(&mut store, "green").unwrap().clone(),
|
||||
)?;
|
||||
module = Module::new(
|
||||
store.engine(),
|
||||
@@ -93,10 +95,14 @@ fn function_interposition() -> Result<()> {
|
||||
)"#,
|
||||
)?;
|
||||
}
|
||||
let instance = linker.instantiate(&module)?;
|
||||
let func = instance.get_export("green").unwrap().into_func().unwrap();
|
||||
let func = func.typed::<(), i32>()?;
|
||||
assert_eq!(func.call(())?, 112);
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
let func = instance
|
||||
.get_export(&mut store, "green")
|
||||
.unwrap()
|
||||
.into_func()
|
||||
.unwrap();
|
||||
let func = func.typed::<(), i32, _>(&store)?;
|
||||
assert_eq!(func.call(&mut store, ())?, 112);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -104,19 +110,19 @@ fn function_interposition() -> Result<()> {
|
||||
// differs from the module's name.
|
||||
#[test]
|
||||
fn function_interposition_renamed() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let mut linker = Linker::new(&store);
|
||||
let mut store = Store::<()>::default();
|
||||
let mut linker = Linker::new(store.engine());
|
||||
linker.allow_shadowing(true);
|
||||
let mut module = Module::new(
|
||||
store.engine(),
|
||||
r#"(module (func (export "export") (result i32) (i32.const 7)))"#,
|
||||
)?;
|
||||
for _ in 0..4 {
|
||||
let instance = linker.instantiate(&module)?;
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
linker.define(
|
||||
"red",
|
||||
"green",
|
||||
instance.get_export("export").unwrap().clone(),
|
||||
instance.get_export(&mut store, "export").unwrap().clone(),
|
||||
)?;
|
||||
module = Module::new(
|
||||
store.engine(),
|
||||
@@ -126,10 +132,10 @@ fn function_interposition_renamed() -> Result<()> {
|
||||
)"#,
|
||||
)?;
|
||||
}
|
||||
let instance = linker.instantiate(&module)?;
|
||||
let func = instance.get_func("export").unwrap();
|
||||
let func = func.typed::<(), i32>()?;
|
||||
assert_eq!(func.call(())?, 112);
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
let func = instance.get_func(&mut store, "export").unwrap();
|
||||
let func = func.typed::<(), i32, _>(&store)?;
|
||||
assert_eq!(func.call(&mut store, ())?, 112);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -137,16 +143,16 @@ fn function_interposition_renamed() -> Result<()> {
|
||||
// `Linker::define`.
|
||||
#[test]
|
||||
fn module_interposition() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let mut linker = Linker::new(&store);
|
||||
let mut store = Store::<()>::default();
|
||||
let mut linker = Linker::new(store.engine());
|
||||
linker.allow_shadowing(true);
|
||||
let mut module = Module::new(
|
||||
store.engine(),
|
||||
r#"(module (func (export "export") (result i32) (i32.const 7)))"#,
|
||||
)?;
|
||||
for _ in 0..4 {
|
||||
let instance = linker.instantiate(&module)?;
|
||||
linker.instance("instance", &instance)?;
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
linker.instance(&mut store, "instance", instance)?;
|
||||
module = Module::new(
|
||||
store.engine(),
|
||||
r#"(module
|
||||
@@ -155,27 +161,31 @@ fn module_interposition() -> Result<()> {
|
||||
)"#,
|
||||
)?;
|
||||
}
|
||||
let instance = linker.instantiate(&module)?;
|
||||
let func = instance.get_export("export").unwrap().into_func().unwrap();
|
||||
let func = func.typed::<(), i32>()?;
|
||||
assert_eq!(func.call(())?, 112);
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
let func = instance
|
||||
.get_export(&mut store, "export")
|
||||
.unwrap()
|
||||
.into_func()
|
||||
.unwrap();
|
||||
let func = func.typed::<(), i32, _>(&store)?;
|
||||
assert_eq!(func.call(&mut store, ())?, 112);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allow_unknown_exports() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let mut linker = Linker::new(&store);
|
||||
let mut store = Store::<()>::default();
|
||||
let mut linker = Linker::new(store.engine());
|
||||
let module = Module::new(
|
||||
store.engine(),
|
||||
r#"(module (func (export "_start")) (global (export "g") i32 (i32.const 0)))"#,
|
||||
)?;
|
||||
|
||||
assert!(linker.module("module", &module).is_err());
|
||||
assert!(linker.module(&mut store, "module", &module).is_err());
|
||||
|
||||
let mut linker = Linker::new(&store);
|
||||
let mut linker = Linker::new(store.engine());
|
||||
linker.allow_unknown_exports(true);
|
||||
linker.module("module", &module)?;
|
||||
linker.module(&mut store, "module", &module)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -192,10 +202,8 @@ fn no_leak() -> Result<()> {
|
||||
|
||||
let flag = Rc::new(Cell::new(false));
|
||||
{
|
||||
let store = Store::default();
|
||||
let mut linker = Linker::new(&store);
|
||||
let drop_me = DropMe(flag.clone());
|
||||
linker.func("", "", move || drop(&drop_me))?;
|
||||
let mut store = Store::new(&Engine::default(), DropMe(flag.clone()));
|
||||
let mut linker = Linker::new(store.engine());
|
||||
let module = Module::new(
|
||||
store.engine(),
|
||||
r#"
|
||||
@@ -204,7 +212,7 @@ fn no_leak() -> Result<()> {
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
linker.module("a", &module)?;
|
||||
linker.module(&mut store, "a", &module)?;
|
||||
}
|
||||
assert!(flag.get(), "store was leaked");
|
||||
Ok(())
|
||||
@@ -212,20 +220,20 @@ fn no_leak() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn no_leak_with_imports() -> Result<()> {
|
||||
struct DropMe(Rc<Cell<bool>>);
|
||||
struct DropMe(Arc<AtomicUsize>);
|
||||
|
||||
impl Drop for DropMe {
|
||||
fn drop(&mut self) {
|
||||
self.0.set(true);
|
||||
self.0.fetch_add(1, SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let flag = Rc::new(Cell::new(false));
|
||||
let flag = Arc::new(AtomicUsize::new(0));
|
||||
{
|
||||
let store = Store::default();
|
||||
let mut linker = Linker::new(&store);
|
||||
let mut store = Store::new(&Engine::default(), DropMe(flag.clone()));
|
||||
let mut linker = Linker::new(store.engine());
|
||||
let drop_me = DropMe(flag.clone());
|
||||
linker.func("", "", move || drop(&drop_me))?;
|
||||
linker.func_wrap("", "", move || drop(&drop_me))?;
|
||||
let module = Module::new(
|
||||
store.engine(),
|
||||
r#"
|
||||
@@ -235,45 +243,68 @@ fn no_leak_with_imports() -> Result<()> {
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
linker.module("a", &module)?;
|
||||
linker.module(&mut store, "a", &module)?;
|
||||
}
|
||||
assert!(flag.get(), "store was leaked");
|
||||
assert!(flag.load(SeqCst) == 2, "something was leaked");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_host_function() -> Result<()> {
|
||||
let mut config = Config::default();
|
||||
config.wrap_host_func("mod", "f1", || {});
|
||||
|
||||
let engine = Engine::new(&config)?;
|
||||
let engine = Engine::default();
|
||||
let module = Module::new(&engine, r#"(module (import "mod" "f1" (func)))"#)?;
|
||||
let store = Store::new(&engine);
|
||||
|
||||
let linker = Linker::new(&store);
|
||||
assert!(linker.get(&module.imports().nth(0).unwrap()).is_some());
|
||||
let mut linker = Linker::new(&engine);
|
||||
linker.func_wrap("mod", "f1", || {})?;
|
||||
let mut store = Store::<()>::default();
|
||||
assert!(linker
|
||||
.get_by_import(&mut store, &module.imports().nth(0).unwrap())
|
||||
.is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadowing_host_function() -> Result<()> {
|
||||
let mut config = Config::default();
|
||||
config.wrap_host_func("mod", "f1", || {});
|
||||
fn funcs_live_on_to_fight_another_day() -> Result<()> {
|
||||
struct DropMe(Arc<AtomicUsize>);
|
||||
|
||||
let engine = Engine::new(&config)?;
|
||||
let store = Store::new(&engine);
|
||||
impl Drop for DropMe {
|
||||
fn drop(&mut self) {
|
||||
self.0.fetch_add(1, SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let mut linker = Linker::new(&store);
|
||||
assert!(linker
|
||||
.define("mod", "f1", Func::wrap(&store, || {}))
|
||||
.is_err());
|
||||
linker.define("mod", "f2", Func::wrap(&store, || {}))?;
|
||||
let flag = Arc::new(AtomicUsize::new(0));
|
||||
let engine = Engine::default();
|
||||
let mut linker = Linker::new(&engine);
|
||||
let drop_me = DropMe(flag.clone());
|
||||
linker.func_wrap("", "", move || drop(&drop_me))?;
|
||||
assert_eq!(flag.load(SeqCst), 0);
|
||||
|
||||
let mut linker = Linker::new(&store);
|
||||
linker.allow_shadowing(true);
|
||||
linker.define("mod", "f1", Func::wrap(&store, || {}))?;
|
||||
linker.define("mod", "f2", Func::wrap(&store, || {}))?;
|
||||
let get_and_call = || -> Result<()> {
|
||||
assert_eq!(flag.load(SeqCst), 0);
|
||||
let mut store = Store::new(&engine, ());
|
||||
let func = linker.get(&mut store, "", Some("")).unwrap();
|
||||
func.into_func().unwrap().call(&mut store, &[])?;
|
||||
assert_eq!(flag.load(SeqCst), 0);
|
||||
Ok(())
|
||||
};
|
||||
|
||||
get_and_call()?;
|
||||
get_and_call()?;
|
||||
drop(linker);
|
||||
assert_eq!(flag.load(SeqCst), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_one() -> Result<()> {
|
||||
let mut store = Store::<()>::default();
|
||||
let mut linker = Linker::new(store.engine());
|
||||
assert!(linker.alias("a", "b", "c", "d").is_err());
|
||||
linker.func_wrap("a", "b", || {})?;
|
||||
assert!(linker.alias("a", "b", "c", "d").is_ok());
|
||||
assert!(linker.get(&mut store, "a", Some("b")).is_some());
|
||||
assert!(linker.get(&mut store, "c", Some("d")).is_some());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user