Disconnects Store state fields from Compiler (#1761)

*  Moves CodeMemory, VMInterrupts and SignatureRegistry from Compiler
*  CompiledModule holds CodeMemory and GdbJitImageRegistration
*  Store keeps track of its JIT code
*  Makes "jit_int.rs" stuff Send+Sync
*  Adds the threads example.
This commit is contained in:
Yury Delendik
2020-06-02 13:44:39 -05:00
committed by GitHub
parent b41330393d
commit 15c68f2cc1
61 changed files with 982 additions and 663 deletions

1
Cargo.lock generated
View File

@@ -2381,6 +2381,7 @@ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
"indexmap", "indexmap",
"lazy_static",
"libc", "libc",
"memoffset", "memoffset",
"more-asserts", "more-asserts",

View File

@@ -43,6 +43,10 @@ is now enabled by default.
[#1667](https://github.com/bytecodealliance/wasmtime/pull/1667) [#1667](https://github.com/bytecodealliance/wasmtime/pull/1667)
The Rust API does not require a store provided during `Module::new` operation. The `Module` can be send accross threads and instantiate for a specific store. The `Instance::new` now requires the store.
[#1761](https://github.com/bytecodealliance/wasmtime/pull/1761)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
## 0.16.0 ## 0.16.0

View File

@@ -244,6 +244,7 @@ WASM_API_EXTERN own wasmtime_error_t *wasmtime_global_set(
// instance is returned), or an instance can be returned (meaning no error or // instance is returned), or an instance can be returned (meaning no error or
// trap is returned). // trap is returned).
WASM_API_EXTERN own wasmtime_error_t *wasmtime_instance_new( WASM_API_EXTERN own wasmtime_error_t *wasmtime_instance_new(
wasm_store_t *store,
const wasm_module_t *module, const wasm_module_t *module,
const wasm_extern_t* const imports[], const wasm_extern_t* const imports[],
size_t num_imports, size_t num_imports,

View File

@@ -36,19 +36,10 @@ pub unsafe extern "C" fn wasm_instance_new(
imports: *const Box<wasm_extern_t>, imports: *const Box<wasm_extern_t>,
result: Option<&mut *mut wasm_trap_t>, result: Option<&mut *mut wasm_trap_t>,
) -> Option<Box<wasm_instance_t>> { ) -> Option<Box<wasm_instance_t>> {
let store = &store.store;
let module = &wasm_module.module.borrow();
if !Store::same(&store, module.store()) {
if let Some(result) = result {
let trap = Trap::new("wasm_store_t must match store in wasm_module_t");
let trap = Box::new(wasm_trap_t::new(store, trap));
*result = Box::into_raw(trap);
}
return None;
}
let mut instance = ptr::null_mut(); let mut instance = ptr::null_mut();
let mut trap = ptr::null_mut(); let mut trap = ptr::null_mut();
let err = wasmtime_instance_new( let err = wasmtime_instance_new(
store,
wasm_module, wasm_module,
imports, imports,
wasm_module.imports.len(), wasm_module.imports.len(),
@@ -60,7 +51,7 @@ pub unsafe extern "C" fn wasm_instance_new(
assert!(trap.is_null()); assert!(trap.is_null());
assert!(instance.is_null()); assert!(instance.is_null());
if let Some(result) = result { if let Some(result) = result {
*result = Box::into_raw(err.to_trap(store)); *result = Box::into_raw(err.to_trap(&store.store));
} }
None None
} }
@@ -83,6 +74,7 @@ pub unsafe extern "C" fn wasm_instance_new(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn wasmtime_instance_new( pub unsafe extern "C" fn wasmtime_instance_new(
store: &wasm_store_t,
module: &wasm_module_t, module: &wasm_module_t,
imports: *const Box<wasm_extern_t>, imports: *const Box<wasm_extern_t>,
num_imports: usize, num_imports: usize,
@@ -90,6 +82,7 @@ pub unsafe extern "C" fn wasmtime_instance_new(
trap_ptr: &mut *mut wasm_trap_t, trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
_wasmtime_instance_new( _wasmtime_instance_new(
store,
module, module,
std::slice::from_raw_parts(imports, num_imports), std::slice::from_raw_parts(imports, num_imports),
instance_ptr, instance_ptr,
@@ -98,11 +91,13 @@ pub unsafe extern "C" fn wasmtime_instance_new(
} }
fn _wasmtime_instance_new( fn _wasmtime_instance_new(
store: &wasm_store_t,
module: &wasm_module_t, module: &wasm_module_t,
imports: &[Box<wasm_extern_t>], imports: &[Box<wasm_extern_t>],
instance_ptr: &mut *mut wasm_instance_t, instance_ptr: &mut *mut wasm_instance_t,
trap_ptr: &mut *mut wasm_trap_t, trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let store = &store.store;
let imports = imports let imports = imports
.iter() .iter()
.map(|import| match &import.which { .map(|import| match &import.which {
@@ -114,8 +109,8 @@ fn _wasmtime_instance_new(
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let module = &module.module.borrow(); let module = &module.module.borrow();
handle_instantiate( handle_instantiate(
module.store(), store,
Instance::new(module, &imports), Instance::new(store, module, &imports),
instance_ptr, instance_ptr,
trap_ptr, trap_ptr,
) )

View File

@@ -1,9 +1,10 @@
use crate::host_ref::HostRef; use crate::host_ref::HostRef;
use crate::{handle_result, wasmtime_error_t}; use crate::{
use crate::{wasm_byte_vec_t, wasm_exporttype_vec_t, wasm_importtype_vec_t}; handle_result, wasm_byte_vec_t, wasm_exporttype_t, wasm_exporttype_vec_t, wasm_importtype_t,
use crate::{wasm_exporttype_t, wasm_importtype_t, wasm_store_t}; wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t,
};
use std::ptr; use std::ptr;
use wasmtime::Module; use wasmtime::{Engine, Module};
#[repr(C)] #[repr(C)]
#[derive(Clone)] #[derive(Clone)]
@@ -21,6 +22,14 @@ impl wasm_module_t {
} }
} }
#[repr(C)]
#[derive(Clone)]
pub struct wasm_shared_module_t {
module: Module,
}
wasmtime_c_api_macros::declare_own!(wasm_shared_module_t);
#[no_mangle] #[no_mangle]
pub extern "C" fn wasm_module_new( pub extern "C" fn wasm_module_new(
store: &wasm_store_t, store: &wasm_store_t,
@@ -44,7 +53,7 @@ pub extern "C" fn wasmtime_module_new(
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let binary = binary.as_slice(); let binary = binary.as_slice();
let store = &store.store; let store = &store.store;
handle_result(Module::from_binary(store, binary), |module| { handle_result(Module::from_binary(store.engine(), binary), |module| {
let imports = module let imports = module
.imports() .imports()
.map(|i| wasm_importtype_t::new(i.module().to_owned(), i.name().to_owned(), i.ty())) .map(|i| wasm_importtype_t::new(i.module().to_owned(), i.name().to_owned(), i.ty()))
@@ -73,8 +82,7 @@ pub extern "C" fn wasmtime_module_validate(
binary: &wasm_byte_vec_t, binary: &wasm_byte_vec_t,
) -> Option<Box<wasmtime_error_t>> { ) -> Option<Box<wasmtime_error_t>> {
let binary = binary.as_slice(); let binary = binary.as_slice();
let store = &store.store; handle_result(Module::validate(store.store.engine(), binary), |()| {})
handle_result(Module::validate(store, binary), |()| {})
} }
#[no_mangle] #[no_mangle]
@@ -96,3 +104,34 @@ pub extern "C" fn wasm_module_imports(module: &wasm_module_t, out: &mut wasm_imp
.collect::<Vec<_>>(); .collect::<Vec<_>>();
out.set_buffer(buffer); out.set_buffer(buffer);
} }
#[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.borrow().clone(),
})
}
#[no_mangle]
pub extern "C" fn wasm_module_obtain(
store: &wasm_store_t,
shared_module: &wasm_shared_module_t,
) -> Option<Box<wasm_module_t>> {
let module = shared_module.module.clone();
if !Engine::same(store.store.engine(), module.engine()) {
return None;
}
let imports = module
.imports()
.map(|i| wasm_importtype_t::new(i.module().to_owned(), i.name().to_owned(), i.ty()))
.collect::<Vec<_>>();
let exports = module
.exports()
.map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty()))
.collect::<Vec<_>>();
Some(Box::new(wasm_module_t {
module: HostRef::new(&store.store, module),
imports,
exports,
}))
}

View File

@@ -56,7 +56,7 @@ pub fn instantiate_with_config(wasm: &[u8], config: Config) {
let store = Store::new(&engine); let store = Store::new(&engine);
log_wasm(wasm); log_wasm(wasm);
let module = match Module::new(&store, wasm) { let module = match Module::new(&engine, wasm) {
Ok(module) => module, Ok(module) => module,
Err(_) => return, Err(_) => return,
}; };
@@ -75,7 +75,7 @@ pub fn instantiate_with_config(wasm: &[u8], config: Config) {
// aren't caught during validation or compilation. For example, an imported // aren't caught during validation or compilation. For example, an imported
// table might not have room for an element segment that we want to // table might not have room for an element segment that we want to
// initialize into it. // initialize into it.
let _result = Instance::new(&module, &imports); let _result = Instance::new(&store, &module, &imports);
} }
/// Compile the Wasm buffer, and implicitly fail if we have an unexpected /// Compile the Wasm buffer, and implicitly fail if we have an unexpected
@@ -88,9 +88,8 @@ pub fn compile(wasm: &[u8], strategy: Strategy) {
crate::init_fuzzing(); crate::init_fuzzing();
let engine = Engine::new(&crate::fuzz_default_config(strategy).unwrap()); let engine = Engine::new(&crate::fuzz_default_config(strategy).unwrap());
let store = Store::new(&engine);
log_wasm(wasm); log_wasm(wasm);
let _ = Module::new(&store, wasm); let _ = Module::new(&engine, wasm);
} }
/// Instantiate the given Wasm module with each `Config` and call all of its /// Instantiate the given Wasm module with each `Config` and call all of its
@@ -128,7 +127,7 @@ pub fn differential_execution(
let engine = Engine::new(config); let engine = Engine::new(config);
let store = Store::new(&engine); let store = Store::new(&engine);
let module = match Module::new(&store, &ttf.wasm) { let module = match Module::new(&engine, &ttf.wasm) {
Ok(module) => module, Ok(module) => module,
// The module might rely on some feature that our config didn't // The module might rely on some feature that our config didn't
// enable or something like that. // enable or something like that.
@@ -158,7 +157,7 @@ pub fn differential_execution(
// aren't caught during validation or compilation. For example, an imported // aren't caught during validation or compilation. For example, an imported
// table might not have room for an element segment that we want to // table might not have room for an element segment that we want to
// initialize into it. // initialize into it.
let instance = match Instance::new(&module, &imports) { let instance = match Instance::new(&store, &module, &imports) {
Ok(instance) => instance, Ok(instance) => instance,
Err(e) => { Err(e) => {
eprintln!( eprintln!(
@@ -304,7 +303,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
ApiCall::ModuleNew { id, wasm } => { ApiCall::ModuleNew { id, wasm } => {
log::debug!("creating module: {}", id); log::debug!("creating module: {}", id);
log_wasm(&wasm.wasm); log_wasm(&wasm.wasm);
let module = match Module::new(store.as_ref().unwrap(), &wasm.wasm) { let module = match Module::new(engine.as_ref().unwrap(), &wasm.wasm) {
Ok(m) => m, Ok(m) => m,
Err(_) => continue, Err(_) => continue,
}; };
@@ -324,7 +323,9 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
None => continue, None => continue,
}; };
let imports = match dummy_imports(store.as_ref().unwrap(), module.imports()) { let store = store.as_ref().unwrap();
let imports = match dummy_imports(store, module.imports()) {
Ok(imps) => imps, Ok(imps) => imps,
Err(_) => { Err(_) => {
// There are some value types that we can't synthesize a // There are some value types that we can't synthesize a
@@ -338,7 +339,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
// aren't caught during validation or compilation. For example, an imported // aren't caught during validation or compilation. For example, an imported
// table might not have room for an element segment that we want to // table might not have room for an element segment that we want to
// initialize into it. // initialize into it.
if let Ok(instance) = Instance::new(&module, &imports) { if let Ok(instance) = Instance::new(store, &module, &imports) {
instances.insert(id, instance); instances.insert(id, instance);
} }
} }

View File

@@ -22,10 +22,10 @@ impl CodeMemoryEntry {
Ok(Self { mmap, registry }) Ok(Self { mmap, registry })
} }
fn contains(&self, addr: usize) -> bool { fn range(&self) -> (usize, usize) {
let start = self.mmap.as_ptr() as usize; let start = self.mmap.as_ptr() as usize;
let end = start + self.mmap.len(); let end = start + self.mmap.len();
start <= addr && addr < end (start, end)
} }
} }
@@ -243,11 +243,10 @@ impl CodeMemory {
Ok(()) Ok(())
} }
/// Returns whether any published segment of this code memory contains /// Returns all published segment ranges.
/// `addr`. pub fn published_ranges<'a>(&'a self) -> impl Iterator<Item = (usize, usize)> + 'a {
pub fn published_contains(&self, addr: usize) -> bool {
self.entries[..self.published] self.entries[..self.published]
.iter() .iter()
.any(|entry| entry.contains(addr)) .map(|entry| entry.range())
} }
} }

View File

@@ -9,20 +9,16 @@ use cranelift_codegen::Context;
use cranelift_codegen::{binemit, ir}; use cranelift_codegen::{binemit, ir};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use wasmtime_debug::{emit_debugsections_image, DebugInfoData}; use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::entity::{EntityRef, PrimaryMap};
use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa}; use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex}; use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex, SignatureIndex};
use wasmtime_environ::{ use wasmtime_environ::{
CacheConfig, CompileError, CompiledFunction, Compiler as _C, ModuleAddressMap, CacheConfig, CompileError, CompiledFunction, Compiler as _C, ModuleAddressMap,
ModuleMemoryOffset, ModuleTranslation, ModuleVmctxInfo, Relocation, RelocationTarget, ModuleMemoryOffset, ModuleTranslation, ModuleVmctxInfo, Relocation, RelocationTarget,
Relocations, Traps, Tunables, VMOffsets, Relocations, Traps, Tunables, VMOffsets,
}; };
use wasmtime_runtime::{ use wasmtime_runtime::{InstantiationError, VMFunctionBody, VMTrampoline};
InstantiationError, SignatureRegistry, VMFunctionBody, VMInterrupts, VMSharedSignatureIndex,
VMTrampoline,
};
/// Select which kind of compilation to use. /// Select which kind of compilation to use.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@@ -48,12 +44,9 @@ pub enum CompilationStrategy {
/// TODO: Consider using cranelift-module. /// TODO: Consider using cranelift-module.
pub struct Compiler { pub struct Compiler {
isa: Box<dyn TargetIsa>, isa: Box<dyn TargetIsa>,
code_memory: CodeMemory,
signatures: SignatureRegistry,
strategy: CompilationStrategy, strategy: CompilationStrategy,
cache_config: CacheConfig, cache_config: CacheConfig,
tunables: Tunables, tunables: Tunables,
interrupts: Arc<VMInterrupts>,
} }
impl Compiler { impl Compiler {
@@ -66,22 +59,25 @@ impl Compiler {
) -> Self { ) -> Self {
Self { Self {
isa, isa,
code_memory: CodeMemory::new(),
signatures: SignatureRegistry::new(),
strategy, strategy,
cache_config, cache_config,
tunables, tunables,
interrupts: Arc::new(VMInterrupts::default()),
} }
} }
} }
fn _assert_compiler_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<Compiler>();
}
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct Compilation { pub struct Compilation {
pub code_memory: CodeMemory,
pub finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, pub finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
pub relocations: Relocations, pub relocations: Relocations,
pub trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>, pub trampolines: PrimaryMap<SignatureIndex, VMTrampoline>,
pub trampoline_relocations: HashMap<VMSharedSignatureIndex, Vec<Relocation>>, pub trampoline_relocations: HashMap<SignatureIndex, Vec<Relocation>>,
pub jt_offsets: PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets>, pub jt_offsets: PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets>,
pub dbg_image: Option<Vec<u8>>, pub dbg_image: Option<Vec<u8>>,
pub traps: Traps, pub traps: Traps,
@@ -89,6 +85,11 @@ pub struct Compilation {
} }
impl Compiler { impl Compiler {
/// Return the isa.
pub fn isa(&self) -> &dyn TargetIsa {
self.isa.as_ref()
}
/// Return the target's frontend configuration settings. /// Return the target's frontend configuration settings.
pub fn frontend_config(&self) -> TargetFrontendConfig { pub fn frontend_config(&self) -> TargetFrontendConfig {
self.isa.frontend_config() self.isa.frontend_config()
@@ -99,17 +100,14 @@ impl Compiler {
&self.tunables &self.tunables
} }
/// Return the handle by which to interrupt instances
pub fn interrupts(&self) -> &Arc<VMInterrupts> {
&self.interrupts
}
/// Compile the given function bodies. /// Compile the given function bodies.
pub(crate) fn compile<'data>( pub(crate) fn compile<'data>(
&mut self, &self,
translation: &ModuleTranslation, translation: &ModuleTranslation,
debug_data: Option<DebugInfoData>, debug_data: Option<DebugInfoData>,
) -> Result<Compilation, SetupError> { ) -> Result<Compilation, SetupError> {
let mut code_memory = CodeMemory::new();
let (compilation, relocations, address_transform, value_ranges, stack_slots, traps) = let (compilation, relocations, address_transform, value_ranges, stack_slots, traps) =
match self.strategy { match self.strategy {
// For now, interpret `Auto` as `Cranelift` since that's the most stable // For now, interpret `Auto` as `Cranelift` since that's the most stable
@@ -135,7 +133,7 @@ impl Compiler {
// Allocate all of the compiled functions into executable memory, // Allocate all of the compiled functions into executable memory,
// copying over their contents. // copying over their contents.
let finished_functions = let finished_functions =
allocate_functions(&mut self.code_memory, &compilation).map_err(|message| { allocate_functions(&mut code_memory, &compilation).map_err(|message| {
SetupError::Instantiate(InstantiationError::Resource(format!( SetupError::Instantiate(InstantiationError::Resource(format!(
"failed to allocate memory for functions: {}", "failed to allocate memory for functions: {}",
message message
@@ -147,23 +145,17 @@ impl Compiler {
// guarantees that all functions (including indirect ones through // guarantees that all functions (including indirect ones through
// tables) have a trampoline when invoked through the wasmtime API. // tables) have a trampoline when invoked through the wasmtime API.
let mut cx = FunctionBuilderContext::new(); let mut cx = FunctionBuilderContext::new();
let mut trampolines = HashMap::new(); let mut trampolines = PrimaryMap::new();
let mut trampoline_relocations = HashMap::new(); let mut trampoline_relocations = HashMap::new();
for (wasm_func_ty, native_sig) in translation.module.local.signatures.values() { for (index, (_, native_sig)) in translation.module.local.signatures.iter() {
let index = self
.signatures
.register(wasm_func_ty.clone(), native_sig.clone());
if trampolines.contains_key(&index) {
continue;
}
let (trampoline, relocations) = make_trampoline( let (trampoline, relocations) = make_trampoline(
&*self.isa, &*self.isa,
&mut self.code_memory, &mut code_memory,
&mut cx, &mut cx,
native_sig, native_sig,
std::mem::size_of::<u128>(), std::mem::size_of::<u128>(),
)?; )?;
trampolines.insert(index, trampoline); trampolines.push(trampoline);
// Typically trampolines do not have relocations, so if one does // Typically trampolines do not have relocations, so if one does
// show up be sure to log it in case anyone's listening and there's // show up be sure to log it in case anyone's listening and there's
@@ -217,6 +209,7 @@ impl Compiler {
let jt_offsets = compilation.get_jt_offsets(); let jt_offsets = compilation.get_jt_offsets();
Ok(Compilation { Ok(Compilation {
code_memory,
finished_functions, finished_functions,
relocations, relocations,
trampolines, trampolines,
@@ -227,22 +220,6 @@ impl Compiler {
address_transform, address_transform,
}) })
} }
/// Make memory containing compiled code executable.
pub(crate) fn publish_compiled_code(&mut self) {
self.code_memory.publish(self.isa.as_ref());
}
/// Shared signature registry.
pub fn signatures(&self) -> &SignatureRegistry {
&self.signatures
}
/// Returns whether or not the given address falls within the JIT code
/// managed by the compiler
pub fn is_in_jit_code(&self, addr: usize) -> bool {
self.code_memory.published_contains(addr)
}
} }
/// Create a trampoline for invoking a function. /// Create a trampoline for invoking a function.

View File

@@ -3,6 +3,7 @@
//! `CompiledModule` to allow compiling and instantiating to be done as separate //! `CompiledModule` to allow compiling and instantiating to be done as separate
//! steps. //! steps.
use crate::code_memory::CodeMemory;
use crate::compiler::Compiler; use crate::compiler::Compiler;
use crate::imports::resolve_imports; use crate::imports::resolve_imports;
use crate::link::link_module; use crate::link::link_module;
@@ -10,7 +11,6 @@ use crate::resolver::Resolver;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Write; use std::io::Write;
use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use thiserror::Error; use thiserror::Error;
use wasmtime_debug::read_debuginfo; use wasmtime_debug::read_debuginfo;
@@ -24,7 +24,7 @@ use wasmtime_profiling::ProfilingAgent;
use wasmtime_runtime::VMInterrupts; use wasmtime_runtime::VMInterrupts;
use wasmtime_runtime::{ use wasmtime_runtime::{
GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator, GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator,
SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline, SignatureRegistry, VMFunctionBody, VMTrampoline,
}; };
/// An error condition while setting up a wasm instance, be it validation, /// An error condition while setting up a wasm instance, be it validation,
@@ -49,23 +49,33 @@ pub enum SetupError {
DebugInfo(#[from] anyhow::Error), DebugInfo(#[from] anyhow::Error),
} }
/// This is similar to `CompiledModule`, but references the data initializers struct FinishedFunctions(BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>);
/// from the wasm buffer rather than holding its own copy.
struct RawCompiledModule<'data> { unsafe impl Send for FinishedFunctions {}
module: Module, unsafe impl Sync for FinishedFunctions {}
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>, /// Container for data needed for an Instance function to exist.
data_initializers: Box<[DataInitializer<'data>]>, pub struct ModuleCode {
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>, code_memory: CodeMemory,
#[allow(dead_code)]
dbg_jit_registration: Option<GdbJitImageRegistration>, dbg_jit_registration: Option<GdbJitImageRegistration>,
}
/// A compiled wasm module, ready to be instantiated.
pub struct CompiledModule {
module: Arc<Module>,
code: Arc<ModuleCode>,
finished_functions: FinishedFunctions,
trampolines: PrimaryMap<SignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>,
traps: Traps, traps: Traps,
address_transform: ModuleAddressMap, address_transform: ModuleAddressMap,
} }
impl<'data> RawCompiledModule<'data> { impl CompiledModule {
/// Create a new `RawCompiledModule` by compiling the wasm module in `data` and instatiating it. /// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
fn new( pub fn new<'data>(
compiler: &mut Compiler, compiler: &Compiler,
data: &'data [u8], data: &'data [u8],
profiler: &dyn ProfilingAgent, profiler: &dyn ProfilingAgent,
) -> Result<Self, SetupError> { ) -> Result<Self, SetupError> {
@@ -83,26 +93,24 @@ impl<'data> RawCompiledModule<'data> {
let compilation = compiler.compile(&translation, debug_data)?; let compilation = compiler.compile(&translation, debug_data)?;
link_module(&translation.module, &compilation); let module = translation.module;
// Compute indices into the shared signature table. link_module(&module, &compilation);
let signatures = {
let signature_registry = compiler.signatures();
translation
.module
.local
.signatures
.values()
.map(|(wasm, native)| signature_registry.register(wasm.clone(), native.clone()))
.collect::<PrimaryMap<_, _>>()
};
// Make all code compiled thus far executable. // Make all code compiled thus far executable.
compiler.publish_compiled_code(); let mut code_memory = compilation.code_memory;
code_memory.publish(compiler.isa());
let data_initializers = translation
.data_initializers
.into_iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice();
// Initialize profiler and load the wasm module // Initialize profiler and load the wasm module
profiler.module_load( profiler.module_load(
&translation.module, &module,
&compilation.finished_functions, &compilation.finished_functions,
compilation.dbg_image.as_deref(), compilation.dbg_image.as_deref(),
); );
@@ -116,82 +124,22 @@ impl<'data> RawCompiledModule<'data> {
None None
}; };
let finished_functions =
FinishedFunctions(compilation.finished_functions.into_boxed_slice());
Ok(Self { Ok(Self {
module: translation.module, module: Arc::new(module),
finished_functions: compilation.finished_functions.into_boxed_slice(), code: Arc::new(ModuleCode {
code_memory,
dbg_jit_registration,
}),
finished_functions,
trampolines: compilation.trampolines, trampolines: compilation.trampolines,
data_initializers: translation.data_initializers.into_boxed_slice(), data_initializers,
signatures: signatures.into_boxed_slice(),
dbg_jit_registration,
traps: compilation.traps, traps: compilation.traps,
address_transform: compilation.address_transform, address_transform: compilation.address_transform,
}) })
} }
}
/// A compiled wasm module, ready to be instantiated.
pub struct CompiledModule {
module: Arc<Module>,
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
traps: Traps,
address_transform: ModuleAddressMap,
interrupts: Arc<VMInterrupts>,
}
impl CompiledModule {
/// Compile a data buffer into a `CompiledModule`, which may then be instantiated.
pub fn new<'data>(
compiler: &mut Compiler,
data: &'data [u8],
profiler: &dyn ProfilingAgent,
) -> Result<Self, SetupError> {
let raw = RawCompiledModule::<'data>::new(compiler, data, profiler)?;
Ok(Self::from_parts(
raw.module,
raw.finished_functions,
raw.trampolines,
raw.data_initializers
.iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice(),
raw.signatures.clone(),
raw.dbg_jit_registration,
raw.traps,
raw.address_transform,
compiler.interrupts().clone(),
))
}
/// Construct a `CompiledModule` from component parts.
pub fn from_parts(
module: Module,
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<GdbJitImageRegistration>,
traps: Traps,
address_transform: ModuleAddressMap,
interrupts: Arc<VMInterrupts>,
) -> Self {
Self {
module: Arc::new(module),
finished_functions,
trampolines,
data_initializers,
signatures,
dbg_jit_registration: dbg_jit_registration.map(Rc::new),
traps,
address_transform,
interrupts,
}
}
/// Crate an `Instance` from this `CompiledModule`. /// Crate an `Instance` from this `CompiledModule`.
/// ///
@@ -205,21 +153,41 @@ impl CompiledModule {
pub unsafe fn instantiate( pub unsafe fn instantiate(
&self, &self,
resolver: &mut dyn Resolver, resolver: &mut dyn Resolver,
sig_registry: &SignatureRegistry, signature_registry: &mut SignatureRegistry,
mem_creator: Option<&dyn RuntimeMemoryCreator>, mem_creator: Option<&dyn RuntimeMemoryCreator>,
interrupts: Arc<VMInterrupts>,
host_state: Box<dyn Any>, host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> { ) -> Result<InstanceHandle, InstantiationError> {
let imports = resolve_imports(&self.module, &sig_registry, resolver)?; // Compute indices into the shared signature table.
let signatures = {
self.module
.local
.signatures
.values()
.map(|(wasm_sig, native)| {
signature_registry.register(wasm_sig.clone(), native.clone())
})
.collect::<PrimaryMap<_, _>>()
};
let mut trampolines = HashMap::new();
for (i, trampoline) in self.trampolines.iter() {
trampolines.insert(signatures[i], trampoline.clone());
}
let finished_functions = self.finished_functions.0.clone();
let imports = resolve_imports(&self.module, signature_registry, resolver)?;
InstanceHandle::new( InstanceHandle::new(
Arc::clone(&self.module), self.module.clone(),
self.finished_functions.clone(), self.code.clone(),
self.trampolines.clone(), finished_functions,
trampolines,
imports, imports,
mem_creator, mem_creator,
self.signatures.clone(), signatures.into_boxed_slice(),
self.dbg_jit_registration.as_ref().map(|r| Rc::clone(&r)),
host_state, host_state,
self.interrupts.clone(), interrupts,
) )
} }
@@ -239,19 +207,14 @@ impl CompiledModule {
&self.module &self.module
} }
/// Return a reference-counting pointer to a module. /// Return a reference to a mutable module (if possible).
pub fn module_mut(&mut self) -> &mut Arc<Module> { pub fn module_mut(&mut self) -> Option<&mut Module> {
&mut self.module Arc::get_mut(&mut self.module)
}
/// Return a reference to a module.
pub fn module_ref(&self) -> &Module {
&self.module
} }
/// Returns the map of all finished JIT functions compiled for this module /// Returns the map of all finished JIT functions compiled for this module
pub fn finished_functions(&self) -> &BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]> { pub fn finished_functions(&self) -> &BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]> {
&self.finished_functions &self.finished_functions.0
} }
/// Returns the a map for all traps in this module. /// Returns the a map for all traps in this module.
@@ -263,6 +226,16 @@ impl CompiledModule {
pub fn address_transform(&self) -> &ModuleAddressMap { pub fn address_transform(&self) -> &ModuleAddressMap {
&self.address_transform &self.address_transform
} }
/// Returns all ranges convered by JIT code.
pub fn jit_code_ranges<'a>(&'a self) -> impl Iterator<Item = (usize, usize)> + 'a {
self.code.code_memory.published_ranges()
}
/// Returns module's JIT code.
pub fn code(&self) -> &Arc<ModuleCode> {
&self.code
}
} }
/// Similar to `DataInitializer`, but owns its own copy of the data rather /// Similar to `DataInitializer`, but owns its own copy of the data rather
@@ -276,7 +249,7 @@ pub struct OwnedDataInitializer {
} }
impl OwnedDataInitializer { impl OwnedDataInitializer {
fn new(borrowed: &DataInitializer<'_>) -> Self { fn new(borrowed: DataInitializer<'_>) -> Self {
Self { Self {
location: borrowed.location.clone(), location: borrowed.location.clone(),
data: borrowed.data.to_vec().into_boxed_slice(), data: borrowed.data.to_vec().into_boxed_slice(),

View File

@@ -22,7 +22,7 @@ pub fn link_module(module: &Module, compilation: &Compilation) {
for (i, function_relocs) in compilation.trampoline_relocations.iter() { for (i, function_relocs) in compilation.trampoline_relocations.iter() {
for r in function_relocs.iter() { for r in function_relocs.iter() {
println!("tramopline relocation"); println!("tramopline relocation");
let body = compilation.trampolines[&i] as *const VMFunctionBody; let body = compilation.trampolines[*i] as *const VMFunctionBody;
apply_reloc(module, compilation, body, r); apply_reloc(module, compilation, body, r);
} }
} }

View File

@@ -57,7 +57,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
let data = #root::wasmtime_interface_types::ModuleData::new(bytes.as_ref())?; let data = #root::wasmtime_interface_types::ModuleData::new(bytes.as_ref())?;
let module = Module::new(&store, bytes.as_ref())?; let module = Module::new(&engine, bytes.as_ref())?;
let mut imports: Vec<Extern> = Vec::new(); let mut imports: Vec<Extern> = Vec::new();
if let Some(module_name) = data.find_wasi_module_name() { if let Some(module_name) = data.find_wasi_module_name() {
@@ -75,7 +75,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
} }
} }
let instance = let instance =
Instance::new(&module, &imports).map_err(|t| format_err!("instantiation trap: {:?}", t))?; Instance::new(&store, &module, &imports).map_err(|t| format_err!("instantiation trap: {:?}", t))?;
Ok(#name { instance, data }) Ok(#name { instance, data })
} }

View File

@@ -21,6 +21,7 @@ thiserror = "1.0.4"
more-asserts = "0.2.1" more-asserts = "0.2.1"
cfg-if = "0.1.9" cfg-if = "0.1.9"
backtrace = "0.3.42" backtrace = "0.3.42"
lazy_static = "1.3.0"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.7", features = ["winbase", "memoryapi", "errhandlingapi"] } winapi = { version = "0.3.7", features = ["winbase", "memoryapi", "errhandlingapi"] }

View File

@@ -4,7 +4,6 @@
use crate::export::Export; use crate::export::Export;
use crate::imports::Imports; use crate::imports::Imports;
use crate::jit_int::GdbJitImageRegistration;
use crate::memory::{DefaultMemoryCreator, RuntimeLinearMemory, RuntimeMemoryCreator}; use crate::memory::{DefaultMemoryCreator, RuntimeLinearMemory, RuntimeMemoryCreator};
use crate::table::Table; use crate::table::Table;
use crate::traphandlers::Trap; use crate::traphandlers::Trap;
@@ -21,7 +20,6 @@ use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::{mem, ptr, slice}; use std::{mem, ptr, slice};
use thiserror::Error; use thiserror::Error;
@@ -40,6 +38,9 @@ pub(crate) struct Instance {
/// The `Module` this `Instance` was instantiated from. /// The `Module` this `Instance` was instantiated from.
module: Arc<Module>, module: Arc<Module>,
/// The module's JIT code (if exists).
code: Arc<dyn Any>,
/// Offsets in the `vmctx` region. /// Offsets in the `vmctx` region.
offsets: VMOffsets, offsets: VMOffsets,
@@ -67,9 +68,6 @@ pub(crate) struct Instance {
/// Hosts can store arbitrary per-instance information here. /// Hosts can store arbitrary per-instance information here.
host_state: Box<dyn Any>, host_state: Box<dyn Any>,
/// Optional image of JIT'ed code for debugger registration.
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
/// Externally allocated data indicating how this instance will be /// Externally allocated data indicating how this instance will be
/// interrupted. /// interrupted.
pub(crate) interrupts: Arc<VMInterrupts>, pub(crate) interrupts: Arc<VMInterrupts>,
@@ -96,14 +94,10 @@ impl Instance {
unsafe { *self.signature_ids_ptr().add(index) } unsafe { *self.signature_ids_ptr().add(index) }
} }
pub(crate) fn module(&self) -> &Arc<Module> { pub(crate) fn module(&self) -> &Module {
&self.module &self.module
} }
pub(crate) fn module_ref(&self) -> &Module {
&*self.module
}
/// Return a pointer to the `VMSharedSignatureIndex`s. /// Return a pointer to the `VMSharedSignatureIndex`s.
fn signature_ids_ptr(&self) -> *mut VMSharedSignatureIndex { fn signature_ids_ptr(&self) -> *mut VMSharedSignatureIndex {
unsafe { self.vmctx_plus_offset(self.offsets.vmctx_signature_ids_begin()) } unsafe { self.vmctx_plus_offset(self.offsets.vmctx_signature_ids_begin()) }
@@ -782,12 +776,12 @@ impl InstanceHandle {
/// safety. /// safety.
pub unsafe fn new( pub unsafe fn new(
module: Arc<Module>, module: Arc<Module>,
code: Arc<dyn Any>,
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>, finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>, trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
imports: Imports, imports: Imports,
mem_creator: Option<&dyn RuntimeMemoryCreator>, mem_creator: Option<&dyn RuntimeMemoryCreator>,
vmshared_signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>, vmshared_signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
host_state: Box<dyn Any>, host_state: Box<dyn Any>,
interrupts: Arc<VMInterrupts>, interrupts: Arc<VMInterrupts>,
) -> Result<Self, InstantiationError> { ) -> Result<Self, InstantiationError> {
@@ -815,6 +809,7 @@ impl InstanceHandle {
let handle = { let handle = {
let instance = Instance { let instance = Instance {
module, module,
code,
offsets, offsets,
memories, memories,
tables, tables,
@@ -822,7 +817,6 @@ impl InstanceHandle {
passive_data, passive_data,
finished_functions, finished_functions,
trampolines, trampolines,
dbg_jit_registration,
host_state, host_state,
interrupts, interrupts,
vmctx: VMContext {}, vmctx: VMContext {},
@@ -941,14 +935,9 @@ impl InstanceHandle {
self.instance().vmctx_ptr() self.instance().vmctx_ptr()
} }
/// Return a reference-counting pointer to a module.
pub fn module(&self) -> &Arc<Module> {
self.instance().module()
}
/// Return a reference to a module. /// Return a reference to a module.
pub fn module_ref(&self) -> &Module { pub fn module(&self) -> &Module {
self.instance().module_ref() self.instance().module()
} }
/// Lookup an export with the given name. /// Lookup an export with the given name.
@@ -1065,8 +1054,7 @@ impl InstanceHandle {
} }
fn check_table_init_bounds(instance: &Instance) -> Result<(), InstantiationError> { fn check_table_init_bounds(instance: &Instance) -> Result<(), InstantiationError> {
let module = Arc::clone(&instance.module); for init in &instance.module().table_elements {
for init in &module.table_elements {
let start = get_table_init_start(init, instance); let start = get_table_init_start(init, instance);
let table = instance.get_table(init.table_index); let table = instance.get_table(init.table_index);
@@ -1170,8 +1158,7 @@ fn get_table_init_start(init: &TableElements, instance: &Instance) -> usize {
/// Initialize the table memory from the provided initializers. /// Initialize the table memory from the provided initializers.
fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> { fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> {
let module = Arc::clone(&instance.module); for init in &instance.module().table_elements {
for init in &module.table_elements {
let start = get_table_init_start(init, instance); let start = get_table_init_start(init, instance);
let table = instance.get_table(init.table_index); let table = instance.get_table(init.table_index);
@@ -1284,7 +1271,7 @@ fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDef
} }
fn initialize_globals(instance: &Instance) { fn initialize_globals(instance: &Instance) {
let module = Arc::clone(&instance.module); let module = instance.module();
let num_imports = module.local.num_imported_globals; let num_imports = module.local.num_imported_globals;
for (index, global) in module.local.globals.iter().skip(num_imports) { for (index, global) in module.local.globals.iter().skip(num_imports) {
let def_index = module.local.defined_global_index(index).unwrap(); let def_index = module.local.defined_global_index(index).unwrap();

View File

@@ -2,7 +2,10 @@
//! the __jit_debug_register_code() and __jit_debug_descriptor to register //! the __jit_debug_register_code() and __jit_debug_descriptor to register
//! or unregister generated object images with debuggers. //! or unregister generated object images with debuggers.
use lazy_static::lazy_static;
use std::pin::Pin;
use std::ptr; use std::ptr;
use std::sync::Mutex;
#[repr(C)] #[repr(C)]
struct JITCodeEntry { struct JITCodeEntry {
@@ -43,19 +46,41 @@ extern "C" fn __jit_debug_register_code() {
} }
} }
lazy_static! {
// The process controls access to the __jit_debug_descriptor by itself --
// the GDB/LLDB accesses this structure and its data at the process startup
// and when paused in __jit_debug_register_code.
//
// The GDB_REGISTRATION lock is needed for GdbJitImageRegistration to protect
// access to the __jit_debug_descriptor within this process.
pub static ref GDB_REGISTRATION: Mutex<()> = Mutex::new(Default::default());
}
/// Registeration for JIT image /// Registeration for JIT image
pub struct GdbJitImageRegistration { pub struct GdbJitImageRegistration {
entry: *mut JITCodeEntry, entry: Pin<Box<JITCodeEntry>>,
file: Vec<u8>, file: Pin<Box<[u8]>>,
} }
impl GdbJitImageRegistration { impl GdbJitImageRegistration {
/// Registers JIT image using __jit_debug_register_code /// Registers JIT image using __jit_debug_register_code
pub fn register(file: Vec<u8>) -> Self { pub fn register(file: Vec<u8>) -> Self {
Self { let file = Pin::new(file.into_boxed_slice());
entry: unsafe { register_gdb_jit_image(&file) },
file, // Create a code entry for the file, which gives the start and size
// of the symbol file.
let mut entry = Pin::new(Box::new(JITCodeEntry {
next_entry: ptr::null_mut(),
prev_entry: ptr::null_mut(),
symfile_addr: file.as_ptr(),
symfile_size: file.len() as u64,
}));
unsafe {
register_gdb_jit_image(&mut *entry);
} }
Self { entry, file }
} }
/// JIT image used in registration /// JIT image used in registration
@@ -67,20 +92,19 @@ impl GdbJitImageRegistration {
impl Drop for GdbJitImageRegistration { impl Drop for GdbJitImageRegistration {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
unregister_gdb_jit_image(self.entry); unregister_gdb_jit_image(&mut *self.entry);
} }
} }
} }
unsafe fn register_gdb_jit_image(file: &[u8]) -> *mut JITCodeEntry { unsafe impl Send for GdbJitImageRegistration {}
// Create a code entry for the file, which gives the start and size of the symbol file. unsafe impl Sync for GdbJitImageRegistration {}
let entry = Box::into_raw(Box::new(JITCodeEntry {
next_entry: __jit_debug_descriptor.first_entry, unsafe fn register_gdb_jit_image(entry: *mut JITCodeEntry) {
prev_entry: ptr::null_mut(), let _lock = GDB_REGISTRATION.lock().unwrap();
symfile_addr: file.as_ptr(),
symfile_size: file.len() as u64,
}));
// Add it to the linked list in the JIT descriptor. // Add it to the linked list in the JIT descriptor.
(*entry).next_entry = __jit_debug_descriptor.first_entry;
if !__jit_debug_descriptor.first_entry.is_null() { if !__jit_debug_descriptor.first_entry.is_null() {
(*__jit_debug_descriptor.first_entry).prev_entry = entry; (*__jit_debug_descriptor.first_entry).prev_entry = entry;
} }
@@ -93,10 +117,11 @@ unsafe fn register_gdb_jit_image(file: &[u8]) -> *mut JITCodeEntry {
__jit_debug_descriptor.action_flag = JIT_NOACTION; __jit_debug_descriptor.action_flag = JIT_NOACTION;
__jit_debug_descriptor.relevant_entry = ptr::null_mut(); __jit_debug_descriptor.relevant_entry = ptr::null_mut();
entry
} }
unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) { unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) {
let _lock = GDB_REGISTRATION.lock().unwrap();
// Remove the code entry corresponding to the code from the linked list. // Remove the code entry corresponding to the code from the linked list.
if !(*entry).prev_entry.is_null() { if !(*entry).prev_entry.is_null() {
(*(*entry).prev_entry).next_entry = (*entry).next_entry; (*(*entry).prev_entry).next_entry = (*entry).next_entry;
@@ -114,5 +139,4 @@ unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) {
__jit_debug_descriptor.action_flag = JIT_NOACTION; __jit_debug_descriptor.action_flag = JIT_NOACTION;
__jit_debug_descriptor.relevant_entry = ptr::null_mut(); __jit_debug_descriptor.relevant_entry = ptr::null_mut();
let _box = Box::from_raw(entry);
} }

View File

@@ -3,27 +3,16 @@
use crate::vmcontext::VMSharedSignatureIndex; use crate::vmcontext::VMSharedSignatureIndex;
use more_asserts::assert_lt; use more_asserts::assert_lt;
use std::collections::HashMap; use std::collections::{hash_map, HashMap};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::sync::RwLock;
use wasmtime_environ::{ir, wasm::WasmFuncType}; use wasmtime_environ::{ir, wasm::WasmFuncType};
/// WebAssembly requires that the caller and callee signatures in an indirect /// WebAssembly requires that the caller and callee signatures in an indirect
/// call must match. To implement this efficiently, keep a registry of all /// call must match. To implement this efficiently, keep a registry of all
/// signatures, shared by all instances, so that call sites can just do an /// signatures, shared by all instances, so that call sites can just do an
/// index comparison. /// index comparison.
#[derive(Debug)]
pub struct SignatureRegistry {
// This structure is stored in a `Compiler` and is intended to be shared
// across many instances. Ideally instances can themselves be sent across
// threads, and ideally we can compile across many threads. As a result we
// use interior mutability here with a lock to avoid having callers to
// externally synchronize calls to compilation.
inner: RwLock<Inner>,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct Inner { pub struct SignatureRegistry {
wasm2index: HashMap<WasmFuncType, VMSharedSignatureIndex>, wasm2index: HashMap<WasmFuncType, VMSharedSignatureIndex>,
// Maps the index to the original Wasm signature. // Maps the index to the original Wasm signature.
@@ -34,35 +23,31 @@ struct Inner {
} }
impl SignatureRegistry { impl SignatureRegistry {
/// Create a new `SignatureRegistry`.
pub fn new() -> Self {
Self {
inner: Default::default(),
}
}
/// Register a signature and return its unique index. /// Register a signature and return its unique index.
pub fn register(&self, wasm: WasmFuncType, native: ir::Signature) -> VMSharedSignatureIndex { pub fn register(
let Inner { &mut self,
wasm2index, wasm: WasmFuncType,
index2wasm, native: ir::Signature,
index2native, ) -> VMSharedSignatureIndex {
} = &mut *self.inner.write().unwrap(); let len = self.wasm2index.len();
let len = wasm2index.len();
*wasm2index.entry(wasm.clone()).or_insert_with(|| { match self.wasm2index.entry(wasm.clone()) {
// Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX) hash_map::Entry::Occupied(entry) => *entry.get(),
// is reserved for VMSharedSignatureIndex::default(). hash_map::Entry::Vacant(entry) => {
assert_lt!( // Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX)
len, // is reserved for VMSharedSignatureIndex::default().
std::u32::MAX as usize, assert_lt!(
"Invariant check: signature_hash.len() < std::u32::MAX" len,
); std::u32::MAX as usize,
let index = VMSharedSignatureIndex::new(u32::try_from(len).unwrap()); "Invariant check: signature_hash.len() < std::u32::MAX"
index2wasm.insert(index, wasm); );
index2native.insert(index, native); let index = VMSharedSignatureIndex::new(u32::try_from(len).unwrap());
index entry.insert(index);
}) self.index2wasm.insert(index, wasm);
self.index2native.insert(index, native);
index
}
}
} }
/// Looks up a shared native signature within this registry. /// Looks up a shared native signature within this registry.
@@ -70,7 +55,7 @@ impl SignatureRegistry {
/// Note that for this operation to be semantically correct the `idx` must /// Note that for this operation to be semantically correct the `idx` must
/// have previously come from a call to `register` of this same object. /// have previously come from a call to `register` of this same object.
pub fn lookup_native(&self, idx: VMSharedSignatureIndex) -> Option<ir::Signature> { pub fn lookup_native(&self, idx: VMSharedSignatureIndex) -> Option<ir::Signature> {
self.inner.read().unwrap().index2native.get(&idx).cloned() self.index2native.get(&idx).cloned()
} }
/// Looks up a shared Wasm signature within this registry. /// Looks up a shared Wasm signature within this registry.
@@ -78,6 +63,6 @@ impl SignatureRegistry {
/// Note that for this operation to be semantically correct the `idx` must /// Note that for this operation to be semantically correct the `idx` must
/// have previously come from a call to `register` of this same object. /// have previously come from a call to `register` of this same object.
pub fn lookup_wasm(&self, idx: VMSharedSignatureIndex) -> Option<WasmFuncType> { pub fn lookup_wasm(&self, idx: VMSharedSignatureIndex) -> Option<WasmFuncType> {
self.inner.read().unwrap().index2wasm.get(&idx).cloned() self.index2wasm.get(&idx).cloned()
} }
} }

View File

@@ -53,7 +53,7 @@ pub fn instantiate(
snapshot1.add_to_linker(&mut linker)?; snapshot1.add_to_linker(&mut linker)?;
let module = Module::new(&store, &data).context("failed to create wasm module")?; let module = Module::new(store.engine(), &data).context("failed to create wasm module")?;
linker linker
.module("", &module) .module("", &module)

View File

@@ -659,13 +659,14 @@ impl Memory {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// let store = Store::default(); /// let engine = Engine::default();
/// let store = Store::new(&engine);
/// ///
/// let memory_ty = MemoryType::new(Limits::new(1, None)); /// let memory_ty = MemoryType::new(Limits::new(1, None));
/// let memory = Memory::new(&store, memory_ty); /// let memory = Memory::new(&store, memory_ty);
/// ///
/// let module = Module::new(&store, "(module (memory (import \"\" \"\") 1))")?; /// let module = Module::new(&engine, "(module (memory (import \"\" \"\") 1))")?;
/// let instance = Instance::new(&module, &[memory.into()])?; /// let instance = Instance::new(&store, &module, &[memory.into()])?;
/// // ... /// // ...
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -686,9 +687,10 @@ impl Memory {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// let store = Store::default(); /// let engine = Engine::default();
/// let module = Module::new(&store, "(module (memory (export \"mem\") 1))")?; /// let store = Store::new(&engine);
/// let instance = Instance::new(&module, &[])?; /// let module = Module::new(&engine, "(module (memory (export \"mem\") 1))")?;
/// let instance = Instance::new(&store, &module, &[])?;
/// let memory = instance.get_memory("mem").unwrap(); /// let memory = instance.get_memory("mem").unwrap();
/// let ty = memory.ty(); /// let ty = memory.ty();
/// assert_eq!(ty.limits().min(), 1); /// assert_eq!(ty.limits().min(), 1);
@@ -798,9 +800,10 @@ impl Memory {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// let store = Store::default(); /// let engine = Engine::default();
/// let module = Module::new(&store, "(module (memory (export \"mem\") 1 2))")?; /// let store = Store::new(&engine);
/// let instance = Instance::new(&module, &[])?; /// let module = Module::new(&engine, "(module (memory (export \"mem\") 1 2))")?;
/// let instance = Instance::new(&store, &module, &[])?;
/// let memory = instance.get_memory("mem").unwrap(); /// let memory = instance.get_memory("mem").unwrap();
/// ///
/// assert_eq!(memory.size(), 1); /// assert_eq!(memory.size(), 1);

View File

@@ -42,6 +42,8 @@ struct ModuleFrameInfo {
start: usize, start: usize,
functions: BTreeMap<usize, FunctionInfo>, functions: BTreeMap<usize, FunctionInfo>,
module: Arc<Module>, module: Arc<Module>,
#[allow(dead_code)]
module_code: Arc<dyn std::any::Any + Send + Sync>,
} }
struct FunctionInfo { struct FunctionInfo {
@@ -192,6 +194,7 @@ pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration>
start: min, start: min,
functions, functions,
module: module.module().clone(), module: module.module().clone(),
module_code: module.code().clone(),
}, },
); );
assert!(prev.is_none()); assert!(prev.is_none());

View File

@@ -38,9 +38,10 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// let store = Store::default(); /// let engine = Engine::default();
/// let module = Module::new(&store, r#"(module (func (export "foo")))"#)?; /// let store = Store::new(&engine);
/// let instance = Instance::new(&module, &[])?; /// let module = Module::new(&engine, r#"(module (func (export "foo")))"#)?;
/// let instance = Instance::new(&store, &module, &[])?;
/// let foo = instance.get_func("foo").expect("export wasn't a function"); /// let foo = instance.get_func("foo").expect("export wasn't a function");
/// ///
/// // Work with `foo` as a `Func` at this point, such as calling it /// // Work with `foo` as a `Func` at this point, such as calling it
@@ -76,7 +77,7 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
/// ///
/// // Next we can hook that up to a wasm module which uses it. /// // Next we can hook that up to a wasm module which uses it.
/// let module = Module::new( /// let module = Module::new(
/// &store, /// store.engine(),
/// r#" /// r#"
/// (module /// (module
/// (import "" "" (func $add (param i32 i32) (result i32))) /// (import "" "" (func $add (param i32 i32) (result i32)))
@@ -90,7 +91,7 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
/// i32.add)) /// i32.add))
/// "#, /// "#,
/// )?; /// )?;
/// let instance = Instance::new(&module, &[add.into()])?; /// let instance = Instance::new(&store, &module, &[add.into()])?;
/// let call_add_twice = instance.get_func("call_add_twice").expect("export wasn't a function"); /// let call_add_twice = instance.get_func("call_add_twice").expect("export wasn't a function");
/// let call_add_twice = call_add_twice.get0::<i32>()?; /// let call_add_twice = call_add_twice.get0::<i32>()?;
/// ///
@@ -120,7 +121,7 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
/// }); /// });
/// ///
/// let module = Module::new( /// let module = Module::new(
/// &store, /// store.engine(),
/// r#" /// r#"
/// (module /// (module
/// (import "" "" (func $double (param i32) (result i32))) /// (import "" "" (func $double (param i32) (result i32)))
@@ -131,7 +132,7 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody};
/// (start $start)) /// (start $start))
/// "#, /// "#,
/// )?; /// )?;
/// let instance = Instance::new(&module, &[double.into()])?; /// let instance = Instance::new(&store, &module, &[double.into()])?;
/// // .. work with `instance` if necessary /// // .. work with `instance` if necessary
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -335,7 +336,7 @@ impl Func {
/// # let store = Store::default(); /// # let store = Store::default();
/// let add = Func::wrap(&store, |a: i32, b: i32| a + b); /// let add = Func::wrap(&store, |a: i32, b: i32| a + b);
/// let module = Module::new( /// let module = Module::new(
/// &store, /// store.engine(),
/// r#" /// r#"
/// (module /// (module
/// (import "" "" (func $add (param i32 i32) (result i32))) /// (import "" "" (func $add (param i32 i32) (result i32)))
@@ -345,7 +346,7 @@ impl Func {
/// call $add)) /// call $add))
/// "#, /// "#,
/// )?; /// )?;
/// let instance = Instance::new(&module, &[add.into()])?; /// let instance = Instance::new(&store, &module, &[add.into()])?;
/// let foo = instance.get_func("foo").unwrap().get2::<i32, i32, i32>()?; /// let foo = instance.get_func("foo").unwrap().get2::<i32, i32, i32>()?;
/// assert_eq!(foo(1, 2)?, 3); /// assert_eq!(foo(1, 2)?, 3);
/// # Ok(()) /// # Ok(())
@@ -366,7 +367,7 @@ impl Func {
/// } /// }
/// }); /// });
/// let module = Module::new( /// let module = Module::new(
/// &store, /// store.engine(),
/// r#" /// r#"
/// (module /// (module
/// (import "" "" (func $add (param i32 i32) (result i32))) /// (import "" "" (func $add (param i32 i32) (result i32)))
@@ -376,7 +377,7 @@ impl Func {
/// call $add)) /// call $add))
/// "#, /// "#,
/// )?; /// )?;
/// let instance = Instance::new(&module, &[add.into()])?; /// let instance = Instance::new(&store, &module, &[add.into()])?;
/// let foo = instance.get_func("foo").unwrap().get2::<i32, i32, i32>()?; /// let foo = instance.get_func("foo").unwrap().get2::<i32, i32, i32>()?;
/// assert_eq!(foo(1, 2)?, 3); /// assert_eq!(foo(1, 2)?, 3);
/// assert!(foo(i32::max_value(), 1).is_err()); /// assert!(foo(i32::max_value(), 1).is_err());
@@ -397,7 +398,7 @@ impl Func {
/// println!("d={}", d); /// println!("d={}", d);
/// }); /// });
/// let module = Module::new( /// let module = Module::new(
/// &store, /// store.engine(),
/// r#" /// r#"
/// (module /// (module
/// (import "" "" (func $debug (param i32 f32 i64 f64))) /// (import "" "" (func $debug (param i32 f32 i64 f64)))
@@ -409,7 +410,7 @@ impl Func {
/// call $debug)) /// call $debug))
/// "#, /// "#,
/// )?; /// )?;
/// let instance = Instance::new(&module, &[debug.into()])?; /// let instance = Instance::new(&store, &module, &[debug.into()])?;
/// let foo = instance.get_func("foo").unwrap().get0::<()>()?; /// let foo = instance.get_func("foo").unwrap().get0::<()>()?;
/// foo()?; /// foo()?;
/// # Ok(()) /// # Ok(())
@@ -453,7 +454,7 @@ impl Func {
/// Ok(()) /// Ok(())
/// }); /// });
/// let module = Module::new( /// let module = Module::new(
/// &store, /// store.engine(),
/// r#" /// r#"
/// (module /// (module
/// (import "" "" (func $log_str (param i32 i32))) /// (import "" "" (func $log_str (param i32 i32)))
@@ -465,7 +466,7 @@ impl Func {
/// (data (i32.const 4) "Hello, world!")) /// (data (i32.const 4) "Hello, world!"))
/// "#, /// "#,
/// )?; /// )?;
/// let instance = Instance::new(&module, &[log_str.into()])?; /// let instance = Instance::new(&store, &module, &[log_str.into()])?;
/// let foo = instance.get_func("foo").unwrap().get0::<()>()?; /// let foo = instance.get_func("foo").unwrap().get0::<()>()?;
/// foo()?; /// foo()?;
/// # Ok(()) /// # Ok(())
@@ -479,13 +480,7 @@ impl Func {
pub fn ty(&self) -> FuncType { pub fn ty(&self) -> FuncType {
// Signatures should always be registered in the store's registry of // Signatures should always be registered in the store's registry of
// shared signatures, so we should be able to unwrap safely here. // shared signatures, so we should be able to unwrap safely here.
let sig = self let sig = self.instance.store.lookup_signature(self.export.signature);
.instance
.store
.compiler()
.signatures()
.lookup_wasm(self.export.signature)
.expect("failed to lookup signature");
// This is only called with `Export::Function`, and since it's coming // This is only called with `Export::Function`, and since it's coming
// from wasmtime_runtime itself we should support all the types coming // from wasmtime_runtime itself we should support all the types coming
@@ -495,25 +490,13 @@ impl Func {
/// Returns the number of parameters that this function takes. /// Returns the number of parameters that this function takes.
pub fn param_arity(&self) -> usize { pub fn param_arity(&self) -> usize {
self.instance let sig = self.instance.store.lookup_signature(self.export.signature);
.store sig.params.len()
.compiler()
.signatures()
.lookup_wasm(self.export.signature)
.expect("failed to lookup signature")
.params
.len()
} }
/// Returns the number of results this function produces. /// Returns the number of results this function produces.
pub fn result_arity(&self) -> usize { pub fn result_arity(&self) -> usize {
let sig = self let sig = self.instance.store.lookup_signature(self.export.signature);
.instance
.store
.compiler()
.signatures()
.lookup_wasm(self.export.signature)
.expect("failed to lookup signature");
sig.returns.len() sig.returns.len()
} }
@@ -749,7 +732,7 @@ pub(crate) fn catch_traps(
wasmtime_runtime::catch_traps( wasmtime_runtime::catch_traps(
vmctx, vmctx,
store.engine().config().max_wasm_stack, store.engine().config().max_wasm_stack,
|addr| store.compiler().is_in_jit_code(addr), |addr| store.is_in_jit_code(addr),
signalhandler.as_deref(), signalhandler.as_deref(),
closure, closure,
) )

View File

@@ -1,11 +1,11 @@
use crate::trampoline::StoreInstanceHandle; use crate::trampoline::StoreInstanceHandle;
use crate::{Export, Extern, Func, Global, Memory, Module, Store, Table, Trap}; use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
use anyhow::{bail, Error, Result}; use anyhow::{bail, Error, Result};
use std::any::Any; use std::any::Any;
use std::mem; use std::mem;
use wasmtime_environ::EntityIndex; use wasmtime_environ::EntityIndex;
use wasmtime_jit::{CompiledModule, Resolver}; use wasmtime_jit::{CompiledModule, Resolver};
use wasmtime_runtime::{InstantiationError, SignatureRegistry, VMContext, VMFunctionBody}; use wasmtime_runtime::{InstantiationError, VMContext, VMFunctionBody};
struct SimpleResolver<'a> { struct SimpleResolver<'a> {
imports: &'a [Extern], imports: &'a [Extern],
@@ -23,7 +23,6 @@ fn instantiate(
store: &Store, store: &Store,
compiled_module: &CompiledModule, compiled_module: &CompiledModule,
imports: &[Extern], imports: &[Extern],
sig_registry: &SignatureRegistry,
host: Box<dyn Any>, host: Box<dyn Any>,
) -> Result<StoreInstanceHandle, Error> { ) -> Result<StoreInstanceHandle, Error> {
// For now we have a restriction that the `Store` that we're working // For now we have a restriction that the `Store` that we're working
@@ -47,8 +46,9 @@ fn instantiate(
let instance = unsafe { let instance = unsafe {
let instance = compiled_module.instantiate( let instance = compiled_module.instantiate(
&mut resolver, &mut resolver,
sig_registry, &mut store.signatures_mut(),
config.memory_creator.as_ref().map(|a| a as _), config.memory_creator.as_ref().map(|a| a as _),
store.interrupts().clone(),
host, host,
)?; )?;
@@ -120,6 +120,7 @@ fn instantiate(
#[derive(Clone)] #[derive(Clone)]
pub struct Instance { pub struct Instance {
pub(crate) handle: StoreInstanceHandle, pub(crate) handle: StoreInstanceHandle,
store: Store,
module: Module, module: Module,
} }
@@ -177,19 +178,19 @@ impl Instance {
/// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation /// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation
/// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727 /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727
/// [`ExternType`]: crate::ExternType /// [`ExternType`]: crate::ExternType
pub fn new(module: &Module, imports: &[Extern]) -> Result<Instance, Error> { pub fn new(store: &Store, module: &Module, imports: &[Extern]) -> Result<Instance, Error> {
let store = module.store(); if !Engine::same(store.engine(), module.engine()) {
bail!("cross-`Engine` instantiation is not currently supported");
}
let info = module.register_frame_info(); let info = module.register_frame_info();
let handle = instantiate( store.register_jit_code(module.compiled_module().jit_code_ranges());
store,
module.compiled_module(), let handle = instantiate(store, module.compiled_module(), imports, Box::new(info))?;
imports,
store.compiler().signatures(),
Box::new(info),
)?;
Ok(Instance { Ok(Instance {
handle, handle,
store: store.clone(),
module: module.clone(), module: module.clone(),
}) })
} }
@@ -199,7 +200,7 @@ impl Instance {
/// This is the [`Store`] that generally serves as a sort of global cache /// This is the [`Store`] that generally serves as a sort of global cache
/// for various instance-related things. /// for various instance-related things.
pub fn store(&self) -> &Store { pub fn store(&self) -> &Store {
self.module.store() &self.store
} }
/// Returns the list of exported items from this [`Instance`]. /// Returns the list of exported items from this [`Instance`].

View File

@@ -150,7 +150,7 @@ impl Linker {
/// (data (global.get 0) "foo") /// (data (global.get 0) "foo")
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// linker.instantiate(&module)?; /// linker.instantiate(&module)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -203,7 +203,7 @@ impl Linker {
/// (import "host" "log_str" (func (param i32 i32))) /// (import "host" "log_str" (func (param i32 i32)))
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// linker.instantiate(&module)?; /// linker.instantiate(&module)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -241,7 +241,7 @@ impl Linker {
/// ///
/// // Instantiate a small instance... /// // Instantiate a small instance...
/// let wat = r#"(module (func (export "run") ))"#; /// let wat = r#"(module (func (export "run") ))"#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// let instance = linker.instantiate(&module)?; /// let instance = linker.instantiate(&module)?;
/// ///
/// // ... and inform the linker that the name of this instance is /// // ... and inform the linker that the name of this instance is
@@ -257,7 +257,7 @@ impl Linker {
/// ) /// )
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// let instance = linker.instantiate(&module)?; /// let instance = linker.instantiate(&module)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -312,7 +312,7 @@ impl Linker {
/// // this instance is `instance1`. This defines the `instance1::run` name /// // this instance is `instance1`. This defines the `instance1::run` name
/// // for our next module to use. /// // for our next module to use.
/// let wat = r#"(module (func (export "run") ))"#; /// let wat = r#"(module (func (export "run") ))"#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// linker.module("instance1", &module)?; /// linker.module("instance1", &module)?;
/// ///
/// let wat = r#" /// let wat = r#"
@@ -323,7 +323,7 @@ impl Linker {
/// ) /// )
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// let instance = linker.instantiate(&module)?; /// let instance = linker.instantiate(&module)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -350,7 +350,7 @@ impl Linker {
/// ) /// )
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// linker.module("commander", &module)?; /// linker.module("commander", &module)?;
/// let run = linker.get_default("")?.get0::<()>()?; /// let run = linker.get_default("")?.get0::<()>()?;
/// run()?; /// run()?;
@@ -369,7 +369,7 @@ impl Linker {
/// ) /// )
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// linker.module("", &module)?; /// linker.module("", &module)?;
/// let count = linker.get_one_by_name("", "run")?.into_func().unwrap().get0::<i32>()?()?; /// let count = linker.get_one_by_name("", "run")?.into_func().unwrap().get0::<i32>()?()?;
/// assert_eq!(count, 0, "a Command should get a fresh instance on each invocation"); /// assert_eq!(count, 0, "a Command should get a fresh instance on each invocation");
@@ -400,11 +400,12 @@ impl Linker {
for export in module.exports() { for export in module.exports() {
if let Some(func_ty) = export.ty().func() { if let Some(func_ty) = export.ty().func() {
let imports = self.compute_imports(module)?; let imports = self.compute_imports(module)?;
let store = self.store.clone();
let module = module.clone(); let module = module.clone();
let export_name = export.name().to_owned(); let export_name = export.name().to_owned();
let func = Func::new(&self.store, func_ty.clone(), move |_, params, results| { let func = Func::new(&self.store, func_ty.clone(), move |_, params, results| {
// Create a new instance for this command execution. // Create a new instance for this command execution.
let instance = Instance::new(&module, &imports)?; let instance = Instance::new(&store, &module, &imports)?;
// `unwrap()` everything here because we know the instance contains a // `unwrap()` everything here because we know the instance contains a
// function export with the given name and signature because we're // function export with the given name and signature because we're
@@ -560,7 +561,7 @@ impl Linker {
/// (import "host" "double" (func (param i32) (result i32))) /// (import "host" "double" (func (param i32) (result i32)))
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(store.engine(), wat)?;
/// linker.instantiate(&module)?; /// linker.instantiate(&module)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -568,7 +569,7 @@ impl Linker {
pub fn instantiate(&self, module: &Module) -> Result<Instance> { pub fn instantiate(&self, module: &Module) -> Result<Instance> {
let imports = self.compute_imports(module)?; let imports = self.compute_imports(module)?;
Instance::new(module, &imports) Instance::new(&self.store, module, &imports)
} }
fn compute_imports(&self, module: &Module) -> Result<Vec<Extern>> { fn compute_imports(&self, module: &Module) -> Result<Vec<Extern>> {

View File

@@ -1,5 +1,5 @@
use crate::frame_info::GlobalFrameInfoRegistration; use crate::frame_info::GlobalFrameInfoRegistration;
use crate::runtime::Store; use crate::runtime::Engine;
use crate::types::{EntityType, ExportType, ExternType, ImportType}; use crate::types::{EntityType, ExportType, ExternType, ImportType};
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use std::path::Path; use std::path::Path;
@@ -23,6 +23,8 @@ use wasmtime_jit::CompiledModule;
/// compiling the original wasm module only once with a single [`Module`] /// compiling the original wasm module only once with a single [`Module`]
/// instance. /// instance.
/// ///
/// The `Module` is threadsafe and safe to share accross threads.
///
/// ## Modules and `Clone` /// ## Modules and `Clone`
/// ///
/// Using `clone` on a `Module` is a cheap operation. It will not create an /// Using `clone` on a `Module` is a cheap operation. It will not create an
@@ -38,8 +40,8 @@ use wasmtime_jit::CompiledModule;
/// ```no_run /// ```no_run
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// let store = Store::default(); /// let engine = Engine::default();
/// let module = Module::from_file(&store, "path/to/foo.wasm")?; /// let module = Module::from_file(&engine, "path/to/foo.wasm")?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@@ -49,9 +51,9 @@ use wasmtime_jit::CompiledModule;
/// ```no_run /// ```no_run
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// let store = Store::default(); /// let engine = Engine::default();
/// // Now we're using the WebAssembly text extension: `.wat`! /// // Now we're using the WebAssembly text extension: `.wat`!
/// let module = Module::from_file(&store, "path/to/foo.wat")?; /// let module = Module::from_file(&engine, "path/to/foo.wat")?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@@ -62,12 +64,12 @@ use wasmtime_jit::CompiledModule;
/// ```no_run /// ```no_run
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// let store = Store::default(); /// let engine = Engine::default();
/// # let wasm_bytes: Vec<u8> = Vec::new(); /// # let wasm_bytes: Vec<u8> = Vec::new();
/// let module = Module::new(&store, &wasm_bytes)?; /// let module = Module::new(&engine, &wasm_bytes)?;
/// ///
/// // It also works with the text format! /// // It also works with the text format!
/// let module = Module::new(&store, "(module (func))")?; /// let module = Module::new(&engine, "(module (func))")?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@@ -75,13 +77,9 @@ use wasmtime_jit::CompiledModule;
/// [`Config`]: crate::Config /// [`Config`]: crate::Config
#[derive(Clone)] #[derive(Clone)]
pub struct Module { pub struct Module {
inner: Arc<ModuleInner>, engine: Engine,
} compiled: Arc<CompiledModule>,
frame_info_registration: Arc<Mutex<Option<Option<Arc<GlobalFrameInfoRegistration>>>>>,
struct ModuleInner {
store: Store,
compiled: CompiledModule,
frame_info_registration: Mutex<Option<Option<Arc<GlobalFrameInfoRegistration>>>>,
} }
impl Module { impl Module {
@@ -103,12 +101,7 @@ impl Module {
/// compilation of a module. /// compilation of a module.
/// ///
/// The WebAssembly binary will be decoded and validated. It will also be /// The WebAssembly binary will be decoded and validated. It will also be
/// compiled according to the configuration of the provided `store` and /// compiled according to the configuration of the provided `engine`.
/// cached in this type.
///
/// The provided `store` is a global cache for compiled resources as well as
/// configuration for what wasm features are enabled. It's recommended to
/// share a `store` among modules if possible.
/// ///
/// # Errors /// # Errors
/// ///
@@ -121,7 +114,7 @@ impl Module {
/// * Implementation-specific limits were exceeded with a valid binary (for /// * Implementation-specific limits were exceeded with a valid binary (for
/// example too many locals) /// example too many locals)
/// * The wasm binary may use features that are not enabled in the /// * The wasm binary may use features that are not enabled in the
/// configuration of `store` /// configuration of `enging`
/// * If the `wat` feature is enabled and the input is text, then it may be /// * If the `wat` feature is enabled and the input is text, then it may be
/// rejected if it fails to parse. /// rejected if it fails to parse.
/// ///
@@ -138,9 +131,9 @@ impl Module {
/// ```no_run /// ```no_run
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// # let wasm_bytes: Vec<u8> = Vec::new(); /// # let wasm_bytes: Vec<u8> = Vec::new();
/// let module = Module::new(&store, &wasm_bytes)?; /// let module = Module::new(&engine, &wasm_bytes)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@@ -151,25 +144,28 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let module = Module::new(&store, "(module (func))")?; /// let module = Module::new(&engine, "(module (func))")?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Module> { pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Module> {
#[cfg(feature = "wat")] #[cfg(feature = "wat")]
let bytes = wat::parse_bytes(bytes.as_ref())?; let bytes = wat::parse_bytes(bytes.as_ref())?;
Module::from_binary(store, bytes.as_ref()) Module::from_binary(engine, bytes.as_ref())
} }
/// Creates a new WebAssembly `Module` from the given in-memory `binary` /// Creates a new WebAssembly `Module` from the given in-memory `binary`
/// data. The provided `name` will be used in traps/backtrace details. /// data. The provided `name` will be used in traps/backtrace details.
/// ///
/// See [`Module::new`] for other details. /// See [`Module::new`] for other details.
pub fn new_with_name(store: &Store, bytes: impl AsRef<[u8]>, name: &str) -> Result<Module> { pub fn new_with_name(engine: &Engine, bytes: impl AsRef<[u8]>, name: &str) -> Result<Module> {
let mut module = Module::new(store, bytes.as_ref())?; let mut module = Module::new(engine, bytes.as_ref())?;
let inner = Arc::get_mut(&mut module.inner).unwrap(); Arc::get_mut(&mut module.compiled)
Arc::get_mut(inner.compiled.module_mut()).unwrap().name = Some(name.to_string()); .unwrap()
.module_mut()
.expect("mutable module")
.name = Some(name.to_string());
Ok(module) Ok(module)
} }
@@ -185,8 +181,8 @@ impl Module {
/// ```no_run /// ```no_run
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// let store = Store::default(); /// let engine = Engine::default();
/// let module = Module::from_file(&store, "./path/to/foo.wasm")?; /// let module = Module::from_file(&engine, "./path/to/foo.wasm")?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@@ -196,17 +192,17 @@ impl Module {
/// ```no_run /// ```no_run
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let module = Module::from_file(&store, "./path/to/foo.wat")?; /// let module = Module::from_file(&engine, "./path/to/foo.wat")?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn from_file(store: &Store, file: impl AsRef<Path>) -> Result<Module> { pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Module> {
#[cfg(feature = "wat")] #[cfg(feature = "wat")]
let wasm = wat::parse_file(file)?; let wasm = wat::parse_file(file)?;
#[cfg(not(feature = "wat"))] #[cfg(not(feature = "wat"))]
let wasm = std::fs::read(file)?; let wasm = std::fs::read(file)?;
Module::new(store, &wasm) Module::new(engine, &wasm)
} }
/// Creates a new WebAssembly `Module` from the given in-memory `binary` /// Creates a new WebAssembly `Module` from the given in-memory `binary`
@@ -223,9 +219,9 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let wasm = b"\0asm\x01\0\0\0"; /// let wasm = b"\0asm\x01\0\0\0";
/// let module = Module::from_binary(&store, wasm)?; /// let module = Module::from_binary(&engine, wasm)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@@ -235,17 +231,17 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// assert!(Module::from_binary(&store, b"(module)").is_err()); /// assert!(Module::from_binary(&engine, b"(module)").is_err());
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn from_binary(store: &Store, binary: &[u8]) -> Result<Module> { pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
Module::validate(store, binary)?; Module::validate(engine, binary)?;
// Note that the call to `from_binary_unchecked` here should be ok // Note that the call to `from_binary_unchecked` here should be ok
// because we previously validated the binary, meaning we're guaranteed // because we previously validated the binary, meaning we're guaranteed
// to pass a valid binary for `store`. // to pass a valid binary for `engine`.
unsafe { Module::from_binary_unchecked(store, binary) } unsafe { Module::from_binary_unchecked(engine, binary) }
} }
/// Creates a new WebAssembly `Module` from the given in-memory `binary` /// Creates a new WebAssembly `Module` from the given in-memory `binary`
@@ -257,7 +253,7 @@ impl Module {
/// WebAssembly. The WebAssembly binary is not validated for /// WebAssembly. The WebAssembly binary is not validated for
/// correctness and it is simply assumed as valid. /// correctness and it is simply assumed as valid.
/// ///
/// For more information about creation of a module and the `store` argument /// For more information about creation of a module and the `engine` argument
/// see the documentation of [`Module::new`]. /// see the documentation of [`Module::new`].
/// ///
/// # Unsafety /// # Unsafety
@@ -275,17 +271,17 @@ impl Module {
/// While this assumes that the binary is valid it still needs to actually /// While this assumes that the binary is valid it still needs to actually
/// be somewhat valid for decoding purposes, and the basics of decoding can /// be somewhat valid for decoding purposes, and the basics of decoding can
/// still fail. /// still fail.
pub unsafe fn from_binary_unchecked(store: &Store, binary: &[u8]) -> Result<Module> { pub unsafe fn from_binary_unchecked(engine: &Engine, binary: &[u8]) -> Result<Module> {
Module::compile(store, binary) Module::compile(engine, binary)
} }
/// Validates `binary` input data as a WebAssembly binary given the /// Validates `binary` input data as a WebAssembly binary given the
/// configuration in `store`. /// configuration in `engine`.
/// ///
/// This function will perform a speedy validation of the `binary` input /// This function will perform a speedy validation of the `binary` input
/// WebAssembly module (which is in [binary form][binary], the text format /// WebAssembly module (which is in [binary form][binary], the text format
/// is not accepted by this function) and return either `Ok` or `Err` /// is not accepted by this function) and return either `Ok` or `Err`
/// depending on the results of validation. The `store` argument indicates /// depending on the results of validation. The `engine` argument indicates
/// configuration for WebAssembly features, for example, which are used to /// configuration for WebAssembly features, for example, which are used to
/// indicate what should be valid and what shouldn't be. /// indicate what should be valid and what shouldn't be.
/// ///
@@ -299,29 +295,23 @@ impl Module {
/// validation issue will be returned. /// validation issue will be returned.
/// ///
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html /// [binary]: https://webassembly.github.io/spec/core/binary/index.html
pub fn validate(store: &Store, binary: &[u8]) -> Result<()> { pub fn validate(engine: &Engine, binary: &[u8]) -> Result<()> {
let config = store.engine().config().validating_config.clone(); let config = engine.config().validating_config.clone();
validate(binary, Some(config)).map_err(Error::new) validate(binary, Some(config)).map_err(Error::new)
} }
unsafe fn compile(store: &Store, binary: &[u8]) -> Result<Self> { unsafe fn compile(engine: &Engine, binary: &[u8]) -> Result<Self> {
let compiled = CompiledModule::new( let compiled = CompiledModule::new(engine.compiler(), binary, &*engine.config().profiler)?;
&mut store.compiler_mut(),
binary,
&*store.engine().config().profiler,
)?;
Ok(Module { Ok(Module {
inner: Arc::new(ModuleInner { engine: engine.clone(),
store: store.clone(), compiled: Arc::new(compiled),
compiled, frame_info_registration: Arc::new(Mutex::new(None)),
frame_info_registration: Mutex::new(None),
}),
}) })
} }
pub(crate) fn compiled_module(&self) -> &CompiledModule { pub(crate) fn compiled_module(&self) -> &CompiledModule {
&self.inner.compiled &self.compiled
} }
/// Returns identifier/name that this [`Module`] has. This name /// Returns identifier/name that this [`Module`] has. This name
@@ -336,20 +326,20 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let module = Module::new(&store, "(module $foo)")?; /// let module = Module::new(&engine, "(module $foo)")?;
/// assert_eq!(module.name(), Some("foo")); /// assert_eq!(module.name(), Some("foo"));
/// ///
/// let module = Module::new(&store, "(module)")?; /// let module = Module::new(&engine, "(module)")?;
/// assert_eq!(module.name(), None); /// assert_eq!(module.name(), None);
/// ///
/// let module = Module::new_with_name(&store, "(module)", "bar")?; /// let module = Module::new_with_name(&engine, "(module)", "bar")?;
/// assert_eq!(module.name(), Some("bar")); /// assert_eq!(module.name(), Some("bar"));
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn name(&self) -> Option<&str> { pub fn name(&self) -> Option<&str> {
self.inner.compiled.module().name.as_deref() self.compiled.module().name.as_deref()
} }
/// Returns the list of imports that this [`Module`] has and must be /// Returns the list of imports that this [`Module`] has and must be
@@ -371,8 +361,8 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let module = Module::new(&store, "(module)")?; /// let module = Module::new(&engine, "(module)")?;
/// assert_eq!(module.imports().len(), 0); /// assert_eq!(module.imports().len(), 0);
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -383,13 +373,13 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let wat = r#" /// let wat = r#"
/// (module /// (module
/// (import "host" "foo" (func)) /// (import "host" "foo" (func))
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(&engine, wat)?;
/// assert_eq!(module.imports().len(), 1); /// assert_eq!(module.imports().len(), 1);
/// let import = module.imports().next().unwrap(); /// let import = module.imports().next().unwrap();
/// assert_eq!(import.module(), "host"); /// assert_eq!(import.module(), "host");
@@ -404,7 +394,7 @@ impl Module {
pub fn imports<'module>( pub fn imports<'module>(
&'module self, &'module self,
) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module { ) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module {
let module = self.inner.compiled.module_ref(); let module = self.compiled.module();
module module
.imports .imports
.iter() .iter()
@@ -429,8 +419,8 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let module = Module::new(&store, "(module)")?; /// let module = Module::new(&engine, "(module)")?;
/// assert!(module.exports().next().is_none()); /// assert!(module.exports().next().is_none());
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -441,14 +431,14 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let wat = r#" /// let wat = r#"
/// (module /// (module
/// (func (export "foo")) /// (func (export "foo"))
/// (memory (export "memory") 1) /// (memory (export "memory") 1)
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(&engine, wat)?;
/// assert_eq!(module.exports().len(), 2); /// assert_eq!(module.exports().len(), 2);
/// ///
/// let mut exports = module.exports(); /// let mut exports = module.exports();
@@ -471,7 +461,7 @@ impl Module {
pub fn exports<'module>( pub fn exports<'module>(
&'module self, &'module self,
) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module { ) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module {
let module = self.inner.compiled.module_ref(); let module = self.compiled.module();
module.exports.iter().map(move |(name, entity_index)| { module.exports.iter().map(move |(name, entity_index)| {
let r#type = EntityType::new(entity_index, module); let r#type = EntityType::new(entity_index, module);
ExportType::new(name, r#type) ExportType::new(name, r#type)
@@ -489,8 +479,8 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let module = Module::new(&store, "(module)")?; /// let module = Module::new(&engine, "(module)")?;
/// assert!(module.get_export("foo").is_none()); /// assert!(module.get_export("foo").is_none());
/// # Ok(()) /// # Ok(())
/// # } /// # }
@@ -501,14 +491,14 @@ impl Module {
/// ``` /// ```
/// # use wasmtime::*; /// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> { /// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default(); /// # let engine = Engine::default();
/// let wat = r#" /// let wat = r#"
/// (module /// (module
/// (func (export "foo")) /// (func (export "foo"))
/// (memory (export "memory") 1) /// (memory (export "memory") 1)
/// ) /// )
/// "#; /// "#;
/// let module = Module::new(&store, wat)?; /// let module = Module::new(&engine, wat)?;
/// let foo = module.get_export("foo"); /// let foo = module.get_export("foo");
/// assert!(foo.is_some()); /// assert!(foo.is_some());
/// ///
@@ -522,26 +512,31 @@ impl Module {
/// # } /// # }
/// ``` /// ```
pub fn get_export<'module>(&'module self, name: &'module str) -> Option<ExternType> { pub fn get_export<'module>(&'module self, name: &'module str) -> Option<ExternType> {
let module = self.inner.compiled.module_ref(); let module = self.compiled.module();
let entity_index = module.exports.get(name)?; let entity_index = module.exports.get(name)?;
Some(EntityType::new(entity_index, module).extern_type()) Some(EntityType::new(entity_index, module).extern_type())
} }
/// Returns the [`Store`] that this [`Module`] was compiled into. /// Returns the [`Engine`] that this [`Module`] was compiled by.
pub fn store(&self) -> &Store { pub fn engine(&self) -> &Engine {
&self.inner.store &self.engine
} }
/// Register this module's stack frame information into the global scope. /// Register this module's stack frame information into the global scope.
/// ///
/// This is required to ensure that any traps can be properly symbolicated. /// This is required to ensure that any traps can be properly symbolicated.
pub(crate) fn register_frame_info(&self) -> Option<Arc<GlobalFrameInfoRegistration>> { pub(crate) fn register_frame_info(&self) -> Option<Arc<GlobalFrameInfoRegistration>> {
let mut info = self.inner.frame_info_registration.lock().unwrap(); let mut info = self.frame_info_registration.lock().unwrap();
if let Some(info) = &*info { if let Some(info) = &*info {
return info.clone(); return info.clone();
} }
let ret = super::frame_info::register(&self.inner.compiled).map(Arc::new); let ret = super::frame_info::register(&self.compiled).map(Arc::new);
*info = Some(ret.clone()); *info = Some(ret.clone());
return ret; return ret;
} }
} }
fn _assert_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<Module>();
}

View File

@@ -14,11 +14,12 @@ use std::rc::{Rc, Weak};
use std::sync::Arc; use std::sync::Arc;
use wasmparser::{OperatorValidatorConfig, ValidatingParserConfig}; use wasmparser::{OperatorValidatorConfig, ValidatingParserConfig};
use wasmtime_environ::settings::{self, Configurable}; use wasmtime_environ::settings::{self, Configurable};
use wasmtime_environ::{CacheConfig, Tunables}; use wasmtime_environ::{ir, wasm, CacheConfig, Tunables};
use wasmtime_jit::{native, CompilationStrategy, Compiler}; use wasmtime_jit::{native, CompilationStrategy, Compiler};
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent}; use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
use wasmtime_runtime::{ use wasmtime_runtime::{
debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, VMExternRef, VMInterrupts, debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, SignatureRegistry,
VMExternRef, VMInterrupts, VMSharedSignatureIndex,
}; };
// Runtime Environment // Runtime Environment
@@ -571,6 +572,16 @@ impl Config {
cmp::max(guard_size, self.tunables.static_memory_offset_guard_size); cmp::max(guard_size, self.tunables.static_memory_offset_guard_size);
self self
} }
fn build_compiler(&self) -> Compiler {
let isa = native::builder().finish(settings::Flags::new(self.flags.clone()));
Compiler::new(
isa,
self.strategy,
self.cache_config.clone(),
self.tunables.clone(),
)
}
} }
fn round_up_to_pages(val: u64) -> u64 { fn round_up_to_pages(val: u64) -> u64 {
@@ -687,9 +698,14 @@ pub enum ProfilingStrategy {
/// You can create an engine with default configuration settings using /// You can create an engine with default configuration settings using
/// `Engine::default()`. Be sure to consult the documentation of [`Config`] for /// `Engine::default()`. Be sure to consult the documentation of [`Config`] for
/// default settings. /// default settings.
#[derive(Default, Clone)] #[derive(Clone)]
pub struct Engine { pub struct Engine {
config: Arc<Config>, inner: Arc<EngineInner>,
}
struct EngineInner {
config: Config,
compiler: Compiler,
} }
impl Engine { impl Engine {
@@ -698,13 +714,31 @@ impl Engine {
pub fn new(config: &Config) -> Engine { pub fn new(config: &Config) -> Engine {
debug_builtins::ensure_exported(); debug_builtins::ensure_exported();
Engine { Engine {
config: Arc::new(config.clone()), inner: Arc::new(EngineInner {
config: config.clone(),
compiler: config.build_compiler(),
}),
} }
} }
/// Returns the configuration settings that this engine is using. /// Returns the configuration settings that this engine is using.
pub fn config(&self) -> &Config { pub fn config(&self) -> &Config {
&self.config &self.inner.config
}
pub(crate) fn compiler(&self) -> &Compiler {
&self.inner.compiler
}
/// Returns whether the engine `a` and `b` refer to the same configuration.
pub fn same(a: &Engine, b: &Engine) -> bool {
Arc::ptr_eq(&a.inner, &b.inner)
}
}
impl Default for Engine {
fn default() -> Engine {
Engine::new(&Config::default())
} }
} }
@@ -734,9 +768,11 @@ pub struct Store {
pub(crate) struct StoreInner { pub(crate) struct StoreInner {
engine: Engine, engine: Engine,
compiler: RefCell<Compiler>, interrupts: Arc<VMInterrupts>,
signatures: RefCell<SignatureRegistry>,
instances: RefCell<Vec<InstanceHandle>>, instances: RefCell<Vec<InstanceHandle>>,
signal_handler: RefCell<Option<Box<SignalHandler<'static>>>>, signal_handler: RefCell<Option<Box<SignalHandler<'static>>>>,
jit_code_ranges: RefCell<Vec<(usize, usize)>>,
host_info: RefCell<HashMap<HostInfoKey, Rc<RefCell<dyn Any>>>>, host_info: RefCell<HashMap<HostInfoKey, Rc<RefCell<dyn Any>>>>,
} }
@@ -769,19 +805,14 @@ impl Store {
// each one that's not relevant just won't do anything. // each one that's not relevant just won't do anything.
wasmtime_runtime::init_traps(); wasmtime_runtime::init_traps();
let isa = native::builder().finish(settings::Flags::new(engine.config.flags.clone()));
let compiler = Compiler::new(
isa,
engine.config.strategy,
engine.config.cache_config.clone(),
engine.config.tunables.clone(),
);
Store { Store {
inner: Rc::new(StoreInner { inner: Rc::new(StoreInner {
engine: engine.clone(), engine: engine.clone(),
compiler: RefCell::new(compiler), interrupts: Arc::new(Default::default()),
signatures: RefCell::new(Default::default()),
instances: RefCell::new(Vec::new()), instances: RefCell::new(Vec::new()),
signal_handler: RefCell::new(None), signal_handler: RefCell::new(None),
jit_code_ranges: RefCell::new(Vec::new()),
host_info: RefCell::new(HashMap::new()), host_info: RefCell::new(HashMap::new()),
}), }),
} }
@@ -798,15 +829,61 @@ impl Store {
/// Returns an optional reference to a ['RuntimeMemoryCreator'] /// Returns an optional reference to a ['RuntimeMemoryCreator']
pub(crate) fn memory_creator(&self) -> Option<&dyn RuntimeMemoryCreator> { pub(crate) fn memory_creator(&self) -> Option<&dyn RuntimeMemoryCreator> {
self.engine().config.memory_creator.as_ref().map(|x| x as _) self.engine()
.config()
.memory_creator
.as_ref()
.map(|x| x as _)
} }
pub(crate) fn compiler(&self) -> std::cell::Ref<'_, Compiler> { pub(crate) fn lookup_signature(&self, sig_index: VMSharedSignatureIndex) -> wasm::WasmFuncType {
self.inner.compiler.borrow() self.inner
.signatures
.borrow()
.lookup_wasm(sig_index)
.expect("failed to lookup signature")
} }
pub(crate) fn compiler_mut(&self) -> std::cell::RefMut<'_, Compiler> { pub(crate) fn register_signature(
self.inner.compiler.borrow_mut() &self,
wasm_sig: wasm::WasmFuncType,
native: ir::Signature,
) -> VMSharedSignatureIndex {
self.inner
.signatures
.borrow_mut()
.register(wasm_sig, native)
}
pub(crate) fn signatures_mut(&self) -> std::cell::RefMut<'_, SignatureRegistry> {
self.inner.signatures.borrow_mut()
}
/// Returns whether or not the given address falls within the JIT code
/// managed by the compiler
pub(crate) fn is_in_jit_code(&self, addr: usize) -> bool {
self.inner
.jit_code_ranges
.borrow()
.iter()
.any(|(start, end)| *start <= addr && addr < *end)
}
pub(crate) fn register_jit_code(&self, mut ranges: impl Iterator<Item = (usize, usize)>) {
// Checking of we already registered JIT code ranges by searching
// first range start.
match ranges.next() {
None => (),
Some(first) => {
if !self.is_in_jit_code(first.0) {
// The range is not registered -- add all ranges (including
// first one) to the jit_code_ranges.
let mut jit_code_ranges = self.inner.jit_code_ranges.borrow_mut();
jit_code_ranges.push(first);
jit_code_ranges.extend(ranges);
}
}
}
} }
pub(crate) unsafe fn add_instance(&self, handle: InstanceHandle) -> StoreInstanceHandle { pub(crate) unsafe fn add_instance(&self, handle: InstanceHandle) -> StoreInstanceHandle {
@@ -875,6 +952,10 @@ impl Store {
self.inner.signal_handler.borrow_mut() self.inner.signal_handler.borrow_mut()
} }
pub(crate) fn interrupts(&self) -> &Arc<VMInterrupts> {
&self.inner.interrupts
}
/// Returns whether the stores `a` and `b` refer to the same underlying /// Returns whether the stores `a` and `b` refer to the same underlying
/// `Store`. /// `Store`.
/// ///
@@ -946,10 +1027,10 @@ impl Store {
/// let interrupt_handle = store.interrupt_handle()?; /// let interrupt_handle = store.interrupt_handle()?;
/// ///
/// // Compile and instantiate a small example with an infinite loop. /// // Compile and instantiate a small example with an infinite loop.
/// let module = Module::new(&store, r#" /// let module = Module::new(&engine, r#"
/// (func (export "run") (loop br 0)) /// (func (export "run") (loop br 0))
/// "#)?; /// "#)?;
/// let instance = Instance::new(&module, &[])?; /// let instance = Instance::new(&store, &module, &[])?;
/// let run = instance /// let run = instance
/// .get_func("run") /// .get_func("run")
/// .ok_or(anyhow::format_err!("failed to find `run` function export"))? /// .ok_or(anyhow::format_err!("failed to find `run` function export"))?
@@ -967,9 +1048,9 @@ impl Store {
/// # } /// # }
/// ``` /// ```
pub fn interrupt_handle(&self) -> Result<InterruptHandle> { pub fn interrupt_handle(&self) -> Result<InterruptHandle> {
if self.engine().config.tunables.interruptable { if self.engine().config().tunables.interruptable {
Ok(InterruptHandle { Ok(InterruptHandle {
interrupts: self.compiler().interrupts().clone(), interrupts: self.interrupts().clone(),
}) })
} else { } else {
bail!("interrupts aren't enabled for this `Store`") bail!("interrupts aren't enabled for this `Store`")
@@ -1052,47 +1133,47 @@ mod tests {
let mut cfg = Config::new(); let mut cfg = Config::new();
cfg.cranelift_opt_level(OptLevel::None) cfg.cranelift_opt_level(OptLevel::None)
.cache_config_load(&config_path)?; .cache_config_load(&config_path)?;
let store = Store::new(&Engine::new(&cfg)); let engine = Engine::new(&cfg);
Module::new(&store, "(module (func))")?; Module::new(&engine, "(module (func))")?;
assert_eq!(store.engine().config.cache_config.cache_hits(), 0); assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(store.engine().config.cache_config.cache_misses(), 1); assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&store, "(module (func))")?; Module::new(&engine, "(module (func))")?;
assert_eq!(store.engine().config.cache_config.cache_hits(), 1); assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(store.engine().config.cache_config.cache_misses(), 1); assert_eq!(engine.config().cache_config.cache_misses(), 1);
let mut cfg = Config::new(); let mut cfg = Config::new();
cfg.cranelift_opt_level(OptLevel::Speed) cfg.cranelift_opt_level(OptLevel::Speed)
.cache_config_load(&config_path)?; .cache_config_load(&config_path)?;
let store = Store::new(&Engine::new(&cfg)); let engine = Engine::new(&cfg);
Module::new(&store, "(module (func))")?; Module::new(&engine, "(module (func))")?;
assert_eq!(store.engine().config.cache_config.cache_hits(), 0); assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(store.engine().config.cache_config.cache_misses(), 1); assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&store, "(module (func))")?; Module::new(&engine, "(module (func))")?;
assert_eq!(store.engine().config.cache_config.cache_hits(), 1); assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(store.engine().config.cache_config.cache_misses(), 1); assert_eq!(engine.config().cache_config.cache_misses(), 1);
let mut cfg = Config::new(); let mut cfg = Config::new();
cfg.cranelift_opt_level(OptLevel::SpeedAndSize) cfg.cranelift_opt_level(OptLevel::SpeedAndSize)
.cache_config_load(&config_path)?; .cache_config_load(&config_path)?;
let store = Store::new(&Engine::new(&cfg)); let engine = Engine::new(&cfg);
Module::new(&store, "(module (func))")?; Module::new(&engine, "(module (func))")?;
assert_eq!(store.engine().config.cache_config.cache_hits(), 0); assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(store.engine().config.cache_config.cache_misses(), 1); assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&store, "(module (func))")?; Module::new(&engine, "(module (func))")?;
assert_eq!(store.engine().config.cache_config.cache_hits(), 1); assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(store.engine().config.cache_config.cache_misses(), 1); assert_eq!(engine.config().cache_config.cache_misses(), 1);
// FIXME(#1523) need debuginfo on aarch64 before we run this test there // FIXME(#1523) need debuginfo on aarch64 before we run this test there
if !cfg!(target_arch = "aarch64") { if !cfg!(target_arch = "aarch64") {
let mut cfg = Config::new(); let mut cfg = Config::new();
cfg.debug_info(true).cache_config_load(&config_path)?; cfg.debug_info(true).cache_config_load(&config_path)?;
let store = Store::new(&Engine::new(&cfg)); let engine = Engine::new(&cfg);
Module::new(&store, "(module (func))")?; Module::new(&engine, "(module (func))")?;
assert_eq!(store.engine().config.cache_config.cache_hits(), 0); assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(store.engine().config.cache_config.cache_misses(), 1); assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&store, "(module (func))")?; Module::new(&engine, "(module (func))")?;
assert_eq!(store.engine().config.cache_config.cache_hits(), 1); assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(store.engine().config.cache_config.cache_misses(), 1); assert_eq!(engine.config().cache_config.cache_misses(), 1);
} }
Ok(()) Ok(())

View File

@@ -32,25 +32,20 @@ pub(crate) fn create_handle(
.local .local
.signatures .signatures
.values() .values()
.map(|(wasm, native)| { .map(|(wasm, native)| store.register_signature(wasm.clone(), native.clone()))
store
.compiler()
.signatures()
.register(wasm.clone(), native.clone())
})
.collect::<PrimaryMap<_, _>>(); .collect::<PrimaryMap<_, _>>();
unsafe { unsafe {
let handle = InstanceHandle::new( let handle = InstanceHandle::new(
Arc::new(module), Arc::new(module),
Arc::new(()),
finished_functions.into_boxed_slice(), finished_functions.into_boxed_slice(),
trampolines, trampolines,
imports, imports,
store.memory_creator(), store.memory_creator(),
signatures.into_boxed_slice(), signatures.into_boxed_slice(),
None,
state, state,
store.compiler().interrupts().clone(), store.interrupts().clone(),
)?; )?;
Ok(store.add_instance(handle)) Ok(store.add_instance(handle))
} }

View File

@@ -247,10 +247,7 @@ pub fn create_handle_with_function(
mem::size_of::<u128>(), mem::size_of::<u128>(),
)?; )?;
assert!(relocations.is_empty()); assert!(relocations.is_empty());
let sig_id = store let sig_id = store.register_signature(ft.to_wasm_func_type(), sig);
.compiler()
.signatures()
.register(ft.to_wasm_func_type(), sig);
trampolines.insert(sig_id, trampoline); trampolines.insert(sig_id, trampoline);
// Next up we wrap everything up into an `InstanceHandle` by publishing our // Next up we wrap everything up into an `InstanceHandle` by publishing our
@@ -300,10 +297,7 @@ pub unsafe fn create_handle_with_raw_function(
.exports .exports
.insert("trampoline".to_string(), EntityIndex::Function(func_id)); .insert("trampoline".to_string(), EntityIndex::Function(func_id));
finished_functions.push(func); finished_functions.push(func);
let sig_id = store let sig_id = store.register_signature(ft.to_wasm_func_type(), sig);
.compiler()
.signatures()
.register(ft.to_wasm_func_type(), sig);
trampolines.insert(sig_id, trampoline); trampolines.insert(sig_id, trampoline);
create_handle(module, store, finished_functions, trampolines, state) create_handle(module, store, finished_functions, trampolines, state)

View File

@@ -84,7 +84,7 @@ impl WastContext {
} }
fn instantiate(&mut self, module: &[u8]) -> Result<Outcome<Instance>> { fn instantiate(&mut self, module: &[u8]) -> Result<Outcome<Instance>> {
let module = Module::new(&self.store, module)?; let module = Module::new(self.store.engine(), module)?;
self.modules.push(module.clone()); self.modules.push(module.clone());
let instance = match self.linker.instantiate(&module) { let instance = match self.linker.instantiate(&module) {
Ok(i) => i, Ok(i) => i,

View File

@@ -55,21 +55,22 @@ use std::error::Error;
use wasmtime::*; use wasmtime::*;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let engine = Engine::default();
// A `Store` is a sort of "global object" in a sense, but for now it suffices // A `Store` is a sort of "global object" in a sense, but for now it suffices
// to say that it's generally passed to most constructors. // to say that it's generally passed to most constructors.
let store = Store::default(); let store = Store::new(&engine);
# if false { # if false {
// We start off by creating a `Module` which represents a compiled form // We start off by creating a `Module` which represents a compiled form
// of our input wasm module. In this case it'll be JIT-compiled after // of our input wasm module. In this case it'll be JIT-compiled after
// we parse the text format. // we parse the text format.
let module = Module::from_file(&store, "hello.wat")?; let module = Module::from_file(&engine, "hello.wat")?;
# } # }
# let module = Module::new(&store, r#"(module (func (export "answer") (result i32) i32.const 42))"#)?; # let module = Module::new(&engine, r#"(module (func (export "answer") (result i32) i32.const 42))"#)?;
// After we have a compiled `Module` we can then instantiate it, creating // After we have a compiled `Module` we can then instantiate it, creating
// an `Instance` which we can actually poke at functions on. // an `Instance` which we can actually poke at functions on.
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
// The `Instance` gives us access to various exported functions and items, // The `Instance` gives us access to various exported functions and items,
// which we access here to pull out our `answer` exported function and // which we access here to pull out our `answer` exported function and
@@ -142,11 +143,12 @@ use std::error::Error;
use wasmtime::*; use wasmtime::*;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let store = Store::default(); let engine = Engine::default();
let store = Store::new(&engine);
# if false { # if false {
let module = Module::from_file(&store, "hello.wat")?; let module = Module::from_file(&engine, "hello.wat")?;
# } # }
# let module = Module::new(&store, r#"(module (import "" "log" (func $log (param i32))) (import "" "double" (func $double (param i32) (result i32))) (func (export "run") i32.const 0 call $log i32.const 1 call $log i32.const 2 call $double call $log))"#)?; # let module = Module::new(&engine, r#"(module (import "" "log" (func $log (param i32))) (import "" "double" (func $double (param i32) (result i32))) (func (export "run") i32.const 0 call $log i32.const 1 call $log i32.const 2 call $double call $log))"#)?;
// First we can create our `log` function, which will simply print out the // First we can create our `log` function, which will simply print out the
// parameter it receives. // parameter it receives.
@@ -160,7 +162,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// When instantiating the module we now need to provide the imports to the // When instantiating the module we now need to provide the imports to the
// instantiation process. This is the second slice argument, where each // instantiation process. This is the second slice argument, where each
// entry in the slice must line up with the imports in the module. // entry in the slice must line up with the imports in the module.
let instance = Instance::new(&module, &[log.into(), double.into()])?; let instance = Instance::new(&store, &module, &[log.into(), double.into()])?;
let run = instance let run = instance
.get_func("run") .get_func("run")

View File

@@ -37,7 +37,8 @@ You can also see how this works in the Rust API like so:
use wasmtime::*; use wasmtime::*;
# fn main() -> anyhow::Result<()> { # fn main() -> anyhow::Result<()> {
let store = Store::default(); let engine = Engine::default();
let store = Store::new(&engine);
let wat = r#" let wat = r#"
(module (module
(func (export "add") (param i32 i32) (result i32) (func (export "add") (param i32 i32) (result i32)
@@ -45,8 +46,8 @@ let wat = r#"
local.get 1 local.get 1
i32.add)) i32.add))
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(&engine, wat)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let add = instance.get_func("add").unwrap(); let add = instance.get_func("add").unwrap();
let add = add.get2::<i32, i32, i32>()?; let add = add.get2::<i32, i32, i32>()?;
println!("1 + 2 = {}", add(1, 2)?); println!("1 + 2 = {}", add(1, 2)?);

View File

@@ -71,7 +71,7 @@ int main(int argc, const char* argv[]) {
printf("Instantiating module...\n"); printf("Instantiating module...\n");
wasm_instance_t* instance = NULL; wasm_instance_t* instance = NULL;
wasm_trap_t *trap = NULL; wasm_trap_t *trap = NULL;
error = wasmtime_instance_new(module, NULL, 0, &instance, &trap); error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap);
if (error != NULL || trap != NULL) if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate", error, trap); exit_with_error("failed to instantiate", error, trap);
wasm_module_delete(module); wasm_module_delete(module);

View File

@@ -17,8 +17,8 @@ fn main() -> Result<()> {
// debugged in GDB. // debugged in GDB.
let engine = Engine::new(Config::new().debug_info(true)); let engine = Engine::new(Config::new().debug_info(true));
let store = Store::new(&engine); let store = Store::new(&engine);
let module = Module::from_file(&store, "target/wasm32-unknown-unknown/debug/fib.wasm")?; let module = Module::from_file(&engine, "target/wasm32-unknown-unknown/debug/fib.wasm")?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
// Invoke `fib` export // Invoke `fib` export
let fib = instance let fib = instance

View File

@@ -65,7 +65,7 @@ int main() {
wasm_byte_vec_delete(&wasm); wasm_byte_vec_delete(&wasm);
wasm_trap_t *trap = NULL; wasm_trap_t *trap = NULL;
wasm_instance_t *instance = NULL; wasm_instance_t *instance = NULL;
error = wasmtime_instance_new(module, NULL, 0, &instance, &trap); error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap);
if (instance == NULL) if (instance == NULL)
exit_with_error("failed to instantiate", error, trap); exit_with_error("failed to instantiate", error, trap);

View File

@@ -11,8 +11,8 @@ fn main() -> Result<()> {
// `Module` which is attached to a `Store` cache. After we've got that we // `Module` which is attached to a `Store` cache. After we've got that we
// can instantiate it. // can instantiate it.
let store = Store::default(); let store = Store::default();
let module = Module::from_file(&store, "examples/gcd.wat")?; let module = Module::from_file(store.engine(), "examples/gcd.wat")?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
// Invoke `gcd` export // Invoke `gcd` export
let gcd = instance let gcd = instance

View File

@@ -87,7 +87,7 @@ int main() {
wasm_trap_t *trap = NULL; wasm_trap_t *trap = NULL;
wasm_instance_t *instance = NULL; wasm_instance_t *instance = NULL;
const wasm_extern_t *imports[] = { wasm_func_as_extern(hello) }; const wasm_extern_t *imports[] = { wasm_func_as_extern(hello) };
error = wasmtime_instance_new(module, imports, 1, &instance, &trap); error = wasmtime_instance_new(store, module, imports, 1, &instance, &trap);
if (instance == NULL) if (instance == NULL)
exit_with_error("failed to instantiate", error, trap); exit_with_error("failed to instantiate", error, trap);

View File

@@ -15,7 +15,7 @@ fn main() -> Result<()> {
// Compile the wasm binary into an in-memory instance of a `Module`. // Compile the wasm binary into an in-memory instance of a `Module`.
println!("Compiling module..."); println!("Compiling module...");
let module = Module::from_file(&store, "examples/hello.wat")?; let module = Module::from_file(store.engine(), "examples/hello.wat")?;
// Here we handle the imports of the module, which in this case is our // Here we handle the imports of the module, which in this case is our
// `HelloCallback` type and its associated implementation of `Callback. // `HelloCallback` type and its associated implementation of `Callback.
@@ -30,7 +30,7 @@ fn main() -> Result<()> {
// Note that this is where the wasm `start` function, if any, would run. // Note that this is where the wasm `start` function, if any, would run.
println!("Instantiating module..."); println!("Instantiating module...");
let imports = [hello_func.into()]; let imports = [hello_func.into()];
let instance = Instance::new(&module, &imports)?; let instance = Instance::new(&store, &module, &imports)?;
// Next we poke around a bit to extract the `run` function from the module. // Next we poke around a bit to extract the `run` function from the module.
println!("Extracting export..."); println!("Extracting export...");

View File

@@ -93,7 +93,7 @@ int main() {
wasm_byte_vec_delete(&wasm); wasm_byte_vec_delete(&wasm);
if (error != NULL) if (error != NULL)
exit_with_error("failed to compile module", error, NULL); exit_with_error("failed to compile module", error, NULL);
error = wasmtime_instance_new(module, NULL, 0, &instance, &trap); error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap);
if (instance == NULL) if (instance == NULL)
exit_with_error("failed to instantiate", error, trap); exit_with_error("failed to instantiate", error, trap);

View File

@@ -14,8 +14,8 @@ fn main() -> Result<()> {
let interrupt_handle = store.interrupt_handle()?; let interrupt_handle = store.interrupt_handle()?;
// Compile and instantiate a small example with an infinite loop. // Compile and instantiate a small example with an infinite loop.
let module = Module::from_file(&store, "examples/interrupt.wat")?; let module = Module::from_file(&engine, "examples/interrupt.wat")?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let run = instance let run = instance
.get_func("run") .get_func("run")
.ok_or(anyhow::format_err!("failed to find `run` function export"))? .ok_or(anyhow::format_err!("failed to find `run` function export"))?

View File

@@ -7,7 +7,8 @@ use wasmtime::*;
use wasmtime_wasi::{Wasi, WasiCtx}; use wasmtime_wasi::{Wasi, WasiCtx};
fn main() -> Result<()> { fn main() -> Result<()> {
let store = Store::default(); let engine = Engine::default();
let store = Store::new(&engine);
// First set up our linker which is going to be linking modules together. We // First set up our linker which is going to be linking modules together. We
// want our linker to have wasi available, so we set that up here as well. // want our linker to have wasi available, so we set that up here as well.
@@ -16,8 +17,8 @@ fn main() -> Result<()> {
wasi.add_to_linker(&mut linker)?; wasi.add_to_linker(&mut linker)?;
// Load and compile our two modules // Load and compile our two modules
let linking1 = Module::from_file(&store, "examples/linking1.wat")?; let linking1 = Module::from_file(&engine, "examples/linking1.wat")?;
let linking2 = Module::from_file(&store, "examples/linking2.wat")?; let linking2 = Module::from_file(&engine, "examples/linking2.wat")?;
// Instantiate our first module which only uses WASI, then register that // Instantiate our first module which only uses WASI, then register that
// instance with the linker since the next linking will use it. // instance with the linker since the next linking will use it.

View File

@@ -167,7 +167,7 @@ int main(int argc, const char* argv[]) {
printf("Instantiating module...\n"); printf("Instantiating module...\n");
wasm_instance_t* instance = NULL; wasm_instance_t* instance = NULL;
wasm_trap_t *trap = NULL; wasm_trap_t *trap = NULL;
error = wasmtime_instance_new(module, NULL, 0, &instance, &trap); error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap);
if (!instance) if (!instance)
exit_with_error("failed to instantiate", error, trap); exit_with_error("failed to instantiate", error, trap);

View File

@@ -13,8 +13,8 @@ fn main() -> Result<()> {
// Create our `Store` context and then compile a module and create an // Create our `Store` context and then compile a module and create an
// instance from the compiled module all in one go. // instance from the compiled module all in one go.
let wasmtime_store = Store::default(); let wasmtime_store = Store::default();
let module = Module::from_file(&wasmtime_store, "examples/memory.wat")?; let module = Module::from_file(wasmtime_store.engine(), "examples/memory.wat")?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&wasmtime_store, &module, &[])?;
// Load up our exports from the instance // Load up our exports from the instance
let memory = instance let memory = instance

View File

@@ -115,7 +115,7 @@ int main(int argc, const char* argv[]) {
const wasm_extern_t* imports[] = {wasm_func_as_extern(callback_func)}; const wasm_extern_t* imports[] = {wasm_func_as_extern(callback_func)};
wasm_instance_t* instance = NULL; wasm_instance_t* instance = NULL;
wasm_trap_t* trap = NULL; wasm_trap_t* trap = NULL;
error = wasmtime_instance_new(module, imports, 1, &instance, &trap); error = wasmtime_instance_new(store, module, imports, 1, &instance, &trap);
if (!instance) if (!instance)
exit_with_error("failed to instantiate", error, trap); exit_with_error("failed to instantiate", error, trap);

View File

@@ -12,11 +12,12 @@ use wasmtime::*;
fn main() -> Result<()> { fn main() -> Result<()> {
println!("Initializing..."); println!("Initializing...");
let store = Store::default(); let engine = Engine::default();
let store = Store::new(&engine);
// Compile. // Compile.
println!("Compiling module..."); println!("Compiling module...");
let module = Module::from_file(&store, "examples/multi.wat")?; let module = Module::from_file(&engine, "examples/multi.wat")?;
// Create external print functions. // Create external print functions.
println!("Creating callback..."); println!("Creating callback...");
@@ -35,7 +36,7 @@ fn main() -> Result<()> {
// Instantiate. // Instantiate.
println!("Instantiating module..."); println!("Instantiating module...");
let instance = Instance::new(&module, &[callback_func.into()])?; let instance = Instance::new(&store, &module, &[callback_func.into()])?;
// Extract exports. // Extract exports.
println!("Extracting export..."); println!("Extracting export...");

184
examples/threads.c Normal file
View File

@@ -0,0 +1,184 @@
#ifndef _WIN32
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <wasm.h>
#include <wasmtime.h>
#define own
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap);
const int N_THREADS = 10;
const int N_REPS = 3;
// A function to be called from Wasm code.
own wasm_trap_t* callback(const wasm_val_t args[], wasm_val_t results[]) {
assert(args[0].kind == WASM_I32);
printf("> Thread %d running\n", args[0].of.i32);
return NULL;
}
typedef struct {
wasm_engine_t* engine;
wasm_shared_module_t* module;
int id;
} thread_args;
void* run(void* args_abs) {
thread_args* args = (thread_args*)args_abs;
// Rereate store and module.
own wasm_store_t* store = wasm_store_new(args->engine);
own wasm_module_t* module = wasm_module_obtain(store, args->module);
// Run the example N times.
for (int i = 0; i < N_REPS; ++i) {
usleep(100000);
// Create imports.
own wasm_functype_t* func_type = wasm_functype_new_1_0(wasm_valtype_new_i32());
own wasm_func_t* func = wasm_func_new(store, func_type, callback);
wasm_functype_delete(func_type);
wasm_val_t val = {.kind = WASM_I32, .of = {.i32 = (int32_t)args->id}};
own wasm_globaltype_t* global_type =
wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST);
own wasm_global_t* global = wasm_global_new(store, global_type, &val);
wasm_globaltype_delete(global_type);
// Instantiate.
const wasm_extern_t* imports[] = {
wasm_func_as_extern(func), wasm_global_as_extern(global),
};
own wasm_instance_t* instance =
wasm_instance_new(store, module, imports, NULL);
if (!instance) {
printf("> Error instantiating module!\n");
return NULL;
}
wasm_func_delete(func);
wasm_global_delete(global);
// Extract export.
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
if (exports.size == 0) {
printf("> Error accessing exports!\n");
return NULL;
}
const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]);
if (run_func == NULL) {
printf("> Error accessing export!\n");
return NULL;
}
wasm_instance_delete(instance);
// Call.
if (wasm_func_call(run_func, NULL, NULL)) {
printf("> Error calling function!\n");
return NULL;
}
wasm_extern_vec_delete(&exports);
}
wasm_module_delete(module);
wasm_store_delete(store);
free(args_abs);
return NULL;
}
int main(int argc, const char *argv[]) {
// Initialize.
wasm_engine_t* engine = wasm_engine_new();
// Load our input file to parse it next
FILE* file = fopen("examples/threads.wat", "r");
if (!file) {
printf("> Error loading file!\n");
return 1;
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t wat;
wasm_byte_vec_new_uninitialized(&wat, file_size);
if (fread(wat.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
return 1;
}
fclose(file);
// Parse the wat into the binary wasm format
wasm_byte_vec_t binary;
wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &binary);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
// Compile and share.
own wasm_store_t* store = wasm_store_new(engine);
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
}
wasm_byte_vec_delete(&binary);
own wasm_shared_module_t* shared = wasm_module_share(module);
wasm_module_delete(module);
wasm_store_delete(store);
// Spawn threads.
pthread_t threads[N_THREADS];
for (int i = 0; i < N_THREADS; i++) {
thread_args* args = malloc(sizeof(thread_args));
args->id = i;
args->engine = engine;
args->module = shared;
printf("Initializing thread %d...\n", i);
pthread_create(&threads[i], NULL, &run, args);
}
for (int i = 0; i < N_THREADS; i++) {
printf("Waiting for thread: %d\n", i);
pthread_join(threads[i], NULL);
}
wasm_shared_module_delete(shared);
wasm_engine_delete(engine);
return 0;
}
static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) {
fprintf(stderr, "error: %s\n", message);
wasm_byte_vec_t error_message;
if (error != NULL) {
wasmtime_error_message(error, &error_message);
} else {
wasm_trap_message(trap, &error_message);
}
fprintf(stderr, "%.*s\n", (int) error_message.size, error_message.data);
wasm_byte_vec_delete(&error_message);
exit(1);
}
#else
// TODO implement example for Windows
int main(int argc, const char *argv[]) {
return 0;
}
#endif // _WIN32

70
examples/threads.rs Normal file
View File

@@ -0,0 +1,70 @@
// You can execute this example with `cargo run --example threads`
use anyhow::{format_err, Result};
use std::thread;
use std::time;
use wasmtime::*;
const N_THREADS: i32 = 10;
const N_REPS: i32 = 3;
fn print_message(_: Caller<'_>, args: &[Val], _: &mut [Val]) -> Result<(), Trap> {
println!("> Thread {} is running", args[0].unwrap_i32());
Ok(())
}
fn run(engine: &Engine, module: Module, id: i32) -> Result<()> {
let store = Store::new(&engine);
// Create external print functions.
println!("Creating callback...");
let callback_type = FuncType::new(Box::new([ValType::I32]), Box::new([]));
let callback_func = Func::new(&store, callback_type, print_message);
let id_type = GlobalType::new(ValType::I32, Mutability::Const);
let id_global = Global::new(&store, id_type, Val::I32(id))?;
// Instantiate.
println!("Instantiating module...");
let instance = Instance::new(&store, &module, &[callback_func.into(), id_global.into()])?;
// Extract exports.
println!("Extracting export...");
let g = instance
.get_func("run")
.ok_or(format_err!("failed to find export `eun`"))?;
for _ in 0..N_REPS {
thread::sleep(time::Duration::from_millis(100));
// Call `$run`.
drop(g.call(&[])?);
}
Ok(())
}
fn main() -> Result<()> {
println!("Initializing...");
let engine = Engine::default();
// Compile.
println!("Compiling module...");
let module = Module::from_file(&engine, "examples/threads.wat")?;
let mut children = Vec::new();
for id in 0..N_THREADS {
let engine = engine.clone();
let module = module.clone();
children.push(thread::spawn(move || {
run(&engine, module, id).expect("Success");
}));
}
for (i, child) in children.into_iter().enumerate() {
if let Err(_) = child.join() {
println!("Thread #{} errors", i);
}
}
Ok(())
}

5
examples/threads.wat Normal file
View File

@@ -0,0 +1,5 @@
(module
(func $message (import "" "hello") (param i32))
(global $id (import "" "id") i32)
(func (export "run") (call $message (global.get $id)))
)

View File

@@ -18,7 +18,7 @@ fn main() -> Result<()> {
wasi.add_to_linker(&mut linker)?; wasi.add_to_linker(&mut linker)?;
// Instantiate our module with the imports we've created, and run it. // Instantiate our module with the imports we've created, and run it.
let module = Module::from_file(&store, "target/wasm32-wasi/debug/wasi.wasm")?; let module = Module::from_file(store.engine(), "target/wasm32-wasi/debug/wasi.wasm")?;
linker.module("", &module)?; linker.module("", &module)?;
linker.get_default("")?.get0::<()>()?()?; linker.get_default("")?.get0::<()>()?()?;

View File

@@ -141,7 +141,7 @@ impl RunCommand {
// Load the preload wasm modules. // Load the preload wasm modules.
for (name, path) in self.preloads.iter() { for (name, path) in self.preloads.iter() {
// Read the wasm module binary either as `*.wat` or a raw binary // Read the wasm module binary either as `*.wat` or a raw binary
let module = Module::from_file(linker.store(), path)?; let module = Module::from_file(&engine, path)?;
// Add the module's functions to the linker. // Add the module's functions to the linker.
linker.module(name, &module).context(format!( linker.module(name, &module).context(format!(
@@ -247,7 +247,7 @@ impl RunCommand {
// Read the wasm module binary either as `*.wat` or a raw binary. // Read the wasm module binary either as `*.wat` or a raw binary.
// Use "" as a default module name. // Use "" as a default module name.
let module = Module::from_file(linker.store(), &self.module)?; let module = Module::from_file(linker.store().engine(), &self.module)?;
linker linker
.module("", &module) .module("", &module)
.context(format!("failed to instantiate {:?}", self.module))?; .context(format!("failed to instantiate {:?}", self.module))?;

View File

@@ -90,8 +90,8 @@ mod tests {
fn test_custom_signal_handler_single_instance() -> Result<()> { fn test_custom_signal_handler_single_instance() -> Result<()> {
let engine = Engine::new(&Config::default()); let engine = Engine::new(&Config::default());
let store = Store::new(&engine); let store = Store::new(&engine);
let module = Module::new(&store, WAT1)?; let module = Module::new(&engine, WAT1)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let (base, length) = set_up_memory(&instance); let (base, length) = set_up_memory(&instance);
unsafe { unsafe {
@@ -150,11 +150,11 @@ mod tests {
fn test_custom_signal_handler_multiple_instances() -> Result<()> { fn test_custom_signal_handler_multiple_instances() -> Result<()> {
let engine = Engine::new(&Config::default()); let engine = Engine::new(&Config::default());
let store = Store::new(&engine); let store = Store::new(&engine);
let module = Module::new(&store, WAT1)?; let module = Module::new(&engine, WAT1)?;
// Set up multiple instances // Set up multiple instances
let instance1 = Instance::new(&module, &[])?; let instance1 = Instance::new(&store, &module, &[])?;
let instance1_handler_triggered = Rc::new(AtomicBool::new(false)); let instance1_handler_triggered = Rc::new(AtomicBool::new(false));
unsafe { unsafe {
@@ -196,7 +196,7 @@ mod tests {
); );
} }
let instance2 = Instance::new(&module, &[]).expect("failed to instantiate module"); let instance2 = Instance::new(&store, &module, &[]).expect("failed to instantiate module");
let instance2_handler_triggered = Rc::new(AtomicBool::new(false)); let instance2_handler_triggered = Rc::new(AtomicBool::new(false));
unsafe { unsafe {
@@ -244,8 +244,8 @@ mod tests {
let store = Store::new(&engine); let store = Store::new(&engine);
// instance1 which defines 'read' // instance1 which defines 'read'
let module1 = Module::new(&store, WAT1)?; let module1 = Module::new(&engine, WAT1)?;
let instance1 = Instance::new(&module1, &[])?; let instance1 = Instance::new(&store, &module1, &[])?;
let (base1, length1) = set_up_memory(&instance1); let (base1, length1) = set_up_memory(&instance1);
unsafe { unsafe {
store.set_signal_handler(move |signum, siginfo, _| { store.set_signal_handler(move |signum, siginfo, _| {
@@ -258,8 +258,8 @@ mod tests {
let instance1_read = instance1_exports.next().unwrap(); let instance1_read = instance1_exports.next().unwrap();
// instance2 which calls 'instance1.read' // instance2 which calls 'instance1.read'
let module2 = Module::new(&store, WAT2)?; let module2 = Module::new(&engine, WAT2)?;
let instance2 = Instance::new(&module2, &[instance1_read.into_extern()])?; let instance2 = Instance::new(&store, &module2, &[instance1_read.into_extern()])?;
// since 'instance2.run' calls 'instance1.read' we need to set up the signal handler to handle // since 'instance2.run' calls 'instance1.read' we need to set up the signal handler to handle
// SIGSEGV originating from within the memory of instance1 // SIGSEGV originating from within the memory of instance1
unsafe { unsafe {

View File

@@ -57,8 +57,9 @@ fn bad_tables() {
fn cross_store() -> anyhow::Result<()> { fn cross_store() -> anyhow::Result<()> {
let mut cfg = Config::new(); let mut cfg = Config::new();
cfg.wasm_reference_types(true); cfg.wasm_reference_types(true);
let store1 = Store::new(&Engine::new(&cfg)); let engine = Engine::new(&cfg);
let store2 = Store::new(&Engine::new(&cfg)); let store1 = Store::new(&engine);
let store2 = Store::new(&engine);
// ============ Cross-store instantiation ============== // ============ Cross-store instantiation ==============
@@ -70,17 +71,17 @@ fn cross_store() -> anyhow::Result<()> {
let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
let table = Table::new(&store2, ty, Val::ExternRef(None))?; let table = Table::new(&store2, ty, Val::ExternRef(None))?;
let need_func = Module::new(&store1, r#"(module (import "" "" (func)))"#)?; let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?;
assert!(Instance::new(&need_func, &[func.into()]).is_err()); assert!(Instance::new(&store1, &need_func, &[func.into()]).is_err());
let need_global = Module::new(&store1, r#"(module (import "" "" (global i32)))"#)?; let need_global = Module::new(&engine, r#"(module (import "" "" (global i32)))"#)?;
assert!(Instance::new(&need_global, &[global.into()]).is_err()); assert!(Instance::new(&store1, &need_global, &[global.into()]).is_err());
let need_table = Module::new(&store1, r#"(module (import "" "" (table 1 funcref)))"#)?; let need_table = Module::new(&engine, r#"(module (import "" "" (table 1 funcref)))"#)?;
assert!(Instance::new(&need_table, &[table.into()]).is_err()); assert!(Instance::new(&store1, &need_table, &[table.into()]).is_err());
let need_memory = Module::new(&store1, r#"(module (import "" "" (memory 1)))"#)?; let need_memory = Module::new(&engine, r#"(module (import "" "" (memory 1)))"#)?;
assert!(Instance::new(&need_memory, &[memory.into()]).is_err()); assert!(Instance::new(&store1, &need_memory, &[memory.into()]).is_err());
// ============ Cross-store globals ============== // ============ Cross-store globals ==============
@@ -106,7 +107,7 @@ fn cross_store() -> anyhow::Result<()> {
// ============ Cross-store funcs ============== // ============ Cross-store funcs ==============
// TODO: need to actually fill this out once we support externref params/locals // TODO: need to actually fill this out once we support externref params/locals
// let module = Module::new(&store1, r#"(module (func (export "a") (param funcref)))"#)?; // let module = Module::new(&engine, r#"(module (func (export "a") (param funcref)))"#)?;
Ok(()) Ok(())
} }

View File

@@ -61,8 +61,8 @@ fn dtor_delayed() -> Result<()> {
assert_eq!(HITS.load(SeqCst), 0); assert_eq!(HITS.load(SeqCst), 0);
let wasm = wat::parse_str(r#"(import "" "" (func))"#)?; let wasm = wat::parse_str(r#"(import "" "" (func))"#)?;
let module = Module::new(&store, &wasm)?; let module = Module::new(store.engine(), &wasm)?;
let instance = Instance::new(&module, &[func.into()])?; let instance = Instance::new(&store, &module, &[func.into()])?;
assert_eq!(HITS.load(SeqCst), 0); assert_eq!(HITS.load(SeqCst), 0);
drop((instance, module, store)); drop((instance, module, store));
assert_eq!(HITS.load(SeqCst), 1); assert_eq!(HITS.load(SeqCst), 1);
@@ -142,8 +142,9 @@ fn import_works() -> Result<()> {
"#, "#,
)?; )?;
let store = Store::default(); let store = Store::default();
let module = Module::new(&store, &wasm)?; let module = Module::new(store.engine(), &wasm)?;
Instance::new( Instance::new(
&store,
&module, &module,
&[ &[
Func::wrap(&store, || { Func::wrap(&store, || {
@@ -195,8 +196,9 @@ fn trap_import() -> Result<()> {
"#, "#,
)?; )?;
let store = Store::default(); let store = Store::default();
let module = Module::new(&store, &wasm)?; let module = Module::new(store.engine(), &wasm)?;
let trap = Instance::new( let trap = Instance::new(
&store,
&module, &module,
&[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()], &[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()],
) )
@@ -261,7 +263,7 @@ fn get_from_signature() {
fn get_from_module() -> anyhow::Result<()> { fn get_from_module() -> anyhow::Result<()> {
let store = Store::default(); let store = Store::default();
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(module (module
(func (export "f0")) (func (export "f0"))
@@ -272,7 +274,7 @@ fn get_from_module() -> anyhow::Result<()> {
"#, "#,
)?; )?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let f0 = instance.get_func("f0").unwrap(); let f0 = instance.get_func("f0").unwrap();
assert!(f0.get0::<()>().is_ok()); assert!(f0.get0::<()>().is_ok());
assert!(f0.get0::<i32>().is_err()); assert!(f0.get0::<i32>().is_err());
@@ -340,7 +342,7 @@ fn caller_memory() -> anyhow::Result<()> {
assert!(c.get_export("x").is_none()); assert!(c.get_export("x").is_none());
}); });
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(module (module
(import "" "" (func $f)) (import "" "" (func $f))
@@ -349,13 +351,13 @@ fn caller_memory() -> anyhow::Result<()> {
"#, "#,
)?; )?;
Instance::new(&module, &[f.into()])?; Instance::new(&store, &module, &[f.into()])?;
let f = Func::wrap(&store, |c: Caller<'_>| { let f = Func::wrap(&store, |c: Caller<'_>| {
assert!(c.get_export("memory").is_some()); assert!(c.get_export("memory").is_some());
}); });
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(module (module
(import "" "" (func $f)) (import "" "" (func $f))
@@ -365,7 +367,7 @@ fn caller_memory() -> anyhow::Result<()> {
"#, "#,
)?; )?;
Instance::new(&module, &[f.into()])?; Instance::new(&store, &module, &[f.into()])?;
let f = Func::wrap(&store, |c: Caller<'_>| { let f = Func::wrap(&store, |c: Caller<'_>| {
assert!(c.get_export("m").is_some()); assert!(c.get_export("m").is_some());
@@ -374,7 +376,7 @@ fn caller_memory() -> anyhow::Result<()> {
assert!(c.get_export("t").is_none()); assert!(c.get_export("t").is_none());
}); });
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(module (module
(import "" "" (func $f)) (import "" "" (func $f))
@@ -387,7 +389,7 @@ fn caller_memory() -> anyhow::Result<()> {
"#, "#,
)?; )?;
Instance::new(&module, &[f.into()])?; Instance::new(&store, &module, &[f.into()])?;
Ok(()) Ok(())
} }

View File

@@ -63,19 +63,19 @@ fn mutability() -> anyhow::Result<()> {
fn use_after_drop() -> anyhow::Result<()> { fn use_after_drop() -> anyhow::Result<()> {
let store = Store::default(); let store = Store::default();
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(module (module
(global (export "foo") (mut i32) (i32.const 100))) (global (export "foo") (mut i32) (i32.const 100)))
"#, "#,
)?; )?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let g = instance.get_global("foo").unwrap(); let g = instance.get_global("foo").unwrap();
assert_eq!(g.get().i32(), Some(100)); assert_eq!(g.get().i32(), Some(100));
g.set(101.into())?; g.set(101.into())?;
drop(instance); drop(instance);
assert_eq!(g.get().i32(), Some(101)); assert_eq!(g.get().i32(), Some(101));
Instance::new(&module, &[])?; Instance::new(&store, &module, &[])?;
assert_eq!(g.get().i32(), Some(101)); assert_eq!(g.get().i32(), Some(101));
drop(module); drop(module);
assert_eq!(g.get().i32(), Some(101)); assert_eq!(g.get().i32(), Some(101));

View File

@@ -19,14 +19,14 @@ fn hugely_recursive_module(store: &Store) -> anyhow::Result<Module> {
} }
wat.push_str("(func call 0)\n"); wat.push_str("(func call 0)\n");
Module::new(&store, &wat) Module::new(store.engine(), &wat)
} }
#[test] #[test]
fn loops_interruptable() -> anyhow::Result<()> { fn loops_interruptable() -> anyhow::Result<()> {
let store = interruptable_store(); let store = interruptable_store();
let module = Module::new(&store, r#"(func (export "loop") (loop br 0))"#)?; let module = Module::new(store.engine(), r#"(func (export "loop") (loop br 0))"#)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let iloop = instance.get_func("loop").unwrap().get0::<()>()?; let iloop = instance.get_func("loop").unwrap().get0::<()>()?;
store.interrupt_handle()?.interrupt(); store.interrupt_handle()?.interrupt();
let trap = iloop().unwrap_err(); let trap = iloop().unwrap_err();
@@ -39,7 +39,7 @@ fn functions_interruptable() -> anyhow::Result<()> {
let store = interruptable_store(); let store = interruptable_store();
let module = hugely_recursive_module(&store)?; let module = hugely_recursive_module(&store)?;
let func = Func::wrap(&store, || {}); let func = Func::wrap(&store, || {});
let instance = Instance::new(&module, &[func.into()])?; let instance = Instance::new(&store, &module, &[func.into()])?;
let iloop = instance.get_func("loop").unwrap().get0::<()>()?; let iloop = instance.get_func("loop").unwrap().get0::<()>()?;
store.interrupt_handle()?.interrupt(); store.interrupt_handle()?.interrupt();
let trap = iloop().unwrap_err(); let trap = iloop().unwrap_err();
@@ -59,7 +59,7 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> {
static HITS: AtomicUsize = AtomicUsize::new(0); static HITS: AtomicUsize = AtomicUsize::new(0);
let store = interruptable_store(); let store = interruptable_store();
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(import "" "" (func)) (import "" "" (func))
@@ -73,7 +73,7 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> {
let func = Func::wrap(&store, || { let func = Func::wrap(&store, || {
HITS.fetch_add(1, SeqCst); HITS.fetch_add(1, SeqCst);
}); });
let instance = Instance::new(&module, &[func.into()])?; let instance = Instance::new(&store, &module, &[func.into()])?;
// Use the instance's interrupt handle to wait for it to enter the loop long // Use the instance's interrupt handle to wait for it to enter the loop long
// enough and then we signal an interrupt happens. // enough and then we signal an interrupt happens.
@@ -109,7 +109,7 @@ fn function_interrupt_from_afar() -> anyhow::Result<()> {
let func = Func::wrap(&store, || { let func = Func::wrap(&store, || {
HITS.fetch_add(1, SeqCst); HITS.fetch_add(1, SeqCst);
}); });
let instance = Instance::new(&module, &[func.into()])?; let instance = Instance::new(&store, &module, &[func.into()])?;
// Use the instance's interrupt handle to wait for it to enter the loop long // Use the instance's interrupt handle to wait for it to enter the loop long
// enough and then we signal an interrupt happens. // enough and then we signal an interrupt happens.

View File

@@ -17,7 +17,7 @@ fn test_import_calling_export() {
"#; "#;
let store = Store::default(); let store = Store::default();
let module = Module::new(&store, WAT).expect("failed to create module"); let module = Module::new(store.engine(), WAT).expect("failed to create module");
let other = Rc::new(RefCell::new(None::<Func>)); let other = Rc::new(RefCell::new(None::<Func>));
let other2 = Rc::downgrade(&other); let other2 = Rc::downgrade(&other);
@@ -40,7 +40,7 @@ fn test_import_calling_export() {
let imports = vec![callback_func.into()]; let imports = vec![callback_func.into()];
let instance = let instance =
Instance::new(&module, imports.as_slice()).expect("failed to instantiate module"); Instance::new(&store, &module, imports.as_slice()).expect("failed to instantiate module");
let run_func = instance let run_func = instance
.get_func("run") .get_func("run")
@@ -67,7 +67,7 @@ fn test_returns_incorrect_type() -> Result<()> {
"#; "#;
let store = Store::default(); let store = Store::default();
let module = Module::new(&store, WAT)?; let module = Module::new(store.engine(), WAT)?;
let callback_func = Func::new( let callback_func = Func::new(
&store, &store,
@@ -80,7 +80,7 @@ fn test_returns_incorrect_type() -> Result<()> {
); );
let imports = vec![callback_func.into()]; let imports = vec![callback_func.into()];
let instance = Instance::new(&module, imports.as_slice())?; let instance = Instance::new(&store, &module, imports.as_slice())?;
let run_func = instance let run_func = instance
.get_func("run") .get_func("run")

View File

@@ -15,7 +15,7 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> {
"#; "#;
let store = Store::default(); let store = Store::default();
let module = Module::new(&store, WAT)?; let module = Module::new(store.engine(), WAT)?;
let imports = [ let imports = [
Func::new( Func::new(
@@ -41,7 +41,7 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> {
) )
.into(), .into(),
]; ];
let instance = Instance::new(&module, &imports)?; let instance = Instance::new(&store, &module, &imports)?;
let func = instance.get_func("foo").unwrap(); let func = instance.get_func("foo").unwrap();
let results = func.call(&[])?; let results = func.call(&[])?;

View File

@@ -4,10 +4,10 @@ use wasmtime::*;
#[test] #[test]
fn wrong_import_numbers() -> Result<()> { fn wrong_import_numbers() -> Result<()> {
let store = Store::default(); let store = Store::default();
let module = Module::new(&store, r#"(module (import "" "" (func)))"#)?; let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?;
assert!(Instance::new(&module, &[]).is_err()); assert!(Instance::new(&store, &module, &[]).is_err());
let func = Func::wrap(&store, || {}); let func = Func::wrap(&store, || {});
assert!(Instance::new(&module, &[func.clone().into(), func.into()]).is_err()); assert!(Instance::new(&store, &module, &[func.clone().into(), func.into()]).is_err());
Ok(()) Ok(())
} }

View File

@@ -13,8 +13,8 @@ fn test_invoke_func_via_table() -> Result<()> {
(elem (i32.const 0) $f) (elem (i32.const 0) $f)
) )
"#; "#;
let module = Module::new(&store, wat).context("> Error compiling module!")?; let module = Module::new(store.engine(), wat).context("> Error compiling module!")?;
let instance = Instance::new(&module, &[]).context("> Error instantiating module!")?; let instance = Instance::new(&store, &module, &[]).context("> Error instantiating module!")?;
let f = instance let f = instance
.get_table("table") .get_table("table")

View File

@@ -5,13 +5,16 @@ use wasmtime::*;
fn link_undefined() -> Result<()> { fn link_undefined() -> Result<()> {
let store = Store::default(); let store = Store::default();
let linker = Linker::new(&store); let linker = Linker::new(&store);
let module = Module::new(&store, r#"(module (import "" "" (func)))"#)?; let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?;
assert!(linker.instantiate(&module).is_err()); assert!(linker.instantiate(&module).is_err());
let module = Module::new(&store, r#"(module (import "" "" (global i32)))"#)?; let module = Module::new(store.engine(), r#"(module (import "" "" (global i32)))"#)?;
assert!(linker.instantiate(&module).is_err()); assert!(linker.instantiate(&module).is_err());
let module = Module::new(&store, r#"(module (import "" "" (memory 1)))"#)?; let module = Module::new(store.engine(), r#"(module (import "" "" (memory 1)))"#)?;
assert!(linker.instantiate(&module).is_err()); assert!(linker.instantiate(&module).is_err());
let module = Module::new(&store, r#"(module (import "" "" (table 1 funcref)))"#)?; let module = Module::new(
store.engine(),
r#"(module (import "" "" (table 1 funcref)))"#,
)?;
assert!(linker.instantiate(&module).is_err()); assert!(linker.instantiate(&module).is_err());
Ok(()) Ok(())
} }
@@ -71,7 +74,7 @@ fn function_interposition() -> Result<()> {
let mut linker = Linker::new(&store); let mut linker = Linker::new(&store);
linker.allow_shadowing(true); linker.allow_shadowing(true);
let mut module = Module::new( let mut module = Module::new(
&store, store.engine(),
r#"(module (func (export "green") (result i32) (i32.const 7)))"#, r#"(module (func (export "green") (result i32) (i32.const 7)))"#,
)?; )?;
for _ in 0..4 { for _ in 0..4 {
@@ -82,7 +85,7 @@ fn function_interposition() -> Result<()> {
instance.get_export("green").unwrap().clone(), instance.get_export("green").unwrap().clone(),
)?; )?;
module = Module::new( module = Module::new(
&store, store.engine(),
r#"(module r#"(module
(import "red" "green" (func (result i32))) (import "red" "green" (func (result i32)))
(func (export "green") (result i32) (i32.mul (call 0) (i32.const 2))) (func (export "green") (result i32) (i32.mul (call 0) (i32.const 2)))
@@ -104,7 +107,7 @@ fn function_interposition_renamed() -> Result<()> {
let mut linker = Linker::new(&store); let mut linker = Linker::new(&store);
linker.allow_shadowing(true); linker.allow_shadowing(true);
let mut module = Module::new( let mut module = Module::new(
&store, store.engine(),
r#"(module (func (export "export") (result i32) (i32.const 7)))"#, r#"(module (func (export "export") (result i32) (i32.const 7)))"#,
)?; )?;
for _ in 0..4 { for _ in 0..4 {
@@ -115,7 +118,7 @@ fn function_interposition_renamed() -> Result<()> {
instance.get_export("export").unwrap().clone(), instance.get_export("export").unwrap().clone(),
)?; )?;
module = Module::new( module = Module::new(
&store, store.engine(),
r#"(module r#"(module
(import "red" "green" (func (result i32))) (import "red" "green" (func (result i32)))
(func (export "export") (result i32) (i32.mul (call 0) (i32.const 2))) (func (export "export") (result i32) (i32.mul (call 0) (i32.const 2)))
@@ -137,14 +140,14 @@ fn module_interposition() -> Result<()> {
let mut linker = Linker::new(&store); let mut linker = Linker::new(&store);
linker.allow_shadowing(true); linker.allow_shadowing(true);
let mut module = Module::new( let mut module = Module::new(
&store, store.engine(),
r#"(module (func (export "export") (result i32) (i32.const 7)))"#, r#"(module (func (export "export") (result i32) (i32.const 7)))"#,
)?; )?;
for _ in 0..4 { for _ in 0..4 {
let instance = linker.instantiate(&module)?; let instance = linker.instantiate(&module)?;
linker.instance("instance", &instance)?; linker.instance("instance", &instance)?;
module = Module::new( module = Module::new(
&store, store.engine(),
r#"(module r#"(module
(import "instance" "export" (func (result i32))) (import "instance" "export" (func (result i32)))
(func (export "export") (result i32) (i32.mul (call 0) (i32.const 2))) (func (export "export") (result i32) (i32.mul (call 0) (i32.const 2)))

View File

@@ -143,14 +143,14 @@ mod not_for_windows {
fn host_memory() -> anyhow::Result<()> { fn host_memory() -> anyhow::Result<()> {
let (store, mem_creator) = config(); let (store, mem_creator) = config();
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(module (module
(memory (export "memory") 1) (memory (export "memory") 1)
) )
"#, "#,
)?; )?;
Instance::new(&module, &[])?; Instance::new(&store, &module, &[])?;
assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 1); assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 1);
@@ -161,7 +161,7 @@ mod not_for_windows {
fn host_memory_grow() -> anyhow::Result<()> { fn host_memory_grow() -> anyhow::Result<()> {
let (store, mem_creator) = config(); let (store, mem_creator) = config();
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(module (module
(func $f (drop (memory.grow (i32.const 1)))) (func $f (drop (memory.grow (i32.const 1))))
@@ -171,8 +171,8 @@ mod not_for_windows {
"#, "#,
)?; )?;
let instance1 = Instance::new(&module, &[])?; let instance1 = Instance::new(&store, &module, &[])?;
let instance2 = Instance::new(&module, &[])?; let instance2 = Instance::new(&store, &module, &[])?;
assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 2); assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 2);

View File

@@ -2,14 +2,14 @@ use wasmtime::*;
#[test] #[test]
fn test_module_no_name() -> anyhow::Result<()> { fn test_module_no_name() -> anyhow::Result<()> {
let store = Store::default(); let engine = Engine::default();
let wat = r#" let wat = r#"
(module (module
(func (export "run") (nop)) (func (export "run") (nop))
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(&engine, wat)?;
assert_eq!(module.name(), None); assert_eq!(module.name(), None);
Ok(()) Ok(())
@@ -17,17 +17,17 @@ fn test_module_no_name() -> anyhow::Result<()> {
#[test] #[test]
fn test_module_name() -> anyhow::Result<()> { fn test_module_name() -> anyhow::Result<()> {
let store = Store::default(); let engine = Engine::default();
let wat = r#" let wat = r#"
(module $from_name_section (module $from_name_section
(func (export "run") (nop)) (func (export "run") (nop))
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(&engine, wat)?;
assert_eq!(module.name(), Some("from_name_section")); assert_eq!(module.name(), Some("from_name_section"));
let module = Module::new_with_name(&store, wat, "override")?; let module = Module::new_with_name(&engine, wat, "override")?;
assert_eq!(module.name(), Some("override")); assert_eq!(module.name(), Some("override"));
Ok(()) Ok(())

View File

@@ -12,7 +12,7 @@ fn host_always_has_some_stack() -> anyhow::Result<()> {
// Create a module that's infinitely recursive, but calls the host on each // Create a module that's infinitely recursive, but calls the host on each
// level of wasm stack to always test how much host stack we have left. // level of wasm stack to always test how much host stack we have left.
let module = Module::new( let module = Module::new(
&store, store.engine(),
r#" r#"
(module (module
(import "" "" (func $host)) (import "" "" (func $host))
@@ -23,7 +23,7 @@ fn host_always_has_some_stack() -> anyhow::Result<()> {
"#, "#,
)?; )?;
let func = Func::wrap(&store, test_host_stack); let func = Func::wrap(&store, test_host_stack);
let instance = Instance::new(&module, &[func.into()])?; let instance = Instance::new(&store, &module, &[func.into()])?;
let foo = instance.get_func("foo").unwrap().get0::<()>()?; let foo = instance.get_func("foo").unwrap().get0::<()>()?;
// Make sure that our function traps and the trap says that the call stack // Make sure that our function traps and the trap says that the call stack

View File

@@ -12,11 +12,11 @@ fn test_trap_return() -> Result<()> {
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(store.engine(), wat)?;
let hello_type = FuncType::new(Box::new([]), Box::new([])); let hello_type = FuncType::new(Box::new([]), Box::new([]));
let hello_func = Func::new(&store, hello_type, |_, _, _| Err(Trap::new("test 123"))); let hello_func = Func::new(&store, hello_type, |_, _, _| Err(Trap::new("test 123")));
let instance = Instance::new(&module, &[hello_func.into()])?; let instance = Instance::new(&store, &module, &[hello_func.into()])?;
let run_func = instance.get_func("run").expect("expected function export"); let run_func = instance.get_func("run").expect("expected function export");
let e = run_func let e = run_func
@@ -41,8 +41,8 @@ fn test_trap_trace() -> Result<()> {
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let run_func = instance.get_func("run").expect("expected function export"); let run_func = instance.get_func("run").expect("expected function export");
let e = run_func let e = run_func
@@ -87,8 +87,8 @@ fn test_trap_trace_cb() -> Result<()> {
let fn_type = FuncType::new(Box::new([]), Box::new([])); let fn_type = FuncType::new(Box::new([]), Box::new([]));
let fn_func = Func::new(&store, fn_type, |_, _, _| Err(Trap::new("cb throw"))); let fn_func = Func::new(&store, fn_type, |_, _, _| Err(Trap::new("cb throw")));
let module = Module::new(&store, wat)?; let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&module, &[fn_func.into()])?; let instance = Instance::new(&store, &module, &[fn_func.into()])?;
let run_func = instance.get_func("run").expect("expected function export"); let run_func = instance.get_func("run").expect("expected function export");
let e = run_func let e = run_func
@@ -118,8 +118,8 @@ fn test_trap_stack_overflow() -> Result<()> {
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let run_func = instance.get_func("run").expect("expected function export"); let run_func = instance.get_func("run").expect("expected function export");
let e = run_func let e = run_func
@@ -153,8 +153,8 @@ fn trap_display_pretty() -> Result<()> {
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let run_func = instance.get_func("bar").expect("expected function export"); let run_func = instance.get_func("bar").expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function"); let e = run_func.call(&[]).err().expect("error calling function");
@@ -185,8 +185,8 @@ fn trap_display_multi_module() -> Result<()> {
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let bar = instance.get_export("bar").unwrap(); let bar = instance.get_export("bar").unwrap();
let wat = r#" let wat = r#"
@@ -196,8 +196,8 @@ fn trap_display_multi_module() -> Result<()> {
(func (export "bar2") call $middle) (func (export "bar2") call $middle)
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(store.engine(), wat)?;
let instance = Instance::new(&module, &[bar])?; let instance = Instance::new(&store, &module, &[bar])?;
let bar2 = instance.get_func("bar2").expect("expected function export"); let bar2 = instance.get_func("bar2").expect("expected function export");
let e = bar2.call(&[]).err().expect("error calling function"); let e = bar2.call(&[]).err().expect("error calling function");
@@ -230,10 +230,12 @@ fn trap_start_function_import() -> Result<()> {
"#, "#,
)?; )?;
let module = Module::new(&store, &binary)?; let module = Module::new(store.engine(), &binary)?;
let sig = FuncType::new(Box::new([]), Box::new([])); let sig = FuncType::new(Box::new([]), Box::new([]));
let func = Func::new(&store, sig, |_, _, _| Err(Trap::new("user trap"))); let func = Func::new(&store, sig, |_, _, _| Err(Trap::new("user trap")));
let err = Instance::new(&module, &[func.into()]).err().unwrap(); let err = Instance::new(&store, &module, &[func.into()])
.err()
.unwrap();
assert!(err assert!(err
.downcast_ref::<Trap>() .downcast_ref::<Trap>()
.unwrap() .unwrap()
@@ -257,10 +259,11 @@ fn rust_panic_import() -> Result<()> {
"#, "#,
)?; )?;
let module = Module::new(&store, &binary)?; let module = Module::new(store.engine(), &binary)?;
let sig = FuncType::new(Box::new([]), Box::new([])); let sig = FuncType::new(Box::new([]), Box::new([]));
let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic")); let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic"));
let instance = Instance::new( let instance = Instance::new(
&store,
&module, &module,
&[ &[
func.into(), func.into(),
@@ -299,18 +302,18 @@ fn rust_panic_start_function() -> Result<()> {
"#, "#,
)?; )?;
let module = Module::new(&store, &binary)?; let module = Module::new(store.engine(), &binary)?;
let sig = FuncType::new(Box::new([]), Box::new([])); let sig = FuncType::new(Box::new([]), Box::new([]));
let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic")); let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic"));
let err = panic::catch_unwind(AssertUnwindSafe(|| { let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(Instance::new(&module, &[func.into()])); drop(Instance::new(&store, &module, &[func.into()]));
})) }))
.unwrap_err(); .unwrap_err();
assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic")); assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
let func = Func::wrap(&store, || panic!("this is another panic")); let func = Func::wrap(&store, || panic!("this is another panic"));
let err = panic::catch_unwind(AssertUnwindSafe(|| { let err = panic::catch_unwind(AssertUnwindSafe(|| {
drop(Instance::new(&module, &[func.into()])); drop(Instance::new(&store, &module, &[func.into()]));
})) }))
.unwrap_err(); .unwrap_err();
assert_eq!( assert_eq!(
@@ -332,8 +335,8 @@ fn mismatched_arguments() -> Result<()> {
"#, "#,
)?; )?;
let module = Module::new(&store, &binary)?; let module = Module::new(store.engine(), &binary)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let func = instance.get_func("foo").unwrap(); let func = instance.get_func("foo").unwrap();
assert_eq!( assert_eq!(
func.call(&[]).unwrap_err().to_string(), func.call(&[]).unwrap_err().to_string(),
@@ -371,8 +374,8 @@ fn call_signature_mismatch() -> Result<()> {
"#, "#,
)?; )?;
let module = Module::new(&store, &binary)?; let module = Module::new(store.engine(), &binary)?;
let err = Instance::new(&module, &[]) let err = Instance::new(&store, &module, &[])
.err() .err()
.unwrap() .unwrap()
.downcast::<Trap>() .downcast::<Trap>()
@@ -397,8 +400,8 @@ fn start_trap_pretty() -> Result<()> {
) )
"#; "#;
let module = Module::new(&store, wat)?; let module = Module::new(store.engine(), wat)?;
let e = match Instance::new(&module, &[]) { let e = match Instance::new(&store, &module, &[]) {
Ok(_) => panic!("expected failure"), Ok(_) => panic!("expected failure"),
Err(e) => e.downcast::<Trap>()?, Err(e) => e.downcast::<Trap>()?,
}; };
@@ -420,8 +423,8 @@ wasm backtrace:
#[test] #[test]
fn present_after_module_drop() -> Result<()> { fn present_after_module_drop() -> Result<()> {
let store = Store::default(); let store = Store::default();
let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?; let module = Module::new(store.engine(), r#"(func (export "foo") unreachable)"#)?;
let instance = Instance::new(&module, &[])?; let instance = Instance::new(&store, &module, &[])?;
let func = instance.get_func("foo").unwrap(); let func = instance.get_func("foo").unwrap();
println!("asserting before we drop modules"); println!("asserting before we drop modules");

View File

@@ -48,22 +48,25 @@ fn main() {
let tests: &[(&str, fn())] = &[ let tests: &[(&str, fn())] = &[
("normal segfault", || segfault()), ("normal segfault", || segfault()),
("make instance then segfault", || { ("make instance then segfault", || {
let store = Store::default(); let engine = Engine::default();
let module = Module::new(&store, "(module)").unwrap(); let store = Store::new(&engine);
let _instance = Instance::new(&module, &[]).unwrap(); let module = Module::new(&engine, "(module)").unwrap();
let _instance = Instance::new(&store, &module, &[]).unwrap();
segfault(); segfault();
}), }),
("make instance then overrun the stack", || { ("make instance then overrun the stack", || {
let store = Store::default(); let engine = Engine::default();
let module = Module::new(&store, "(module)").unwrap(); let store = Store::new(&engine);
let _instance = Instance::new(&module, &[]).unwrap(); let module = Module::new(&engine, "(module)").unwrap();
let _instance = Instance::new(&store, &module, &[]).unwrap();
println!("stack overrun: {}", overrun_the_stack()); println!("stack overrun: {}", overrun_the_stack());
}), }),
("segfault in a host function", || { ("segfault in a host function", || {
let store = Store::default(); let engine = Engine::default();
let module = Module::new(&store, r#"(import "" "" (func)) (start 0)"#).unwrap(); let store = Store::new(&engine);
let module = Module::new(&engine, r#"(import "" "" (func)) (start 0)"#).unwrap();
let segfault = Func::wrap(&store, || segfault()); let segfault = Func::wrap(&store, || segfault());
Instance::new(&module, &[segfault.into()]).unwrap(); Instance::new(&store, &module, &[segfault.into()]).unwrap();
}), }),
]; ];
match env::var(VAR_NAME) { match env::var(VAR_NAME) {