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:
@@ -557,23 +557,24 @@ impl Table {
|
||||
/// **unsafe** usages of `Memory`. Do not do these things!
|
||||
///
|
||||
/// ```rust
|
||||
/// # use anyhow::Result;
|
||||
/// use wasmtime::Memory;
|
||||
///
|
||||
/// // NOTE: All code in this function is not safe to execute and may cause
|
||||
/// // segfaults/undefined behavior at runtime. Do not copy/paste these examples
|
||||
/// // into production code!
|
||||
/// unsafe fn unsafe_examples(mem: &Memory) {
|
||||
/// unsafe fn unsafe_examples(mem: &Memory) -> Result<()> {
|
||||
/// // First and foremost, any borrow can be invalidated at any time via the
|
||||
/// // `Memory::grow` function. This can relocate memory which causes any
|
||||
/// // previous pointer to be possibly invalid now.
|
||||
/// let pointer: &u8 = &mem.data_unchecked()[0x100];
|
||||
/// mem.grow(1); // invalidates `pointer`!
|
||||
/// mem.grow(1)?; // invalidates `pointer`!
|
||||
/// // println!("{}", *pointer); // FATAL: use-after-free
|
||||
///
|
||||
/// // Note that the use-after-free also applies to slices, whether they're
|
||||
/// // slices of bytes or strings.
|
||||
/// let slice: &[u8] = &mem.data_unchecked()[0x100..0x102];
|
||||
/// mem.grow(1); // invalidates `slice`!
|
||||
/// mem.grow(1)?; // invalidates `slice`!
|
||||
/// // println!("{:?}", slice); // FATAL: use-after-free
|
||||
///
|
||||
/// // Due to the reference-counted nature of `Memory` note that literal
|
||||
@@ -597,6 +598,8 @@ impl Table {
|
||||
/// let slice1: &mut [u8] = &mut mem.data_unchecked_mut()[0x100..][..3];
|
||||
/// let slice2: &mut [u8] = &mut mem.data_unchecked_mut()[0x102..][..4];
|
||||
/// // println!("{:?} {:?}", slice1, slice2); // FATAL: aliasing mutable pointers
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// # fn some_other_function() {}
|
||||
/// ```
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
//! itself for consumption from other languages.
|
||||
|
||||
#![deny(missing_docs, intra_doc_link_resolution_failure)]
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
#![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))]
|
||||
|
||||
mod externals;
|
||||
mod frame_info;
|
||||
|
||||
@@ -32,11 +32,15 @@ use std::rc::Rc;
|
||||
/// name `foo` and item name `bar`, so long as they have different function
|
||||
/// signatures. Currently duplicate memories and tables are not allowed, only
|
||||
/// one-per-name is allowed.
|
||||
///
|
||||
/// Note that allowing duplicates by shadowing the previous definition can be
|
||||
/// controlled with the [`Linker::allow_shadowing`] method as well.
|
||||
pub struct Linker {
|
||||
store: Store,
|
||||
string2idx: HashMap<Rc<str>, usize>,
|
||||
strings: Vec<Rc<str>>,
|
||||
map: HashMap<ImportKey, Extern>,
|
||||
allow_shadowing: bool,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
@@ -77,9 +81,40 @@ impl Linker {
|
||||
map: HashMap::new(),
|
||||
string2idx: HashMap::new(),
|
||||
strings: Vec::new(),
|
||||
allow_shadowing: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures whether this [`Linker`] will shadow previous duplicate
|
||||
/// definitions of the same signature.
|
||||
///
|
||||
/// By default a [`Linker`] will disallow duplicate definitions of the same
|
||||
/// signature. This method, however, can be used to instead allow duplicates
|
||||
/// and have the latest definition take precedence when linking modules.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use wasmtime::*;
|
||||
/// # fn main() -> anyhow::Result<()> {
|
||||
/// # let store = Store::default();
|
||||
/// let mut linker = Linker::new(&store);
|
||||
/// linker.func("", "", || {})?;
|
||||
///
|
||||
/// // by default, duplicates are disallowed
|
||||
/// assert!(linker.func("", "", || {}).is_err());
|
||||
///
|
||||
/// // but shadowing can be configured to be allowed as well
|
||||
/// linker.allow_shadowing(true);
|
||||
/// linker.func("", "", || {})?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn allow_shadowing(&mut self, allow: bool) -> &mut Linker {
|
||||
self.allow_shadowing = allow;
|
||||
self
|
||||
}
|
||||
|
||||
/// Defines a new item in this [`Linker`].
|
||||
///
|
||||
/// This method will add a new definition, by name, to this instance of
|
||||
@@ -89,8 +124,8 @@ impl Linker {
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the `module` and `name` already identify an item
|
||||
/// of the same type as the `item` provided. For more information see the
|
||||
/// documentation on [`Linker`].
|
||||
/// of the same type as the `item` provided and if shadowing is disallowed.
|
||||
/// For more information see the documentation on [`Linker`].
|
||||
///
|
||||
/// Also returns an error if `item` comes from a different store than this
|
||||
/// [`Linker`] was created with.
|
||||
@@ -104,7 +139,7 @@ impl Linker {
|
||||
/// let mut linker = Linker::new(&store);
|
||||
/// let ty = GlobalType::new(ValType::I32, Mutability::Const);
|
||||
/// let global = Global::new(&store, ty, Val::I32(0x1234))?;
|
||||
/// linker.define("host", "offset", global);
|
||||
/// linker.define("host", "offset", global)?;
|
||||
///
|
||||
/// let wat = r#"
|
||||
/// (module
|
||||
@@ -142,9 +177,9 @@ impl Linker {
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the `module` and `name` already identify a function
|
||||
/// of the same signature as `func`. For more information see the
|
||||
/// documentation on [`Linker`].
|
||||
/// Returns an error if the `module` and `name` already identify an item
|
||||
/// of the same type as the `item` provided and if shadowing is disallowed.
|
||||
/// For more information see the documentation on [`Linker`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -190,9 +225,9 @@ impl Linker {
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the any item is redefined twice in this linker (for
|
||||
/// example the same `module_name` was already defined), or if `instance`
|
||||
/// comes from a different [`Store`] than this [`Linker`] originally was
|
||||
/// created with.
|
||||
/// example the same `module_name` was already defined) and shadowing is
|
||||
/// disallowed, or if `instance` comes from a different [`Store`] than this
|
||||
/// [`Linker`] originally was created with.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -238,12 +273,15 @@ impl Linker {
|
||||
fn insert(&mut self, module: &str, name: &str, ty: &ExternType, item: Extern) -> Result<()> {
|
||||
let key = self.import_key(module, name, ty);
|
||||
match self.map.entry(key) {
|
||||
Entry::Occupied(o) => bail!(
|
||||
"import of `{}::{}` with as {:?} defined twice",
|
||||
Entry::Occupied(o) if !self.allow_shadowing => bail!(
|
||||
"import of `{}::{}` with kind {:?} defined twice",
|
||||
module,
|
||||
name,
|
||||
o.key().kind,
|
||||
),
|
||||
Entry::Occupied(mut o) => {
|
||||
o.insert(item);
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(item);
|
||||
}
|
||||
@@ -337,14 +375,14 @@ impl Linker {
|
||||
}
|
||||
if options.len() == 0 {
|
||||
bail!(
|
||||
"import of `{}::{}` has not been defined",
|
||||
"unknown import: `{}::{}` has not been defined",
|
||||
import.module(),
|
||||
import.name()
|
||||
)
|
||||
}
|
||||
|
||||
bail!(
|
||||
"failed to find import of `{}::{}` with matching signature\n\
|
||||
"incompatible import type for `{}::{}` specified\n\
|
||||
desired signature was: {:?}\n\
|
||||
signatures available:\n\n{}",
|
||||
import.module(),
|
||||
@@ -365,4 +403,9 @@ impl Linker {
|
||||
};
|
||||
self.map.get(&key)
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] that this linker is connected to.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,3 +64,34 @@ fn link_twice_bad() -> Result<()> {
|
||||
assert!(linker.define("", "", table.clone()).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interposition() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let mut linker = Linker::new(&store);
|
||||
linker.allow_shadowing(true);
|
||||
let mut module = Module::new(
|
||||
&store,
|
||||
r#"(module (func (export "export") (result i32) (i32.const 7)))"#,
|
||||
)?;
|
||||
for _ in 0..4 {
|
||||
let instance = linker.instantiate(&module)?;
|
||||
linker.define(
|
||||
"red",
|
||||
"green",
|
||||
instance.get_export("export").unwrap().clone(),
|
||||
)?;
|
||||
module = Module::new(
|
||||
&store,
|
||||
r#"(module
|
||||
(import "red" "green" (func (result i32)))
|
||||
(func (export "export") (result i32) (i32.mul (call 0) (i32.const 2)))
|
||||
)"#,
|
||||
)?;
|
||||
}
|
||||
let instance = linker.instantiate(&module)?;
|
||||
let func = instance.get_export("export").unwrap().func().unwrap();
|
||||
let func = func.get0::<i32>()?;
|
||||
assert_eq!(func()?, 112);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user