Add the ability to cache typechecking an instance (#2962)
* Add the ability to cache typechecking an instance This commit adds the abilty to cache the type-checked imports of an instance if an instance is going to be instantiated multiple times. This can also be useful to do a "dry run" of instantiation where no wasm code is run but it's double-checked that a `Linker` possesses everything necessary to instantiate the provided module. This should ideally help cut down repeated instantiation costs slightly by avoiding type-checking and allocation a `Vec<Extern>` on each instantiation. It's expected though that the impact on instantiation time is quite small and likely not super significant. The functionality, though, of pre-checking can be useful for some embeddings. * Fix build with async
This commit is contained in:
@@ -677,7 +677,7 @@ impl Func {
|
|||||||
/// initiates a panic. Also panics if `store` does not own this function.
|
/// initiates a panic. Also panics if `store` does not own this function.
|
||||||
pub fn call(&self, mut store: impl AsContextMut, params: &[Val]) -> Result<Box<[Val]>> {
|
pub fn call(&self, mut store: impl AsContextMut, params: &[Val]) -> Result<Box<[Val]>> {
|
||||||
assert!(
|
assert!(
|
||||||
!cfg!(feature = "async") || !store.as_context().async_support(),
|
!store.as_context().async_support(),
|
||||||
"must use `call_async` when async support is enabled on the config",
|
"must use `call_async` when async support is enabled on the config",
|
||||||
);
|
);
|
||||||
let my_ty = self.ty(&store);
|
let my_ty = self.ty(&store);
|
||||||
@@ -1894,6 +1894,10 @@ impl HostFunc {
|
|||||||
let idx = self.export.anyfunc.as_ref().type_index;
|
let idx = self.export.anyfunc.as_ref().type_index;
|
||||||
store.register_host_trampoline(idx, self.trampoline);
|
store.register_host_trampoline(idx, self.trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sig_index(&self) -> VMSharedSignatureIndex {
|
||||||
|
unsafe { self.export.anyfunc.as_ref().type_index }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for HostFunc {
|
impl Drop for HostFunc {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ where
|
|||||||
pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results, Trap> {
|
pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results, Trap> {
|
||||||
let mut store = store.as_context_mut().opaque();
|
let mut store = store.as_context_mut().opaque();
|
||||||
assert!(
|
assert!(
|
||||||
!cfg!(feature = "async") || !store.async_support(),
|
!store.async_support(),
|
||||||
"must use `call_async` with async stores"
|
"must use `call_async` with async stores"
|
||||||
);
|
);
|
||||||
unsafe { self._call(&mut store, params) }
|
unsafe { self._call(&mut store, params) }
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::linker::Definition;
|
||||||
use crate::store::{InstanceId, StoreData, StoreOpaque, StoreOpaqueSend, Stored};
|
use crate::store::{InstanceId, StoreData, StoreOpaque, StoreOpaqueSend, Stored};
|
||||||
use crate::types::matching;
|
use crate::types::matching;
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -9,7 +10,8 @@ use std::mem;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wasmtime_environ::entity::PrimaryMap;
|
use wasmtime_environ::entity::PrimaryMap;
|
||||||
use wasmtime_environ::wasm::{
|
use wasmtime_environ::wasm::{
|
||||||
EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex,
|
EntityIndex, EntityType, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex,
|
||||||
|
TableIndex,
|
||||||
};
|
};
|
||||||
use wasmtime_environ::Initializer;
|
use wasmtime_environ::Initializer;
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
@@ -96,32 +98,14 @@ impl Instance {
|
|||||||
module: &Module,
|
module: &Module,
|
||||||
imports: &[Extern],
|
imports: &[Extern],
|
||||||
) -> Result<Instance, Error> {
|
) -> Result<Instance, Error> {
|
||||||
Instance::_new(&mut store.as_context_mut().opaque(), module, imports)
|
// This unsafety comes from `Instantiator::new` where we must typecheck
|
||||||
}
|
// first, which we are sure to do here.
|
||||||
|
let mut i = unsafe {
|
||||||
fn _new(
|
let mut cx = store.as_context_mut().opaque();
|
||||||
store: &mut StoreOpaque<'_>,
|
typecheck_externs(&mut cx, module, imports)?;
|
||||||
module: &Module,
|
Instantiator::new(&mut cx, module, ImportSource::Externs(imports))?
|
||||||
imports: &[Extern],
|
};
|
||||||
) -> Result<Instance, Error> {
|
i.run(store.as_context_mut().opaque())
|
||||||
assert!(
|
|
||||||
!store.async_support(),
|
|
||||||
"cannot use `new` when async support is enabled on the config"
|
|
||||||
);
|
|
||||||
|
|
||||||
// NB: this is the same code as `Instance::new_async`. It's intentionally
|
|
||||||
// small but should be kept in sync (modulo the async bits).
|
|
||||||
let mut i = Instantiator::new(store, module, imports)?;
|
|
||||||
loop {
|
|
||||||
if let Some((id, instance)) = i.step(store)? {
|
|
||||||
if let Some(start) = store.instance(id).module().start_func {
|
|
||||||
Instantiator::start_raw(store, id, start)?;
|
|
||||||
}
|
|
||||||
if let Some(instance) = instance {
|
|
||||||
break Ok(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as [`Instance::new`], except for usage in [asynchronous stores].
|
/// Same as [`Instance::new`], except for usage in [asynchronous stores].
|
||||||
@@ -151,37 +135,13 @@ impl Instance {
|
|||||||
where
|
where
|
||||||
T: Send,
|
T: Send,
|
||||||
{
|
{
|
||||||
Instance::_new_async(store.as_context_mut().opaque_send(), module, imports).await
|
// See `new` for unsafety comments
|
||||||
}
|
let mut i = unsafe {
|
||||||
|
let mut cx = store.as_context_mut().opaque();
|
||||||
#[cfg(feature = "async")]
|
typecheck_externs(&mut cx, module, imports)?;
|
||||||
async fn _new_async<'a>(
|
Instantiator::new(&mut cx, module, ImportSource::Externs(imports))?
|
||||||
mut store: StoreOpaqueSend<'a>,
|
};
|
||||||
module: &Module,
|
i.run_async(store.as_context_mut().opaque_send()).await
|
||||||
imports: &[Extern],
|
|
||||||
) -> Result<Instance, Error> {
|
|
||||||
assert!(
|
|
||||||
store.async_support(),
|
|
||||||
"cannot use `new_async` without enabling async support on the config"
|
|
||||||
);
|
|
||||||
|
|
||||||
// NB: this is the same code as `Instance::new`. It's intentionally
|
|
||||||
// small but should be kept in sync (modulo the async bits).
|
|
||||||
let mut i = Instantiator::new(&mut store.opaque(), module, imports)?;
|
|
||||||
loop {
|
|
||||||
let step = i.step(&mut store.opaque())?;
|
|
||||||
if let Some((id, instance)) = step {
|
|
||||||
let start = store.instance(id).module().start_func;
|
|
||||||
if let Some(start) = start {
|
|
||||||
store
|
|
||||||
.on_fiber(|store| Instantiator::start_raw(store, id, start))
|
|
||||||
.await??;
|
|
||||||
}
|
|
||||||
if let Some(instance) = instance {
|
|
||||||
break Ok(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_wasmtime(handle: RuntimeInstance, store: &mut StoreOpaque) -> Instance {
|
pub(crate) fn from_wasmtime(handle: RuntimeInstance, store: &mut StoreOpaque) -> Instance {
|
||||||
@@ -336,7 +296,8 @@ struct ImportsBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ImportSource<'a> {
|
enum ImportSource<'a> {
|
||||||
Runtime(&'a [Extern]),
|
Externs(&'a [Extern]),
|
||||||
|
Definitions(&'a [Definition]),
|
||||||
Outer { initializer: usize },
|
Outer { initializer: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,31 +306,80 @@ impl<'a> Instantiator<'a> {
|
|||||||
/// directives of a module.
|
/// directives of a module.
|
||||||
///
|
///
|
||||||
/// This doesn't do much work itself beyond setting things up.
|
/// This doesn't do much work itself beyond setting things up.
|
||||||
fn new(
|
///
|
||||||
|
/// # Unsafety
|
||||||
|
///
|
||||||
|
/// This function is unsafe for a few reasons:
|
||||||
|
///
|
||||||
|
/// * This assumes that `imports` has already been typechecked and is of the
|
||||||
|
/// appropriate length. It is memory unsafe if the types of `imports` are
|
||||||
|
/// not what `module` expects.
|
||||||
|
///
|
||||||
|
/// * The `imports` must be safely able to get inserted into `store`. This
|
||||||
|
/// only applies if `ImportSource::Definitions` is used because this will
|
||||||
|
/// internally call `Definition::to_extern` which requires that any
|
||||||
|
/// host functions in the list were created with an original `T` as the
|
||||||
|
/// store that's being inserted into.
|
||||||
|
///
|
||||||
|
/// * The `imports` must all come from the `store` specified.
|
||||||
|
unsafe fn new(
|
||||||
store: &mut StoreOpaque<'_>,
|
store: &mut StoreOpaque<'_>,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
imports: &'a [Extern],
|
imports: ImportSource<'a>,
|
||||||
) -> Result<Instantiator<'a>> {
|
) -> Result<Instantiator<'a>> {
|
||||||
if !Engine::same(store.engine(), module.engine()) {
|
if !Engine::same(store.engine(), module.engine()) {
|
||||||
bail!("cross-`Engine` instantiation is not currently supported");
|
bail!("cross-`Engine` instantiation is not currently supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform some pre-flight checks before we get into the meat of
|
Ok(Instantiator {
|
||||||
// instantiation.
|
in_progress: Vec::new(),
|
||||||
let expected = module.compiled_module().module().imports().count();
|
cur: ImportsBuilder::new(module, imports),
|
||||||
if expected != imports.len() {
|
})
|
||||||
bail!("expected {} imports, found {}", expected, imports.len());
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, mut store: StoreOpaque<'_>) -> Result<Instance, Error> {
|
||||||
|
assert!(
|
||||||
|
!store.async_support(),
|
||||||
|
"cannot use `new` when async support is enabled on the config"
|
||||||
|
);
|
||||||
|
|
||||||
|
// NB: this is the same code as `run_async`. It's intentionally
|
||||||
|
// small but should be kept in sync (modulo the async bits).
|
||||||
|
loop {
|
||||||
|
if let Some((id, instance)) = self.step(&mut store)? {
|
||||||
|
if let Some(start) = store.instance(id).module().start_func {
|
||||||
|
Instantiator::start_raw(&mut store, id, start)?;
|
||||||
|
}
|
||||||
|
if let Some(instance) = instance {
|
||||||
|
break Ok(instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for import in imports {
|
|
||||||
if !import.comes_from_same_store(&store) {
|
|
||||||
bail!("cross-`Store` instantiation is not currently supported");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Instantiator {
|
#[cfg(feature = "async")]
|
||||||
in_progress: Vec::new(),
|
async fn run_async(&mut self, mut store: StoreOpaqueSend<'_>) -> Result<Instance, Error> {
|
||||||
cur: ImportsBuilder::new(module, ImportSource::Runtime(imports)),
|
assert!(
|
||||||
})
|
store.async_support(),
|
||||||
|
"cannot use `new_async` without enabling async support on the config"
|
||||||
|
);
|
||||||
|
|
||||||
|
// NB: this is the same code as `run`. It's intentionally
|
||||||
|
// small but should be kept in sync (modulo the async bits).
|
||||||
|
loop {
|
||||||
|
let step = self.step(&mut store.opaque())?;
|
||||||
|
if let Some((id, instance)) = step {
|
||||||
|
let start = store.instance(id).module().start_func;
|
||||||
|
if let Some(start) = start {
|
||||||
|
store
|
||||||
|
.on_fiber(|store| Instantiator::start_raw(store, id, start))
|
||||||
|
.await??;
|
||||||
|
}
|
||||||
|
if let Some(instance) = instance {
|
||||||
|
break Ok(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the next initializer for the next instance being created
|
/// Processes the next initializer for the next instance being created
|
||||||
@@ -410,7 +420,7 @@ impl<'a> Instantiator<'a> {
|
|||||||
.initializers
|
.initializers
|
||||||
.get(self.cur.initializer - 1)
|
.get(self.cur.initializer - 1)
|
||||||
{
|
{
|
||||||
Some(Initializer::Import { index, name, field }) => {
|
Some(Initializer::Import { name, field, .. }) => {
|
||||||
match &mut self.cur.src {
|
match &mut self.cur.src {
|
||||||
// If imports are coming from the runtime-provided list
|
// If imports are coming from the runtime-provided list
|
||||||
// (e.g. the root module being instantiated) then we
|
// (e.g. the root module being instantiated) then we
|
||||||
@@ -418,27 +428,18 @@ impl<'a> Instantiator<'a> {
|
|||||||
//
|
//
|
||||||
// Note the `unwrap` here should be ok given the validation
|
// Note the `unwrap` here should be ok given the validation
|
||||||
// above in `Instantiation::new`.
|
// above in `Instantiation::new`.
|
||||||
ImportSource::Runtime(list) => {
|
ImportSource::Externs(list) => {
|
||||||
let (head, remaining) = list.split_first().unwrap();
|
let (head, remaining) = list.split_first().unwrap();
|
||||||
*list = remaining;
|
*list = remaining;
|
||||||
let expected_ty =
|
|
||||||
self.cur.module.compiled_module().module().type_of(*index);
|
|
||||||
matching::MatchCx {
|
|
||||||
signatures: self.cur.module.signatures(),
|
|
||||||
types: self.cur.module.types(),
|
|
||||||
store_data: store.store_data(),
|
|
||||||
engine: store.engine(),
|
|
||||||
}
|
|
||||||
.extern_(&expected_ty, head)
|
|
||||||
.with_context(|| {
|
|
||||||
let extra = match field {
|
|
||||||
Some(name) => format!("::{}", name),
|
|
||||||
None => String::new(),
|
|
||||||
};
|
|
||||||
format!("incompatible import type for `{}{}`", name, extra)
|
|
||||||
})?;
|
|
||||||
self.cur.push(head.clone(), store);
|
self.cur.push(head.clone(), store);
|
||||||
}
|
}
|
||||||
|
ImportSource::Definitions(list) => {
|
||||||
|
let (head, remaining) = list.split_first().unwrap();
|
||||||
|
*list = remaining;
|
||||||
|
// This unsafety is encapsulated with
|
||||||
|
// `Instantiator::new`, documented above.
|
||||||
|
self.cur.push(unsafe { head.to_extern(store) }, store);
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise if arguments are coming from our outer
|
// Otherwise if arguments are coming from our outer
|
||||||
// instance due to a recursive instantiation then we
|
// instance due to a recursive instantiation then we
|
||||||
@@ -738,25 +739,157 @@ impl<'a> ImportsBuilder<'a> {
|
|||||||
|
|
||||||
pub(crate) type RuntimeInstance = Arc<indexmap::IndexMap<String, Extern>>;
|
pub(crate) type RuntimeInstance = Arc<indexmap::IndexMap<String, Extern>>;
|
||||||
|
|
||||||
/// An internal structure to this crate to build an `Instance` from a list of
|
/// An instance, pre-instantiation, that is ready to be instantiated.
|
||||||
/// items with names. This is intended to stay private for now, it'll need an
|
///
|
||||||
/// audit of APIs if publicly exported.
|
/// This structure represents an instance *just before* it was instantiated,
|
||||||
#[derive(Default)]
|
/// after all type-checking and imports have been resolved. The only thing left
|
||||||
pub(crate) struct InstanceBuilder {
|
/// to do for this instance is to actually run the process of instantiation.
|
||||||
items: RuntimeInstance,
|
///
|
||||||
|
/// Note that an `InstancePre` may not be tied to any particular [`Store`] if
|
||||||
|
/// none of the imports it closed over are tied to any particular [`Store`].
|
||||||
|
///
|
||||||
|
/// This structure is created through the [`Linker::instantiate_pre`] method,
|
||||||
|
/// which also has some more information and examples.
|
||||||
|
///
|
||||||
|
/// [`Store`]: crate::Store
|
||||||
|
/// [`Linker::instantiate_pre`]: crate::Linker::instantiate_pre
|
||||||
|
pub struct InstancePre<T> {
|
||||||
|
module: Module,
|
||||||
|
items: Vec<Definition>,
|
||||||
|
_marker: std::marker::PhantomData<fn() -> T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstanceBuilder {
|
impl<T> InstancePre<T> {
|
||||||
pub(crate) fn new() -> InstanceBuilder {
|
pub(crate) unsafe fn new(
|
||||||
InstanceBuilder::default()
|
store: &mut StoreOpaque,
|
||||||
|
module: &Module,
|
||||||
|
items: Vec<Definition>,
|
||||||
|
) -> Result<InstancePre<T>> {
|
||||||
|
typecheck_defs(store, module, &items)?;
|
||||||
|
Ok(InstancePre {
|
||||||
|
module: module.clone(),
|
||||||
|
items,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, name: &str, item: impl Into<Extern>) {
|
/// Instantiates this instance, creating a new instance within the provided
|
||||||
let items = Arc::get_mut(&mut self.items).unwrap();
|
/// `store`.
|
||||||
items.insert(name.to_string(), item.into());
|
///
|
||||||
|
/// This function will run the actual process of instantiation to
|
||||||
|
/// completion. This will use all of the previously-closed-over items as
|
||||||
|
/// imports to instantiate the module that this was originally created with.
|
||||||
|
///
|
||||||
|
/// For more information about instantiation see [`Instance::new`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if any import closed over by this [`InstancePre`] isn't owned by
|
||||||
|
/// `store`, or if `store` has async support enabled.
|
||||||
|
pub fn instantiate(&self, mut store: impl AsContextMut<Data = T>) -> Result<Instance> {
|
||||||
|
let mut store = store.as_context_mut().opaque();
|
||||||
|
// For the unsafety here the typecheck happened at creation time of this
|
||||||
|
// structure and then othrewise the `T` of `InstancePre<T>` connects any
|
||||||
|
// host functions we have in our definition list to the `store` that was
|
||||||
|
// passed in.
|
||||||
|
unsafe {
|
||||||
|
self.ensure_comes_from_same_store(&store)?;
|
||||||
|
Instantiator::new(
|
||||||
|
&mut store,
|
||||||
|
&self.module,
|
||||||
|
ImportSource::Definitions(&self.items),
|
||||||
|
)?
|
||||||
|
.run(store)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn finish(self, store: &mut StoreOpaque) -> Instance {
|
/// Creates a new instance, running the start function asynchronously
|
||||||
Instance::from_wasmtime(self.items, store)
|
/// instead of inline.
|
||||||
|
///
|
||||||
|
/// For more information about asynchronous instantiation see the
|
||||||
|
/// documentation on [`Instance::new_async`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if any import closed over by this [`InstancePre`] isn't owned by
|
||||||
|
/// `store`, or if `store` does not have async support enabled.
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
|
||||||
|
pub async fn instantiate_async(
|
||||||
|
&self,
|
||||||
|
mut store: impl AsContextMut<Data = T>,
|
||||||
|
) -> Result<Instance>
|
||||||
|
where
|
||||||
|
T: Send,
|
||||||
|
{
|
||||||
|
// For the unsafety here see above
|
||||||
|
let mut i = unsafe {
|
||||||
|
let mut store = store.as_context_mut().opaque();
|
||||||
|
self.ensure_comes_from_same_store(&store)?;
|
||||||
|
Instantiator::new(
|
||||||
|
&mut store,
|
||||||
|
&self.module,
|
||||||
|
ImportSource::Definitions(&self.items),
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
i.run_async(store.as_context_mut().opaque_send()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_comes_from_same_store(&self, store: &StoreOpaque<'_>) -> Result<()> {
|
||||||
|
for import in self.items.iter() {
|
||||||
|
if !import.comes_from_same_store(store) {
|
||||||
|
bail!("cross-`Store` instantiation is not currently supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typecheck_externs(store: &mut StoreOpaque, module: &Module, imports: &[Extern]) -> Result<()> {
|
||||||
|
for import in imports {
|
||||||
|
if !import.comes_from_same_store(store) {
|
||||||
|
bail!("cross-`Store` instantiation is not currently supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typecheck(store, module, imports, |cx, ty, item| cx.extern_(ty, item))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typecheck_defs(store: &mut StoreOpaque, module: &Module, imports: &[Definition]) -> Result<()> {
|
||||||
|
for import in imports {
|
||||||
|
if !import.comes_from_same_store(store) {
|
||||||
|
bail!("cross-`Store` instantiation is not currently supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typecheck(store, module, imports, |cx, ty, item| {
|
||||||
|
cx.definition(ty, item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typecheck<I>(
|
||||||
|
store: &mut StoreOpaque,
|
||||||
|
module: &Module,
|
||||||
|
imports: &[I],
|
||||||
|
check: impl Fn(&matching::MatchCx<'_>, &EntityType, &I) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let env_module = module.compiled_module().module();
|
||||||
|
let expected = env_module.imports().count();
|
||||||
|
if expected != imports.len() {
|
||||||
|
bail!("expected {} imports, found {}", expected, imports.len());
|
||||||
|
}
|
||||||
|
let cx = matching::MatchCx {
|
||||||
|
signatures: module.signatures(),
|
||||||
|
types: module.types(),
|
||||||
|
store_data: store.store_data(),
|
||||||
|
engine: store.engine(),
|
||||||
|
};
|
||||||
|
for ((name, field, expected_ty), actual) in env_module.imports().zip(imports) {
|
||||||
|
check(&cx, &expected_ty, actual).with_context(|| {
|
||||||
|
let extra = match field {
|
||||||
|
Some(name) => format!("::{}", name),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
format!("incompatible import type for `{}{}`", name, extra)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ pub use crate::config::*;
|
|||||||
pub use crate::engine::*;
|
pub use crate::engine::*;
|
||||||
pub use crate::externals::*;
|
pub use crate::externals::*;
|
||||||
pub use crate::func::*;
|
pub use crate::func::*;
|
||||||
pub use crate::instance::Instance;
|
pub use crate::instance::{Instance, InstancePre};
|
||||||
pub use crate::limits::*;
|
pub use crate::limits::*;
|
||||||
pub use crate::linker::*;
|
pub use crate::linker::*;
|
||||||
pub use crate::memory::*;
|
pub use crate::memory::*;
|
||||||
@@ -428,6 +428,8 @@ fn _assert_send_sync() {
|
|||||||
_assert::<Linker<()>>();
|
_assert::<Linker<()>>();
|
||||||
_assert::<Linker<*mut u8>>();
|
_assert::<Linker<*mut u8>>();
|
||||||
_assert::<ExternRef>();
|
_assert::<ExternRef>();
|
||||||
|
_assert::<InstancePre<()>>();
|
||||||
|
_assert::<InstancePre<*mut u8>>();
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
fn _call_async(s: &mut Store<()>, f: Func) {
|
fn _call_async(s: &mut Store<()>, f: Func) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::func::HostFunc;
|
use crate::func::HostFunc;
|
||||||
use crate::instance::InstanceBuilder;
|
use crate::instance::InstancePre;
|
||||||
|
use crate::store::StoreOpaque;
|
||||||
use crate::{
|
use crate::{
|
||||||
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
|
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
|
||||||
IntoFunc, Module, Trap, Val,
|
IntoFunc, Module, Trap, Val,
|
||||||
@@ -75,7 +76,7 @@ pub struct Linker<T> {
|
|||||||
engine: Engine,
|
engine: Engine,
|
||||||
string2idx: HashMap<Arc<str>, usize>,
|
string2idx: HashMap<Arc<str>, usize>,
|
||||||
strings: Vec<Arc<str>>,
|
strings: Vec<Arc<str>>,
|
||||||
map: HashMap<ImportKey, Definition<T>>,
|
map: HashMap<ImportKey, Definition>,
|
||||||
allow_shadowing: bool,
|
allow_shadowing: bool,
|
||||||
allow_unknown_exports: bool,
|
allow_unknown_exports: bool,
|
||||||
_marker: marker::PhantomData<fn() -> T>,
|
_marker: marker::PhantomData<fn() -> T>,
|
||||||
@@ -87,12 +88,11 @@ struct ImportKey {
|
|||||||
module: usize,
|
module: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Definition<T> {
|
#[derive(Clone)]
|
||||||
|
pub(crate) enum Definition {
|
||||||
Extern(Extern),
|
Extern(Extern),
|
||||||
HostFunc {
|
HostFunc(Arc<HostFunc>),
|
||||||
func: Arc<HostFunc>,
|
Instance(Arc<indexmap::IndexMap<String, Definition>>),
|
||||||
t: marker::PhantomData<fn() -> T>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! generate_wrap_async_func {
|
macro_rules! generate_wrap_async_func {
|
||||||
@@ -284,13 +284,7 @@ impl<T> Linker<T> {
|
|||||||
) -> Result<&mut Self> {
|
) -> Result<&mut Self> {
|
||||||
let func = HostFunc::new(&self.engine, ty, func);
|
let func = HostFunc::new(&self.engine, ty, func);
|
||||||
let key = self.import_key(module, Some(name));
|
let key = self.import_key(module, Some(name));
|
||||||
self.insert(
|
self.insert(key, Definition::HostFunc(Arc::new(func)))?;
|
||||||
key,
|
|
||||||
Definition::HostFunc {
|
|
||||||
func: Arc::new(func),
|
|
||||||
t: marker::PhantomData,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,13 +388,7 @@ impl<T> Linker<T> {
|
|||||||
) -> Result<&mut Self> {
|
) -> Result<&mut Self> {
|
||||||
let func = HostFunc::wrap(&self.engine, func);
|
let func = HostFunc::wrap(&self.engine, func);
|
||||||
let key = self.import_key(module, Some(name));
|
let key = self.import_key(module, Some(name));
|
||||||
self.insert(
|
self.insert(key, Definition::HostFunc(Arc::new(func)))?;
|
||||||
key,
|
|
||||||
Definition::HostFunc {
|
|
||||||
func: Arc::new(func),
|
|
||||||
t: marker::PhantomData,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,7 +578,10 @@ impl<T> Linker<T> {
|
|||||||
mut store: impl AsContextMut<Data = T>,
|
mut store: impl AsContextMut<Data = T>,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
) -> Result<&mut Self> {
|
) -> Result<&mut Self>
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
match ModuleKind::categorize(module)? {
|
match ModuleKind::categorize(module)? {
|
||||||
ModuleKind::Command => self.command(store, module_name, module),
|
ModuleKind::Command => self.command(store, module_name, module),
|
||||||
ModuleKind::Reactor => {
|
ModuleKind::Reactor => {
|
||||||
@@ -614,18 +605,20 @@ impl<T> Linker<T> {
|
|||||||
mut store: impl AsContextMut<Data = T>,
|
mut store: impl AsContextMut<Data = T>,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
) -> Result<&mut Self> {
|
) -> Result<&mut Self>
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
for export in module.exports() {
|
for export in module.exports() {
|
||||||
if let Some(func_ty) = export.ty().func() {
|
if let Some(func_ty) = export.ty().func() {
|
||||||
let imports = self.compute_imports(&mut store, module)?;
|
let instance_pre = self.instantiate_pre(&mut store, module)?;
|
||||||
let module = module.clone();
|
|
||||||
let export_name = export.name().to_owned();
|
let export_name = export.name().to_owned();
|
||||||
let func = Func::new(
|
let func = Func::new(
|
||||||
&mut store,
|
&mut store,
|
||||||
func_ty.clone(),
|
func_ty.clone(),
|
||||||
move |mut caller, params, results| {
|
move |mut caller, params, results| {
|
||||||
// Create a new instance for this command execution.
|
// Create a new instance for this command execution.
|
||||||
let instance = Instance::new(&mut caller, &module, &imports)?;
|
let instance = instance_pre.instantiate(&mut caller)?;
|
||||||
|
|
||||||
// `unwrap()` everything here because we know the instance contains a
|
// `unwrap()` everything here because we know the instance contains a
|
||||||
// function export with the given name and signature because we're
|
// function export with the given name and signature because we're
|
||||||
@@ -739,7 +732,7 @@ impl<T> Linker<T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, key: ImportKey, item: Definition<T>) -> Result<()> {
|
fn insert(&mut self, key: ImportKey, item: Definition) -> Result<()> {
|
||||||
match self.map.entry(key) {
|
match self.map.entry(key) {
|
||||||
Entry::Occupied(_) if !self.allow_shadowing => {
|
Entry::Occupied(_) if !self.allow_shadowing => {
|
||||||
let module = &self.strings[key.module];
|
let module = &self.strings[key.module];
|
||||||
@@ -831,8 +824,7 @@ impl<T> Linker<T> {
|
|||||||
mut store: impl AsContextMut<Data = T>,
|
mut store: impl AsContextMut<Data = T>,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
) -> Result<Instance> {
|
) -> Result<Instance> {
|
||||||
let imports = self.compute_imports(&mut store, module)?;
|
self.instantiate_pre(&mut store, module)?.instantiate(store)
|
||||||
Instance::new(store, module, &imports)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to instantiate the `module` provided. This is the same as
|
/// Attempts to instantiate the `module` provided. This is the same as
|
||||||
@@ -847,22 +839,72 @@ impl<T> Linker<T> {
|
|||||||
where
|
where
|
||||||
T: Send,
|
T: Send,
|
||||||
{
|
{
|
||||||
let imports = self.compute_imports(&mut store, module)?;
|
self.instantiate_pre(&mut store, module)?
|
||||||
Instance::new_async(store, module, &imports).await
|
.instantiate_async(store)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_imports(
|
/// Performs all checks necessary for instantiating `module` with this
|
||||||
|
/// linker within `store`, except that instantiation doesn't actually
|
||||||
|
/// finish.
|
||||||
|
///
|
||||||
|
/// This method is used for front-loading type-checking information as well
|
||||||
|
/// as collecting the imports to use to instantiate a module with. The
|
||||||
|
/// returned [`InstancePre`] represents a ready-to-be-instantiated module,
|
||||||
|
/// which can also be instantiated multiple times if desired.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This method will panic if any item defined in this linker used by
|
||||||
|
/// `module` is not owned by `store`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use wasmtime::*;
|
||||||
|
/// # fn main() -> anyhow::Result<()> {
|
||||||
|
/// # let engine = Engine::default();
|
||||||
|
/// # let mut store = Store::new(&engine, ());
|
||||||
|
/// let mut linker = Linker::new(&engine);
|
||||||
|
/// linker.func_wrap("host", "double", |x: i32| x * 2)?;
|
||||||
|
///
|
||||||
|
/// let wat = r#"
|
||||||
|
/// (module
|
||||||
|
/// (import "host" "double" (func (param i32) (result i32)))
|
||||||
|
/// )
|
||||||
|
/// "#;
|
||||||
|
/// let module = Module::new(&engine, wat)?;
|
||||||
|
/// let instance_pre = linker.instantiate_pre(&mut store, &module)?;
|
||||||
|
///
|
||||||
|
/// // Finish instantiation after the type-checking has all completed...
|
||||||
|
/// let instance = instance_pre.instantiate(&mut store)?;
|
||||||
|
///
|
||||||
|
/// // ... and we can even continue to keep instantiating if desired!
|
||||||
|
/// instance_pre.instantiate(&mut store)?;
|
||||||
|
/// instance_pre.instantiate(&mut store)?;
|
||||||
|
///
|
||||||
|
/// // Note that functions defined in a linker with `func_wrap` and similar
|
||||||
|
/// // constructors are not owned by any particular `Store`, so we can also
|
||||||
|
/// // instantiate our `instance_pre` in other stores because no imports
|
||||||
|
/// // belong to the original store.
|
||||||
|
/// let mut new_store = Store::new(&engine, ());
|
||||||
|
/// instance_pre.instantiate(&mut new_store)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn instantiate_pre(
|
||||||
&self,
|
&self,
|
||||||
mut store: impl AsContextMut<Data = T>,
|
mut store: impl AsContextMut<Data = T>,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
) -> Result<Vec<Extern>> {
|
) -> Result<InstancePre<T>> {
|
||||||
module
|
let imports = module
|
||||||
.imports()
|
.imports()
|
||||||
.map(|import| {
|
.map(|import| {
|
||||||
self.get_by_import(&mut store, &import)
|
self._get_by_import(&import)
|
||||||
.ok_or_else(|| self.link_error(&import))
|
.ok_or_else(|| self.link_error(&import))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect::<Result<_>>()?;
|
||||||
|
unsafe { InstancePre::new(&mut store.as_context_mut().opaque(), module, imports) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_error(&self, import: &ImportType) -> Error {
|
fn link_error(&self, import: &ImportType) -> Error {
|
||||||
@@ -887,10 +929,12 @@ impl<T> Linker<T> {
|
|||||||
mut store: impl AsContextMut<Data = T> + 'p,
|
mut store: impl AsContextMut<Data = T> + 'p,
|
||||||
) -> impl Iterator<Item = (&str, &str, Extern)> + 'p {
|
) -> impl Iterator<Item = (&str, &str, Extern)> + 'p {
|
||||||
self.map.iter().map(move |(key, item)| {
|
self.map.iter().map(move |(key, item)| {
|
||||||
|
let mut store = store.as_context_mut().opaque();
|
||||||
(
|
(
|
||||||
&*self.strings[key.module],
|
&*self.strings[key.module],
|
||||||
&*self.strings[key.name],
|
&*self.strings[key.name],
|
||||||
item.to_extern(&mut store),
|
// Should be safe since `T` is connecting the linker and store
|
||||||
|
unsafe { item.to_extern(&mut store) },
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -902,10 +946,16 @@ impl<T> Linker<T> {
|
|||||||
/// [`Linker`].
|
/// [`Linker`].
|
||||||
pub fn get(
|
pub fn get(
|
||||||
&self,
|
&self,
|
||||||
store: impl AsContextMut<Data = T>,
|
mut store: impl AsContextMut<Data = T>,
|
||||||
module: &str,
|
module: &str,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> Option<Extern> {
|
) -> Option<Extern> {
|
||||||
|
let mut store = store.as_context_mut().opaque();
|
||||||
|
// Should be safe since `T` is connecting the linker and store
|
||||||
|
Some(unsafe { self._get(module, name)?.to_extern(&mut store) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _get(&self, module: &str, name: Option<&str>) -> Option<&Definition> {
|
||||||
let key = ImportKey {
|
let key = ImportKey {
|
||||||
module: *self.string2idx.get(module)?,
|
module: *self.string2idx.get(module)?,
|
||||||
name: match name {
|
name: match name {
|
||||||
@@ -913,7 +963,7 @@ impl<T> Linker<T> {
|
|||||||
None => usize::max_value(),
|
None => usize::max_value(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Some(self.map.get(&key)?.to_extern(store))
|
self.map.get(&key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Looks up a value in this `Linker` which matches the `import` type
|
/// Looks up a value in this `Linker` which matches the `import` type
|
||||||
@@ -925,8 +975,14 @@ impl<T> Linker<T> {
|
|||||||
mut store: impl AsContextMut<Data = T>,
|
mut store: impl AsContextMut<Data = T>,
|
||||||
import: &ImportType,
|
import: &ImportType,
|
||||||
) -> Option<Extern> {
|
) -> Option<Extern> {
|
||||||
if let Some(item) = self.get(&mut store, import.module(), import.name()) {
|
let mut store = store.as_context_mut().opaque();
|
||||||
return Some(item);
|
// Should be safe since `T` is connecting the linker and store
|
||||||
|
Some(unsafe { self._get_by_import(import)?.to_extern(&mut store) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _get_by_import(&self, import: &ImportType) -> Option<Definition> {
|
||||||
|
if let Some(item) = self._get(import.module(), import.name()) {
|
||||||
|
return Some(item.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if import.name().is_some() {
|
if import.name().is_some() {
|
||||||
@@ -949,12 +1005,12 @@ impl<T> Linker<T> {
|
|||||||
// it each time a module is instantiated. For now though while the
|
// it each time a module is instantiated. For now though while the
|
||||||
// module linking proposal is under development this should hopefully
|
// module linking proposal is under development this should hopefully
|
||||||
// suffice.
|
// suffice.
|
||||||
let mut builder = InstanceBuilder::new();
|
let mut map = indexmap::IndexMap::new();
|
||||||
for export in t.exports() {
|
for export in t.exports() {
|
||||||
let item = self.get(&mut store, import.module(), Some(export.name()))?;
|
let item = self._get(import.module(), Some(export.name()))?;
|
||||||
builder.insert(export.name(), item);
|
map.insert(export.name().to_string(), item.clone());
|
||||||
}
|
}
|
||||||
return Some(builder.finish(&mut store.as_context_mut().opaque()).into());
|
return Some(Definition::Instance(Arc::new(map)));
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
@@ -999,31 +1055,30 @@ impl<T> Default for Linker<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Definition<T> {
|
impl Definition {
|
||||||
fn to_extern(&self, mut store: impl AsContextMut<Data = T>) -> Extern {
|
/// Note the unsafety here is due to calling `HostFunc::to_func`. The
|
||||||
|
/// requirement here is that the `T` that was originally used to create the
|
||||||
|
/// `HostFunc` matches the `T` on the store.
|
||||||
|
pub(crate) unsafe fn to_extern(&self, store: &mut StoreOpaque) -> Extern {
|
||||||
match self {
|
match self {
|
||||||
Definition::Extern(e) => e.clone(),
|
Definition::Extern(e) => e.clone(),
|
||||||
|
Definition::HostFunc(func) => func.to_func(store).into(),
|
||||||
// Note the unsafety here is due to calling `to_func`. The
|
Definition::Instance(i) => {
|
||||||
// requirement here is that the `T` that was originally used to
|
let items = Arc::new(
|
||||||
// create the `HostFunc` matches the `T` on the store. This is done
|
i.iter()
|
||||||
// with the `T` on `Definition` (and `Linker`) as well as the
|
.map(|(name, item)| (name.clone(), item.to_extern(store)))
|
||||||
// `Data=T` bound above.
|
.collect(),
|
||||||
Definition::HostFunc { func, .. } => unsafe {
|
);
|
||||||
func.to_func(&mut store.as_context_mut().opaque()).into()
|
Instance::from_wasmtime(items, store).into()
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Clone for Definition<T> {
|
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
|
||||||
fn clone(&self) -> Definition<T> {
|
|
||||||
match self {
|
match self {
|
||||||
Definition::Extern(e) => Definition::Extern(e.clone()),
|
Definition::Extern(e) => e.comes_from_same_store(store),
|
||||||
Definition::HostFunc { func, t } => Definition::HostFunc {
|
Definition::HostFunc(_func) => true,
|
||||||
func: func.clone(),
|
Definition::Instance(i) => i.values().all(|e| e.comes_from_same_store(store)),
|
||||||
t: *t,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -575,7 +575,7 @@ impl<T: ?Sized> StoreInner<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn async_support(&self) -> bool {
|
pub fn async_support(&self) -> bool {
|
||||||
self.engine().config().async_support
|
cfg!(feature = "async") && self.engine().config().async_support
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn engine(&self) -> &Engine {
|
pub fn engine(&self) -> &Engine {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::linker::Definition;
|
||||||
use crate::store::StoreData;
|
use crate::store::StoreData;
|
||||||
use crate::{signatures::SignatureCollection, Engine, Extern};
|
use crate::{signatures::SignatureCollection, Engine, Extern};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
@@ -5,6 +6,7 @@ use wasmtime_environ::wasm::{
|
|||||||
EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table,
|
EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table,
|
||||||
};
|
};
|
||||||
use wasmtime_jit::TypeTables;
|
use wasmtime_jit::TypeTables;
|
||||||
|
use wasmtime_runtime::VMSharedSignatureIndex;
|
||||||
|
|
||||||
pub struct MatchCx<'a> {
|
pub struct MatchCx<'a> {
|
||||||
pub signatures: &'a SignatureCollection,
|
pub signatures: &'a SignatureCollection,
|
||||||
@@ -73,8 +75,24 @@ impl MatchCx<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> Result<()> {
|
pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> Result<()> {
|
||||||
|
self.vmshared_signature_index(expected, actual.sig_index(self.store_data))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn host_func(
|
||||||
|
&self,
|
||||||
|
expected: SignatureIndex,
|
||||||
|
actual: &crate::func::HostFunc,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.vmshared_signature_index(expected, actual.sig_index())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vmshared_signature_index(
|
||||||
|
&self,
|
||||||
|
expected: SignatureIndex,
|
||||||
|
actual: VMSharedSignatureIndex,
|
||||||
|
) -> Result<()> {
|
||||||
let matches = match self.signatures.shared_signature(expected) {
|
let matches = match self.signatures.shared_signature(expected) {
|
||||||
Some(idx) => actual.sig_index(self.store_data) == idx,
|
Some(idx) => actual == idx,
|
||||||
// If our expected signature isn't registered, then there's no way
|
// If our expected signature isn't registered, then there's no way
|
||||||
// that `actual` can match it.
|
// that `actual` can match it.
|
||||||
None => false,
|
None => false,
|
||||||
@@ -295,4 +313,44 @@ impl MatchCx<'_> {
|
|||||||
EntityType::Event(_) => unimplemented!(),
|
EntityType::Event(_) => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates that the `expected` type matches the type of `actual`
|
||||||
|
pub(crate) fn definition(&self, expected: &EntityType, actual: &Definition) -> Result<()> {
|
||||||
|
match actual {
|
||||||
|
Definition::Extern(e) => self.extern_(expected, e),
|
||||||
|
Definition::HostFunc(f) => match expected {
|
||||||
|
EntityType::Function(expected) => self.host_func(*expected, f),
|
||||||
|
_ => bail!("expected {}, but found func", entity_desc(expected)),
|
||||||
|
},
|
||||||
|
Definition::Instance(items) => match expected {
|
||||||
|
EntityType::Instance(expected) => {
|
||||||
|
for (name, expected) in self.types.instance_signatures[*expected].exports.iter()
|
||||||
|
{
|
||||||
|
match items.get(name) {
|
||||||
|
Some(item) => {
|
||||||
|
self.definition(expected, item).with_context(|| {
|
||||||
|
format!("instance export {:?} incompatible", name)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
None => bail!("instance type missing export {:?}", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => bail!("expected {}, but found instance", entity_desc(expected)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entity_desc(ty: &EntityType) -> &'static str {
|
||||||
|
match ty {
|
||||||
|
EntityType::Global(_) => "global",
|
||||||
|
EntityType::Table(_) => "table",
|
||||||
|
EntityType::Memory(_) => "memory",
|
||||||
|
EntityType::Function(_) => "func",
|
||||||
|
EntityType::Instance(_) => "instance",
|
||||||
|
EntityType::Module(_) => "module",
|
||||||
|
EntityType::Event(_) => "event",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,3 +308,35 @@ fn alias_one() -> Result<()> {
|
|||||||
assert!(linker.get(&mut store, "c", Some("d")).is_some());
|
assert!(linker.get(&mut store, "c", Some("d")).is_some());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instance_pre() -> Result<()> {
|
||||||
|
let engine = Engine::default();
|
||||||
|
let mut linker = Linker::new(&engine);
|
||||||
|
linker.func_wrap("", "", || {})?;
|
||||||
|
|
||||||
|
let module = Module::new(&engine, r#"(module (import "" "" (func)))"#)?;
|
||||||
|
let instance_pre = linker.instantiate_pre(&mut Store::new(&engine, ()), &module)?;
|
||||||
|
instance_pre.instantiate(&mut Store::new(&engine, ()))?;
|
||||||
|
instance_pre.instantiate(&mut Store::new(&engine, ()))?;
|
||||||
|
|
||||||
|
let mut store = Store::new(&engine, ());
|
||||||
|
let global = Global::new(
|
||||||
|
&mut store,
|
||||||
|
GlobalType::new(ValType::I32, Mutability::Const),
|
||||||
|
1.into(),
|
||||||
|
)?;
|
||||||
|
linker.define("", "g", global)?;
|
||||||
|
|
||||||
|
let module = Module::new(
|
||||||
|
&engine,
|
||||||
|
r#"(module
|
||||||
|
(import "" "" (func))
|
||||||
|
(import "" "g" (global i32))
|
||||||
|
)"#,
|
||||||
|
)?;
|
||||||
|
let instance_pre = linker.instantiate_pre(&mut store, &module)?;
|
||||||
|
instance_pre.instantiate(&mut store)?;
|
||||||
|
instance_pre.instantiate(&mut store)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user