Use Linker in *.wast testing (#1391)
* Use `Linker` in `*.wast` testing By default `Linker` disallows shadowing previously defined items, but it looks like the `*.wast` test suites rely on this so this commit adds a boolean flag to `Linker` as well indicating whether duplicates are allowed. * Review comments * Add a test with a number of recursive instances * Deny warnings in doctests * No tabs
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
mod spectest;
|
||||
mod wast;
|
||||
|
||||
pub use crate::spectest::instantiate_spectest;
|
||||
pub use crate::spectest::link_spectest;
|
||||
pub use crate::wast::WastContext;
|
||||
|
||||
/// Version number of this crate.
|
||||
|
||||
@@ -1,61 +1,46 @@
|
||||
use std::collections::HashMap;
|
||||
use anyhow::Result;
|
||||
use wasmtime::*;
|
||||
|
||||
/// Return an instance implementing the "spectest" interface used in the
|
||||
/// spec testsuite.
|
||||
pub fn instantiate_spectest(store: &Store) -> HashMap<&'static str, Extern> {
|
||||
let mut ret = HashMap::new();
|
||||
|
||||
let func = Func::wrap(store, || {});
|
||||
ret.insert("print", Extern::Func(func));
|
||||
|
||||
let func = Func::wrap(store, |val: i32| println!("{}: i32", val));
|
||||
ret.insert("print_i32", Extern::Func(func));
|
||||
|
||||
let func = Func::wrap(store, |val: i64| println!("{}: i64", val));
|
||||
ret.insert("print_i64", Extern::Func(func));
|
||||
|
||||
let func = Func::wrap(store, |val: f32| println!("{}: f32", val));
|
||||
ret.insert("print_f32", Extern::Func(func));
|
||||
|
||||
let func = Func::wrap(store, |val: f64| println!("{}: f64", val));
|
||||
ret.insert("print_f64", Extern::Func(func));
|
||||
|
||||
let func = Func::wrap(store, |i: i32, f: f32| {
|
||||
pub fn link_spectest(linker: &mut Linker) -> Result<()> {
|
||||
linker.func("spectest", "print", || {})?;
|
||||
linker.func("spectest", "print_i32", |val: i32| println!("{}: i32", val))?;
|
||||
linker.func("spectest", "print_i64", |val: i64| println!("{}: i64", val))?;
|
||||
linker.func("spectest", "print_f32", |val: f32| println!("{}: f32", val))?;
|
||||
linker.func("spectest", "print_f64", |val: f64| println!("{}: f64", val))?;
|
||||
linker.func("spectest", "print_i32_f32", |i: i32, f: f32| {
|
||||
println!("{}: i32", i);
|
||||
println!("{}: f32", f);
|
||||
});
|
||||
ret.insert("print_i32_f32", Extern::Func(func));
|
||||
|
||||
let func = Func::wrap(store, |f1: f64, f2: f64| {
|
||||
})?;
|
||||
linker.func("spectest", "print_f64_f64", |f1: f64, f2: f64| {
|
||||
println!("{}: f64", f1);
|
||||
println!("{}: f64", f2);
|
||||
});
|
||||
ret.insert("print_f64_f64", Extern::Func(func));
|
||||
})?;
|
||||
|
||||
let ty = GlobalType::new(ValType::I32, Mutability::Const);
|
||||
let g = Global::new(store, ty, Val::I32(666)).unwrap();
|
||||
ret.insert("global_i32", Extern::Global(g));
|
||||
let g = Global::new(linker.store(), ty, Val::I32(666))?;
|
||||
linker.define("spectest", "global_i32", g)?;
|
||||
|
||||
let ty = GlobalType::new(ValType::I64, Mutability::Const);
|
||||
let g = Global::new(store, ty, Val::I64(666)).unwrap();
|
||||
ret.insert("global_i64", Extern::Global(g));
|
||||
let g = Global::new(linker.store(), ty, Val::I64(666))?;
|
||||
linker.define("spectest", "global_i64", g)?;
|
||||
|
||||
let ty = GlobalType::new(ValType::F32, Mutability::Const);
|
||||
let g = Global::new(store, ty, Val::F32(0x4426_8000)).unwrap();
|
||||
ret.insert("global_f32", Extern::Global(g));
|
||||
let g = Global::new(linker.store(), ty, Val::F32(0x4426_8000))?;
|
||||
linker.define("spectest", "global_f32", g)?;
|
||||
|
||||
let ty = GlobalType::new(ValType::F64, Mutability::Const);
|
||||
let g = Global::new(store, ty, Val::F64(0x4084_d000_0000_0000)).unwrap();
|
||||
ret.insert("global_f64", Extern::Global(g));
|
||||
let g = Global::new(linker.store(), ty, Val::F64(0x4084_d000_0000_0000))?;
|
||||
linker.define("spectest", "global_f64", g)?;
|
||||
|
||||
let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20)));
|
||||
let table = Table::new(store, ty, Val::AnyRef(AnyRef::Null)).unwrap();
|
||||
ret.insert("table", Extern::Table(table));
|
||||
let table = Table::new(linker.store(), ty, Val::AnyRef(AnyRef::Null))?;
|
||||
linker.define("spectest", "table", table)?;
|
||||
|
||||
let ty = MemoryType::new(Limits::new(1, Some(2)));
|
||||
let memory = Memory::new(store, ty);
|
||||
ret.insert("memory", Extern::Memory(memory));
|
||||
let memory = Memory::new(linker.store(), ty);
|
||||
linker.define("spectest", "memory", memory)?;
|
||||
|
||||
return ret;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::spectest::instantiate_spectest;
|
||||
use crate::spectest::link_spectest;
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
@@ -30,8 +30,8 @@ pub struct WastContext {
|
||||
current: Option<Instance>,
|
||||
|
||||
instances: HashMap<String, Instance>,
|
||||
linker: Linker,
|
||||
store: Store,
|
||||
spectest: Option<HashMap<&'static str, Extern>>,
|
||||
}
|
||||
|
||||
enum Outcome<T = Vec<Val>> {
|
||||
@@ -51,11 +51,16 @@ impl<T> Outcome<T> {
|
||||
impl WastContext {
|
||||
/// Construct a new instance of `WastContext`.
|
||||
pub fn new(store: Store) -> Self {
|
||||
// Spec tests will redefine the same module/name sometimes, so we need
|
||||
// to allow shadowing in the linker which picks the most recent
|
||||
// definition as what to link when linking.
|
||||
let mut linker = Linker::new(&store);
|
||||
linker.allow_shadowing(true);
|
||||
Self {
|
||||
current: None,
|
||||
store,
|
||||
spectest: None,
|
||||
instances: HashMap::new(),
|
||||
linker,
|
||||
store,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,31 +80,7 @@ impl WastContext {
|
||||
|
||||
fn instantiate(&self, module: &[u8]) -> Result<Outcome<Instance>> {
|
||||
let module = Module::new(&self.store, module)?;
|
||||
let mut imports = Vec::new();
|
||||
for import in module.imports() {
|
||||
if import.module() == "spectest" {
|
||||
let spectest = self
|
||||
.spectest
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("spectest module isn't instantiated"))?;
|
||||
let export = spectest
|
||||
.get(import.name())
|
||||
.ok_or_else(|| anyhow!("unknown import `spectest::{}`", import.name()))?;
|
||||
imports.push(export.clone());
|
||||
continue;
|
||||
}
|
||||
|
||||
let instance = self
|
||||
.instances
|
||||
.get(import.module())
|
||||
.ok_or_else(|| anyhow!("no module named `{}`", import.module()))?;
|
||||
let export = instance
|
||||
.get_export(import.name())
|
||||
.ok_or_else(|| anyhow!("unknown import `{}::{}`", import.name(), import.module()))?
|
||||
.clone();
|
||||
imports.push(export);
|
||||
}
|
||||
let instance = match Instance::new(&module, &imports) {
|
||||
let instance = match self.linker.instantiate(&module) {
|
||||
Ok(i) => i,
|
||||
Err(e) => return e.downcast::<Trap>().map(Outcome::Trap),
|
||||
};
|
||||
@@ -108,7 +89,7 @@ impl WastContext {
|
||||
|
||||
/// Register "spectest" which is used by the spec testsuite.
|
||||
pub fn register_spectest(&mut self) -> Result<()> {
|
||||
self.spectest = Some(instantiate_spectest(&self.store));
|
||||
link_spectest(&mut self.linker)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -145,6 +126,7 @@ impl WastContext {
|
||||
};
|
||||
if let Some(name) = instance_name {
|
||||
self.instances.insert(name.to_string(), instance.clone());
|
||||
self.linker.instance(name, &instance)?;
|
||||
}
|
||||
self.current = Some(instance);
|
||||
Ok(())
|
||||
@@ -153,6 +135,7 @@ impl WastContext {
|
||||
/// Register an instance to make it available for performing actions.
|
||||
fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> {
|
||||
let instance = self.get_instance(name)?.clone();
|
||||
self.linker.instance(as_name, &instance)?;
|
||||
self.instances.insert(as_name.to_string(), instance);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user