Remove the module linking implementation in Wasmtime (#3958)
* Remove the module linking implementation in Wasmtime This commit removes the experimental implementation of the module linking WebAssembly proposal from Wasmtime. The module linking is no longer intended for core WebAssembly but is instead incorporated into the component model now at this point. This means that very large parts of Wasmtime's implementation of module linking are no longer applicable and would change greatly with an implementation of the component model. The main purpose of this is to remove Wasmtime's reliance on the support for module-linking in `wasmparser` and tooling crates. With this reliance removed we can move over to the `component-model` branch of `wasmparser` and use the updated support for the component model. Additionally given the trajectory of the component model proposal the embedding API of Wasmtime will not look like what it looks like today for WebAssembly. For example the core wasm `Instance` will not change and instead a `Component` is likely to be added instead. Some more rationale for this is in #3941, but the basic idea is that I feel that it's not going to be viable to develop support for the component model on a non-`main` branch of Wasmtime. Additionaly I don't think it's viable, for the same reasons as `wasm-tools`, to support the old module linking proposal and the new component model at the same time. This commit takes a moment to not only delete the existing module linking implementation but some abstractions are also simplified. For example module serialization is a bit simpler that there's only one module. Additionally instantiation is much simpler since the only initializer we have to deal with are imports and nothing else. Closes #3941 * Fix doc link * Update comments
This commit is contained in:
@@ -185,14 +185,6 @@ WASMTIME_CONFIG_PROP(void, wasm_multi_value, bool)
|
||||
*/
|
||||
WASMTIME_CONFIG_PROP(void, wasm_multi_memory, bool)
|
||||
|
||||
/**
|
||||
* \brief Configures whether the WebAssembly module linking proposal is
|
||||
* enabled.
|
||||
*
|
||||
* This setting is `false` by default.
|
||||
*/
|
||||
WASMTIME_CONFIG_PROP(void, wasm_module_linking, bool)
|
||||
|
||||
/**
|
||||
* \brief Configures whether the WebAssembly memory64 proposal is
|
||||
* enabled.
|
||||
|
||||
@@ -56,20 +56,6 @@ typedef struct wasmtime_memory {
|
||||
size_t index;
|
||||
} wasmtime_memory_t;
|
||||
|
||||
/// \brief Representation of a instance in Wasmtime.
|
||||
///
|
||||
/// Instances are represented with a 64-bit identifying integer in Wasmtime.
|
||||
/// They do not have any destructor associated with them. Instances cannot
|
||||
/// interoperate between #wasmtime_store_t instances and if the wrong instance
|
||||
/// is passed to the wrong store then it may trigger an assertion to abort the
|
||||
/// process.
|
||||
typedef struct wasmtime_instance {
|
||||
/// Internal identifier of what store this belongs to, never zero.
|
||||
uint64_t store_id;
|
||||
/// Internal index within the store.
|
||||
size_t index;
|
||||
} wasmtime_instance_t;
|
||||
|
||||
/// \brief Representation of a global in Wasmtime.
|
||||
///
|
||||
/// Globals are represented with a 64-bit identifying integer in Wasmtime.
|
||||
@@ -99,12 +85,6 @@ typedef uint8_t wasmtime_extern_kind_t;
|
||||
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
|
||||
/// memory
|
||||
#define WASMTIME_EXTERN_MEMORY 3
|
||||
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is
|
||||
/// an instance
|
||||
#define WASMTIME_EXTERN_INSTANCE 4
|
||||
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is
|
||||
/// a module
|
||||
#define WASMTIME_EXTERN_MODULE 5
|
||||
|
||||
/**
|
||||
* \typedef wasmtime_extern_union_t
|
||||
@@ -125,13 +105,6 @@ typedef union wasmtime_extern_union {
|
||||
wasmtime_table_t table;
|
||||
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY
|
||||
wasmtime_memory_t memory;
|
||||
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_INSTANCE
|
||||
wasmtime_instance_t instance;
|
||||
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MODULE
|
||||
///
|
||||
/// Note that this may be an owned pointer depending on the ownership of the
|
||||
/// #wasmtime_extern_t container value.
|
||||
wasmtime_module_t *module;
|
||||
} wasmtime_extern_union_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,39 +16,19 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief An opaque object representing the type of an instance.
|
||||
*/
|
||||
typedef struct wasmtime_instancetype wasmtime_instancetype_t;
|
||||
|
||||
/// \brief Deletes an instance type
|
||||
WASM_API_EXTERN void wasmtime_instancetype_delete(wasmtime_instancetype_t *ty);
|
||||
|
||||
/**
|
||||
* \brief Returns the list of exports that this instance type provides.
|
||||
*
|
||||
* This function does not take ownership of the provided instance type but
|
||||
* ownership of `out` is passed to the caller. Note that `out` is treated as
|
||||
* uninitialized when passed to this function.
|
||||
*/
|
||||
WASM_API_EXTERN void wasmtime_instancetype_exports(const wasmtime_instancetype_t*, wasm_exporttype_vec_t* out);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasmtime_instancetype_t to a #wasm_externtype_t
|
||||
*
|
||||
* The returned value is owned by the #wasmtime_instancetype_t argument and should not
|
||||
* be deleted.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_externtype_t* wasmtime_instancetype_as_externtype(wasmtime_instancetype_t*);
|
||||
|
||||
/**
|
||||
* \brief Attempts to convert a #wasm_externtype_t to a #wasmtime_instancetype_t
|
||||
*
|
||||
* The returned value is owned by the #wasmtime_instancetype_t argument and should not
|
||||
* be deleted. Returns `NULL` if the provided argument is not a
|
||||
* #wasmtime_instancetype_t.
|
||||
*/
|
||||
WASM_API_EXTERN wasmtime_instancetype_t* wasmtime_externtype_as_instancetype(wasm_externtype_t*);
|
||||
/// \brief Representation of a instance in Wasmtime.
|
||||
///
|
||||
/// Instances are represented with a 64-bit identifying integer in Wasmtime.
|
||||
/// They do not have any destructor associated with them. Instances cannot
|
||||
/// interoperate between #wasmtime_store_t instances and if the wrong instance
|
||||
/// is passed to the wrong store then it may trigger an assertion to abort the
|
||||
/// process.
|
||||
typedef struct wasmtime_instance {
|
||||
/// Internal identifier of what store this belongs to, never zero.
|
||||
uint64_t store_id;
|
||||
/// Internal index within the store.
|
||||
size_t index;
|
||||
} wasmtime_instance_t;
|
||||
|
||||
/**
|
||||
* \brief Instantiate a wasm module.
|
||||
@@ -91,16 +71,6 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new(
|
||||
wasm_trap_t **trap
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Returns the type of the specified instance.
|
||||
*
|
||||
* The returned type is owned by the caller.
|
||||
*/
|
||||
WASM_API_EXTERN wasmtime_instancetype_t *wasmtime_instance_type(
|
||||
const wasmtime_context_t *store,
|
||||
const wasmtime_instance_t *instance
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Get an export by name from an instance.
|
||||
*
|
||||
|
||||
@@ -14,51 +14,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief An opaque object representing the type of a module.
|
||||
*/
|
||||
typedef struct wasmtime_moduletype wasmtime_moduletype_t;
|
||||
|
||||
/**
|
||||
* \brief Deletes a module type.
|
||||
*/
|
||||
WASM_API_EXTERN void wasmtime_moduletype_delete(wasmtime_moduletype_t *ty);
|
||||
|
||||
/**
|
||||
* \brief Returns the list of imports that this module type requires.
|
||||
*
|
||||
* This function does not take ownership of the provided module type but
|
||||
* ownership of `out` is passed to the caller. Note that `out` is treated as
|
||||
* uninitialized when passed to this function.
|
||||
*/
|
||||
WASM_API_EXTERN void wasmtime_moduletype_imports(const wasmtime_moduletype_t*, wasm_importtype_vec_t* out);
|
||||
|
||||
/**
|
||||
* \brief Returns the list of exports that this module type provides.
|
||||
*
|
||||
* This function does not take ownership of the provided module type but
|
||||
* ownership of `out` is passed to the caller. Note that `out` is treated as
|
||||
* uninitialized when passed to this function.
|
||||
*/
|
||||
WASM_API_EXTERN void wasmtime_moduletype_exports(const wasmtime_moduletype_t*, wasm_exporttype_vec_t* out);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasmtime_moduletype_t to a #wasm_externtype_t
|
||||
*
|
||||
* The returned value is owned by the #wasmtime_moduletype_t argument and should not
|
||||
* be deleted.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_externtype_t* wasmtime_moduletype_as_externtype(wasmtime_moduletype_t*);
|
||||
|
||||
/**
|
||||
* \brief Attempts to convert a #wasm_externtype_t to a #wasmtime_moduletype_t
|
||||
*
|
||||
* The returned value is owned by the #wasmtime_moduletype_t argument and
|
||||
* should not be deleted. Returns `NULL` if the provided argument is not a
|
||||
* #wasmtime_moduletype_t.
|
||||
*/
|
||||
WASM_API_EXTERN wasmtime_moduletype_t* wasmtime_externtype_as_moduletype(wasm_externtype_t*);
|
||||
|
||||
/**
|
||||
* \typedef wasmtime_module_t
|
||||
* \brief Convenience alias for #wasmtime_module
|
||||
@@ -122,14 +77,6 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate(
|
||||
size_t wasm_len
|
||||
);
|
||||
|
||||
/**
|
||||
* \brief Returns the type of this module.
|
||||
*
|
||||
* The returned #wasmtime_moduletype_t is expected to be deallocated by the
|
||||
* caller.
|
||||
*/
|
||||
WASM_API_EXTERN wasmtime_moduletype_t* wasmtime_module_type(const wasmtime_module_t*);
|
||||
|
||||
/**
|
||||
* \brief This function serializes compiled module artifacts as blob data.
|
||||
*
|
||||
|
||||
@@ -94,11 +94,6 @@ pub extern "C" fn wasmtime_config_wasm_multi_memory_set(c: &mut wasm_config_t, e
|
||||
c.config.wasm_multi_memory(enable);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_config_wasm_module_linking_set(c: &mut wasm_config_t, enable: bool) {
|
||||
c.config.wasm_module_linking(enable);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_config_wasm_memory64_set(c: &mut wasm_config_t, enable: bool) {
|
||||
c.config.wasm_memory64(enable);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
wasm_externkind_t, wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_instance_t,
|
||||
wasm_memory_t, wasm_module_t, wasm_table_t, wasmtime_module_t, CStoreContext, StoreRef,
|
||||
wasm_externkind_t, wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_memory_t, wasm_table_t,
|
||||
CStoreContext, StoreRef,
|
||||
};
|
||||
use std::mem::ManuallyDrop;
|
||||
use wasmtime::{Extern, Func, Global, Instance, Memory, Table};
|
||||
use wasmtime::{Extern, Func, Global, Memory, Table};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_extern_t {
|
||||
@@ -20,8 +20,6 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t {
|
||||
Extern::Global(_) => crate::WASM_EXTERN_GLOBAL,
|
||||
Extern::Table(_) => crate::WASM_EXTERN_TABLE,
|
||||
Extern::Memory(_) => crate::WASM_EXTERN_MEMORY,
|
||||
Extern::Instance(_) => crate::WASM_EXTERN_INSTANCE,
|
||||
Extern::Module(_) => crate::WASM_EXTERN_MODULE,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,26 +68,6 @@ pub extern "C" fn wasm_extern_as_memory_const(e: &wasm_extern_t) -> Option<&wasm
|
||||
wasm_extern_as_memory(e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_extern_as_module(e: &wasm_extern_t) -> Option<&wasm_module_t> {
|
||||
wasm_module_t::try_from(e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_extern_as_module_const(e: &wasm_extern_t) -> Option<&wasm_module_t> {
|
||||
wasm_extern_as_module(e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_extern_as_instance(e: &wasm_extern_t) -> Option<&wasm_instance_t> {
|
||||
wasm_instance_t::try_from(e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_extern_as_instance_const(e: &wasm_extern_t) -> Option<&wasm_instance_t> {
|
||||
wasm_extern_as_instance(e)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasmtime_extern_t {
|
||||
pub kind: wasmtime_extern_kind_t,
|
||||
@@ -101,17 +79,13 @@ pub const WASMTIME_EXTERN_FUNC: wasmtime_extern_kind_t = 0;
|
||||
pub const WASMTIME_EXTERN_GLOBAL: wasmtime_extern_kind_t = 1;
|
||||
pub const WASMTIME_EXTERN_TABLE: wasmtime_extern_kind_t = 2;
|
||||
pub const WASMTIME_EXTERN_MEMORY: wasmtime_extern_kind_t = 3;
|
||||
pub const WASMTIME_EXTERN_INSTANCE: wasmtime_extern_kind_t = 4;
|
||||
pub const WASMTIME_EXTERN_MODULE: wasmtime_extern_kind_t = 5;
|
||||
|
||||
#[repr(C)]
|
||||
pub union wasmtime_extern_union {
|
||||
pub func: Func,
|
||||
pub table: Table,
|
||||
pub global: Global,
|
||||
pub instance: Instance,
|
||||
pub memory: Memory,
|
||||
pub module: ManuallyDrop<Box<wasmtime_module_t>>,
|
||||
}
|
||||
|
||||
impl wasmtime_extern_t {
|
||||
@@ -121,8 +95,6 @@ impl wasmtime_extern_t {
|
||||
WASMTIME_EXTERN_GLOBAL => Extern::Global(self.of.global),
|
||||
WASMTIME_EXTERN_TABLE => Extern::Table(self.of.table),
|
||||
WASMTIME_EXTERN_MEMORY => Extern::Memory(self.of.memory),
|
||||
WASMTIME_EXTERN_INSTANCE => Extern::Instance(self.of.instance),
|
||||
WASMTIME_EXTERN_MODULE => Extern::Module(self.of.module.module.clone()),
|
||||
other => panic!("unknown wasm_extern_kind_t: {}", other),
|
||||
}
|
||||
}
|
||||
@@ -147,26 +119,6 @@ impl From<Extern> for wasmtime_extern_t {
|
||||
kind: WASMTIME_EXTERN_MEMORY,
|
||||
of: wasmtime_extern_union { memory },
|
||||
},
|
||||
Extern::Instance(instance) => wasmtime_extern_t {
|
||||
kind: WASMTIME_EXTERN_INSTANCE,
|
||||
of: wasmtime_extern_union { instance },
|
||||
},
|
||||
Extern::Module(module) => wasmtime_extern_t {
|
||||
kind: WASMTIME_EXTERN_MODULE,
|
||||
of: wasmtime_extern_union {
|
||||
module: ManuallyDrop::new(Box::new(wasmtime_module_t { module })),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for wasmtime_extern_t {
|
||||
fn drop(&mut self) {
|
||||
if self.kind == WASMTIME_EXTERN_MODULE {
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.of.module);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,21 @@
|
||||
use crate::{
|
||||
wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_store_t, wasm_trap_t, wasmtime_error_t,
|
||||
wasmtime_extern_t, wasmtime_instancetype_t, wasmtime_module_t, CStoreContext, CStoreContextMut,
|
||||
StoreRef,
|
||||
wasmtime_extern_t, wasmtime_module_t, CStoreContextMut, StoreRef,
|
||||
};
|
||||
use std::mem::MaybeUninit;
|
||||
use wasmtime::{Extern, Instance, Trap};
|
||||
use wasmtime::{Instance, Trap};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct wasm_instance_t {
|
||||
ext: wasm_extern_t,
|
||||
store: StoreRef,
|
||||
instance: Instance,
|
||||
}
|
||||
|
||||
wasmtime_c_api_macros::declare_ref!(wasm_instance_t);
|
||||
|
||||
impl wasm_instance_t {
|
||||
pub(crate) fn new(store: StoreRef, instance: Instance) -> wasm_instance_t {
|
||||
wasm_instance_t {
|
||||
ext: wasm_extern_t {
|
||||
store: store,
|
||||
which: instance.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_instance_t> {
|
||||
match &e.which {
|
||||
Extern::Instance(_) => Some(unsafe { &*(e as *const _ as *const _) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn instance(&self) -> Instance {
|
||||
match self.ext.which {
|
||||
Extern::Instance(i) => i,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
wasm_instance_t { store, instance }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +34,7 @@ pub unsafe extern "C" fn wasm_instance_new(
|
||||
None => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
match Instance::new(store.store.context_mut(), wasm_module.module(), &imports) {
|
||||
match Instance::new(store.store.context_mut(), &wasm_module.module, &imports) {
|
||||
Ok(instance) => Some(Box::new(wasm_instance_t::new(
|
||||
store.store.clone(),
|
||||
instance,
|
||||
@@ -68,21 +48,16 @@ pub unsafe extern "C" fn wasm_instance_new(
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_instance_as_extern(m: &wasm_instance_t) -> &wasm_extern_t {
|
||||
&m.ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasm_instance_exports(
|
||||
instance: &mut wasm_instance_t,
|
||||
out: &mut wasm_extern_vec_t,
|
||||
) {
|
||||
let store = instance.ext.store.clone();
|
||||
let store = instance.store.clone();
|
||||
out.set_buffer(
|
||||
instance
|
||||
.instance()
|
||||
.exports(instance.ext.store.context_mut())
|
||||
.instance
|
||||
.exports(instance.store.context_mut())
|
||||
.map(|e| {
|
||||
Some(Box::new(wasm_extern_t {
|
||||
which: e.into_extern(),
|
||||
@@ -133,14 +108,6 @@ pub(crate) fn handle_instantiate(
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_instance_type(
|
||||
store: CStoreContext<'_>,
|
||||
instance: &Instance,
|
||||
) -> Box<wasmtime_instancetype_t> {
|
||||
Box::new(wasmtime_instancetype_t::new(instance.ty(store)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wasmtime_instance_export_get(
|
||||
store: CStoreContextMut<'_>,
|
||||
|
||||
@@ -173,13 +173,9 @@ pub unsafe extern "C" fn wasmtime_linker_get(
|
||||
Ok(s) => s,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let name = if name.is_null() {
|
||||
None
|
||||
} else {
|
||||
match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) {
|
||||
Ok(s) => Some(s),
|
||||
Err(_) => return false,
|
||||
}
|
||||
let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return false,
|
||||
};
|
||||
match linker.get(store, module, name) {
|
||||
Some(which) => {
|
||||
|
||||
@@ -1,43 +1,22 @@
|
||||
use crate::{
|
||||
handle_result, wasm_byte_vec_t, wasm_engine_t, wasm_exporttype_t, wasm_exporttype_vec_t,
|
||||
wasm_extern_t, wasm_importtype_t, wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t,
|
||||
wasmtime_moduletype_t, StoreRef,
|
||||
wasm_importtype_t, wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
use wasmtime::{Engine, Extern, Module};
|
||||
use wasmtime::{Engine, Module};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct wasm_module_t {
|
||||
ext: wasm_extern_t,
|
||||
pub(crate) module: Module,
|
||||
}
|
||||
|
||||
wasmtime_c_api_macros::declare_ref!(wasm_module_t);
|
||||
|
||||
impl wasm_module_t {
|
||||
pub(crate) fn new(store: StoreRef, module: Module) -> wasm_module_t {
|
||||
wasm_module_t {
|
||||
ext: wasm_extern_t {
|
||||
store: store,
|
||||
which: module.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_module_t> {
|
||||
match &e.which {
|
||||
Extern::Module(_) => Some(unsafe { &*(e as *const _ as *const _) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn module(&self) -> &Module {
|
||||
match &self.ext.which {
|
||||
Extern::Module(i) => i,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
pub(crate) fn new(module: Module) -> wasm_module_t {
|
||||
wasm_module_t { module }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +34,7 @@ pub unsafe extern "C" fn wasm_module_new(
|
||||
binary: &wasm_byte_vec_t,
|
||||
) -> Option<Box<wasm_module_t>> {
|
||||
match Module::from_binary(store.store.context().engine(), binary.as_slice()) {
|
||||
Ok(module) => Some(Box::new(wasm_module_t::new(store.store.clone(), module))),
|
||||
Ok(module) => Some(Box::new(wasm_module_t::new(module))),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
@@ -68,15 +47,10 @@ pub unsafe extern "C" fn wasm_module_validate(
|
||||
Module::validate(store.store.context().engine(), binary.as_slice()).is_ok()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_as_extern(m: &wasm_module_t) -> &wasm_extern_t {
|
||||
&m.ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_exports(module: &wasm_module_t, out: &mut wasm_exporttype_vec_t) {
|
||||
let exports = module
|
||||
.module()
|
||||
.module
|
||||
.exports()
|
||||
.map(|e| {
|
||||
Some(Box::new(wasm_exporttype_t::new(
|
||||
@@ -91,12 +65,12 @@ pub extern "C" fn wasm_module_exports(module: &wasm_module_t, out: &mut wasm_exp
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_imports(module: &wasm_module_t, out: &mut wasm_importtype_vec_t) {
|
||||
let imports = module
|
||||
.module()
|
||||
.module
|
||||
.imports()
|
||||
.map(|i| {
|
||||
Some(Box::new(wasm_importtype_t::new(
|
||||
i.module().to_owned(),
|
||||
i.name().map(|s| s.to_owned()),
|
||||
i.name().to_owned(),
|
||||
i.ty(),
|
||||
)))
|
||||
})
|
||||
@@ -107,7 +81,7 @@ pub extern "C" fn wasm_module_imports(module: &wasm_module_t, out: &mut wasm_imp
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_share(module: &wasm_module_t) -> Box<wasm_shared_module_t> {
|
||||
Box::new(wasm_shared_module_t {
|
||||
module: module.module().clone(),
|
||||
module: module.module.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -118,7 +92,7 @@ pub unsafe extern "C" fn wasm_module_obtain(
|
||||
) -> Option<Box<wasm_module_t>> {
|
||||
let module = shared_module.module.clone();
|
||||
if Engine::same(store.store.context().engine(), module.engine()) {
|
||||
Some(Box::new(wasm_module_t::new(store.store.clone(), module)))
|
||||
Some(Box::new(wasm_module_t::new(module)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -126,7 +100,7 @@ pub unsafe extern "C" fn wasm_module_obtain(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_serialize(module: &wasm_module_t, ret: &mut wasm_byte_vec_t) {
|
||||
if let Ok(buf) = module.module().serialize() {
|
||||
if let Ok(buf) = module.module.serialize() {
|
||||
ret.set_buffer(buf);
|
||||
}
|
||||
}
|
||||
@@ -137,7 +111,7 @@ pub unsafe extern "C" fn wasm_module_deserialize(
|
||||
binary: &wasm_byte_vec_t,
|
||||
) -> Option<Box<wasm_module_t>> {
|
||||
match Module::deserialize(store.store.context().engine(), binary.as_slice()) {
|
||||
Ok(module) => Some(Box::new(wasm_module_t::new(store.store.clone(), module))),
|
||||
Ok(module) => Some(Box::new(wasm_module_t::new(module))),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
@@ -180,11 +154,6 @@ pub unsafe extern "C" fn wasmtime_module_validate(
|
||||
handle_result(Module::validate(&engine.engine, binary), |()| {})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_module_type(m: &wasmtime_module_t) -> Box<wasmtime_moduletype_t> {
|
||||
Box::new(wasmtime_moduletype_t::new(m.module.ty()))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_module_serialize(
|
||||
module: &wasmtime_module_t,
|
||||
|
||||
@@ -20,18 +20,14 @@ mod r#extern;
|
||||
mod func;
|
||||
mod global;
|
||||
mod import;
|
||||
mod instance;
|
||||
mod memory;
|
||||
mod module;
|
||||
mod table;
|
||||
mod val;
|
||||
pub use self::export::*;
|
||||
pub use self::func::*;
|
||||
pub use self::global::*;
|
||||
pub use self::import::*;
|
||||
pub use self::instance::*;
|
||||
pub use self::memory::*;
|
||||
pub use self::module::*;
|
||||
pub use self::r#extern::*;
|
||||
pub use self::table::*;
|
||||
pub use self::val::*;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{wasm_functype_t, wasm_globaltype_t, wasm_memorytype_t, wasm_tabletype_t};
|
||||
use crate::{wasmtime_instancetype_t, wasmtime_moduletype_t};
|
||||
use crate::{CFuncType, CGlobalType, CInstanceType, CMemoryType, CModuleType, CTableType};
|
||||
use crate::{CFuncType, CGlobalType, CMemoryType, CTableType};
|
||||
use wasmtime::ExternType;
|
||||
|
||||
#[repr(C)]
|
||||
@@ -17,8 +16,6 @@ pub(crate) enum CExternType {
|
||||
Global(CGlobalType),
|
||||
Memory(CMemoryType),
|
||||
Table(CTableType),
|
||||
Instance(CInstanceType),
|
||||
Module(CModuleType),
|
||||
}
|
||||
|
||||
pub type wasm_externkind_t = u8;
|
||||
@@ -27,8 +24,6 @@ pub const WASM_EXTERN_FUNC: wasm_externkind_t = 0;
|
||||
pub const WASM_EXTERN_GLOBAL: wasm_externkind_t = 1;
|
||||
pub const WASM_EXTERN_TABLE: wasm_externkind_t = 2;
|
||||
pub const WASM_EXTERN_MEMORY: wasm_externkind_t = 3;
|
||||
pub const WASM_EXTERN_MODULE: wasm_externkind_t = 4;
|
||||
pub const WASM_EXTERN_INSTANCE: wasm_externkind_t = 5;
|
||||
|
||||
impl wasm_externtype_t {
|
||||
pub(crate) fn new(ty: ExternType) -> wasm_externtype_t {
|
||||
@@ -38,8 +33,6 @@ impl wasm_externtype_t {
|
||||
ExternType::Global(f) => CExternType::Global(CGlobalType::new(f)),
|
||||
ExternType::Memory(f) => CExternType::Memory(CMemoryType::new(f)),
|
||||
ExternType::Table(f) => CExternType::Table(CTableType::new(f)),
|
||||
ExternType::Instance(f) => CExternType::Instance(CInstanceType::new(f)),
|
||||
ExternType::Module(f) => CExternType::Module(CModuleType::new(f)),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -50,8 +43,6 @@ impl wasm_externtype_t {
|
||||
CExternType::Table(f) => ExternType::Table(f.ty.clone()),
|
||||
CExternType::Global(f) => ExternType::Global(f.ty.clone()),
|
||||
CExternType::Memory(f) => ExternType::Memory(f.ty.clone()),
|
||||
CExternType::Instance(f) => ExternType::Instance(f.ty.clone()),
|
||||
CExternType::Module(f) => ExternType::Module(f.ty.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,8 +54,6 @@ pub extern "C" fn wasm_externtype_kind(et: &wasm_externtype_t) -> wasm_externkin
|
||||
CExternType::Table(_) => WASM_EXTERN_TABLE,
|
||||
CExternType::Global(_) => WASM_EXTERN_GLOBAL,
|
||||
CExternType::Memory(_) => WASM_EXTERN_MEMORY,
|
||||
CExternType::Instance(_) => WASM_EXTERN_INSTANCE,
|
||||
CExternType::Module(_) => WASM_EXTERN_MODULE,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,17 +110,3 @@ pub extern "C" fn wasm_externtype_as_memorytype_const(
|
||||
) -> Option<&wasm_memorytype_t> {
|
||||
wasm_memorytype_t::try_from(et)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_externtype_as_moduletype(
|
||||
et: &wasm_externtype_t,
|
||||
) -> Option<&wasmtime_moduletype_t> {
|
||||
wasmtime_moduletype_t::try_from(et)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_externtype_as_instancetype(
|
||||
et: &wasm_externtype_t,
|
||||
) -> Option<&wasmtime_instancetype_t> {
|
||||
wasmtime_instancetype_t::try_from(et)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use wasmtime::ExternType;
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_importtype_t {
|
||||
pub(crate) module: String,
|
||||
pub(crate) name: Option<String>,
|
||||
pub(crate) name: String,
|
||||
pub(crate) ty: ExternType,
|
||||
module_cache: OnceCell<wasm_name_t>,
|
||||
name_cache: OnceCell<wasm_name_t>,
|
||||
@@ -16,7 +16,7 @@ pub struct wasm_importtype_t {
|
||||
wasmtime_c_api_macros::declare_ty!(wasm_importtype_t);
|
||||
|
||||
impl wasm_importtype_t {
|
||||
pub(crate) fn new(module: String, name: Option<String>, ty: ExternType) -> wasm_importtype_t {
|
||||
pub(crate) fn new(module: String, name: String, ty: ExternType) -> wasm_importtype_t {
|
||||
wasm_importtype_t {
|
||||
module,
|
||||
name,
|
||||
@@ -31,16 +31,13 @@ impl wasm_importtype_t {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_importtype_new(
|
||||
module: &mut wasm_name_t,
|
||||
name: Option<&mut wasm_name_t>,
|
||||
name: &mut wasm_name_t,
|
||||
ty: Box<wasm_externtype_t>,
|
||||
) -> Option<Box<wasm_importtype_t>> {
|
||||
let module = module.take();
|
||||
let name = name.map(|n| n.take());
|
||||
let name = name.take();
|
||||
let module = String::from_utf8(module).ok()?;
|
||||
let name = match name {
|
||||
Some(name) => Some(String::from_utf8(name).ok()?),
|
||||
None => None,
|
||||
};
|
||||
let name = String::from_utf8(name).ok()?;
|
||||
Some(Box::new(wasm_importtype_t::new(module, name, ty.ty())))
|
||||
}
|
||||
|
||||
@@ -51,12 +48,9 @@ pub extern "C" fn wasm_importtype_module(it: &wasm_importtype_t) -> &wasm_name_t
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_importtype_name(it: &wasm_importtype_t) -> Option<&wasm_name_t> {
|
||||
let name = it.name.as_ref()?;
|
||||
Some(
|
||||
it.name_cache
|
||||
.get_or_init(|| wasm_name_t::from_name(name.to_string())),
|
||||
)
|
||||
pub extern "C" fn wasm_importtype_name(it: &wasm_importtype_t) -> &wasm_name_t {
|
||||
it.name_cache
|
||||
.get_or_init(|| wasm_name_t::from_name(it.name.to_string()))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
use crate::{wasm_exporttype_t, wasm_exporttype_vec_t, wasm_externtype_t, CExternType};
|
||||
use wasmtime::InstanceType;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmtime_instancetype_t {
|
||||
ext: wasm_externtype_t,
|
||||
}
|
||||
|
||||
wasmtime_c_api_macros::declare_ty!(wasmtime_instancetype_t);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CInstanceType {
|
||||
pub(crate) ty: InstanceType,
|
||||
}
|
||||
|
||||
impl wasmtime_instancetype_t {
|
||||
pub(crate) fn new(ty: InstanceType) -> wasmtime_instancetype_t {
|
||||
wasmtime_instancetype_t {
|
||||
ext: wasm_externtype_t::new(ty.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasmtime_instancetype_t> {
|
||||
match &e.which {
|
||||
CExternType::Instance(_) => Some(unsafe { &*(e as *const _ as *const _) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ty(&self) -> &CInstanceType {
|
||||
match &self.ext.which {
|
||||
CExternType::Instance(f) => &f,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CInstanceType {
|
||||
pub(crate) fn new(ty: InstanceType) -> CInstanceType {
|
||||
CInstanceType { ty }
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_instancetype_as_externtype(
|
||||
ty: &wasmtime_instancetype_t,
|
||||
) -> &wasm_externtype_t {
|
||||
&ty.ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_instancetype_exports(
|
||||
instance: &wasmtime_instancetype_t,
|
||||
out: &mut wasm_exporttype_vec_t,
|
||||
) {
|
||||
let exports = instance
|
||||
.ty()
|
||||
.ty
|
||||
.exports()
|
||||
.map(|e| {
|
||||
Some(Box::new(wasm_exporttype_t::new(
|
||||
e.name().to_owned(),
|
||||
e.ty(),
|
||||
)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
out.set_buffer(exports);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
use crate::{
|
||||
wasm_exporttype_t, wasm_exporttype_vec_t, wasm_externtype_t, wasm_importtype_t,
|
||||
wasm_importtype_vec_t, CExternType,
|
||||
};
|
||||
use wasmtime::ModuleType;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasmtime_moduletype_t {
|
||||
ext: wasm_externtype_t,
|
||||
}
|
||||
|
||||
wasmtime_c_api_macros::declare_ty!(wasmtime_moduletype_t);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CModuleType {
|
||||
pub(crate) ty: ModuleType,
|
||||
}
|
||||
|
||||
impl wasmtime_moduletype_t {
|
||||
pub(crate) fn new(ty: ModuleType) -> wasmtime_moduletype_t {
|
||||
wasmtime_moduletype_t {
|
||||
ext: wasm_externtype_t::new(ty.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasmtime_moduletype_t> {
|
||||
match &e.which {
|
||||
CExternType::Module(_) => Some(unsafe { &*(e as *const _ as *const _) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ty(&self) -> &CModuleType {
|
||||
match &self.ext.which {
|
||||
CExternType::Module(f) => &f,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CModuleType {
|
||||
pub(crate) fn new(ty: ModuleType) -> CModuleType {
|
||||
CModuleType { ty }
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_moduletype_as_externtype(
|
||||
ty: &wasmtime_moduletype_t,
|
||||
) -> &wasm_externtype_t {
|
||||
&ty.ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_moduletype_exports(
|
||||
module: &wasmtime_moduletype_t,
|
||||
out: &mut wasm_exporttype_vec_t,
|
||||
) {
|
||||
let exports = module
|
||||
.ty()
|
||||
.ty
|
||||
.exports()
|
||||
.map(|e| {
|
||||
Some(Box::new(wasm_exporttype_t::new(
|
||||
e.name().to_owned(),
|
||||
e.ty(),
|
||||
)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
out.set_buffer(exports);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_moduletype_imports(
|
||||
module: &wasmtime_moduletype_t,
|
||||
out: &mut wasm_importtype_vec_t,
|
||||
) {
|
||||
let imports = module
|
||||
.ty()
|
||||
.ty
|
||||
.imports()
|
||||
.map(|i| {
|
||||
Some(Box::new(wasm_importtype_t::new(
|
||||
i.module().to_owned(),
|
||||
i.name().map(|s| s.to_owned()),
|
||||
i.ty(),
|
||||
)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
out.set_buffer(imports);
|
||||
}
|
||||
@@ -836,8 +836,6 @@ impl Default for TableInitialization {
|
||||
#[allow(missing_docs)]
|
||||
pub enum ModuleType {
|
||||
Function(SignatureIndex),
|
||||
Module(ModuleTypeIndex),
|
||||
Instance(InstanceTypeIndex),
|
||||
}
|
||||
|
||||
impl ModuleType {
|
||||
@@ -846,7 +844,6 @@ impl ModuleType {
|
||||
pub fn unwrap_function(&self) -> SignatureIndex {
|
||||
match self {
|
||||
ModuleType::Function(f) => *f,
|
||||
_ => panic!("not a function type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -915,12 +912,6 @@ pub struct Module {
|
||||
|
||||
/// WebAssembly global variables.
|
||||
pub globals: PrimaryMap<GlobalIndex, Global>,
|
||||
|
||||
/// The type of each wasm instance this module defines.
|
||||
pub instances: PrimaryMap<InstanceIndex, InstanceTypeIndex>,
|
||||
|
||||
/// The type of each nested wasm module this module contains.
|
||||
pub modules: PrimaryMap<ModuleIndex, ModuleTypeIndex>,
|
||||
}
|
||||
|
||||
/// Initialization routines for creating an instance, encompassing imports,
|
||||
@@ -931,59 +922,12 @@ pub enum Initializer {
|
||||
Import {
|
||||
/// Name of this import
|
||||
name: String,
|
||||
/// The field name projection of this import. When module-linking is
|
||||
/// enabled this is always `None`. Otherwise this is always `Some`.
|
||||
field: Option<String>,
|
||||
/// The field name projection of this import
|
||||
field: String,
|
||||
/// Where this import will be placed, which also has type information
|
||||
/// about the import.
|
||||
index: EntityIndex,
|
||||
},
|
||||
|
||||
/// An export from a previously defined instance is being inserted into our
|
||||
/// index space.
|
||||
///
|
||||
/// Note that when the module linking proposal is enabled two-level imports
|
||||
/// will implicitly desugar to this initializer.
|
||||
AliasInstanceExport {
|
||||
/// The instance that we're referencing.
|
||||
instance: InstanceIndex,
|
||||
/// Which export is being inserted into our index space.
|
||||
export: String,
|
||||
},
|
||||
|
||||
/// A module is being instantiated with previously configured initializers
|
||||
/// as arguments.
|
||||
Instantiate {
|
||||
/// The module that this instance is instantiating.
|
||||
module: ModuleIndex,
|
||||
/// The arguments provided to instantiation, along with their name in
|
||||
/// the instance being instantiated.
|
||||
args: IndexMap<String, EntityIndex>,
|
||||
},
|
||||
|
||||
/// A module is being created from a set of compiled artifacts.
|
||||
CreateModule {
|
||||
/// The index of the artifact that's being converted into a module.
|
||||
artifact_index: usize,
|
||||
/// The list of artifacts that this module value will be inheriting.
|
||||
artifacts: Vec<usize>,
|
||||
/// The list of modules that this module value will inherit.
|
||||
modules: Vec<ModuleUpvar>,
|
||||
},
|
||||
|
||||
/// A module is created from a closed-over-module value, defined when this
|
||||
/// module was created.
|
||||
DefineModule(usize),
|
||||
}
|
||||
|
||||
/// Where module values can come from when creating a new module from a compiled
|
||||
/// artifact.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ModuleUpvar {
|
||||
/// A module value is inherited from the module creating the new module.
|
||||
Inherit(usize),
|
||||
/// A module value comes from the instance-to-be-created module index space.
|
||||
Local(ModuleIndex),
|
||||
}
|
||||
|
||||
impl Module {
|
||||
@@ -1100,12 +1044,11 @@ impl Module {
|
||||
|
||||
/// Returns an iterator of all the imports in this module, along with their
|
||||
/// module name, field name, and type that's being imported.
|
||||
pub fn imports(&self) -> impl Iterator<Item = (&str, Option<&str>, EntityType)> {
|
||||
self.initializers.iter().filter_map(move |i| match i {
|
||||
pub fn imports(&self) -> impl Iterator<Item = (&str, &str, EntityType)> {
|
||||
self.initializers.iter().map(move |i| match i {
|
||||
Initializer::Import { name, field, index } => {
|
||||
Some((name.as_str(), field.as_deref(), self.type_of(*index)))
|
||||
(name.as_str(), field.as_str(), self.type_of(*index))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1116,8 +1059,6 @@ impl Module {
|
||||
EntityIndex::Table(i) => EntityType::Table(self.table_plans[i].table),
|
||||
EntityIndex::Memory(i) => EntityType::Memory(self.memory_plans[i].memory),
|
||||
EntityIndex::Function(i) => EntityType::Function(self.functions[i].signature),
|
||||
EntityIndex::Instance(i) => EntityType::Instance(self.instances[i]),
|
||||
EntityIndex::Module(i) => EntityType::Module(self.modules[i]),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1149,26 +1090,6 @@ impl Module {
|
||||
#[allow(missing_docs)]
|
||||
pub struct TypeTables {
|
||||
pub wasm_signatures: PrimaryMap<SignatureIndex, WasmFuncType>,
|
||||
pub module_signatures: PrimaryMap<ModuleTypeIndex, ModuleSignature>,
|
||||
pub instance_signatures: PrimaryMap<InstanceTypeIndex, InstanceSignature>,
|
||||
}
|
||||
|
||||
/// The type signature of known modules.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModuleSignature {
|
||||
/// All imports in this module, listed in order with their name and
|
||||
/// what type they're importing.
|
||||
pub imports: IndexMap<String, EntityType>,
|
||||
/// Exports are what an instance type conveys, so we go through an
|
||||
/// indirection over there.
|
||||
pub exports: InstanceTypeIndex,
|
||||
}
|
||||
|
||||
/// The type signature of known instances.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct InstanceSignature {
|
||||
/// The name of what's being exported as well as its type signature.
|
||||
pub exports: IndexMap<String, EntityType>,
|
||||
}
|
||||
|
||||
/// Type information about functions in a wasm module.
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
use crate::module::{
|
||||
AnyfuncIndex, Initializer, InstanceSignature, MemoryInitialization, MemoryInitializer,
|
||||
MemoryPlan, Module, ModuleSignature, ModuleType, ModuleUpvar, TableInitializer, TablePlan,
|
||||
TypeTables,
|
||||
AnyfuncIndex, Initializer, MemoryInitialization, MemoryInitializer, MemoryPlan, Module,
|
||||
ModuleType, TableInitializer, TablePlan, TypeTables,
|
||||
};
|
||||
use crate::{
|
||||
DataIndex, DefinedFuncIndex, ElemIndex, EntityIndex, EntityType, FuncIndex, Global,
|
||||
GlobalIndex, GlobalInit, InstanceIndex, InstanceTypeIndex, MemoryIndex, ModuleIndex,
|
||||
ModuleTypeIndex, PrimaryMap, SignatureIndex, TableIndex, TableInitialization, Tunables,
|
||||
TypeIndex, WasmError, WasmFuncType, WasmResult,
|
||||
GlobalIndex, GlobalInit, MemoryIndex, PrimaryMap, SignatureIndex, TableIndex,
|
||||
TableInitialization, Tunables, TypeIndex, WasmError, WasmFuncType, WasmResult,
|
||||
};
|
||||
use cranelift_entity::packed_option::ReservedValue;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use wasmparser::Type as WasmType;
|
||||
use wasmparser::{
|
||||
Alias, DataKind, ElementItem, ElementKind, ExternalKind, FuncValidator, FunctionBody,
|
||||
DataKind, ElementItem, ElementKind, ExternalKind, FuncValidator, FunctionBody,
|
||||
ImportSectionEntryType, NameSectionReader, Naming, Operator, Parser, Payload, TypeDef,
|
||||
Validator, ValidatorResources, WasmFeatures,
|
||||
};
|
||||
@@ -28,18 +25,6 @@ pub struct ModuleEnvironment<'data> {
|
||||
/// The current module being translated
|
||||
result: ModuleTranslation<'data>,
|
||||
|
||||
/// Modules which have finished translation. This only really applies for
|
||||
/// the module linking proposal.
|
||||
results: Vec<ModuleTranslation<'data>>,
|
||||
|
||||
/// Modules which are in-progress being translated, or otherwise also known
|
||||
/// as the outer modules of the current module being processed.
|
||||
in_progress: Vec<ModuleTranslation<'data>>,
|
||||
|
||||
/// How many modules that have not yet made their way into `results` which
|
||||
/// are coming at some point.
|
||||
modules_to_be: usize,
|
||||
|
||||
/// Intern'd types for this entire translation, shared by all modules.
|
||||
types: TypeTables,
|
||||
|
||||
@@ -48,7 +33,6 @@ pub struct ModuleEnvironment<'data> {
|
||||
// Various bits and pieces of configuration
|
||||
features: WasmFeatures,
|
||||
tunables: Tunables,
|
||||
first_module: bool,
|
||||
}
|
||||
|
||||
/// The result of translating via `ModuleEnvironment`. Function bodies are not
|
||||
@@ -102,16 +86,6 @@ pub struct ModuleTranslation<'data> {
|
||||
/// When we're parsing the code section this will be incremented so we know
|
||||
/// which function is currently being defined.
|
||||
code_index: u32,
|
||||
|
||||
implicit_instances: HashMap<&'data str, InstanceIndex>,
|
||||
|
||||
/// The artifacts which are needed from the parent module when this module
|
||||
/// is created. This is used to insert into `Initializer::CreateModule` when
|
||||
/// this module is defined in the parent.
|
||||
creation_artifacts: Vec<usize>,
|
||||
|
||||
/// Same as `creation_artifacts`, but for modules instead of artifacts.
|
||||
creation_modules: Vec<ModuleUpvar>,
|
||||
}
|
||||
|
||||
/// Contains function data: byte code and its offset in the module.
|
||||
@@ -168,13 +142,9 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
pub fn new(tunables: &Tunables, features: &WasmFeatures) -> Self {
|
||||
Self {
|
||||
result: ModuleTranslation::default(),
|
||||
results: Vec::with_capacity(1),
|
||||
in_progress: Vec::new(),
|
||||
modules_to_be: 1,
|
||||
types: Default::default(),
|
||||
tunables: tunables.clone(),
|
||||
features: *features,
|
||||
first_module: true,
|
||||
interned_func_types: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -198,7 +168,7 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
pub fn translate(
|
||||
mut self,
|
||||
data: &'data [u8],
|
||||
) -> WasmResult<(usize, Vec<ModuleTranslation<'data>>, TypeTables)> {
|
||||
) -> WasmResult<(ModuleTranslation<'data>, TypeTables)> {
|
||||
let mut validator = Validator::new();
|
||||
validator.wasm_features(self.features);
|
||||
|
||||
@@ -206,8 +176,7 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
self.translate_payload(&mut validator, payload?)?;
|
||||
}
|
||||
|
||||
assert!(self.results.len() > 0);
|
||||
Ok((self.results.len() - 1, self.results, self.types))
|
||||
Ok((self.result, self.types))
|
||||
}
|
||||
|
||||
fn translate_payload(
|
||||
@@ -218,18 +187,6 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
match payload {
|
||||
Payload::Version { num, range } => {
|
||||
validator.version(num, &range)?;
|
||||
|
||||
// If this is the first time this method is called, nothing to
|
||||
// do.
|
||||
if self.first_module {
|
||||
self.first_module = false;
|
||||
} else {
|
||||
// Reset our internal state for a new module by saving the
|
||||
// current module in `results`.
|
||||
let in_progress = mem::replace(&mut self.result, ModuleTranslation::default());
|
||||
self.in_progress.push(in_progress);
|
||||
self.modules_to_be -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
Payload::End => {
|
||||
@@ -253,46 +210,6 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
.collect();
|
||||
self.result.exported_signatures.sort_unstable();
|
||||
self.result.exported_signatures.dedup();
|
||||
|
||||
self.result.creation_artifacts.shrink_to_fit();
|
||||
self.result.creation_modules.shrink_to_fit();
|
||||
|
||||
let (record_initializer, mut done) = match self.in_progress.pop() {
|
||||
Some(m) => (true, mem::replace(&mut self.result, m)),
|
||||
None => (false, mem::take(&mut self.result)),
|
||||
};
|
||||
|
||||
if record_initializer {
|
||||
// Record the type of the module we just finished in our own
|
||||
// module's list of modules.
|
||||
let sig = self.gen_type_of_module(&done.module);
|
||||
self.result.module.modules.push(sig);
|
||||
|
||||
// The root module will store the artifacts for this
|
||||
// finished module at `artifact_index`. This then needs to
|
||||
// be inherited by all later modules coming down to our
|
||||
// now-current `self.result`...
|
||||
let mut artifact_index = self.results.len();
|
||||
for result in self.in_progress.iter_mut().chain(Some(&mut self.result)) {
|
||||
result.creation_artifacts.push(artifact_index);
|
||||
artifact_index = result.creation_artifacts.len() - 1;
|
||||
}
|
||||
// ... and then `self.result` needs to create a new module
|
||||
// with whatever was record to save off as its own
|
||||
// artifacts/modules.
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::CreateModule {
|
||||
artifact_index,
|
||||
artifacts: mem::take(&mut done.creation_artifacts),
|
||||
modules: mem::take(&mut done.creation_modules),
|
||||
});
|
||||
}
|
||||
|
||||
// And the final step is to insert the module into the list of
|
||||
// finished modules to get returned at the end.
|
||||
self.results.push(done);
|
||||
}
|
||||
|
||||
Payload::TypeSection(types) => {
|
||||
@@ -306,26 +223,10 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
TypeDef::Func(wasm_func_ty) => {
|
||||
self.declare_type_func(wasm_func_ty.try_into()?)?;
|
||||
}
|
||||
TypeDef::Module(t) => {
|
||||
let imports = t
|
||||
.imports
|
||||
.iter()
|
||||
.map(|i| Ok((i.module, i.field, self.entity_type(i.ty)?)))
|
||||
.collect::<WasmResult<Vec<_>>>()?;
|
||||
let exports = t
|
||||
.exports
|
||||
.iter()
|
||||
.map(|e| Ok((e.name, self.entity_type(e.ty)?)))
|
||||
.collect::<WasmResult<Vec<_>>>()?;
|
||||
self.declare_type_module(&imports, &exports)?;
|
||||
}
|
||||
TypeDef::Instance(t) => {
|
||||
let exports = t
|
||||
.exports
|
||||
.iter()
|
||||
.map(|e| Ok((e.name, self.entity_type(e.ty)?)))
|
||||
.collect::<WasmResult<Vec<_>>>()?;
|
||||
self.declare_type_instance(&exports)?;
|
||||
|
||||
// doesn't get past validation
|
||||
TypeDef::Module(_) | TypeDef::Instance(_) => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,16 +248,6 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
self.result.debuginfo.wasm_file.imported_func_count += 1;
|
||||
EntityType::Function(sig_index)
|
||||
}
|
||||
ImportSectionEntryType::Module(index) => {
|
||||
let index = TypeIndex::from_u32(index);
|
||||
let signature = self.type_to_module_type(index)?;
|
||||
EntityType::Module(signature)
|
||||
}
|
||||
ImportSectionEntryType::Instance(index) => {
|
||||
let index = TypeIndex::from_u32(index);
|
||||
let signature = self.type_to_instance_type(index)?;
|
||||
EntityType::Instance(signature)
|
||||
}
|
||||
ImportSectionEntryType::Memory(ty) => {
|
||||
if ty.shared {
|
||||
return Err(WasmError::Unsupported("shared memories".to_owned()));
|
||||
@@ -374,9 +265,11 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
}
|
||||
|
||||
// doesn't get past validation
|
||||
ImportSectionEntryType::Tag(_) => unreachable!(),
|
||||
ImportSectionEntryType::Module(_)
|
||||
| ImportSectionEntryType::Instance(_)
|
||||
| ImportSectionEntryType::Tag(_) => unreachable!(),
|
||||
};
|
||||
self.declare_import(import.module, import.field, ty);
|
||||
self.declare_import(import.module, import.field.unwrap(), ty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,13 +378,12 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(index)),
|
||||
ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(index)),
|
||||
ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(index)),
|
||||
ExternalKind::Module => EntityIndex::Module(ModuleIndex::from_u32(index)),
|
||||
ExternalKind::Instance => {
|
||||
EntityIndex::Instance(InstanceIndex::from_u32(index))
|
||||
}
|
||||
|
||||
// this never gets past validation
|
||||
ExternalKind::Tag | ExternalKind::Type => unreachable!(),
|
||||
ExternalKind::Module
|
||||
| ExternalKind::Instance
|
||||
| ExternalKind::Tag
|
||||
| ExternalKind::Type => unreachable!(),
|
||||
};
|
||||
self.result
|
||||
.module
|
||||
@@ -725,177 +617,14 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
// the passive count, do not reserve anything here.
|
||||
}
|
||||
|
||||
Payload::InstanceSection(s) => {
|
||||
validator.instance_section(&s)?;
|
||||
|
||||
let cnt = usize::try_from(s.get_count()).unwrap();
|
||||
self.result.module.instances.reserve(cnt);
|
||||
self.result.module.initializers.reserve(cnt);
|
||||
|
||||
for instance in s {
|
||||
let instance = instance?;
|
||||
let module = ModuleIndex::from_u32(instance.module());
|
||||
let args = instance
|
||||
.args()?
|
||||
.into_iter()
|
||||
.map(|arg| {
|
||||
let arg = arg?;
|
||||
let index = match arg.kind {
|
||||
ExternalKind::Function => {
|
||||
EntityIndex::Function(FuncIndex::from_u32(arg.index))
|
||||
}
|
||||
ExternalKind::Table => {
|
||||
EntityIndex::Table(TableIndex::from_u32(arg.index))
|
||||
}
|
||||
ExternalKind::Memory => {
|
||||
EntityIndex::Memory(MemoryIndex::from_u32(arg.index))
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
EntityIndex::Global(GlobalIndex::from_u32(arg.index))
|
||||
}
|
||||
ExternalKind::Module => {
|
||||
EntityIndex::Module(ModuleIndex::from_u32(arg.index))
|
||||
}
|
||||
ExternalKind::Instance => {
|
||||
EntityIndex::Instance(InstanceIndex::from_u32(arg.index))
|
||||
}
|
||||
|
||||
// this won't pass validation
|
||||
ExternalKind::Tag | ExternalKind::Type => unreachable!(),
|
||||
};
|
||||
Ok((arg.name.to_string(), index))
|
||||
})
|
||||
.collect::<WasmResult<_>>()?;
|
||||
|
||||
// Record the type of this instance with the type signature of the
|
||||
// module we're instantiating and then also add an initializer which
|
||||
// records that we'll be adding to the instance index space here.
|
||||
let module_ty = self.result.module.modules[module];
|
||||
let instance_ty = self.types.module_signatures[module_ty].exports;
|
||||
self.result.module.instances.push(instance_ty);
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::Instantiate { module, args });
|
||||
}
|
||||
}
|
||||
Payload::AliasSection(s) => {
|
||||
validator.alias_section(&s)?;
|
||||
unreachable!() // should never get past validation
|
||||
}
|
||||
|
||||
for alias in s {
|
||||
match alias? {
|
||||
// Types are easy, we statically know everything so
|
||||
// we're just copying some pointers from our parent
|
||||
// module to our own module.
|
||||
//
|
||||
// Note that we don't add an initializer for this alias
|
||||
// because we statically know where all types point to.
|
||||
Alias::OuterType {
|
||||
relative_depth,
|
||||
index,
|
||||
} => {
|
||||
let index = TypeIndex::from_u32(index);
|
||||
let module_idx = self.in_progress.len() - 1 - (relative_depth as usize);
|
||||
let ty = self.in_progress[module_idx].module.types[index];
|
||||
self.result.module.types.push(ty);
|
||||
}
|
||||
|
||||
// Modules are a bit trickier since we need to record
|
||||
// how to track the state from the original module down
|
||||
// to our own.
|
||||
Alias::OuterModule {
|
||||
relative_depth,
|
||||
index,
|
||||
} => {
|
||||
let index = ModuleIndex::from_u32(index);
|
||||
|
||||
// First we can copy the type from the parent module
|
||||
// into our own module to record what type our
|
||||
// module definition will have.
|
||||
let module_idx = self.in_progress.len() - 1 - (relative_depth as usize);
|
||||
let module_ty = self.in_progress[module_idx].module.modules[index];
|
||||
self.result.module.modules.push(module_ty);
|
||||
|
||||
// Next we'll be injecting a module value that is
|
||||
// closed over, and that will be used to define the
|
||||
// module into the index space. Record an
|
||||
// initializer about where our module is sourced
|
||||
// from (which will be stored within each module
|
||||
// value itself).
|
||||
let module_index = self.result.creation_modules.len();
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::DefineModule(module_index));
|
||||
|
||||
// And finally we need to record a breadcrumb trail
|
||||
// of how to get the module value into
|
||||
// `module_index`. The module just after our
|
||||
// destination module will use a `ModuleIndex` to
|
||||
// fetch the module value, and everything else
|
||||
// inbetween will inherit that module's closed-over
|
||||
// value.
|
||||
let mut upvar = ModuleUpvar::Local(index);
|
||||
for outer in self.in_progress[module_idx + 1..].iter_mut() {
|
||||
let upvar = mem::replace(
|
||||
&mut upvar,
|
||||
ModuleUpvar::Inherit(outer.creation_modules.len()),
|
||||
);
|
||||
outer.creation_modules.push(upvar);
|
||||
}
|
||||
self.result.creation_modules.push(upvar);
|
||||
}
|
||||
|
||||
// This case is slightly more involved, we'll be
|
||||
// recording all the type information for each kind of
|
||||
// entity, and then we also need to record an
|
||||
// initialization step to get the export from the
|
||||
// instance.
|
||||
Alias::InstanceExport {
|
||||
instance,
|
||||
export,
|
||||
kind: _,
|
||||
} => {
|
||||
let instance = InstanceIndex::from_u32(instance);
|
||||
let ty = self.result.module.instances[instance];
|
||||
match &self.types.instance_signatures[ty].exports[export] {
|
||||
EntityType::Global(g) => {
|
||||
self.result.module.globals.push(g.clone());
|
||||
self.result.module.num_imported_globals += 1;
|
||||
}
|
||||
EntityType::Memory(mem) => {
|
||||
let plan = MemoryPlan::for_memory(*mem, &self.tunables);
|
||||
self.result.module.memory_plans.push(plan);
|
||||
self.result.module.num_imported_memories += 1;
|
||||
}
|
||||
EntityType::Table(t) => {
|
||||
let plan = TablePlan::for_table(*t, &self.tunables);
|
||||
self.result.module.table_plans.push(plan);
|
||||
self.result.module.num_imported_tables += 1;
|
||||
}
|
||||
EntityType::Function(sig) => {
|
||||
self.result.module.push_function(*sig);
|
||||
self.result.module.num_imported_funcs += 1;
|
||||
self.result.debuginfo.wasm_file.imported_func_count += 1;
|
||||
}
|
||||
EntityType::Instance(sig) => {
|
||||
self.result.module.instances.push(*sig);
|
||||
}
|
||||
EntityType::Module(sig) => {
|
||||
self.result.module.modules.push(*sig);
|
||||
}
|
||||
EntityType::Tag(_) => unimplemented!(),
|
||||
}
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::AliasInstanceExport {
|
||||
instance,
|
||||
export: export.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Payload::InstanceSection(s) => {
|
||||
validator.instance_section(&s)?;
|
||||
unreachable!() // should never get past validation
|
||||
}
|
||||
|
||||
Payload::ModuleSectionStart {
|
||||
@@ -904,22 +633,12 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
size: _,
|
||||
} => {
|
||||
validator.module_section_start(count, &range)?;
|
||||
|
||||
// Go ahead and reserve space in the final `results` array for `amount`
|
||||
// more modules.
|
||||
self.modules_to_be += count as usize;
|
||||
self.results.reserve(self.modules_to_be);
|
||||
|
||||
// Then also reserve space in our own local module's metadata fields
|
||||
// we'll be adding to.
|
||||
self.result.module.modules.reserve(count as usize);
|
||||
self.result.module.initializers.reserve(count as usize);
|
||||
unreachable!() // should never get past validation
|
||||
}
|
||||
|
||||
Payload::ModuleSectionEntry { .. } => {
|
||||
validator.module_section_entry();
|
||||
// note that nothing else happens here since we rely on the next
|
||||
// `Version` payload to recurse in the parsed modules.
|
||||
unreachable!() // should never get past validation
|
||||
}
|
||||
|
||||
Payload::CustomSection {
|
||||
@@ -1032,95 +751,13 @@ and for re-adding support for interface types you can see this issue:
|
||||
/// When the module linking proposal is disabled, however, disregard this
|
||||
/// logic and instead work directly with two-level imports since no
|
||||
/// instances are defined.
|
||||
fn declare_import(&mut self, module: &'data str, field: Option<&'data str>, ty: EntityType) {
|
||||
if !self.features.module_linking {
|
||||
assert!(field.is_some());
|
||||
let index = self.push_type(ty);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
name: module.to_owned(),
|
||||
field: field.map(|s| s.to_string()),
|
||||
index,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
match field {
|
||||
Some(field) => {
|
||||
// If this is a two-level import then this is actually an
|
||||
// implicit import of an instance, where each two-level import
|
||||
// is an alias directive from the original instance. The first
|
||||
// thing we do here is lookup our implicit instance, creating a
|
||||
// blank one if it wasn't already created.
|
||||
let instance = match self.result.implicit_instances.entry(module) {
|
||||
Entry::Occupied(e) => *e.get(),
|
||||
Entry::Vacant(v) => {
|
||||
let ty = self
|
||||
.types
|
||||
.instance_signatures
|
||||
.push(InstanceSignature::default());
|
||||
let idx = self.result.module.instances.push(ty);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
name: module.to_owned(),
|
||||
field: None,
|
||||
index: EntityIndex::Instance(idx),
|
||||
});
|
||||
*v.insert(idx)
|
||||
}
|
||||
};
|
||||
|
||||
// Update the implicit instance's type signature with this new
|
||||
// field and its type.
|
||||
self.types.instance_signatures[self.result.module.instances[instance]]
|
||||
.exports
|
||||
.insert(field.to_string(), ty.clone());
|
||||
|
||||
// Record our implicit alias annotation which corresponds to
|
||||
// this import that we're processing.
|
||||
self.result
|
||||
.module
|
||||
.initializers
|
||||
.push(Initializer::AliasInstanceExport {
|
||||
instance,
|
||||
export: field.to_string(),
|
||||
});
|
||||
|
||||
// And then record the type information for the item that we're
|
||||
// processing.
|
||||
self.push_type(ty);
|
||||
}
|
||||
None => {
|
||||
// Without a field then this is a single-level import (a feature
|
||||
// of module linking) which means we're simply importing that
|
||||
// name with the specified type. Record the type information and
|
||||
// then the name that we're importing.
|
||||
let index = self.push_type(ty);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
name: module.to_owned(),
|
||||
field: None,
|
||||
index,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn entity_type(&self, ty: ImportSectionEntryType) -> WasmResult<EntityType> {
|
||||
Ok(match ty {
|
||||
ImportSectionEntryType::Function(sig) => {
|
||||
EntityType::Function(self.type_to_signature(TypeIndex::from_u32(sig))?)
|
||||
}
|
||||
ImportSectionEntryType::Module(sig) => {
|
||||
EntityType::Module(self.type_to_module_type(TypeIndex::from_u32(sig))?)
|
||||
}
|
||||
ImportSectionEntryType::Instance(sig) => {
|
||||
EntityType::Instance(self.type_to_instance_type(TypeIndex::from_u32(sig))?)
|
||||
}
|
||||
ImportSectionEntryType::Memory(ty) => EntityType::Memory(ty.into()),
|
||||
ImportSectionEntryType::Tag(t) => EntityType::Tag(t.into()),
|
||||
ImportSectionEntryType::Global(ty) => {
|
||||
EntityType::Global(Global::new(ty, GlobalInit::Import)?)
|
||||
}
|
||||
ImportSectionEntryType::Table(ty) => EntityType::Table(ty.try_into()?),
|
||||
})
|
||||
fn declare_import(&mut self, module: &'data str, field: &'data str, ty: EntityType) {
|
||||
let index = self.push_type(ty);
|
||||
self.result.module.initializers.push(Initializer::Import {
|
||||
name: module.to_owned(),
|
||||
field: field.to_owned(),
|
||||
index,
|
||||
});
|
||||
}
|
||||
|
||||
fn push_type(&mut self, ty: EntityType) -> EntityIndex {
|
||||
@@ -1135,39 +772,10 @@ and for re-adding support for interface types you can see this issue:
|
||||
EntityIndex::Memory(self.result.module.memory_plans.push(plan))
|
||||
}
|
||||
EntityType::Global(ty) => EntityIndex::Global(self.result.module.globals.push(ty)),
|
||||
EntityType::Instance(ty) => {
|
||||
EntityIndex::Instance(self.result.module.instances.push(ty))
|
||||
}
|
||||
EntityType::Module(ty) => EntityIndex::Module(self.result.module.modules.push(ty)),
|
||||
EntityType::Tag(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_type_of_module(&mut self, module: &Module) -> ModuleTypeIndex {
|
||||
let imports = module
|
||||
.imports()
|
||||
.map(|(s, field, ty)| {
|
||||
assert!(field.is_none());
|
||||
(s.to_string(), ty)
|
||||
})
|
||||
.collect();
|
||||
let exports = module
|
||||
.exports
|
||||
.iter()
|
||||
.map(|(name, idx)| (name.clone(), module.type_of(*idx)))
|
||||
.collect();
|
||||
|
||||
// FIXME(#2469): this instance/module signature insertion should likely
|
||||
// be deduplicated.
|
||||
let exports = self
|
||||
.types
|
||||
.instance_signatures
|
||||
.push(InstanceSignature { exports });
|
||||
self.types
|
||||
.module_signatures
|
||||
.push(ModuleSignature { imports, exports })
|
||||
}
|
||||
|
||||
fn flag_func_escaped(&mut self, func: FuncIndex) {
|
||||
let ty = &mut self.result.module.functions[func];
|
||||
// If this was already assigned an anyfunc index no need to re-assign it.
|
||||
@@ -1197,90 +805,6 @@ and for re-adding support for interface types you can see this issue:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn declare_type_module(
|
||||
&mut self,
|
||||
declared_imports: &[(&'data str, Option<&'data str>, EntityType)],
|
||||
exports: &[(&'data str, EntityType)],
|
||||
) -> WasmResult<()> {
|
||||
let mut imports = indexmap::IndexMap::new();
|
||||
let mut instance_types = HashMap::new();
|
||||
for (module, field, ty) in declared_imports {
|
||||
match field {
|
||||
Some(field) => {
|
||||
let idx = *instance_types
|
||||
.entry(module)
|
||||
.or_insert_with(|| self.types.instance_signatures.push(Default::default()));
|
||||
self.types.instance_signatures[idx]
|
||||
.exports
|
||||
.insert(field.to_string(), ty.clone());
|
||||
if !imports.contains_key(*module) {
|
||||
imports.insert(module.to_string(), EntityType::Instance(idx));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
imports.insert(module.to_string(), ty.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let exports = exports
|
||||
.iter()
|
||||
.map(|e| (e.0.to_string(), e.1.clone()))
|
||||
.collect();
|
||||
|
||||
// FIXME(#2469): Like signatures above we should probably deduplicate
|
||||
// the listings of module types since with module linking it's possible
|
||||
// you'll need to write down the module type in multiple locations.
|
||||
let exports = self
|
||||
.types
|
||||
.instance_signatures
|
||||
.push(InstanceSignature { exports });
|
||||
let idx = self
|
||||
.types
|
||||
.module_signatures
|
||||
.push(ModuleSignature { imports, exports });
|
||||
self.result.module.types.push(ModuleType::Module(idx));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn declare_type_instance(&mut self, exports: &[(&'data str, EntityType)]) -> WasmResult<()> {
|
||||
let exports = exports
|
||||
.iter()
|
||||
.map(|e| (e.0.to_string(), e.1.clone()))
|
||||
.collect();
|
||||
|
||||
// FIXME(#2469): Like signatures above we should probably deduplicate
|
||||
// the listings of instance types since with module linking it's
|
||||
// possible you'll need to write down the module type in multiple
|
||||
// locations.
|
||||
let idx = self
|
||||
.types
|
||||
.instance_signatures
|
||||
.push(InstanceSignature { exports });
|
||||
self.result.module.types.push(ModuleType::Instance(idx));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn type_to_signature(&self, index: TypeIndex) -> WasmResult<SignatureIndex> {
|
||||
match self.result.module.types[index] {
|
||||
ModuleType::Function(sig) => Ok(sig),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_to_module_type(&self, index: TypeIndex) -> WasmResult<ModuleTypeIndex> {
|
||||
match self.result.module.types[index] {
|
||||
ModuleType::Module(sig) => Ok(sig),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_to_instance_type(&self, index: TypeIndex) -> WasmResult<InstanceTypeIndex> {
|
||||
match self.result.module.types[index] {
|
||||
ModuleType::Instance(sig) => Ok(sig),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the Name section of the wasm module.
|
||||
fn name_section(&mut self, names: NameSectionReader<'data>) -> WasmResult<()> {
|
||||
for subsection in names {
|
||||
|
||||
@@ -382,7 +382,6 @@ impl Config {
|
||||
let mut cfg = wasmtime::Config::new();
|
||||
cfg.wasm_bulk_memory(true)
|
||||
.wasm_reference_types(true)
|
||||
.wasm_module_linking(self.module_config.config.module_linking_enabled)
|
||||
.wasm_multi_value(self.module_config.config.multi_value_enabled)
|
||||
.wasm_multi_memory(self.module_config.config.max_memories > 1)
|
||||
.wasm_simd(self.module_config.config.simd_enabled)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Dummy implementations of things that a Wasm module can import.
|
||||
|
||||
use anyhow::Result;
|
||||
use std::fmt::Write;
|
||||
use wasmtime::*;
|
||||
|
||||
/// Create a set of dummy functions/globals/etc for the given imports.
|
||||
@@ -9,27 +8,13 @@ pub fn dummy_linker<'module, T>(store: &mut Store<T>, module: &Module) -> Result
|
||||
let mut linker = Linker::new(store.engine());
|
||||
linker.allow_shadowing(true);
|
||||
for import in module.imports() {
|
||||
match import.name() {
|
||||
Some(name) => {
|
||||
linker
|
||||
.define(import.module(), name, dummy_extern(store, import.ty())?)
|
||||
.unwrap();
|
||||
}
|
||||
None => match import.ty() {
|
||||
ExternType::Instance(ty) => {
|
||||
for ty in ty.exports() {
|
||||
linker
|
||||
.define(import.module(), ty.name(), dummy_extern(store, ty.ty())?)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
other => {
|
||||
linker
|
||||
.define_name(import.module(), dummy_extern(store, other)?)
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
}
|
||||
linker
|
||||
.define(
|
||||
import.module(),
|
||||
import.name(),
|
||||
dummy_extern(store, import.ty())?,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
Ok(linker)
|
||||
}
|
||||
@@ -41,8 +26,6 @@ pub fn dummy_extern<T>(store: &mut Store<T>, ty: ExternType) -> Result<Extern> {
|
||||
ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_ty)),
|
||||
ExternType::Table(table_ty) => Extern::Table(dummy_table(store, table_ty)?),
|
||||
ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(store, mem_ty)?),
|
||||
ExternType::Instance(instance_ty) => Extern::Instance(dummy_instance(store, instance_ty)?),
|
||||
ExternType::Module(module_ty) => Extern::Module(dummy_module(store.engine(), module_ty)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -91,297 +74,12 @@ pub fn dummy_memory<T>(store: &mut Store<T>, ty: MemoryType) -> Result<Memory> {
|
||||
Memory::new(store, ty)
|
||||
}
|
||||
|
||||
/// Construct a dummy instance for the given instance type.
|
||||
///
|
||||
/// This is done by using the expected type to generate a module on-the-fly
|
||||
/// which we the instantiate.
|
||||
pub fn dummy_instance<T>(store: &mut Store<T>, ty: InstanceType) -> Result<Instance> {
|
||||
let mut wat = WatGenerator::new();
|
||||
for ty in ty.exports() {
|
||||
wat.export(&ty);
|
||||
}
|
||||
let module = Module::new(store.engine(), &wat.finish()).unwrap();
|
||||
Instance::new(store, &module, &[])
|
||||
}
|
||||
|
||||
/// Construct a dummy module for the given module type.
|
||||
///
|
||||
/// This is done by using the expected type to generate a module on-the-fly.
|
||||
pub fn dummy_module(engine: &Engine, ty: ModuleType) -> Module {
|
||||
let mut wat = WatGenerator::new();
|
||||
for ty in ty.imports() {
|
||||
wat.import(&ty);
|
||||
}
|
||||
for ty in ty.exports() {
|
||||
wat.export(&ty);
|
||||
}
|
||||
Module::new(engine, &wat.finish()).unwrap()
|
||||
}
|
||||
|
||||
struct WatGenerator {
|
||||
tmp: usize,
|
||||
dst: String,
|
||||
}
|
||||
|
||||
impl WatGenerator {
|
||||
fn new() -> WatGenerator {
|
||||
WatGenerator {
|
||||
tmp: 0,
|
||||
dst: String::from("(module\n"),
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(mut self) -> String {
|
||||
self.dst.push_str(")\n");
|
||||
self.dst
|
||||
}
|
||||
|
||||
fn import(&mut self, ty: &ImportType<'_>) {
|
||||
write!(self.dst, "(import ").unwrap();
|
||||
self.str(ty.module());
|
||||
write!(self.dst, " ").unwrap();
|
||||
if let Some(field) = ty.name() {
|
||||
self.str(field);
|
||||
write!(self.dst, " ").unwrap();
|
||||
}
|
||||
self.item_ty(&ty.ty());
|
||||
writeln!(self.dst, ")").unwrap();
|
||||
}
|
||||
|
||||
fn item_ty(&mut self, ty: &ExternType) {
|
||||
match ty {
|
||||
ExternType::Memory(mem) => {
|
||||
write!(
|
||||
self.dst,
|
||||
"(memory {} {})",
|
||||
mem.minimum(),
|
||||
match mem.maximum() {
|
||||
Some(max) => max.to_string(),
|
||||
None => String::new(),
|
||||
}
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
ExternType::Table(table) => {
|
||||
write!(
|
||||
self.dst,
|
||||
"(table {} {} {})",
|
||||
table.minimum(),
|
||||
match table.maximum() {
|
||||
Some(max) => max.to_string(),
|
||||
None => String::new(),
|
||||
},
|
||||
wat_ty(&table.element()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
ExternType::Global(ty) => {
|
||||
if ty.mutability() == Mutability::Const {
|
||||
write!(self.dst, "(global {})", wat_ty(ty.content())).unwrap();
|
||||
} else {
|
||||
write!(self.dst, "(global (mut {}))", wat_ty(ty.content())).unwrap();
|
||||
}
|
||||
}
|
||||
ExternType::Func(ty) => {
|
||||
write!(self.dst, "(func ").unwrap();
|
||||
self.func_sig(ty);
|
||||
write!(self.dst, ")").unwrap();
|
||||
}
|
||||
ExternType::Instance(ty) => {
|
||||
writeln!(self.dst, "(instance").unwrap();
|
||||
for ty in ty.exports() {
|
||||
write!(self.dst, "(export ").unwrap();
|
||||
self.str(ty.name());
|
||||
write!(self.dst, " ").unwrap();
|
||||
self.item_ty(&ty.ty());
|
||||
writeln!(self.dst, ")").unwrap();
|
||||
}
|
||||
write!(self.dst, ")").unwrap();
|
||||
}
|
||||
ExternType::Module(ty) => {
|
||||
writeln!(self.dst, "(module").unwrap();
|
||||
for ty in ty.imports() {
|
||||
self.import(&ty);
|
||||
writeln!(self.dst, "").unwrap();
|
||||
}
|
||||
for ty in ty.exports() {
|
||||
write!(self.dst, "(export ").unwrap();
|
||||
self.str(ty.name());
|
||||
write!(self.dst, " ").unwrap();
|
||||
self.item_ty(&ty.ty());
|
||||
writeln!(self.dst, ")").unwrap();
|
||||
}
|
||||
write!(self.dst, ")").unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn export(&mut self, ty: &ExportType<'_>) {
|
||||
let wat_name = format!("item{}", self.tmp);
|
||||
self.tmp += 1;
|
||||
let item_ty = ty.ty();
|
||||
self.item(&wat_name, &item_ty);
|
||||
|
||||
write!(self.dst, "(export ").unwrap();
|
||||
self.str(ty.name());
|
||||
write!(self.dst, " (").unwrap();
|
||||
match item_ty {
|
||||
ExternType::Memory(_) => write!(self.dst, "memory").unwrap(),
|
||||
ExternType::Global(_) => write!(self.dst, "global").unwrap(),
|
||||
ExternType::Func(_) => write!(self.dst, "func").unwrap(),
|
||||
ExternType::Instance(_) => write!(self.dst, "instance").unwrap(),
|
||||
ExternType::Table(_) => write!(self.dst, "table").unwrap(),
|
||||
ExternType::Module(_) => write!(self.dst, "module").unwrap(),
|
||||
}
|
||||
writeln!(self.dst, " ${}))", wat_name).unwrap();
|
||||
}
|
||||
|
||||
fn item(&mut self, name: &str, ty: &ExternType) {
|
||||
match ty {
|
||||
ExternType::Memory(mem) => {
|
||||
write!(
|
||||
self.dst,
|
||||
"(memory ${} {} {})\n",
|
||||
name,
|
||||
mem.minimum(),
|
||||
match mem.maximum() {
|
||||
Some(max) => max.to_string(),
|
||||
None => String::new(),
|
||||
}
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
ExternType::Table(table) => {
|
||||
write!(
|
||||
self.dst,
|
||||
"(table ${} {} {} {})\n",
|
||||
name,
|
||||
table.minimum(),
|
||||
match table.maximum() {
|
||||
Some(max) => max.to_string(),
|
||||
None => String::new(),
|
||||
},
|
||||
wat_ty(&table.element()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
ExternType::Global(ty) => {
|
||||
write!(self.dst, "(global ${} ", name).unwrap();
|
||||
if ty.mutability() == Mutability::Var {
|
||||
write!(self.dst, "(mut ").unwrap();
|
||||
}
|
||||
write!(self.dst, "{}", wat_ty(ty.content())).unwrap();
|
||||
if ty.mutability() == Mutability::Var {
|
||||
write!(self.dst, ")").unwrap();
|
||||
}
|
||||
write!(self.dst, " (").unwrap();
|
||||
self.value(ty.content());
|
||||
writeln!(self.dst, "))").unwrap();
|
||||
}
|
||||
ExternType::Func(ty) => {
|
||||
write!(self.dst, "(func ${} ", name).unwrap();
|
||||
self.func_sig(ty);
|
||||
for ty in ty.results() {
|
||||
writeln!(self.dst, "").unwrap();
|
||||
self.value(&ty);
|
||||
}
|
||||
writeln!(self.dst, ")").unwrap();
|
||||
}
|
||||
ExternType::Module(ty) => {
|
||||
writeln!(self.dst, "(module ${}", name).unwrap();
|
||||
for ty in ty.imports() {
|
||||
self.import(&ty);
|
||||
}
|
||||
for ty in ty.exports() {
|
||||
self.export(&ty);
|
||||
}
|
||||
self.dst.push_str(")\n");
|
||||
}
|
||||
ExternType::Instance(ty) => {
|
||||
writeln!(self.dst, "(module ${}_module", name).unwrap();
|
||||
for ty in ty.exports() {
|
||||
self.export(&ty);
|
||||
}
|
||||
self.dst.push_str(")\n");
|
||||
writeln!(self.dst, "(instance ${} (instantiate ${0}_module))", name).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn func_sig(&mut self, ty: &FuncType) {
|
||||
write!(self.dst, "(param ").unwrap();
|
||||
for ty in ty.params() {
|
||||
write!(self.dst, "{} ", wat_ty(&ty)).unwrap();
|
||||
}
|
||||
write!(self.dst, ") (result ").unwrap();
|
||||
for ty in ty.results() {
|
||||
write!(self.dst, "{} ", wat_ty(&ty)).unwrap();
|
||||
}
|
||||
write!(self.dst, ")").unwrap();
|
||||
}
|
||||
|
||||
fn value(&mut self, ty: &ValType) {
|
||||
match ty {
|
||||
ValType::I32 => write!(self.dst, "i32.const 0").unwrap(),
|
||||
ValType::I64 => write!(self.dst, "i64.const 0").unwrap(),
|
||||
ValType::F32 => write!(self.dst, "f32.const 0").unwrap(),
|
||||
ValType::F64 => write!(self.dst, "f64.const 0").unwrap(),
|
||||
ValType::V128 => write!(self.dst, "v128.const i32x4 0 0 0 0").unwrap(),
|
||||
ValType::ExternRef => write!(self.dst, "ref.null extern").unwrap(),
|
||||
ValType::FuncRef => write!(self.dst, "ref.null func").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn str(&mut self, name: &str) {
|
||||
let mut bytes = [0; 4];
|
||||
self.dst.push_str("\"");
|
||||
for c in name.chars() {
|
||||
let v = c as u32;
|
||||
if v >= 0x20 && v < 0x7f && c != '"' && c != '\\' && v < 0xff {
|
||||
self.dst.push(c);
|
||||
} else {
|
||||
for byte in c.encode_utf8(&mut bytes).as_bytes() {
|
||||
self.hex_byte(*byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.dst.push_str("\"");
|
||||
}
|
||||
|
||||
fn hex_byte(&mut self, byte: u8) {
|
||||
fn to_hex(b: u8) -> char {
|
||||
if b < 10 {
|
||||
(b'0' + b) as char
|
||||
} else {
|
||||
(b'a' + b - 10) as char
|
||||
}
|
||||
}
|
||||
self.dst.push('\\');
|
||||
self.dst.push(to_hex((byte >> 4) & 0xf));
|
||||
self.dst.push(to_hex(byte & 0xf));
|
||||
}
|
||||
}
|
||||
|
||||
fn wat_ty(ty: &ValType) -> &'static str {
|
||||
match ty {
|
||||
ValType::I32 => "i32",
|
||||
ValType::I64 => "i64",
|
||||
ValType::F32 => "f32",
|
||||
ValType::F64 => "f64",
|
||||
ValType::V128 => "v128",
|
||||
ValType::ExternRef => "externref",
|
||||
ValType::FuncRef => "funcref",
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn store() -> Store<()> {
|
||||
let mut config = Config::default();
|
||||
config.wasm_module_linking(true);
|
||||
config.wasm_multi_memory(true);
|
||||
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||
Store::new(&engine, ())
|
||||
@@ -423,171 +121,4 @@ mod tests {
|
||||
let func = dummy_func(&mut store, func_ty.clone());
|
||||
assert_eq!(func.ty(&store), func_ty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dummy_instance_import() {
|
||||
let mut store = store();
|
||||
|
||||
let mut instance_ty = InstanceType::new();
|
||||
|
||||
// Functions.
|
||||
instance_ty.add_named_export("func0", FuncType::new(vec![ValType::I32], vec![]).into());
|
||||
instance_ty.add_named_export("func1", FuncType::new(vec![], vec![ValType::I64]).into());
|
||||
|
||||
// Globals.
|
||||
instance_ty.add_named_export(
|
||||
"global0",
|
||||
GlobalType::new(ValType::I32, Mutability::Const).into(),
|
||||
);
|
||||
instance_ty.add_named_export(
|
||||
"global1",
|
||||
GlobalType::new(ValType::I64, Mutability::Var).into(),
|
||||
);
|
||||
|
||||
// Tables.
|
||||
instance_ty.add_named_export("table0", TableType::new(ValType::ExternRef, 1, None).into());
|
||||
instance_ty.add_named_export("table1", TableType::new(ValType::ExternRef, 1, None).into());
|
||||
|
||||
// Memories.
|
||||
instance_ty.add_named_export("memory0", MemoryType::new(1, None).into());
|
||||
instance_ty.add_named_export("memory1", MemoryType::new(1, None).into());
|
||||
|
||||
// Modules.
|
||||
instance_ty.add_named_export("module0", ModuleType::new().into());
|
||||
instance_ty.add_named_export("module1", ModuleType::new().into());
|
||||
|
||||
// Instances.
|
||||
instance_ty.add_named_export("instance0", InstanceType::new().into());
|
||||
instance_ty.add_named_export("instance1", InstanceType::new().into());
|
||||
|
||||
let instance = dummy_instance(&mut store, instance_ty.clone()).unwrap();
|
||||
|
||||
let mut expected_exports = vec![
|
||||
"func0",
|
||||
"func1",
|
||||
"global0",
|
||||
"global1",
|
||||
"table0",
|
||||
"table1",
|
||||
"memory0",
|
||||
"memory1",
|
||||
"module0",
|
||||
"module1",
|
||||
"instance0",
|
||||
"instance1",
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<HashSet<_>>();
|
||||
for exp in instance.ty(&store).exports() {
|
||||
let was_expected = expected_exports.remove(exp.name());
|
||||
assert!(was_expected);
|
||||
}
|
||||
assert!(expected_exports.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dummy_module_import() {
|
||||
let store = store();
|
||||
|
||||
let mut module_ty = ModuleType::new();
|
||||
|
||||
// Multiple exported and imported functions.
|
||||
module_ty.add_named_export("func0", FuncType::new(vec![ValType::I32], vec![]).into());
|
||||
module_ty.add_named_export("func1", FuncType::new(vec![], vec![ValType::I64]).into());
|
||||
module_ty.add_named_import(
|
||||
"func2",
|
||||
None,
|
||||
FuncType::new(vec![ValType::I64], vec![]).into(),
|
||||
);
|
||||
module_ty.add_named_import(
|
||||
"func3",
|
||||
None,
|
||||
FuncType::new(vec![], vec![ValType::I32]).into(),
|
||||
);
|
||||
|
||||
// Multiple exported and imported globals.
|
||||
module_ty.add_named_export(
|
||||
"global0",
|
||||
GlobalType::new(ValType::I32, Mutability::Const).into(),
|
||||
);
|
||||
module_ty.add_named_export(
|
||||
"global1",
|
||||
GlobalType::new(ValType::I64, Mutability::Var).into(),
|
||||
);
|
||||
module_ty.add_named_import(
|
||||
"global2",
|
||||
None,
|
||||
GlobalType::new(ValType::I32, Mutability::Var).into(),
|
||||
);
|
||||
module_ty.add_named_import(
|
||||
"global3",
|
||||
None,
|
||||
GlobalType::new(ValType::I64, Mutability::Const).into(),
|
||||
);
|
||||
|
||||
// Multiple exported and imported tables.
|
||||
module_ty.add_named_export("table0", TableType::new(ValType::ExternRef, 1, None).into());
|
||||
module_ty.add_named_export("table1", TableType::new(ValType::ExternRef, 1, None).into());
|
||||
module_ty.add_named_import(
|
||||
"table2",
|
||||
None,
|
||||
TableType::new(ValType::ExternRef, 1, None).into(),
|
||||
);
|
||||
module_ty.add_named_import(
|
||||
"table3",
|
||||
None,
|
||||
TableType::new(ValType::ExternRef, 1, None).into(),
|
||||
);
|
||||
|
||||
// Multiple exported and imported memories.
|
||||
module_ty.add_named_export("memory0", MemoryType::new(1, None).into());
|
||||
module_ty.add_named_export("memory1", MemoryType::new(1, None).into());
|
||||
module_ty.add_named_import("memory2", None, MemoryType::new(1, None).into());
|
||||
module_ty.add_named_import("memory3", None, MemoryType::new(1, None).into());
|
||||
|
||||
// An exported and an imported module.
|
||||
module_ty.add_named_export("module0", ModuleType::new().into());
|
||||
module_ty.add_named_import("module1", None, ModuleType::new().into());
|
||||
|
||||
// An exported and an imported instance.
|
||||
module_ty.add_named_export("instance0", InstanceType::new().into());
|
||||
module_ty.add_named_import("instance1", None, InstanceType::new().into());
|
||||
|
||||
// Create the module.
|
||||
let module = dummy_module(store.engine(), module_ty);
|
||||
|
||||
// Check that we have the expected exports.
|
||||
assert!(module.get_export("func0").is_some());
|
||||
assert!(module.get_export("func1").is_some());
|
||||
assert!(module.get_export("global0").is_some());
|
||||
assert!(module.get_export("global1").is_some());
|
||||
assert!(module.get_export("table0").is_some());
|
||||
assert!(module.get_export("table1").is_some());
|
||||
assert!(module.get_export("memory0").is_some());
|
||||
assert!(module.get_export("memory1").is_some());
|
||||
assert!(module.get_export("instance0").is_some());
|
||||
assert!(module.get_export("module0").is_some());
|
||||
|
||||
// Check that we have the exported imports.
|
||||
let mut expected_imports = vec![
|
||||
"func2",
|
||||
"func3",
|
||||
"global2",
|
||||
"global3",
|
||||
"table2",
|
||||
"table3",
|
||||
"memory2",
|
||||
"memory3",
|
||||
"instance1",
|
||||
"module1",
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<HashSet<_>>();
|
||||
for imp in module.imports() {
|
||||
assert!(imp.name().is_none());
|
||||
let was_expected = expected_imports.remove(imp.module());
|
||||
assert!(was_expected);
|
||||
}
|
||||
assert!(expected_imports.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,8 @@ use std::str;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use wasmtime_environ::{
|
||||
CompileError, DefinedFuncIndex, FuncIndex, FunctionInfo, InstanceSignature, InstanceTypeIndex,
|
||||
Module, ModuleSignature, ModuleTranslation, ModuleTypeIndex, PrimaryMap, SignatureIndex,
|
||||
StackMapInformation, Trampoline, Tunables, WasmFuncType, ELF_WASMTIME_ADDRMAP,
|
||||
CompileError, DefinedFuncIndex, FuncIndex, FunctionInfo, Module, ModuleTranslation, PrimaryMap,
|
||||
SignatureIndex, StackMapInformation, Trampoline, Tunables, WasmFuncType, ELF_WASMTIME_ADDRMAP,
|
||||
ELF_WASMTIME_TRAPS,
|
||||
};
|
||||
use wasmtime_runtime::{
|
||||
@@ -365,8 +364,6 @@ pub fn mmap_vec_from_obj(obj: Object) -> Result<MmapVec> {
|
||||
#[allow(missing_docs)]
|
||||
pub struct TypeTables {
|
||||
pub wasm_signatures: PrimaryMap<SignatureIndex, WasmFuncType>,
|
||||
pub module_signatures: PrimaryMap<ModuleTypeIndex, ModuleSignature>,
|
||||
pub instance_signatures: PrimaryMap<InstanceTypeIndex, InstanceSignature>,
|
||||
}
|
||||
|
||||
/// A compiled wasm module, ready to be instantiated.
|
||||
|
||||
@@ -336,10 +336,6 @@ impl Instance {
|
||||
global: self.module().globals[*index],
|
||||
}
|
||||
.into(),
|
||||
|
||||
EntityIndex::Instance(_) | EntityIndex::Module(_) => {
|
||||
panic!("can't use this api for modules/instances")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -213,31 +213,11 @@ entity_impl!(ElemIndex);
|
||||
pub struct TypeIndex(u32);
|
||||
entity_impl!(TypeIndex);
|
||||
|
||||
/// Index type of a module inside the WebAssembly module.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub struct ModuleIndex(u32);
|
||||
entity_impl!(ModuleIndex);
|
||||
|
||||
/// Index type of an instance inside the WebAssembly module.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub struct InstanceIndex(u32);
|
||||
entity_impl!(InstanceIndex);
|
||||
|
||||
/// Index type of an event inside the WebAssembly module.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub struct TagIndex(u32);
|
||||
entity_impl!(TagIndex);
|
||||
|
||||
/// Specialized index for just module types.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub struct ModuleTypeIndex(u32);
|
||||
entity_impl!(ModuleTypeIndex);
|
||||
|
||||
/// Specialized index for just instance types.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub struct InstanceTypeIndex(u32);
|
||||
entity_impl!(InstanceTypeIndex);
|
||||
|
||||
/// An index of an entity.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub enum EntityIndex {
|
||||
@@ -249,10 +229,6 @@ pub enum EntityIndex {
|
||||
Memory(MemoryIndex),
|
||||
/// Global index.
|
||||
Global(GlobalIndex),
|
||||
/// Module index.
|
||||
Module(ModuleIndex),
|
||||
/// Instance index.
|
||||
Instance(InstanceIndex),
|
||||
}
|
||||
|
||||
/// A type of an item in a wasm module where an item is typically something that
|
||||
@@ -271,12 +247,6 @@ pub enum EntityType {
|
||||
/// A function type where the index points to the type section and records a
|
||||
/// function signature.
|
||||
Function(SignatureIndex),
|
||||
/// An instance where the index points to the type section and records a
|
||||
/// instance's exports.
|
||||
Instance(InstanceTypeIndex),
|
||||
/// A module where the index points to the type section and records a
|
||||
/// module's imports and exports.
|
||||
Module(ModuleTypeIndex),
|
||||
}
|
||||
|
||||
/// A WebAssembly global.
|
||||
|
||||
@@ -602,20 +602,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether the WebAssembly module linking [proposal] will
|
||||
/// be enabled for compilation.
|
||||
///
|
||||
/// Note that development of this feature is still underway, so enabling
|
||||
/// this is likely to be full of bugs.
|
||||
///
|
||||
/// This is `false` by default.
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/module-linking
|
||||
pub fn wasm_module_linking(&mut self, enable: bool) -> &mut Self {
|
||||
self.features.module_linking = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether the WebAssembly memory64 [proposal] will
|
||||
/// be enabled for compilation.
|
||||
///
|
||||
@@ -1374,7 +1360,6 @@ impl fmt::Debug for Config {
|
||||
.field("wasm_bulk_memory", &self.features.bulk_memory)
|
||||
.field("wasm_simd", &self.features.simd)
|
||||
.field("wasm_multi_value", &self.features.multi_value)
|
||||
.field("wasm_module_linking", &self.features.module_linking)
|
||||
.field(
|
||||
"static_memory_maximum_size",
|
||||
&(u64::from(self.tunables.static_memory_bound)
|
||||
|
||||
@@ -193,9 +193,8 @@ impl Engine {
|
||||
pub fn precompile_module(&self, bytes: &[u8]) -> Result<Vec<u8>> {
|
||||
#[cfg(feature = "wat")]
|
||||
let bytes = wat::parse_bytes(&bytes)?;
|
||||
let (_, artifacts, types) = crate::Module::build_artifacts(self, &bytes)?;
|
||||
let artifacts = artifacts.into_iter().map(|i| i.0).collect::<Vec<_>>();
|
||||
crate::module::SerializedModule::from_artifacts(self, &artifacts, &types)
|
||||
let (mmap, _, types) = crate::Module::build_artifacts(self, &bytes)?;
|
||||
crate::module::SerializedModule::from_artifacts(self, &mmap, &types)
|
||||
.to_bytes(&self.config().module_version)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::store::{StoreData, StoreOpaque, Stored};
|
||||
use crate::trampoline::{generate_global_export, generate_table_export};
|
||||
use crate::{
|
||||
AsContext, AsContextMut, ExternRef, ExternType, Func, GlobalType, Instance, Memory, Module,
|
||||
Mutability, TableType, Trap, Val, ValType,
|
||||
AsContext, AsContextMut, ExternRef, ExternType, Func, GlobalType, Memory, Mutability,
|
||||
TableType, Trap, Val, ValType,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use std::mem;
|
||||
@@ -29,10 +29,6 @@ pub enum Extern {
|
||||
Table(Table),
|
||||
/// A WebAssembly linear memory.
|
||||
Memory(Memory),
|
||||
/// A WebAssembly instance.
|
||||
Instance(Instance),
|
||||
/// A WebAssembly module.
|
||||
Module(Module),
|
||||
}
|
||||
|
||||
impl Extern {
|
||||
@@ -76,26 +72,6 @@ impl Extern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying `Instance`, if this external is a instance.
|
||||
///
|
||||
/// Returns `None` if this is not a instance.
|
||||
pub fn into_instance(self) -> Option<Instance> {
|
||||
match self {
|
||||
Extern::Instance(instance) => Some(instance),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying `Module`, if this external is a module.
|
||||
///
|
||||
/// Returns `None` if this is not a module.
|
||||
pub fn into_module(self) -> Option<Module> {
|
||||
match self {
|
||||
Extern::Module(module) => Some(module),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type associated with this `Extern`.
|
||||
///
|
||||
/// The `store` argument provided must own this `Extern` and is used to look
|
||||
@@ -111,8 +87,6 @@ impl Extern {
|
||||
Extern::Memory(ft) => ExternType::Memory(ft.ty(store)),
|
||||
Extern::Table(tt) => ExternType::Table(tt.ty(store)),
|
||||
Extern::Global(gt) => ExternType::Global(gt.ty(store)),
|
||||
Extern::Instance(i) => ExternType::Instance(i.ty(store)),
|
||||
Extern::Module(m) => ExternType::Module(m.ty()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,10 +116,6 @@ impl Extern {
|
||||
Extern::Global(g) => store.store_data().contains(g.0),
|
||||
Extern::Memory(m) => m.comes_from_same_store(store),
|
||||
Extern::Table(t) => store.store_data().contains(t.0),
|
||||
Extern::Instance(i) => i.comes_from_same_store(store),
|
||||
// Modules don't live in stores right now, so they're compatible
|
||||
// with all stores.
|
||||
Extern::Module(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +125,6 @@ impl Extern {
|
||||
Extern::Table(_) => "table",
|
||||
Extern::Memory(_) => "memory",
|
||||
Extern::Global(_) => "global",
|
||||
Extern::Instance(_) => "instance",
|
||||
Extern::Module(_) => "module",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,18 +153,6 @@ impl From<Table> for Extern {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Instance> for Extern {
|
||||
fn from(r: Instance) -> Self {
|
||||
Extern::Instance(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Module> for Extern {
|
||||
fn from(r: Module) -> Self {
|
||||
Extern::Module(r)
|
||||
}
|
||||
}
|
||||
|
||||
/// A WebAssembly `global` value which can be read and written to.
|
||||
///
|
||||
/// A `global` in WebAssembly is sort of like a global variable within an
|
||||
@@ -205,9 +161,10 @@ impl From<Module> for Extern {
|
||||
/// can either be imported or exported from wasm modules.
|
||||
///
|
||||
/// A [`Global`] "belongs" to the store that it was originally created within
|
||||
/// (either via [`Global::new`] or via instantiating a [`Module`]). Operations
|
||||
/// on a [`Global`] only work with the store it belongs to, and if another store
|
||||
/// is passed in by accident then methods will panic.
|
||||
/// (either via [`Global::new`] or via instantiating a
|
||||
/// [`Module`](crate::Module)). Operations on a [`Global`] only work with the
|
||||
/// store it belongs to, and if another store is passed in by accident then
|
||||
/// methods will panic.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)] // here for the C API
|
||||
pub struct Global(Stored<wasmtime_runtime::ExportGlobal>);
|
||||
@@ -385,9 +342,10 @@ impl Global {
|
||||
/// `funcref` table), where each element has the `ValType::FuncRef` type.
|
||||
///
|
||||
/// A [`Table`] "belongs" to the store that it was originally created within
|
||||
/// (either via [`Table::new`] or via instantiating a [`Module`]). Operations
|
||||
/// on a [`Table`] only work with the store it belongs to, and if another store
|
||||
/// is passed in by accident then methods will panic.
|
||||
/// (either via [`Table::new`] or via instantiating a
|
||||
/// [`Module`](crate::Module)). Operations on a [`Table`] only work with the
|
||||
/// store it belongs to, and if another store is passed in by accident then
|
||||
/// methods will panic.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)] // here for the C API
|
||||
pub struct Table(Stored<wasmtime_runtime::ExportTable>);
|
||||
@@ -787,16 +745,4 @@ impl<'instance> Export<'instance> {
|
||||
pub fn into_global(self) -> Option<Global> {
|
||||
self.definition.into_global()
|
||||
}
|
||||
|
||||
/// Consume this `Export` and return the contained `Instance`, if it's a
|
||||
/// instance, or `None` otherwise.
|
||||
pub fn into_instance(self) -> Option<Instance> {
|
||||
self.definition.into_instance()
|
||||
}
|
||||
|
||||
/// Consume this `Export` and return the contained `Module`, if it's a
|
||||
/// module, or `None` otherwise.
|
||||
pub fn into_module(self) -> Option<Module> {
|
||||
self.definition.into_module()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
use crate::linker::Definition;
|
||||
use crate::signatures::SignatureCollection;
|
||||
use crate::store::{InstanceId, StoreData, StoreOpaque, Stored};
|
||||
use crate::store::{InstanceId, StoreOpaque, Stored};
|
||||
use crate::types::matching;
|
||||
use crate::{
|
||||
AsContext, AsContextMut, Engine, Export, Extern, ExternType, Func, Global, InstanceType,
|
||||
Memory, Module, StoreContextMut, Table, Trap, TypedFunc,
|
||||
AsContextMut, Engine, Export, Extern, Func, Global, Memory, Module, StoreContextMut, Table,
|
||||
Trap, TypedFunc,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::{
|
||||
EntityIndex, EntityType, FuncIndex, GlobalIndex, Initializer, InstanceIndex, MemoryIndex,
|
||||
ModuleIndex, PrimaryMap, TableIndex,
|
||||
EntityIndex, EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex,
|
||||
};
|
||||
use wasmtime_jit::TypeTables;
|
||||
use wasmtime_runtime::{
|
||||
Imports, InstanceAllocationRequest, InstantiationError, StorePtr, VMContext, VMFunctionBody,
|
||||
VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport,
|
||||
@@ -36,25 +33,14 @@ use wasmtime_runtime::{
|
||||
#[repr(transparent)]
|
||||
pub struct Instance(Stored<InstanceData>);
|
||||
|
||||
pub(crate) enum InstanceData {
|
||||
/// This variant is used for instances created through instantiation of a
|
||||
/// module, e.g. `Instance::new` or various linker methods.
|
||||
Instantiated {
|
||||
/// The id of the instance within the store, used to find the original
|
||||
/// `InstanceHandle`.
|
||||
id: InstanceId,
|
||||
/// A lazily-populated list of exports of this instance. The order of
|
||||
/// exports here matches the order of the exports in the the original
|
||||
/// module.
|
||||
exports: Vec<Option<Extern>>,
|
||||
/// The type information of the module that this was instantiated with.
|
||||
types: Arc<TypeTables>,
|
||||
signatures: Arc<SignatureCollection>,
|
||||
},
|
||||
|
||||
/// This variant is used for synthetically created instances via `Linker`
|
||||
/// APIs. This is only used for the module linking proposal at this time.
|
||||
Synthetic(Arc<indexmap::IndexMap<String, Extern>>),
|
||||
pub(crate) struct InstanceData {
|
||||
/// The id of the instance within the store, used to find the original
|
||||
/// `InstanceHandle`.
|
||||
id: InstanceId,
|
||||
/// A lazily-populated list of exports of this instance. The order of
|
||||
/// exports here matches the order of the exports in the the original
|
||||
/// module.
|
||||
exports: Vec<Option<Extern>>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
@@ -181,39 +167,6 @@ impl Instance {
|
||||
Instance(store.store_data_mut().insert(handle))
|
||||
}
|
||||
|
||||
/// Returns the type signature of this instance.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `store` does not own this instance.
|
||||
pub fn ty(&self, store: impl AsContext) -> InstanceType {
|
||||
let store = store.as_context();
|
||||
let mut ty = InstanceType::new();
|
||||
match &store[self.0] {
|
||||
InstanceData::Synthetic(items) => {
|
||||
for (name, item) in items.iter() {
|
||||
ty.add_named_export(name, item.ty(&store));
|
||||
}
|
||||
}
|
||||
InstanceData::Instantiated { id, types, .. } => {
|
||||
let module = store.0.instance(*id).module();
|
||||
for (name, idx) in module.exports.iter() {
|
||||
let export_ty = module.type_of(*idx);
|
||||
ty.add_named_export(name, ExternType::from_wasmtime(types, &export_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
||||
pub(crate) fn data<'a>(&self, store: &'a StoreData) -> &'a InstanceData {
|
||||
&store[self.0]
|
||||
}
|
||||
|
||||
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
|
||||
store.store_data().contains(self.0)
|
||||
}
|
||||
|
||||
/// Returns the list of exported items from this [`Instance`].
|
||||
///
|
||||
/// # Panics
|
||||
@@ -232,70 +185,21 @@ impl Instance {
|
||||
) -> impl ExactSizeIterator<Item = Export<'a>> + 'a {
|
||||
// If this is an `Instantiated` instance then all the `exports` may not
|
||||
// be filled in. Fill them all in now if that's the case.
|
||||
if let InstanceData::Instantiated { exports, id, .. } = &store[self.0] {
|
||||
if exports.iter().any(|e| e.is_none()) {
|
||||
let module = Arc::clone(store.instance(*id).module());
|
||||
for name in module.exports.keys() {
|
||||
self._get_export(store, name);
|
||||
}
|
||||
let InstanceData { exports, id, .. } = &store[self.0];
|
||||
if exports.iter().any(|e| e.is_none()) {
|
||||
let module = Arc::clone(store.instance(*id).module());
|
||||
for name in module.exports.keys() {
|
||||
self._get_export(store, name);
|
||||
}
|
||||
}
|
||||
|
||||
return match &store.store_data()[self.0] {
|
||||
InstanceData::Synthetic(names) => {
|
||||
Either::A(names.iter().map(|(k, v)| Export::new(k, v.clone())))
|
||||
}
|
||||
InstanceData::Instantiated { exports, id, .. } => {
|
||||
let module = store.instance(*id).module();
|
||||
Either::B(
|
||||
module
|
||||
.exports
|
||||
.iter()
|
||||
.zip(exports)
|
||||
.map(|((name, _), export)| Export::new(name, export.clone().unwrap())),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
enum Either<A, B> {
|
||||
A(A),
|
||||
B(B),
|
||||
}
|
||||
|
||||
impl<A, B> Iterator for Either<A, B>
|
||||
where
|
||||
A: Iterator,
|
||||
B: Iterator<Item = A::Item>,
|
||||
{
|
||||
type Item = A::Item;
|
||||
|
||||
fn next(&mut self) -> Option<A::Item> {
|
||||
match self {
|
||||
Either::A(a) => a.next(),
|
||||
Either::B(b) => b.next(),
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self {
|
||||
Either::A(a) => a.size_hint(),
|
||||
Either::B(b) => b.size_hint(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> ExactSizeIterator for Either<A, B>
|
||||
where
|
||||
A: ExactSizeIterator,
|
||||
B: ExactSizeIterator<Item = A::Item>,
|
||||
{
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
Either::A(a) => a.len(),
|
||||
Either::B(b) => b.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
let data = &store.store_data()[self.0];
|
||||
let module = store.instance(data.id).module();
|
||||
module
|
||||
.exports
|
||||
.iter()
|
||||
.zip(&data.exports)
|
||||
.map(|((name, _), export)| Export::new(name, export.clone().unwrap()))
|
||||
}
|
||||
|
||||
/// Looks up an exported [`Extern`] value by name.
|
||||
@@ -320,33 +224,23 @@ impl Instance {
|
||||
}
|
||||
|
||||
fn _get_export(&self, store: &mut StoreOpaque, name: &str) -> Option<Extern> {
|
||||
match &store[self.0] {
|
||||
// Synthetic instances always have their entire list of exports
|
||||
// already specified.
|
||||
InstanceData::Synthetic(names) => names.get(name).cloned(),
|
||||
// Instantiated instances will lazily fill in exports, so we process
|
||||
// all that lazy logic here.
|
||||
let data = &store[self.0];
|
||||
|
||||
// Instantiated instances will lazily fill in exports, so we process
|
||||
// all that lazy logic here.
|
||||
InstanceData::Instantiated { id, exports, .. } => {
|
||||
let id = *id;
|
||||
let instance = store.instance(id);
|
||||
let (i, _, &index) = instance.module().exports.get_full(name)?;
|
||||
if let Some(export) = &exports[i] {
|
||||
return Some(export.clone());
|
||||
}
|
||||
|
||||
let instance = store.instance_mut(id); // reborrow the &mut Instancehandle
|
||||
let item = unsafe {
|
||||
Extern::from_wasmtime_export(instance.lookup_by_declaration(&index), store)
|
||||
};
|
||||
let exports = match &mut store[self.0] {
|
||||
InstanceData::Instantiated { exports, .. } => exports,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
exports[i] = Some(item.clone());
|
||||
Some(item)
|
||||
}
|
||||
let instance = store.instance(data.id);
|
||||
let (i, _, &index) = instance.module().exports.get_full(name)?;
|
||||
if let Some(export) = &data.exports[i] {
|
||||
return Some(export.clone());
|
||||
}
|
||||
|
||||
let id = data.id;
|
||||
let instance = store.instance_mut(id); // reborrow the &mut Instancehandle
|
||||
let item =
|
||||
unsafe { Extern::from_wasmtime_export(instance.lookup_by_declaration(&index), store) };
|
||||
let data = &mut store[self.0];
|
||||
data.exports[i] = Some(item.clone());
|
||||
Some(item)
|
||||
}
|
||||
|
||||
/// Looks up an exported [`Func`] value by name.
|
||||
@@ -428,26 +322,17 @@ impl Instance {
|
||||
}
|
||||
|
||||
struct Instantiator<'a> {
|
||||
in_progress: Vec<ImportsBuilder<'a>>,
|
||||
cur: ImportsBuilder<'a>,
|
||||
}
|
||||
|
||||
struct ImportsBuilder<'a> {
|
||||
src: ImportSource<'a>,
|
||||
imports: ImportSource<'a>,
|
||||
functions: PrimaryMap<FuncIndex, VMFunctionImport>,
|
||||
tables: PrimaryMap<TableIndex, VMTableImport>,
|
||||
memories: PrimaryMap<MemoryIndex, VMMemoryImport>,
|
||||
globals: PrimaryMap<GlobalIndex, VMGlobalImport>,
|
||||
instances: PrimaryMap<InstanceIndex, Instance>,
|
||||
modules: PrimaryMap<ModuleIndex, Module>,
|
||||
initializer: usize,
|
||||
module: Module,
|
||||
module: &'a Module,
|
||||
}
|
||||
|
||||
enum ImportSource<'a> {
|
||||
Externs(&'a [Extern]),
|
||||
Definitions(&'a [Definition]),
|
||||
Outer { initializer: usize },
|
||||
}
|
||||
|
||||
impl<'a> Instantiator<'a> {
|
||||
@@ -473,225 +358,77 @@ impl<'a> Instantiator<'a> {
|
||||
/// * The `imports` must all come from the `store` specified.
|
||||
unsafe fn new(
|
||||
store: &StoreOpaque,
|
||||
module: &Module,
|
||||
module: &'a Module,
|
||||
imports: ImportSource<'a>,
|
||||
) -> Result<Instantiator<'a>> {
|
||||
if !Engine::same(store.engine(), module.engine()) {
|
||||
bail!("cross-`Engine` instantiation is not currently supported");
|
||||
}
|
||||
|
||||
let raw = module.compiled_module().module();
|
||||
Ok(Instantiator {
|
||||
in_progress: Vec::new(),
|
||||
cur: ImportsBuilder::new(module, imports),
|
||||
imports,
|
||||
functions: PrimaryMap::with_capacity(raw.num_imported_funcs),
|
||||
tables: PrimaryMap::with_capacity(raw.num_imported_tables),
|
||||
memories: PrimaryMap::with_capacity(raw.num_imported_memories),
|
||||
globals: PrimaryMap::with_capacity(raw.num_imported_globals),
|
||||
module,
|
||||
})
|
||||
}
|
||||
|
||||
fn run<T>(&mut self, store: &mut StoreContextMut<'_, T>) -> Result<Instance, Error> {
|
||||
loop {
|
||||
if let Some((instance, start, toplevel)) = self.step(store.0)? {
|
||||
if let Some(start) = start {
|
||||
Instantiator::start_raw(store, instance, start)?;
|
||||
}
|
||||
if toplevel {
|
||||
break Ok(instance);
|
||||
}
|
||||
}
|
||||
let (instance, start) = self.resolve_imports(store.0)?;
|
||||
if let Some(start) = start {
|
||||
Instantiator::start_raw(store, instance, start)?;
|
||||
}
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
/// Processes the next initializer for the next instance being created
|
||||
/// without running any wasm code.
|
||||
///
|
||||
/// This function will process module initializers, handling recursive
|
||||
/// instantiations of modules for module linking if necessary as well. This
|
||||
/// does not actually execute any WebAssembly code, which means that it
|
||||
/// will return whenever an instance is created (because its `start`
|
||||
/// function may need to be executed).
|
||||
///
|
||||
/// If this function returns `None`, then it simply needs to be called
|
||||
/// again to execute the next initializer. Otherwise this function has two
|
||||
/// return values:
|
||||
///
|
||||
/// * The first is the raw handle to the instance that was just created.
|
||||
/// This instance must have its start function executed by the caller.
|
||||
/// * The second is an optional list of items to get wrapped up in an
|
||||
/// `Instance`. This is only `Some` for the outermost instance that was
|
||||
/// created. If this is `None` callers need to keep calling this function
|
||||
/// since the instance created was simply for a recursive instance
|
||||
/// defined here.
|
||||
fn step(
|
||||
/// Resolve all the imports for the module being instantiated, extracting
|
||||
/// the raw representations and building up the `PrimaryMap` instance for
|
||||
/// each set of exports.
|
||||
fn resolve_imports(
|
||||
&mut self,
|
||||
store: &mut StoreOpaque,
|
||||
) -> Result<Option<(Instance, Option<FuncIndex>, bool)>> {
|
||||
if self.cur.initializer == 0 {
|
||||
store.bump_resource_counts(&self.cur.module)?;
|
||||
}
|
||||
) -> Result<(Instance, Option<FuncIndex>)> {
|
||||
store.bump_resource_counts(&self.module)?;
|
||||
|
||||
// Read the current module's initializer and move forward the
|
||||
// initializer pointer as well.
|
||||
self.cur.initializer += 1;
|
||||
match self
|
||||
.cur
|
||||
.module
|
||||
.env_module()
|
||||
.initializers
|
||||
.get(self.cur.initializer - 1)
|
||||
{
|
||||
Some(Initializer::Import { name, field, .. }) => {
|
||||
match &mut self.cur.src {
|
||||
// If imports are coming from the runtime-provided list
|
||||
// (e.g. the root module being instantiated) then we
|
||||
// need to typecheck each item here before recording it.
|
||||
//
|
||||
// Note the `unwrap` here should be ok given the validation
|
||||
// above in `Instantiation::new`.
|
||||
ImportSource::Externs(list) => {
|
||||
let (head, remaining) = list.split_first().unwrap();
|
||||
*list = remaining;
|
||||
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
|
||||
// instance due to a recursive instantiation then we
|
||||
// look in the previous initializer's mapping of
|
||||
// arguments to figure out where to load the item from.
|
||||
// Note that no typechecking is necessary here due to
|
||||
// validation.
|
||||
ImportSource::Outer { initializer } => {
|
||||
debug_assert!(field.is_none());
|
||||
let outer = self.in_progress.last().unwrap();
|
||||
let args = match &outer.module.env_module().initializers[*initializer] {
|
||||
Initializer::Instantiate { args, .. } => args,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let index = args.get(name).expect("should be present after validation");
|
||||
match *index {
|
||||
EntityIndex::Global(i) => {
|
||||
self.cur.globals.push(outer.globals[i]);
|
||||
}
|
||||
EntityIndex::Function(i) => {
|
||||
self.cur.functions.push(outer.functions[i]);
|
||||
}
|
||||
EntityIndex::Table(i) => {
|
||||
self.cur.tables.push(outer.tables[i]);
|
||||
}
|
||||
EntityIndex::Memory(i) => {
|
||||
self.cur.memories.push(outer.memories[i]);
|
||||
}
|
||||
EntityIndex::Module(i) => {
|
||||
self.cur.modules.push(outer.modules[i].clone());
|
||||
}
|
||||
EntityIndex::Instance(i) => {
|
||||
self.cur.instances.push(outer.instances[i].clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let num_imports = self.module.env_module().initializers.len();
|
||||
match &self.imports {
|
||||
ImportSource::Externs(list) => {
|
||||
assert_eq!(list.len(), num_imports);
|
||||
for item in list.iter() {
|
||||
self.push(item.clone(), store);
|
||||
}
|
||||
}
|
||||
|
||||
// Here we lookup our instance handle, find the right export,
|
||||
// and then push that item into our own index space. We eschew
|
||||
// type-checking since only valid modules should reach this point.
|
||||
Some(Initializer::AliasInstanceExport { instance, export }) => {
|
||||
let instance = self.cur.instances[*instance];
|
||||
let export = instance._get_export(store, export).unwrap();
|
||||
self.cur.push(export, store);
|
||||
}
|
||||
|
||||
// A recursive instantiation of an instance.
|
||||
//
|
||||
// The `module` argument is used to create an import builder
|
||||
// object, and we specify that the source of imports for the builder is
|
||||
// this initializer's position so we can look at the `args` payload
|
||||
// later.
|
||||
//
|
||||
// Once that's set up we save off `self.cur` into
|
||||
// `self.in_progress` and start the instantiation of the child
|
||||
// instance on the next execution of this function.
|
||||
Some(Initializer::Instantiate { module, args: _ }) => {
|
||||
let module = &self.cur.modules[*module];
|
||||
let imports = ImportsBuilder::new(
|
||||
module,
|
||||
ImportSource::Outer {
|
||||
initializer: self.cur.initializer - 1,
|
||||
},
|
||||
);
|
||||
let prev = mem::replace(&mut self.cur, imports);
|
||||
self.in_progress.push(prev);
|
||||
}
|
||||
|
||||
// A new module is being defined, and the source of this module is
|
||||
// our module's list of closed-over-modules.
|
||||
//
|
||||
// This is used for outer aliases.
|
||||
Some(Initializer::DefineModule(upvar_index)) => {
|
||||
self.cur
|
||||
.modules
|
||||
.push(self.cur.module.module_upvar(*upvar_index).clone());
|
||||
}
|
||||
|
||||
// A new module is defined, created from a set of compiled
|
||||
// artifacts. The new module value will be created with the
|
||||
// specified artifacts being closed over as well as the specified
|
||||
// set of module values in our index/upvar index spaces being closed
|
||||
// over.
|
||||
//
|
||||
// This is used for defining submodules.
|
||||
Some(Initializer::CreateModule {
|
||||
artifact_index,
|
||||
artifacts,
|
||||
modules,
|
||||
}) => {
|
||||
let submodule = self.cur.module.create_submodule(
|
||||
*artifact_index,
|
||||
artifacts,
|
||||
modules,
|
||||
&self.cur.modules,
|
||||
)?;
|
||||
self.cur.modules.push(submodule);
|
||||
}
|
||||
|
||||
// All initializers have been processed, which means we're ready to
|
||||
// perform the actual raw instantiation with the raw import values.
|
||||
// Once that's done if there's an in-progress module we record the
|
||||
// instance in the index space. Otherwise this is the final module
|
||||
// and we return the items out.
|
||||
//
|
||||
// Note that in all cases we return the raw instance handle to get
|
||||
// the start function executed by the outer context.
|
||||
None => {
|
||||
let (instance, start) = self.instantiate_raw(store)?;
|
||||
let toplevel = match self.in_progress.pop() {
|
||||
Some(imports) => {
|
||||
self.cur = imports;
|
||||
self.cur.instances.push(instance);
|
||||
false
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
return Ok(Some((instance, start, toplevel)));
|
||||
ImportSource::Definitions(list) => {
|
||||
assert_eq!(list.len(), num_imports);
|
||||
for item in list.iter() {
|
||||
// This unsafety is encapsulated with
|
||||
// `Instantiator::new`, documented above.
|
||||
self.push(unsafe { item.to_extern(store) }, store);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
// All initializers have been processed, which means we're ready to
|
||||
// perform the actual raw instantiation with the raw import values. This
|
||||
// will register everything except the start function's completion and
|
||||
// the finished instance will be returned.
|
||||
self.instantiate_raw(store)
|
||||
}
|
||||
|
||||
fn instantiate_raw(
|
||||
&mut self,
|
||||
store: &mut StoreOpaque,
|
||||
) -> Result<(Instance, Option<FuncIndex>)> {
|
||||
let compiled_module = self.cur.module.compiled_module();
|
||||
let compiled_module = self.module.compiled_module();
|
||||
|
||||
// Register the module just before instantiation to ensure we keep the module
|
||||
// properly referenced while in use by the store.
|
||||
store.modules_mut().register(&self.cur.module);
|
||||
store.modules_mut().register(&self.module);
|
||||
|
||||
unsafe {
|
||||
// The first thing we do is issue an instance allocation request
|
||||
@@ -710,8 +447,8 @@ impl<'a> Instantiator<'a> {
|
||||
.engine()
|
||||
.allocator()
|
||||
.allocate(InstanceAllocationRequest {
|
||||
runtime_info: &self.cur.module.runtime_info(),
|
||||
imports: self.cur.build(),
|
||||
runtime_info: &self.module.runtime_info(),
|
||||
imports: self.build(),
|
||||
host_state: Box::new(Instance(instance_to_be)),
|
||||
store: StorePtr::new(store.traitobj()),
|
||||
})?;
|
||||
@@ -747,28 +484,9 @@ impl<'a> Instantiator<'a> {
|
||||
.module()
|
||||
.exports
|
||||
.values()
|
||||
.map(|index| {
|
||||
// Note that instances and modules are not handled by
|
||||
// `wasmtime_runtime`, they're handled by us in this crate. That
|
||||
// means we need to handle that here, otherwise we defer to the
|
||||
// instance to load the values.
|
||||
match *index {
|
||||
EntityIndex::Instance(i) => {
|
||||
Some(Extern::Instance(self.cur.instances[i].clone()))
|
||||
}
|
||||
EntityIndex::Module(i) => {
|
||||
Some(Extern::Module(self.cur.modules[i].clone()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.map(|_index| None)
|
||||
.collect();
|
||||
let data = InstanceData::Instantiated {
|
||||
id,
|
||||
exports,
|
||||
types: Arc::clone(self.cur.module.types()),
|
||||
signatures: Arc::clone(self.cur.module.signatures()),
|
||||
};
|
||||
let data = InstanceData { id, exports };
|
||||
Instance::from_wasmtime(data, store)
|
||||
};
|
||||
|
||||
@@ -810,10 +528,7 @@ impl<'a> Instantiator<'a> {
|
||||
instance: Instance,
|
||||
start: FuncIndex,
|
||||
) -> Result<()> {
|
||||
let id = match &store.0.store_data()[instance.0] {
|
||||
InstanceData::Instantiated { id, .. } => *id,
|
||||
InstanceData::Synthetic(_) => return Ok(()),
|
||||
};
|
||||
let id = store.0.store_data()[instance.0].id;
|
||||
// If a start function is present, invoke it. Make sure we use all the
|
||||
// trap-handling configuration in `store` as well.
|
||||
let instance = store.0.instance_mut(id);
|
||||
@@ -834,23 +549,6 @@ impl<'a> Instantiator<'a> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ImportsBuilder<'a> {
|
||||
fn new(module: &Module, src: ImportSource<'a>) -> ImportsBuilder<'a> {
|
||||
let raw = module.compiled_module().module();
|
||||
ImportsBuilder {
|
||||
src,
|
||||
functions: PrimaryMap::with_capacity(raw.num_imported_funcs),
|
||||
tables: PrimaryMap::with_capacity(raw.num_imported_tables),
|
||||
memories: PrimaryMap::with_capacity(raw.num_imported_memories),
|
||||
globals: PrimaryMap::with_capacity(raw.num_imported_globals),
|
||||
instances: PrimaryMap::with_capacity(raw.instances.len()),
|
||||
modules: PrimaryMap::with_capacity(raw.modules.len()),
|
||||
module: module.clone(),
|
||||
initializer: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, item: Extern, store: &mut StoreOpaque) {
|
||||
match item {
|
||||
@@ -866,12 +564,6 @@ impl<'a> ImportsBuilder<'a> {
|
||||
Extern::Memory(i) => {
|
||||
self.memories.push(i.vmimport(store));
|
||||
}
|
||||
Extern::Instance(i) => {
|
||||
self.instances.push(i);
|
||||
}
|
||||
Extern::Module(m) => {
|
||||
self.modules.push(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1067,13 +759,8 @@ fn typecheck<I>(
|
||||
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)
|
||||
})?;
|
||||
check(&cx, &expected_ty, actual)
|
||||
.with_context(|| format!("incompatible import type for `{name}::{field}`"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::func::HostFunc;
|
||||
use crate::instance::{InstanceData, InstancePre};
|
||||
use crate::instance::InstancePre;
|
||||
use crate::store::StoreOpaque;
|
||||
use crate::{
|
||||
AsContextMut, Caller, Engine, Extern, ExternType, Func, FuncType, ImportType, Instance,
|
||||
IntoFunc, Module, StoreContextMut, Trap, Val, ValRaw,
|
||||
AsContextMut, Caller, Engine, Extern, Func, FuncType, ImportType, Instance, IntoFunc, Module,
|
||||
StoreContextMut, Trap, Val, ValRaw,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use log::warn;
|
||||
@@ -115,7 +115,6 @@ struct ImportKey {
|
||||
pub(crate) enum Definition {
|
||||
Extern(Extern),
|
||||
HostFunc(Arc<HostFunc>),
|
||||
Instance(Arc<indexmap::IndexMap<String, Definition>>),
|
||||
}
|
||||
|
||||
macro_rules! generate_wrap_async_func {
|
||||
@@ -622,7 +621,7 @@ impl<T> Linker<T> {
|
||||
/// "#;
|
||||
/// let module = Module::new(&engine, wat)?;
|
||||
/// linker.module(&mut store, "", &module)?;
|
||||
/// let run = linker.get(&mut store, "", Some("run")).unwrap().into_func().unwrap();
|
||||
/// let run = linker.get(&mut store, "", "run").unwrap().into_func().unwrap();
|
||||
/// let count = run.typed::<(), i32, _>(&store)?.call(&mut store, ())?;
|
||||
/// assert_eq!(count, 0, "a Command should get a fresh instance on each invocation");
|
||||
///
|
||||
@@ -1093,20 +1092,17 @@ impl<T> Linker<T> {
|
||||
&self,
|
||||
mut store: impl AsContextMut<Data = T>,
|
||||
module: &str,
|
||||
name: Option<&str>,
|
||||
name: &str,
|
||||
) -> Option<Extern> {
|
||||
let store = store.as_context_mut().0;
|
||||
// Should be safe since `T` is connecting the linker and store
|
||||
Some(unsafe { self._get(module, name)?.to_extern(store) })
|
||||
}
|
||||
|
||||
fn _get(&self, module: &str, name: Option<&str>) -> Option<&Definition> {
|
||||
fn _get(&self, module: &str, name: &str) -> Option<&Definition> {
|
||||
let key = ImportKey {
|
||||
module: *self.string2idx.get(module)?,
|
||||
name: match name {
|
||||
Some(name) => *self.string2idx.get(name)?,
|
||||
None => usize::max_value(),
|
||||
},
|
||||
name: *self.string2idx.get(name)?,
|
||||
};
|
||||
self.map.get(&key)
|
||||
}
|
||||
@@ -1139,37 +1135,11 @@ impl<T> Linker<T> {
|
||||
return Ok(item.clone());
|
||||
}
|
||||
|
||||
if let Some(name) = import.name() {
|
||||
return Err(undef_err(&format!("{}::{}", import.module(), name)));
|
||||
}
|
||||
|
||||
if let ExternType::Instance(t) = import.ty() {
|
||||
// This is a key location where the module linking proposal is
|
||||
// implemented. This logic allows single-level imports of an instance to
|
||||
// get satisfied by multiple definitions of items within this `Linker`.
|
||||
//
|
||||
// The instance being import is iterated over to load the names from
|
||||
// this `Linker` (recursively calling `get`). If anything isn't defined
|
||||
// we return `None` since the entire value isn't defined. Otherwise when
|
||||
// all values are loaded it's assembled into an `Instance` and
|
||||
// returned`.
|
||||
//
|
||||
// Note that this isn't exactly the speediest implementation in the
|
||||
// world. Ideally we would pre-create the `Instance` instead of creating
|
||||
// it each time a module is instantiated. For now though while the
|
||||
// module linking proposal is under development this should hopefully
|
||||
// suffice.
|
||||
let mut map = indexmap::IndexMap::new();
|
||||
for export in t.exports() {
|
||||
let item = self
|
||||
._get(import.module(), Some(export.name()))
|
||||
.ok_or_else(|| undef_err(&format!("{}::{}", import.module(), export.name())))?;
|
||||
map.insert(export.name().to_string(), item.clone());
|
||||
}
|
||||
return Ok(Definition::Instance(Arc::new(map)));
|
||||
}
|
||||
|
||||
Err(undef_err(&import.module()))
|
||||
Err(undef_err(&format!(
|
||||
"{}::{}",
|
||||
import.module(),
|
||||
import.name()
|
||||
)))
|
||||
}
|
||||
|
||||
/// Returns the "default export" of a module.
|
||||
@@ -1187,7 +1157,7 @@ impl<T> Linker<T> {
|
||||
mut store: impl AsContextMut<Data = T>,
|
||||
module: &str,
|
||||
) -> Result<Func> {
|
||||
if let Some(external) = self.get(&mut store, module, Some("")) {
|
||||
if let Some(external) = self.get(&mut store, module, "") {
|
||||
if let Extern::Func(func) = external {
|
||||
return Ok(func.clone());
|
||||
}
|
||||
@@ -1195,7 +1165,7 @@ impl<T> Linker<T> {
|
||||
}
|
||||
|
||||
// For compatibility, also recognize "_start".
|
||||
if let Some(external) = self.get(&mut store, module, Some("_start")) {
|
||||
if let Some(external) = self.get(&mut store, module, "_start") {
|
||||
if let Extern::Func(func) = external {
|
||||
return Ok(func.clone());
|
||||
}
|
||||
@@ -1221,14 +1191,6 @@ impl Definition {
|
||||
match self {
|
||||
Definition::Extern(e) => e.clone(),
|
||||
Definition::HostFunc(func) => func.to_func(store).into(),
|
||||
Definition::Instance(i) => {
|
||||
let items = Arc::new(
|
||||
i.iter()
|
||||
.map(|(name, item)| (name.clone(), item.to_extern(store)))
|
||||
.collect(),
|
||||
);
|
||||
Instance::from_wasmtime(InstanceData::Synthetic(items), store).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,7 +1198,6 @@ impl Definition {
|
||||
match self {
|
||||
Definition::Extern(e) => e.comes_from_same_store(store),
|
||||
Definition::HostFunc(_func) => true,
|
||||
Definition::Instance(i) => i.values().all(|e| e.comes_from_same_store(store)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::Engine;
|
||||
use crate::{
|
||||
signatures::SignatureCollection,
|
||||
types::{ExportType, ExternType, ImportType},
|
||||
};
|
||||
use crate::{Engine, ModuleType};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::fs;
|
||||
@@ -12,7 +12,7 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use wasmparser::{Parser, ValidPayload, Validator};
|
||||
use wasmtime_environ::{
|
||||
DefinedFuncIndex, DefinedMemoryIndex, FunctionInfo, ModuleEnvironment, ModuleIndex, PrimaryMap,
|
||||
DefinedFuncIndex, DefinedMemoryIndex, FunctionInfo, ModuleEnvironment, PrimaryMap,
|
||||
SignatureIndex,
|
||||
};
|
||||
use wasmtime_jit::{CompiledModule, CompiledModuleInfo, TypeTables};
|
||||
@@ -104,14 +104,7 @@ struct ModuleInner {
|
||||
/// The compiled artifacts for this module that will be instantiated and
|
||||
/// executed.
|
||||
module: Arc<CompiledModule>,
|
||||
/// Closed-over compilation artifacts used to create submodules when this
|
||||
/// module is instantiated.
|
||||
artifact_upvars: Vec<Arc<CompiledModule>>,
|
||||
/// Closed-over module values which are used when this module is
|
||||
/// instantiated.
|
||||
module_upvars: Vec<Module>,
|
||||
/// Type information of this module and all `artifact_upvars` compiled
|
||||
/// modules.
|
||||
/// Type information of this module.
|
||||
types: Arc<TypeTables>,
|
||||
/// Registered shared signature for the module.
|
||||
signatures: Arc<SignatureCollection>,
|
||||
@@ -307,7 +300,7 @@ impl Module {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "cache")] {
|
||||
let state = (HashedEngineCompileEnv(engine), binary);
|
||||
let (main_module, artifacts, types) = wasmtime_cache::ModuleCacheEntry::new(
|
||||
let (mmap, info, types) = wasmtime_cache::ModuleCacheEntry::new(
|
||||
"wasmtime",
|
||||
engine.cache_config(),
|
||||
)
|
||||
@@ -318,40 +311,35 @@ impl Module {
|
||||
|(engine, wasm)| Module::build_artifacts(engine.0, wasm),
|
||||
|
||||
// Implementation of how to serialize artifacts
|
||||
|(engine, _wasm), (_, artifacts, types)| {
|
||||
|(engine, _wasm), (mmap, _info, types)| {
|
||||
SerializedModule::from_artifacts(
|
||||
engine.0,
|
||||
artifacts.iter().map(|p| &p.0),
|
||||
mmap,
|
||||
types,
|
||||
).to_bytes(&engine.0.config().module_version).ok()
|
||||
},
|
||||
|
||||
// Cache hit, deserialize the provided artifacts
|
||||
|(engine, _wasm), serialized_bytes| {
|
||||
let (i, m, t, upvars) = SerializedModule::from_bytes(&serialized_bytes, &engine.0.config().module_version)
|
||||
SerializedModule::from_bytes(&serialized_bytes, &engine.0.config().module_version)
|
||||
.ok()?
|
||||
.into_parts(engine.0)
|
||||
.ok()?;
|
||||
// This upvars list is always empty for top-level modules
|
||||
assert!(upvars.is_empty());
|
||||
Some((i, m, t))
|
||||
.ok()
|
||||
},
|
||||
)?;
|
||||
} else {
|
||||
let (main_module, artifacts, types) = Module::build_artifacts(engine, binary)?;
|
||||
let (mmap, info, types) = Module::build_artifacts(engine, binary)?;
|
||||
}
|
||||
};
|
||||
|
||||
let modules = engine.run_maybe_parallel(artifacts, |(a, b)| {
|
||||
CompiledModule::from_artifacts(
|
||||
a,
|
||||
b,
|
||||
&*engine.config().profiler,
|
||||
engine.unique_id_allocator(),
|
||||
)
|
||||
})?;
|
||||
let module = CompiledModule::from_artifacts(
|
||||
mmap,
|
||||
info,
|
||||
&*engine.config().profiler,
|
||||
engine.unique_id_allocator(),
|
||||
)?;
|
||||
|
||||
Self::from_parts(engine, modules, main_module, Arc::new(types), &[])
|
||||
Self::from_parts(engine, module, Arc::new(types))
|
||||
}
|
||||
|
||||
/// Converts an input binary-encoded WebAssembly module to compilation
|
||||
@@ -375,79 +363,66 @@ impl Module {
|
||||
pub(crate) fn build_artifacts(
|
||||
engine: &Engine,
|
||||
wasm: &[u8],
|
||||
) -> Result<(
|
||||
usize,
|
||||
Vec<(MmapVec, Option<CompiledModuleInfo>)>,
|
||||
TypeTables,
|
||||
)> {
|
||||
) -> Result<(MmapVec, Option<CompiledModuleInfo>, TypeTables)> {
|
||||
let tunables = &engine.config().tunables;
|
||||
|
||||
// First a `ModuleEnvironment` is created which records type information
|
||||
// about the wasm module. This is where the WebAssembly is parsed and
|
||||
// validated. Afterwards `types` will have all the type information for
|
||||
// this module.
|
||||
let (main_module, translations, types) =
|
||||
ModuleEnvironment::new(tunables, &engine.config().features)
|
||||
.translate(wasm)
|
||||
.context("failed to parse WebAssembly module")?;
|
||||
let (mut translation, types) = ModuleEnvironment::new(tunables, &engine.config().features)
|
||||
.translate(wasm)
|
||||
.context("failed to parse WebAssembly module")?;
|
||||
|
||||
// Perform a two-level map/reduce here to get the final list of
|
||||
// compilation artifacts. The first level of map/reduce maps over all
|
||||
// modules found and reduces to collection into a vector. The second
|
||||
// level of map/reduce here maps over all functions within each wasm
|
||||
// module found and collects into an ELF image via `emit_obj`.
|
||||
let list = engine.run_maybe_parallel(translations, |mut translation| -> Result<_> {
|
||||
let functions = mem::take(&mut translation.function_body_inputs);
|
||||
let functions = functions.into_iter().collect::<Vec<_>>();
|
||||
|
||||
let funcs = engine
|
||||
.run_maybe_parallel(functions, |(index, func)| {
|
||||
engine
|
||||
.compiler()
|
||||
.compile_function(&translation, index, func, tunables, &types)
|
||||
})?
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let mut obj = engine.compiler().object()?;
|
||||
let (funcs, trampolines) =
|
||||
// Next compile all functions in parallel using rayon. This will perform
|
||||
// the actual validation of all the function bodies.
|
||||
let functions = mem::take(&mut translation.function_body_inputs);
|
||||
let functions = functions.into_iter().collect::<Vec<_>>();
|
||||
let funcs = engine
|
||||
.run_maybe_parallel(functions, |(index, func)| {
|
||||
engine
|
||||
.compiler()
|
||||
.emit_obj(&translation, &types, funcs, tunables, &mut obj)?;
|
||||
.compile_function(&translation, index, func, tunables, &types)
|
||||
})?
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
// If configured, attempt to use paged memory initialization
|
||||
// instead of the default mode of memory initialization
|
||||
if engine.config().paged_memory_initialization {
|
||||
translation.try_paged_init();
|
||||
}
|
||||
// Collect all the function results into a final ELF object.
|
||||
let mut obj = engine.compiler().object()?;
|
||||
let (funcs, trampolines) =
|
||||
engine
|
||||
.compiler()
|
||||
.emit_obj(&translation, &types, funcs, tunables, &mut obj)?;
|
||||
|
||||
// If configured attempt to use static memory initialization which
|
||||
// can either at runtime be implemented as a single memcpy to
|
||||
// initialize memory or otherwise enabling virtual-memory-tricks
|
||||
// such as mmap'ing from a file to get copy-on-write.
|
||||
if engine.config().memory_init_cow {
|
||||
let align = engine.compiler().page_size_align();
|
||||
let max_always_allowed = engine.config().memory_guaranteed_dense_image_size;
|
||||
translation.try_static_init(align, max_always_allowed);
|
||||
}
|
||||
// If configured, attempt to use paged memory initialization
|
||||
// instead of the default mode of memory initialization
|
||||
if engine.config().paged_memory_initialization {
|
||||
translation.try_paged_init();
|
||||
}
|
||||
|
||||
// Attempt to convert table initializer segments to
|
||||
// FuncTable representation where possible, to enable
|
||||
// table lazy init.
|
||||
translation.try_func_table_init();
|
||||
// If configured attempt to use static memory initialization which
|
||||
// can either at runtime be implemented as a single memcpy to
|
||||
// initialize memory or otherwise enabling virtual-memory-tricks
|
||||
// such as mmap'ing from a file to get copy-on-write.
|
||||
if engine.config().memory_init_cow {
|
||||
let align = engine.compiler().page_size_align();
|
||||
let max_always_allowed = engine.config().memory_guaranteed_dense_image_size;
|
||||
translation.try_static_init(align, max_always_allowed);
|
||||
}
|
||||
|
||||
let (mmap, info) =
|
||||
wasmtime_jit::finish_compile(translation, obj, funcs, trampolines, tunables)?;
|
||||
Ok((mmap, Some(info)))
|
||||
})?;
|
||||
// Attempt to convert table initializer segments to
|
||||
// FuncTable representation where possible, to enable
|
||||
// table lazy init.
|
||||
translation.try_func_table_init();
|
||||
|
||||
let (mmap, info) =
|
||||
wasmtime_jit::finish_compile(translation, obj, funcs, trampolines, tunables)?;
|
||||
|
||||
Ok((
|
||||
main_module,
|
||||
list,
|
||||
mmap,
|
||||
Some(info),
|
||||
TypeTables {
|
||||
wasm_signatures: types.wasm_signatures,
|
||||
module_signatures: types.module_signatures,
|
||||
instance_signatures: types.instance_signatures,
|
||||
},
|
||||
))
|
||||
}
|
||||
@@ -529,91 +504,27 @@ impl Module {
|
||||
|
||||
fn from_parts(
|
||||
engine: &Engine,
|
||||
mut modules: Vec<Arc<CompiledModule>>,
|
||||
main_module: usize,
|
||||
module: Arc<CompiledModule>,
|
||||
types: Arc<TypeTables>,
|
||||
module_upvars: &[serialization::SerializedModuleUpvar],
|
||||
) -> Result<Self> {
|
||||
// Validate all modules can be used with the current allocator
|
||||
for module in modules.iter() {
|
||||
engine.allocator().validate(module.module())?;
|
||||
}
|
||||
// Validate the module can be used with the current allocator
|
||||
engine.allocator().validate(module.module())?;
|
||||
|
||||
let signatures = Arc::new(SignatureCollection::new_for_module(
|
||||
engine.signatures(),
|
||||
&types.wasm_signatures,
|
||||
modules
|
||||
.iter()
|
||||
.flat_map(|m| m.trampolines().map(|(idx, f, _)| (idx, f))),
|
||||
module.trampolines().map(|(idx, f, _)| (idx, f)),
|
||||
));
|
||||
|
||||
let module = modules.remove(main_module);
|
||||
|
||||
let module_upvars = module_upvars
|
||||
.iter()
|
||||
.map(|m| {
|
||||
mk(
|
||||
engine,
|
||||
&modules,
|
||||
&types,
|
||||
m.index,
|
||||
&m.artifact_upvars,
|
||||
&m.module_upvars,
|
||||
&signatures,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Ok(Self {
|
||||
Ok(Self {
|
||||
inner: Arc::new(ModuleInner {
|
||||
engine: engine.clone(),
|
||||
types,
|
||||
artifact_upvars: modules,
|
||||
module_upvars,
|
||||
signatures,
|
||||
memory_images: OnceCell::new(),
|
||||
module,
|
||||
}),
|
||||
});
|
||||
|
||||
fn mk(
|
||||
engine: &Engine,
|
||||
artifacts: &[Arc<CompiledModule>],
|
||||
types: &Arc<TypeTables>,
|
||||
module_index: usize,
|
||||
artifact_upvars: &[usize],
|
||||
module_upvars: &[serialization::SerializedModuleUpvar],
|
||||
signatures: &Arc<SignatureCollection>,
|
||||
) -> Module {
|
||||
let module = artifacts[module_index].clone();
|
||||
Module {
|
||||
inner: Arc::new(ModuleInner {
|
||||
engine: engine.clone(),
|
||||
types: types.clone(),
|
||||
memory_images: OnceCell::new(),
|
||||
module,
|
||||
artifact_upvars: artifact_upvars
|
||||
.iter()
|
||||
.map(|i| artifacts[*i].clone())
|
||||
.collect(),
|
||||
module_upvars: module_upvars
|
||||
.into_iter()
|
||||
.map(|m| {
|
||||
mk(
|
||||
engine,
|
||||
artifacts,
|
||||
types,
|
||||
m.index,
|
||||
&m.artifact_upvars,
|
||||
&m.module_upvars,
|
||||
signatures,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
signatures: signatures.clone(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Validates `binary` input data as a WebAssembly binary given the
|
||||
@@ -650,23 +561,6 @@ impl Module {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the type signature of this module.
|
||||
pub fn ty(&self) -> ModuleType {
|
||||
let mut sig = ModuleType::new();
|
||||
let env_module = self.compiled_module().module();
|
||||
let types = self.types();
|
||||
for (module, field, ty) in env_module.imports() {
|
||||
sig.add_named_import(module, field, ExternType::from_wasmtime(types, &ty));
|
||||
}
|
||||
for (name, index) in env_module.exports.iter() {
|
||||
sig.add_named_export(
|
||||
name,
|
||||
ExternType::from_wasmtime(types, &env_module.type_of(*index)),
|
||||
);
|
||||
}
|
||||
sig
|
||||
}
|
||||
|
||||
/// Serializes this module to a vector of bytes.
|
||||
///
|
||||
/// This function is similar to the [`Engine::precompile_module`] method
|
||||
@@ -682,60 +576,6 @@ impl Module {
|
||||
SerializedModule::new(self).to_bytes(&self.inner.engine.config().module_version)
|
||||
}
|
||||
|
||||
/// Creates a submodule `Module` value from the specified parameters.
|
||||
///
|
||||
/// This is used for creating submodules as part of module instantiation.
|
||||
///
|
||||
/// * `artifact_index` - the index in `artifact_upvars` that we're creating
|
||||
/// a module for
|
||||
/// * `artifact_upvars` - the mapping of indices of what artifact upvars are
|
||||
/// needed for the submodule. The length of this array is the length of
|
||||
/// the upvars array in the submodule to be created, and each element of
|
||||
/// this array is an index into this module's upvar array.
|
||||
/// * `module_upvars` - similar to `artifact_upvars` this is a mapping of
|
||||
/// how to create the `module_upvars` of the submodule being created.
|
||||
/// Each entry in this array is either an index into this module's own
|
||||
/// module upvars array or it's an index into `modules`, the list of
|
||||
/// modules so far for the instance where this submodule is being
|
||||
/// created.
|
||||
/// * `modules` - array indexed by `module_upvars`.
|
||||
///
|
||||
/// Note that the real meat of this happens in `ModuleEnvironment`
|
||||
/// translation inside of `wasmtime_environ`. This just does the easy thing
|
||||
/// of handling all the indices, over there is where the indices are
|
||||
/// actually calculated and such.
|
||||
pub(crate) fn create_submodule(
|
||||
&self,
|
||||
artifact_index: usize,
|
||||
artifact_upvars: &[usize],
|
||||
module_upvars: &[wasmtime_environ::ModuleUpvar],
|
||||
modules: &PrimaryMap<ModuleIndex, Module>,
|
||||
) -> Result<Module> {
|
||||
let module = self.inner.artifact_upvars[artifact_index].clone();
|
||||
Ok(Module {
|
||||
inner: Arc::new(ModuleInner {
|
||||
types: self.inner.types.clone(),
|
||||
engine: self.inner.engine.clone(),
|
||||
memory_images: OnceCell::new(),
|
||||
module,
|
||||
artifact_upvars: artifact_upvars
|
||||
.iter()
|
||||
.map(|i| self.inner.artifact_upvars[*i].clone())
|
||||
.collect(),
|
||||
module_upvars: module_upvars
|
||||
.iter()
|
||||
.map(|i| match *i {
|
||||
wasmtime_environ::ModuleUpvar::Inherit(i) => {
|
||||
self.inner.module_upvars[i].clone()
|
||||
}
|
||||
wasmtime_environ::ModuleUpvar::Local(i) => modules[i].clone(),
|
||||
})
|
||||
.collect(),
|
||||
signatures: self.inner.signatures.clone(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compiled_module(&self) -> &Arc<CompiledModule> {
|
||||
&self.inner.module
|
||||
}
|
||||
@@ -752,14 +592,6 @@ impl Module {
|
||||
&self.inner.signatures
|
||||
}
|
||||
|
||||
/// Looks up the module upvar value at the `index` specified.
|
||||
///
|
||||
/// Note that this panics if `index` is out of bounds since this should
|
||||
/// only be called for valid indices as part of instantiation.
|
||||
pub(crate) fn module_upvar(&self, index: usize) -> &Module {
|
||||
&self.inner.module_upvars[index]
|
||||
}
|
||||
|
||||
/// Returns identifier/name that this [`Module`] has. This name
|
||||
/// is used in traps/backtrace details.
|
||||
///
|
||||
@@ -829,7 +661,7 @@ impl Module {
|
||||
/// assert_eq!(module.imports().len(), 1);
|
||||
/// let import = module.imports().next().unwrap();
|
||||
/// assert_eq!(import.module(), "host");
|
||||
/// assert_eq!(import.name(), Some("foo"));
|
||||
/// assert_eq!(import.name(), "foo");
|
||||
/// match import.ty() {
|
||||
/// ExternType::Func(_) => { /* ... */ }
|
||||
/// _ => panic!("unexpected import type!"),
|
||||
|
||||
@@ -6,36 +6,22 @@
|
||||
//!
|
||||
//! There are two main pieces of data associated with a binary artifact:
|
||||
//!
|
||||
//! 1. A list of compiled modules. The reason this is a list as opposed to one
|
||||
//! singular module is that a module-linking module may encompass a number
|
||||
//! of other modules.
|
||||
//! 2. Compilation metadata shared by all modules, including the global
|
||||
//! `TypeTables` information. This metadata is validated for compilation
|
||||
//! settings and also has information shared by all modules (such as the
|
||||
//! shared `TypeTables`).
|
||||
//! 1. The compiled module image, currently an ELF file.
|
||||
//! 2. Compilation metadata for the module, including the `TypeTables`
|
||||
//! information. This metadata is validated for compilation settings.
|
||||
//!
|
||||
//! Compiled modules are, at this time, represented as an ELF file. This ELF
|
||||
//! file contains all the necessary data needed to decode each individual
|
||||
//! module, and conveniently also handles things like alignment so we can
|
||||
//! actually directly `mmap` compilation artifacts from disk.
|
||||
//! file contains all the necessary data needed to decode a module, and
|
||||
//! conveniently also handles things like alignment so we can actually directly
|
||||
//! `mmap` compilation artifacts from disk.
|
||||
//!
|
||||
//! With all this in mind, the current serialization format is as follows:
|
||||
//! With this in mind, the current serialization format is as follows:
|
||||
//!
|
||||
//! * The first, primary, module starts the final artifact. This means that the
|
||||
//! final artifact is actually, and conveniently, a valid ELF file. ELF files
|
||||
//! don't place any restrictions on data coming after the ELF file itself,
|
||||
//! so that's where everything else will go. Another reason for using this
|
||||
//! format is that our compilation artifacts are then consumable by standard
|
||||
//! debugging tools like `objdump` to poke around and see what's what.
|
||||
//! * First the ELF image for the compiled module starts the artifact. This
|
||||
//! helps developers use standard ELF-reading utilities like `objdump` to poke
|
||||
//! around and see what's inside the compiled image.
|
||||
//!
|
||||
//! * Next, all other modules are encoded. Each module has its own alignment,
|
||||
//! though, so modules aren't simply concatenated. Instead directly after an
|
||||
//! ELF file there is a 64-bit little-endian integer which is the offset,
|
||||
//! from the end of the previous ELF file, to the next ELF file.
|
||||
//!
|
||||
//! * Finally, once all modules have been encoded (there's always at least
|
||||
//! one), the 8-byte value `u64::MAX` is encoded. Following this is a
|
||||
//! number of fields:
|
||||
//! * After the ELF file is a number of fields:
|
||||
//!
|
||||
//! 1. The `HEADER` value
|
||||
//! 2. A byte indicating how long the next field is
|
||||
@@ -46,15 +32,19 @@
|
||||
//! other random ELF files, as well as provide better error messages for
|
||||
//! using wasmtime artifacts across versions.
|
||||
//!
|
||||
//! Note that the structure of the ELF format is what enables this
|
||||
//! representation. We can have trailing data after an ELF file which isn't read
|
||||
//! by any parsing of the ELF itself, which provides a convenient location for
|
||||
//! the metadata information to go.
|
||||
//!
|
||||
//! This format is implemented by the `to_bytes` and `from_mmap` function.
|
||||
|
||||
use crate::{Engine, Module, ModuleVersionStrategy};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use object::read::elf::FileHeader;
|
||||
use object::{Bytes, File, Object, ObjectSection};
|
||||
use object::Bytes;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
@@ -165,50 +155,8 @@ impl<'a, 'b, T: Deserialize<'a>> Deserialize<'a> for MyCow<'b, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A small helper struct for serialized module upvars.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SerializedModuleUpvar {
|
||||
/// The module's index into the compilation artifact.
|
||||
pub index: usize,
|
||||
/// Indexes into the list of all compilation artifacts for this module.
|
||||
pub artifact_upvars: Vec<usize>,
|
||||
/// Closed-over module values that are also needed for this module.
|
||||
pub module_upvars: Vec<SerializedModuleUpvar>,
|
||||
}
|
||||
|
||||
impl SerializedModuleUpvar {
|
||||
pub fn new(module: &Module, artifacts: &[Arc<CompiledModule>]) -> Self {
|
||||
// TODO: improve upon the linear searches in the artifact list
|
||||
let index = artifacts
|
||||
.iter()
|
||||
.position(|a| Arc::as_ptr(a) == Arc::as_ptr(&module.inner.module))
|
||||
.expect("module should be in artifacts list");
|
||||
|
||||
SerializedModuleUpvar {
|
||||
index,
|
||||
artifact_upvars: module
|
||||
.inner
|
||||
.artifact_upvars
|
||||
.iter()
|
||||
.map(|m| {
|
||||
artifacts
|
||||
.iter()
|
||||
.position(|a| Arc::as_ptr(a) == Arc::as_ptr(m))
|
||||
.expect("artifact should be in artifacts list")
|
||||
})
|
||||
.collect(),
|
||||
module_upvars: module
|
||||
.inner
|
||||
.module_upvars
|
||||
.iter()
|
||||
.map(|m| SerializedModuleUpvar::new(m, artifacts))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializedModule<'a> {
|
||||
artifacts: Vec<MyCow<'a, MmapVec>>,
|
||||
artifacts: MyCow<'a, MmapVec>,
|
||||
metadata: Metadata<'a>,
|
||||
}
|
||||
|
||||
@@ -219,54 +167,28 @@ struct Metadata<'a> {
|
||||
isa_flags: BTreeMap<String, FlagValue>,
|
||||
tunables: Tunables,
|
||||
features: WasmFeatures,
|
||||
module_upvars: Vec<SerializedModuleUpvar>,
|
||||
types: MyCow<'a, TypeTables>,
|
||||
}
|
||||
|
||||
impl<'a> SerializedModule<'a> {
|
||||
#[cfg(compiler)]
|
||||
pub fn new(module: &'a Module) -> Self {
|
||||
let artifacts = module
|
||||
.inner
|
||||
.artifact_upvars
|
||||
.iter()
|
||||
.map(|m| MyCow::Borrowed(m.mmap()))
|
||||
.chain(Some(MyCow::Borrowed(module.inner.module.mmap())))
|
||||
.collect::<Vec<_>>();
|
||||
let module_upvars = module
|
||||
.inner
|
||||
.module_upvars
|
||||
.iter()
|
||||
.map(|m| SerializedModuleUpvar::new(m, &module.inner.artifact_upvars))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Self::with_data(
|
||||
module.engine(),
|
||||
artifacts,
|
||||
module_upvars,
|
||||
MyCow::Borrowed(module.compiled_module().mmap()),
|
||||
MyCow::Borrowed(module.types()),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(compiler)]
|
||||
pub fn from_artifacts(
|
||||
engine: &Engine,
|
||||
artifacts: impl IntoIterator<Item = &'a MmapVec>,
|
||||
types: &'a TypeTables,
|
||||
) -> Self {
|
||||
Self::with_data(
|
||||
engine,
|
||||
artifacts.into_iter().map(MyCow::Borrowed).collect(),
|
||||
Vec::new(),
|
||||
MyCow::Borrowed(types),
|
||||
)
|
||||
pub fn from_artifacts(engine: &Engine, artifacts: &'a MmapVec, types: &'a TypeTables) -> Self {
|
||||
Self::with_data(engine, MyCow::Borrowed(artifacts), MyCow::Borrowed(types))
|
||||
}
|
||||
|
||||
#[cfg(compiler)]
|
||||
fn with_data(
|
||||
engine: &Engine,
|
||||
artifacts: Vec<MyCow<'a, MmapVec>>,
|
||||
module_upvars: Vec<SerializedModuleUpvar>,
|
||||
artifacts: MyCow<'a, MmapVec>,
|
||||
types: MyCow<'a, TypeTables>,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -277,35 +199,27 @@ impl<'a> SerializedModule<'a> {
|
||||
isa_flags: engine.compiler().isa_flags(),
|
||||
tunables: engine.config().tunables.clone(),
|
||||
features: (&engine.config().features).into(),
|
||||
module_upvars,
|
||||
types,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_module(self, engine: &Engine) -> Result<Module> {
|
||||
let (main_module, modules, types, upvars) = self.into_parts(engine)?;
|
||||
let modules = engine.run_maybe_parallel(modules, |(i, m)| {
|
||||
CompiledModule::from_artifacts(
|
||||
i,
|
||||
m,
|
||||
&*engine.config().profiler,
|
||||
engine.unique_id_allocator(),
|
||||
)
|
||||
})?;
|
||||
let (mmap, info, types) = self.into_parts(engine)?;
|
||||
let module = CompiledModule::from_artifacts(
|
||||
mmap,
|
||||
info,
|
||||
&*engine.config().profiler,
|
||||
engine.unique_id_allocator(),
|
||||
)?;
|
||||
|
||||
Module::from_parts(engine, modules, main_module, Arc::new(types), &upvars)
|
||||
Module::from_parts(engine, module, Arc::new(types))
|
||||
}
|
||||
|
||||
pub fn into_parts(
|
||||
mut self,
|
||||
engine: &Engine,
|
||||
) -> Result<(
|
||||
usize,
|
||||
Vec<(MmapVec, Option<CompiledModuleInfo>)>,
|
||||
TypeTables,
|
||||
Vec<SerializedModuleUpvar>,
|
||||
)> {
|
||||
) -> Result<(MmapVec, Option<CompiledModuleInfo>, TypeTables)> {
|
||||
// Verify that the compilation settings in the engine match the
|
||||
// compilation settings of the module that's being loaded.
|
||||
self.check_triple(engine)?;
|
||||
@@ -315,45 +229,18 @@ impl<'a> SerializedModule<'a> {
|
||||
self.check_tunables(&engine.config().tunables)?;
|
||||
self.check_features(&engine.config().features)?;
|
||||
|
||||
assert!(!self.artifacts.is_empty());
|
||||
let modules = self.artifacts.into_iter().map(|i| (i.unwrap_owned(), None));
|
||||
let module = self.artifacts.unwrap_owned();
|
||||
|
||||
let main_module = modules.len() - 1;
|
||||
|
||||
Ok((
|
||||
main_module,
|
||||
modules.collect(),
|
||||
self.metadata.types.unwrap_owned(),
|
||||
self.metadata.module_upvars,
|
||||
))
|
||||
Ok((module, None, self.metadata.types.unwrap_owned()))
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self, version_strat: &ModuleVersionStrategy) -> Result<Vec<u8>> {
|
||||
// First up, create a linked-ish list of ELF files. For more
|
||||
// information on this format, see the doc comment on this module.
|
||||
// The only semi-tricky bit here is that we leave an
|
||||
// offset-to-the-next-file between each set of ELF files. The list
|
||||
// is then terminated with `u64::MAX`.
|
||||
let mut ret = Vec::new();
|
||||
for (i, obj) in self.artifacts.iter().enumerate() {
|
||||
// Anything after the first object needs to respect the alignment of
|
||||
// the object's sections, so insert padding as necessary. Note that
|
||||
// the +8 to the length here is to accomodate the size we'll write
|
||||
// to get to the next object.
|
||||
if i > 0 {
|
||||
let obj = File::parse(&obj.as_ref()[..])?;
|
||||
let align = obj.sections().map(|s| s.align()).max().unwrap_or(0).max(1);
|
||||
let align = usize::try_from(align).unwrap();
|
||||
let new_size = align_to(ret.len() + 8, align);
|
||||
ret.extend_from_slice(&(new_size as u64).to_le_bytes());
|
||||
ret.resize(new_size, 0);
|
||||
}
|
||||
ret.extend_from_slice(obj.as_ref());
|
||||
}
|
||||
ret.extend_from_slice(&[0xff; 8]);
|
||||
// Start off with a copy of the ELF image.
|
||||
let mut ret = self.artifacts.as_ref().to_vec();
|
||||
|
||||
// The last part of our artifact is the bincode-encoded `Metadata`
|
||||
// section with a few other guards to help give better error messages.
|
||||
// Append the bincode-encoded `Metadata` section with a few other guards
|
||||
// to help give better error messages during deserialization if
|
||||
// something goes wrong.
|
||||
ret.extend_from_slice(HEADER);
|
||||
let version = match version_strat {
|
||||
ModuleVersionStrategy::WasmtimeVersion => env!("CARGO_PKG_VERSION"),
|
||||
@@ -385,37 +272,14 @@ impl<'a> SerializedModule<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_mmap(mut mmap: MmapVec, version_strat: &ModuleVersionStrategy) -> Result<Self> {
|
||||
// Artifacts always start with an ELF file, so read that first.
|
||||
// Afterwards we continually read ELF files until we see the `u64::MAX`
|
||||
// marker, meaning we've reached the end.
|
||||
let first_module = read_file(&mut mmap)?;
|
||||
let mut pos = first_module.len();
|
||||
let mut artifacts = vec![MyCow::Owned(first_module)];
|
||||
pub fn from_mmap(mmap: MmapVec, version_strat: &ModuleVersionStrategy) -> Result<Self> {
|
||||
// First validate that this is at least somewhat an elf file within
|
||||
// `mmap` and additionally skip to the end of the elf file to find our
|
||||
// metadata.
|
||||
let metadata = data_after_elf(&mmap)?;
|
||||
|
||||
let metadata = loop {
|
||||
if mmap.len() < 8 {
|
||||
bail!("invalid serialized data");
|
||||
}
|
||||
let next_file_start = u64::from_le_bytes([
|
||||
mmap[0], mmap[1], mmap[2], mmap[3], mmap[4], mmap[5], mmap[6], mmap[7],
|
||||
]);
|
||||
if next_file_start == u64::MAX {
|
||||
mmap.drain(..8);
|
||||
break mmap;
|
||||
}
|
||||
|
||||
// Remove padding leading up to the next file
|
||||
let next_file_start = usize::try_from(next_file_start).unwrap();
|
||||
let _padding = mmap.drain(..next_file_start - pos);
|
||||
let data = read_file(&mut mmap)?;
|
||||
pos = next_file_start + data.len();
|
||||
artifacts.push(MyCow::Owned(data));
|
||||
};
|
||||
|
||||
// Once we've reached the end we parse a `Metadata` object. This has a
|
||||
// few guards up front which we process first, and eventually this
|
||||
// bottoms out in a `bincode::deserialize` call.
|
||||
// The metadata has a few guards up front which we process first, and
|
||||
// eventually this bottoms out in a `bincode::deserialize` call.
|
||||
let metadata = metadata
|
||||
.strip_prefix(HEADER)
|
||||
.ok_or_else(|| anyhow!("bytes are not a compatible serialized wasmtime module"))?;
|
||||
@@ -453,18 +317,13 @@ impl<'a> SerializedModule<'a> {
|
||||
.context("deserialize compilation artifacts")?;
|
||||
|
||||
return Ok(SerializedModule {
|
||||
artifacts,
|
||||
artifacts: MyCow::Owned(mmap),
|
||||
metadata,
|
||||
});
|
||||
|
||||
/// This function will drain the beginning contents of `mmap` which
|
||||
/// correspond to an ELF object file. The ELF file is only very lightly
|
||||
/// validated.
|
||||
///
|
||||
/// The `mmap` passed in will be reset to just after the ELF file, and
|
||||
/// the `MmapVec` returned represents the extend of the ELF file
|
||||
/// itself.
|
||||
fn read_file(mmap: &mut MmapVec) -> Result<MmapVec> {
|
||||
/// This function will return the trailing data behind the ELF file
|
||||
/// parsed from `data` which is where we find our metadata section.
|
||||
fn data_after_elf(data: &[u8]) -> Result<&[u8]> {
|
||||
use object::NativeEndian as NE;
|
||||
// There's not actually a great utility for figuring out where
|
||||
// the end of an ELF file is in the `object` crate. In lieu of that
|
||||
@@ -472,7 +331,7 @@ impl<'a> SerializedModule<'a> {
|
||||
// is that the header comes first, that tells us where the section
|
||||
// headers are, and for our ELF files the end of the file is the
|
||||
// end of the section headers.
|
||||
let mut bytes = Bytes(mmap);
|
||||
let mut bytes = Bytes(data);
|
||||
let header = bytes
|
||||
.read::<object::elf::FileHeader64<NE>>()
|
||||
.map_err(|()| anyhow!("artifact truncated, can't read header"))?;
|
||||
@@ -480,10 +339,10 @@ impl<'a> SerializedModule<'a> {
|
||||
bail!("invalid elf header");
|
||||
}
|
||||
let sections = header
|
||||
.section_headers(NE, &mmap[..])
|
||||
.section_headers(NE, data)
|
||||
.context("failed to read section headers")?;
|
||||
let range = subslice_range(object::bytes_of_slice(sections), mmap);
|
||||
Ok(mmap.drain(..range.end))
|
||||
let range = subslice_range(object::bytes_of_slice(sections), data);
|
||||
Ok(&data[range.end..])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,12 +556,6 @@ impl<'a> SerializedModule<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Aligns the `val` specified up to `align`, which must be a power of two
|
||||
fn align_to(val: usize, align: usize) -> usize {
|
||||
debug_assert!(align.is_power_of_two());
|
||||
(val + (align - 1)) & (!(align - 1))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -49,7 +49,7 @@ pub fn create_global(store: &mut StoreOpaque, gt: &GlobalType, val: Val) -> Resu
|
||||
.initializers
|
||||
.push(wasmtime_environ::Initializer::Import {
|
||||
name: "".into(),
|
||||
field: None,
|
||||
field: "".into(),
|
||||
index: EntityIndex::Function(func_index),
|
||||
});
|
||||
|
||||
|
||||
@@ -115,10 +115,6 @@ pub enum ExternType {
|
||||
Table(TableType),
|
||||
/// This external type is the type of a WebAssembly memory.
|
||||
Memory(MemoryType),
|
||||
/// This external type is the type of a WebAssembly instance.
|
||||
Instance(InstanceType),
|
||||
/// This external type is the type of a WebAssembly module.
|
||||
Module(ModuleType),
|
||||
}
|
||||
|
||||
macro_rules! accessors {
|
||||
@@ -151,8 +147,6 @@ impl ExternType {
|
||||
(Global(GlobalType) global unwrap_global)
|
||||
(Table(TableType) table unwrap_table)
|
||||
(Memory(MemoryType) memory unwrap_memory)
|
||||
(Module(ModuleType) module unwrap_module)
|
||||
(Instance(InstanceType) instance unwrap_instance)
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime(types: &TypeTables, ty: &EntityType) -> ExternType {
|
||||
@@ -163,14 +157,6 @@ impl ExternType {
|
||||
EntityType::Global(ty) => GlobalType::from_wasmtime_global(ty).into(),
|
||||
EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(),
|
||||
EntityType::Table(ty) => TableType::from_wasmtime_table(ty).into(),
|
||||
EntityType::Module(ty) => {
|
||||
let ty = &types.module_signatures[*ty];
|
||||
ModuleType::from_wasmtime(types, ty).into()
|
||||
}
|
||||
EntityType::Instance(ty) => {
|
||||
let ty = &types.instance_signatures[*ty];
|
||||
InstanceType::from_wasmtime(types, ty).into()
|
||||
}
|
||||
EntityType::Tag(_) => unimplemented!("wasm tag support"),
|
||||
}
|
||||
}
|
||||
@@ -200,18 +186,6 @@ impl From<TableType> for ExternType {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ModuleType> for ExternType {
|
||||
fn from(ty: ModuleType) -> ExternType {
|
||||
ExternType::Module(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InstanceType> for ExternType {
|
||||
fn from(ty: InstanceType) -> ExternType {
|
||||
ExternType::Instance(ty)
|
||||
}
|
||||
}
|
||||
|
||||
/// A descriptor for a function in a WebAssembly module.
|
||||
///
|
||||
/// WebAssembly functions can have 0 or more parameters and results.
|
||||
@@ -439,122 +413,6 @@ impl MemoryType {
|
||||
}
|
||||
}
|
||||
|
||||
// Module Types
|
||||
|
||||
/// A descriptor for a WebAssembly module type.
|
||||
///
|
||||
/// This is a part of the [WebAssembly module-linking proposal][proposal].
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/module-linking
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleType {
|
||||
imports: Vec<(String, Option<String>, ExternType)>,
|
||||
exports: Vec<(String, ExternType)>,
|
||||
}
|
||||
|
||||
impl ModuleType {
|
||||
/// Creates a new empty module type.
|
||||
pub fn new() -> ModuleType {
|
||||
ModuleType {
|
||||
imports: Vec::new(),
|
||||
exports: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new export to this `ModuleType`.
|
||||
pub fn add_named_export(&mut self, name: &str, ty: ExternType) {
|
||||
self.exports.push((name.to_string(), ty));
|
||||
}
|
||||
|
||||
/// Adds a new import to this `ModuleType`.
|
||||
pub fn add_named_import(&mut self, module: &str, field: Option<&str>, ty: ExternType) {
|
||||
self.imports
|
||||
.push((module.to_string(), field.map(|f| f.to_string()), ty));
|
||||
}
|
||||
|
||||
/// Returns the list of imports associated with this module type.
|
||||
pub fn imports(&self) -> impl ExactSizeIterator<Item = ImportType<'_>> {
|
||||
self.imports.iter().map(|(name, field, ty)| ImportType {
|
||||
module: name,
|
||||
name: field.as_deref(),
|
||||
ty: EntityOrExtern::Extern(ty),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the list of exports associated with this module type.
|
||||
pub fn exports(&self) -> impl ExactSizeIterator<Item = ExportType<'_>> {
|
||||
self.exports.iter().map(|(name, ty)| ExportType {
|
||||
name,
|
||||
ty: EntityOrExtern::Extern(ty),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime(
|
||||
types: &TypeTables,
|
||||
ty: &wasmtime_environ::ModuleSignature,
|
||||
) -> ModuleType {
|
||||
let exports = &types.instance_signatures[ty.exports].exports;
|
||||
ModuleType {
|
||||
exports: exports
|
||||
.iter()
|
||||
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(types, ty)))
|
||||
.collect(),
|
||||
imports: ty
|
||||
.imports
|
||||
.iter()
|
||||
.map(|(m, ty)| (m.to_string(), None, ExternType::from_wasmtime(types, ty)))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instance Types
|
||||
|
||||
/// A descriptor for a WebAssembly instance type.
|
||||
///
|
||||
/// This is a part of the [WebAssembly module-linking proposal][proposal].
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/module-linking
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InstanceType {
|
||||
exports: Vec<(String, ExternType)>,
|
||||
}
|
||||
|
||||
impl InstanceType {
|
||||
/// Creates a new empty instance type.
|
||||
pub fn new() -> InstanceType {
|
||||
InstanceType {
|
||||
exports: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new export to this `ModuleType`.
|
||||
pub fn add_named_export(&mut self, name: &str, ty: ExternType) {
|
||||
self.exports.push((name.to_string(), ty));
|
||||
}
|
||||
|
||||
/// Returns the list of exports associated with this module type.
|
||||
pub fn exports(&self) -> impl ExactSizeIterator<Item = ExportType<'_>> {
|
||||
self.exports.iter().map(|(name, ty)| ExportType {
|
||||
name,
|
||||
ty: EntityOrExtern::Extern(ty),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn from_wasmtime(
|
||||
types: &TypeTables,
|
||||
ty: &wasmtime_environ::InstanceSignature,
|
||||
) -> InstanceType {
|
||||
InstanceType {
|
||||
exports: ty
|
||||
.exports
|
||||
.iter()
|
||||
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(types, ty)))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Import Types
|
||||
|
||||
/// A descriptor for an imported value into a wasm module.
|
||||
@@ -569,16 +427,11 @@ pub struct ImportType<'module> {
|
||||
module: &'module str,
|
||||
|
||||
/// The field of the import.
|
||||
name: Option<&'module str>,
|
||||
name: &'module str,
|
||||
|
||||
/// The type of the import.
|
||||
ty: EntityOrExtern<'module>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum EntityOrExtern<'a> {
|
||||
Entity(EntityType, &'a TypeTables),
|
||||
Extern(&'a ExternType),
|
||||
ty: EntityType,
|
||||
types: &'module TypeTables,
|
||||
}
|
||||
|
||||
impl<'module> ImportType<'module> {
|
||||
@@ -586,14 +439,15 @@ impl<'module> ImportType<'module> {
|
||||
/// is of type `ty`.
|
||||
pub(crate) fn new(
|
||||
module: &'module str,
|
||||
name: Option<&'module str>,
|
||||
name: &'module str,
|
||||
ty: EntityType,
|
||||
types: &'module TypeTables,
|
||||
) -> ImportType<'module> {
|
||||
ImportType {
|
||||
module,
|
||||
name,
|
||||
ty: EntityOrExtern::Entity(ty, types),
|
||||
ty,
|
||||
types,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,20 +458,13 @@ impl<'module> ImportType<'module> {
|
||||
|
||||
/// Returns the field name of the module that this import is expected to
|
||||
/// come from.
|
||||
///
|
||||
/// Note that this is optional due to the module linking proposal. If the
|
||||
/// module linking proposal is enabled this is always `None`, otherwise this
|
||||
/// is always `Some`.
|
||||
pub fn name(&self) -> Option<&'module str> {
|
||||
pub fn name(&self) -> &'module str {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Returns the expected type of this import.
|
||||
pub fn ty(&self) -> ExternType {
|
||||
match &self.ty {
|
||||
EntityOrExtern::Entity(e, types) => ExternType::from_wasmtime(types, e),
|
||||
EntityOrExtern::Extern(e) => (*e).clone(),
|
||||
}
|
||||
ExternType::from_wasmtime(self.types, &self.ty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,7 +492,8 @@ pub struct ExportType<'module> {
|
||||
name: &'module str,
|
||||
|
||||
/// The type of the export.
|
||||
ty: EntityOrExtern<'module>,
|
||||
ty: EntityType,
|
||||
types: &'module TypeTables,
|
||||
}
|
||||
|
||||
impl<'module> ExportType<'module> {
|
||||
@@ -656,10 +504,7 @@ impl<'module> ExportType<'module> {
|
||||
ty: EntityType,
|
||||
types: &'module TypeTables,
|
||||
) -> ExportType<'module> {
|
||||
ExportType {
|
||||
name,
|
||||
ty: EntityOrExtern::Entity(ty, types),
|
||||
}
|
||||
ExportType { name, ty, types }
|
||||
}
|
||||
|
||||
/// Returns the name by which this export is known.
|
||||
@@ -669,10 +514,7 @@ impl<'module> ExportType<'module> {
|
||||
|
||||
/// Returns the type of this export.
|
||||
pub fn ty(&self) -> ExternType {
|
||||
match &self.ty {
|
||||
EntityOrExtern::Entity(e, types) => ExternType::from_wasmtime(types, e),
|
||||
EntityOrExtern::Extern(e) => (*e).clone(),
|
||||
}
|
||||
ExternType::from_wasmtime(self.types, &self.ty)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
use crate::instance::InstanceData;
|
||||
use crate::linker::Definition;
|
||||
use crate::store::StoreOpaque;
|
||||
use crate::{signatures::SignatureCollection, Engine, Extern};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use wasmtime_environ::{
|
||||
EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table,
|
||||
WasmFuncType, WasmType,
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
use wasmtime_environ::{EntityType, Global, Memory, SignatureIndex, Table, WasmFuncType, WasmType};
|
||||
use wasmtime_jit::TypeTables;
|
||||
use wasmtime_runtime::VMSharedSignatureIndex;
|
||||
|
||||
@@ -156,206 +152,6 @@ impl MatchCx<'_> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn instance(&self, expected: InstanceTypeIndex, actual: &crate::Instance) -> Result<()> {
|
||||
for (name, expected) in self.types.instance_signatures[expected].exports.iter() {
|
||||
match actual.data(self.store.store_data()) {
|
||||
InstanceData::Synthetic(names) => match names.get(name) {
|
||||
Some(item) => {
|
||||
self.extern_(expected, item)
|
||||
.with_context(|| format!("instance export {:?} incompatible", name))?;
|
||||
}
|
||||
None => bail!("instance type missing export {:?}", name),
|
||||
},
|
||||
InstanceData::Instantiated {
|
||||
id,
|
||||
types,
|
||||
signatures,
|
||||
..
|
||||
} => {
|
||||
let module = self.store.instance(*id).module();
|
||||
match module.exports.get(name) {
|
||||
Some(index) => {
|
||||
let actual_ty = module.type_of(*index);
|
||||
self.extern_ty_matches(expected, &actual_ty, signatures, types)
|
||||
.with_context(|| {
|
||||
format!("instance export {:?} incompatible", name)
|
||||
})?;
|
||||
}
|
||||
None => bail!("instance type missing export {:?}", name),
|
||||
}
|
||||
|
||||
// let
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that the type signature of `actual` matches the `expected`
|
||||
/// module type signature.
|
||||
pub fn module(&self, expected: ModuleTypeIndex, actual: &crate::Module) -> Result<()> {
|
||||
// This should only ever be invoked with module linking, and this is an
|
||||
// early check that our `field` assertion below should always work as
|
||||
// well.
|
||||
assert!(self.engine.config().features.module_linking);
|
||||
|
||||
let expected_sig = &self.types.module_signatures[expected];
|
||||
let module = actual.compiled_module().module();
|
||||
self.imports_match(
|
||||
expected,
|
||||
actual.signatures(),
|
||||
actual.types(),
|
||||
module.imports().map(|(name, field, ty)| {
|
||||
assert!(field.is_none()); // should be true if module linking is enabled
|
||||
(name, ty)
|
||||
}),
|
||||
)?;
|
||||
self.exports_match(
|
||||
expected_sig.exports,
|
||||
actual.signatures(),
|
||||
actual.types(),
|
||||
|name| module.exports.get(name).map(|idx| module.type_of(*idx)),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that the `actual_imports` list of module imports matches the
|
||||
/// `expected` module type signature.
|
||||
///
|
||||
/// Types specified in `actual_imports` are relative to `actual_types`.
|
||||
fn imports_match<'a>(
|
||||
&self,
|
||||
expected: ModuleTypeIndex,
|
||||
actual_signatures: &SignatureCollection,
|
||||
actual_types: &TypeTables,
|
||||
actual_imports: impl Iterator<Item = (&'a str, EntityType)>,
|
||||
) -> Result<()> {
|
||||
// Imports match if all of the actual imports are satisfied by the
|
||||
// expected set of imports. Note that we're reversing the order of the
|
||||
// subtytpe matching here too.
|
||||
let expected_sig = &self.types.module_signatures[expected];
|
||||
for (name, actual_ty) in actual_imports {
|
||||
let expected_ty = match expected_sig.imports.get(name) {
|
||||
Some(ty) => ty,
|
||||
None => bail!("expected type doesn't import {:?}", name),
|
||||
};
|
||||
MatchCx {
|
||||
signatures: actual_signatures,
|
||||
types: actual_types,
|
||||
store: self.store,
|
||||
engine: self.engine,
|
||||
}
|
||||
.extern_ty_matches(&actual_ty, expected_ty, self.signatures, self.types)
|
||||
.with_context(|| format!("module import {:?} incompatible", name))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that all exports in `expected` are defined by `lookup` within
|
||||
/// `actual_types`.
|
||||
fn exports_match(
|
||||
&self,
|
||||
expected: InstanceTypeIndex,
|
||||
actual_signatures: &SignatureCollection,
|
||||
actual_types: &TypeTables,
|
||||
lookup: impl Fn(&str) -> Option<EntityType>,
|
||||
) -> Result<()> {
|
||||
// The `expected` type must be a subset of `actual`, meaning that all
|
||||
// names in `expected` must be present in `actual`. Note that we do
|
||||
// name-based lookup here instead of index-based lookup.
|
||||
for (name, expected) in self.types.instance_signatures[expected].exports.iter() {
|
||||
match lookup(name) {
|
||||
Some(ty) => self
|
||||
.extern_ty_matches(expected, &ty, actual_signatures, actual_types)
|
||||
.with_context(|| format!("export {:?} incompatible", name))?,
|
||||
None => bail!("failed to find export {:?}", name),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates that the `expected` entity matches the `actual_ty` defined
|
||||
/// within `actual_types`.
|
||||
fn extern_ty_matches(
|
||||
&self,
|
||||
expected: &EntityType,
|
||||
actual_ty: &EntityType,
|
||||
actual_signatures: &SignatureCollection,
|
||||
actual_types: &TypeTables,
|
||||
) -> Result<()> {
|
||||
let actual_desc = match actual_ty {
|
||||
EntityType::Global(_) => "global",
|
||||
EntityType::Module(_) => "module",
|
||||
EntityType::Memory(_) => "memory",
|
||||
EntityType::Tag(_) => "tag",
|
||||
EntityType::Instance(_) => "instance",
|
||||
EntityType::Table(_) => "table",
|
||||
EntityType::Function(_) => "function",
|
||||
};
|
||||
match expected {
|
||||
EntityType::Global(expected) => match actual_ty {
|
||||
EntityType::Global(actual) => self.global_ty(expected, actual),
|
||||
_ => bail!("expected global, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Table(expected) => match actual_ty {
|
||||
EntityType::Table(actual) => self.table_ty(expected, actual, None),
|
||||
_ => bail!("expected table, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Memory(expected) => match actual_ty {
|
||||
EntityType::Memory(actual) => self.memory_ty(expected, actual, None),
|
||||
_ => bail!("expected memory, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Function(expected) => match *actual_ty {
|
||||
EntityType::Function(actual) => {
|
||||
if self.types.wasm_signatures[*expected] == actual_types.wasm_signatures[actual]
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("function types incompatible")
|
||||
}
|
||||
}
|
||||
_ => bail!("expected function, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Instance(expected) => match actual_ty {
|
||||
EntityType::Instance(actual) => {
|
||||
let sig = &actual_types.instance_signatures[*actual];
|
||||
self.exports_match(*expected, actual_signatures, actual_types, |name| {
|
||||
sig.exports.get(name).cloned()
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
_ => bail!("expected instance, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Module(expected) => match actual_ty {
|
||||
EntityType::Module(actual) => {
|
||||
let expected_module_sig = &self.types.module_signatures[*expected];
|
||||
let actual_module_sig = &actual_types.module_signatures[*actual];
|
||||
let actual_instance_sig =
|
||||
&actual_types.instance_signatures[actual_module_sig.exports];
|
||||
|
||||
self.imports_match(
|
||||
*expected,
|
||||
actual_signatures,
|
||||
actual_types,
|
||||
actual_module_sig
|
||||
.imports
|
||||
.iter()
|
||||
.map(|(module, ty)| (module.as_str(), ty.clone())),
|
||||
)?;
|
||||
self.exports_match(
|
||||
expected_module_sig.exports,
|
||||
actual_signatures,
|
||||
actual_types,
|
||||
|name| actual_instance_sig.exports.get(name).cloned(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => bail!("expected module, but found {}", actual_desc),
|
||||
},
|
||||
EntityType::Tag(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that the `expected` type matches the type of `actual`
|
||||
pub fn extern_(&self, expected: &EntityType, actual: &Extern) -> Result<()> {
|
||||
match expected {
|
||||
@@ -375,14 +171,6 @@ impl MatchCx<'_> {
|
||||
Extern::Func(actual) => self.func(*expected, actual),
|
||||
_ => bail!("expected func, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Instance(expected) => match actual {
|
||||
Extern::Instance(actual) => self.instance(*expected, actual),
|
||||
_ => bail!("expected instance, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Module(expected) => match actual {
|
||||
Extern::Module(actual) => self.module(*expected, actual),
|
||||
_ => bail!("expected module, but found {}", actual.desc()),
|
||||
},
|
||||
EntityType::Tag(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
@@ -395,23 +183,6 @@ impl MatchCx<'_> {
|
||||
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)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -485,8 +256,6 @@ fn entity_desc(ty: &EntityType) -> &'static str {
|
||||
EntityType::Table(_) => "table",
|
||||
EntityType::Memory(_) => "memory",
|
||||
EntityType::Function(_) => "func",
|
||||
EntityType::Instance(_) => "instance",
|
||||
EntityType::Module(_) => "module",
|
||||
EntityType::Tag(_) => "tag",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ impl<T> WastContext<T> {
|
||||
match module {
|
||||
Some(module) => self
|
||||
.linker
|
||||
.get(&mut self.store, module, Some(name))
|
||||
.get(&mut self.store, module, name)
|
||||
.ok_or_else(|| anyhow!("no item named `{}::{}` found", module, name)),
|
||||
None => self
|
||||
.current
|
||||
|
||||
Reference in New Issue
Block a user