Remove the need for HostRef<Store> (#771)

* Remove the need for `HostRef<Store>`

This commit goes through the public API of the `wasmtime` crate and
removes the need for `HostRef<Store>`, as discussed in #708. This commit
is accompanied with a few changes:

* The `Store` type now also implements `Default`, creating a new
  `Engine` with default settings and returning that.

* The `Store` type now implements `Clone`, and is documented as being a
  "cheap clone" aka being reference counted. As before there is no
  supported way to create a deep clone of a `Store`.

* All APIs take/return `&Store` or `Store` instead of `HostRef<Store>`,
  and `HostRef<T>` is left as purely a detail of the C API.

* The `global_exports` function is tagged as `#[doc(hidden)]` for now
  while we await its removal.

* The `Store` type is not yet `Send` nor `Sync` due to the usage of
  `global_exports`, but it is intended to become so eventually.

* Touch up comments on some examples

* Run rustfmt
This commit is contained in:
Alex Crichton
2020-01-07 16:29:44 -06:00
committed by GitHub
parent 296ebc46fd
commit 045d6a7310
31 changed files with 163 additions and 155 deletions

View File

@@ -43,8 +43,7 @@ use wasmtime_runtime::Export;
/// "#)?;
///
/// // Initialise environment and our module.
/// let engine = wasmtime::Engine::default();
/// let store = HostRef::new(wasmtime::Store::new(&engine));
/// let store = wasmtime::Store::default();
/// let module = HostRef::new(wasmtime::Module::new(&store, &binary)?);
///
/// // Define the type of the function we're going to call.
@@ -103,13 +102,13 @@ pub(crate) trait WrappedCallable {
}
pub(crate) struct WasmtimeFn {
store: HostRef<Store>,
store: Store,
instance: InstanceHandle,
export: Export,
}
impl WasmtimeFn {
pub fn new(store: &HostRef<Store>, instance: InstanceHandle, export: Export) -> WasmtimeFn {
pub fn new(store: &Store, instance: InstanceHandle, export: Export) -> WasmtimeFn {
WasmtimeFn {
store: store.clone(),
instance,
@@ -146,7 +145,6 @@ impl WrappedCallable for WasmtimeFn {
// Get the trampoline to call for this function.
let exec_code_buf = self
.store
.borrow_mut()
.context()
.compiler()
.get_published_trampoline(body, &signature, value_size)
@@ -191,11 +189,7 @@ pub struct NativeCallable {
}
impl NativeCallable {
pub(crate) fn new(
callable: Rc<dyn Callable + 'static>,
ft: &FuncType,
store: &HostRef<Store>,
) -> Self {
pub(crate) fn new(callable: Rc<dyn Callable + 'static>, ft: &FuncType, store: &Store) -> Self {
let (instance, export) =
generate_func_export(ft, &callable, store).expect("generated func");
NativeCallable {

View File

@@ -30,7 +30,7 @@ impl Context {
self.debug_info
}
pub(crate) fn compiler(&mut self) -> RefMut<Compiler> {
pub(crate) fn compiler(&self) -> RefMut<Compiler> {
self.compiler.borrow_mut()
}
}

View File

@@ -66,7 +66,7 @@ impl Extern {
}
pub(crate) fn from_wasmtime_export(
store: &HostRef<Store>,
store: &Store,
instance_handle: InstanceHandle,
export: wasmtime_runtime::Export,
) -> Extern {
@@ -112,19 +112,19 @@ impl From<HostRef<Table>> for Extern {
}
pub struct Func {
_store: HostRef<Store>,
_store: Store,
callable: Rc<dyn WrappedCallable + 'static>,
r#type: FuncType,
}
impl Func {
pub fn new(store: &HostRef<Store>, ty: FuncType, callable: Rc<dyn Callable + 'static>) -> Self {
pub fn new(store: &Store, ty: FuncType, callable: Rc<dyn Callable + 'static>) -> Self {
let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
Func::from_wrapped(store, ty, callable)
}
fn from_wrapped(
store: &HostRef<Store>,
store: &Store,
r#type: FuncType,
callable: Rc<dyn WrappedCallable + 'static>,
) -> Func {
@@ -159,7 +159,7 @@ impl Func {
pub(crate) fn from_wasmtime_function(
export: wasmtime_runtime::Export,
store: &HostRef<Store>,
store: &Store,
instance_handle: InstanceHandle,
) -> Self {
let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
@@ -179,7 +179,7 @@ impl fmt::Debug for Func {
}
pub struct Global {
_store: HostRef<Store>,
_store: Store,
r#type: GlobalType,
wasmtime_export: wasmtime_runtime::Export,
#[allow(dead_code)]
@@ -187,7 +187,7 @@ pub struct Global {
}
impl Global {
pub fn new(store: &HostRef<Store>, r#type: GlobalType, val: Val) -> Global {
pub fn new(store: &Store, r#type: GlobalType, val: Val) -> Global {
let (wasmtime_export, wasmtime_state) =
generate_global_export(&r#type, val).expect("generated global");
Global {
@@ -246,10 +246,7 @@ impl Global {
&self.wasmtime_export
}
pub(crate) fn from_wasmtime_global(
export: wasmtime_runtime::Export,
store: &HostRef<Store>,
) -> Global {
pub(crate) fn from_wasmtime_global(export: wasmtime_runtime::Export, store: &Store) -> Global {
let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export {
global
} else {
@@ -266,7 +263,7 @@ impl Global {
}
pub struct Table {
store: HostRef<Store>,
store: Store,
r#type: TableType,
wasmtime_handle: InstanceHandle,
wasmtime_export: wasmtime_runtime::Export,
@@ -274,7 +271,7 @@ pub struct Table {
fn get_table_item(
handle: &InstanceHandle,
store: &HostRef<Store>,
store: &Store,
table_index: wasm::DefinedTableIndex,
item_index: u32,
) -> Val {
@@ -287,7 +284,7 @@ fn get_table_item(
fn set_table_item(
handle: &mut InstanceHandle,
store: &HostRef<Store>,
store: &Store,
table_index: wasm::DefinedTableIndex,
item_index: u32,
val: Val,
@@ -302,7 +299,7 @@ fn set_table_item(
}
impl Table {
pub fn new(store: &HostRef<Store>, r#type: TableType, init: Val) -> Table {
pub fn new(store: &Store, r#type: TableType, init: Val) -> Table {
match r#type.element() {
ValType::FuncRef => (),
_ => panic!("table is not for funcref"),
@@ -387,7 +384,7 @@ impl Table {
pub(crate) fn from_wasmtime_table(
export: wasmtime_runtime::Export,
store: &HostRef<Store>,
store: &Store,
instance_handle: wasmtime_runtime::InstanceHandle,
) -> Table {
let table = if let wasmtime_runtime::Export::Table { ref table, .. } = export {
@@ -406,14 +403,14 @@ impl Table {
}
pub struct Memory {
_store: HostRef<Store>,
_store: Store,
r#type: MemoryType,
wasmtime_handle: InstanceHandle,
wasmtime_export: wasmtime_runtime::Export,
}
impl Memory {
pub fn new(store: &HostRef<Store>, r#type: MemoryType) -> Memory {
pub fn new(store: &Store, r#type: MemoryType) -> Memory {
let (wasmtime_handle, wasmtime_export) =
generate_memory_export(&r#type).expect("generated memory");
Memory {
@@ -473,7 +470,7 @@ impl Memory {
pub(crate) fn from_wasmtime_memory(
export: wasmtime_runtime::Export,
store: &HostRef<Store>,
store: &Store,
instance_handle: wasmtime_runtime::InstanceHandle,
) -> Memory {
let memory = if let wasmtime_runtime::Export::Memory { ref memory, .. } = export {

View File

@@ -30,7 +30,7 @@ impl Resolver for SimpleResolver {
pub fn instantiate_in_context(
data: &[u8],
imports: Vec<(String, String, Extern)>,
mut context: Context,
context: Context,
exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
) -> Result<(InstanceHandle, HashSet<Context>), Error> {
let mut contexts = HashSet::new();
@@ -70,12 +70,12 @@ pub struct Instance {
impl Instance {
pub fn new(
store: &HostRef<Store>,
store: &Store,
module: &HostRef<Module>,
externs: &[Extern],
) -> Result<Instance, Error> {
let context = store.borrow_mut().context().clone();
let exports = store.borrow_mut().global_exports().clone();
let context = store.context().clone();
let exports = store.global_exports().clone();
let imports = module
.borrow()
.imports()
@@ -131,7 +131,7 @@ impl Instance {
Some(&self.exports()[i])
}
pub fn from_handle(store: &HostRef<Store>, instance_handle: InstanceHandle) -> Instance {
pub fn from_handle(store: &Store, instance_handle: InstanceHandle) -> Instance {
let contexts = HashSet::new();
let mut exports = Vec::new();
@@ -143,7 +143,7 @@ impl Instance {
// HACK ensure all handles, instantiated outside Store, present in
// the store's SignatureRegistry, e.g. WASI instances that are
// imported into this store using the from_handle() method.
let _ = store.borrow_mut().register_wasmtime_signature(signature);
let _ = store.register_wasmtime_signature(signature);
}
let extern_type = ExternType::from_wasmtime_export(&export);
exports_types.push(ExportType::new(name, extern_type));

View File

@@ -1,4 +1,3 @@
use crate::r#ref::HostRef;
use crate::runtime::Store;
use crate::types::{
ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability,
@@ -173,7 +172,7 @@ pub(crate) enum ModuleCodeSource {
#[derive(Clone)]
pub struct Module {
store: HostRef<Store>,
store: Store,
source: ModuleCodeSource,
imports: Box<[ImportType]>,
exports: Box<[ExportType]>,
@@ -182,13 +181,13 @@ pub struct Module {
impl Module {
/// Validate and decode the raw wasm data in `binary` and create a new
/// `Module` in the given `store`.
pub fn new(store: &HostRef<Store>, binary: &[u8]) -> Result<Module> {
pub fn new(store: &Store, binary: &[u8]) -> Result<Module> {
Self::validate(store, binary)?;
Self::new_unchecked(store, binary)
}
/// Similar to `new`, but does not perform any validation. Only use this
/// on modules which are known to have been validated already!
pub fn new_unchecked(store: &HostRef<Store>, binary: &[u8]) -> Result<Module> {
pub fn new_unchecked(store: &Store, binary: &[u8]) -> Result<Module> {
let (imports, exports) = read_imports_and_exports(binary)?;
Ok(Module {
store: store.clone(),
@@ -203,8 +202,8 @@ impl Module {
_ => None,
}
}
pub fn validate(store: &HostRef<Store>, binary: &[u8]) -> Result<()> {
let features = store.borrow().engine().config.features.clone();
pub fn validate(store: &Store, binary: &[u8]) -> Result<()> {
let features = store.engine().config.features.clone();
let config = ValidatingParserConfig {
operator_config: OperatorValidatorConfig {
enable_threads: features.threads,
@@ -222,7 +221,7 @@ impl Module {
pub fn exports(&self) -> &[ExportType] {
&self.exports
}
pub fn from_exports(store: &HostRef<Store>, exports: Box<[ExportType]>) -> Self {
pub fn from_exports(store: &Store, exports: Box<[ExportType]>) -> Self {
Module {
store: store.clone(),
source: ModuleCodeSource::Unknown,

View File

@@ -288,12 +288,13 @@ pub enum OptLevel {
///
/// Using `clone` on an `Engine` is a cheap operation. It will not create an
/// entirely new engine, but rather just a new reference to the existing engine.
/// In other words it's a shallow copy, not a deep copy.
///
/// ## Engines and `Default`
///
/// You can create an engine with default settings using `Engine::default()`.
/// This engine will not have any unstable wasm features enabled and will use
/// the default compilation backend configured at this crate's compile time.
/// You can create an engine with default configuration settings using
/// `Engine::default()`. Be sure to consult the documentation of [`Config`] for
/// default settings.
#[derive(Default, Clone)]
pub struct Engine {
pub(crate) config: Arc<Config>,
@@ -311,45 +312,72 @@ impl Engine {
// Store
/// A `Store` is a shared cache of information between WebAssembly modules.
///
/// Each `Module` is compiled into a `Store` and a `Store` is associated with an
/// [`Engine`]. You'll use a `Store` to attach to a number of global items in
/// the production of various items for wasm modules.
///
/// # Stores and `Clone`
///
/// Using `clone` on a `Store` is a cheap operation. It will not create an
/// entirely new store, but rather just a new reference to the existing object.
/// In other words it's a shallow copy, not a deep copy.
///
/// ## Stores and `Default`
///
/// You can create a store with default configuration settings using
/// `Store::default()`. This will create a brand new [`Engine`] with default
/// ocnfiguration (see [`Config`] for more information).
#[derive(Clone)]
pub struct Store {
inner: Rc<StoreInner>,
}
struct StoreInner {
engine: Engine,
context: Context,
global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
signature_cache: HashMap<wasmtime_runtime::VMSharedSignatureIndex, ir::Signature>,
signature_cache: RefCell<HashMap<wasmtime_runtime::VMSharedSignatureIndex, ir::Signature>>,
}
impl Store {
/// Creates a new store to be associated with the given [`Engine`].
pub fn new(engine: &Engine) -> Store {
Store {
engine: engine.clone(),
context: Context::new(&engine.config),
global_exports: Rc::new(RefCell::new(HashMap::new())),
signature_cache: HashMap::new(),
inner: Rc::new(StoreInner {
engine: engine.clone(),
context: Context::new(&engine.config),
global_exports: Rc::new(RefCell::new(HashMap::new())),
signature_cache: RefCell::new(HashMap::new()),
}),
}
}
/// Returns the [`Engine`] that this store is associated with.
pub fn engine(&self) -> &Engine {
&self.engine
&self.inner.engine
}
pub(crate) fn context(&mut self) -> &mut Context {
&mut self.context
pub(crate) fn context(&self) -> &Context {
&self.inner.context
}
// Specific to wasmtime: hack to pass memory around to wasi
#[doc(hidden)]
pub fn global_exports(
&self,
) -> &Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> {
&self.global_exports
&self.inner.global_exports
}
pub(crate) fn register_wasmtime_signature(
&mut self,
&self,
signature: &ir::Signature,
) -> wasmtime_runtime::VMSharedSignatureIndex {
use std::collections::hash_map::Entry;
let index = self.context().compiler().signatures().register(signature);
match self.signature_cache.entry(index) {
match self.inner.signature_cache.borrow_mut().entry(index) {
Entry::Vacant(v) => {
v.insert(signature.clone());
}
@@ -361,8 +389,18 @@ impl Store {
pub(crate) fn lookup_wasmtime_signature(
&self,
type_index: wasmtime_runtime::VMSharedSignatureIndex,
) -> Option<&ir::Signature> {
self.signature_cache.get(&type_index)
) -> Option<ir::Signature> {
self.inner
.signature_cache
.borrow()
.get(&type_index)
.cloned()
}
}
impl Default for Store {
fn default() -> Store {
Store::new(&Engine::default())
}
}

View File

@@ -3,7 +3,7 @@
use crate::runtime::Store;
use anyhow::Result;
use std::any::Any;
use std::cell::{RefCell, RefMut};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use wasmtime_environ::entity::PrimaryMap;
@@ -13,7 +13,7 @@ use wasmtime_runtime::{Imports, InstanceHandle, VMFunctionBody};
pub(crate) fn create_handle(
module: Module,
signature_registry: Option<RefMut<Store>>,
signature_registry: Option<&Store>,
finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>,
state: Box<dyn Any>,
) -> Result<InstanceHandle> {
@@ -31,7 +31,7 @@ pub(crate) fn create_handle(
// Compute indices into the shared signature table.
let signatures = signature_registry
.map(|mut signature_registry| {
.map(|signature_registry| {
module
.signatures
.values()

View File

@@ -2,7 +2,6 @@
use super::create_handle::create_handle;
use super::trap::{record_api_trap, TrapSink, API_TRAP_CODE};
use crate::r#ref::HostRef;
use crate::{Callable, FuncType, Store, Val};
use anyhow::Result;
use std::cmp;
@@ -234,7 +233,7 @@ fn make_trampoline(
pub fn create_handle_with_function(
ft: &FuncType,
func: &Rc<dyn Callable + 'static>,
store: &HostRef<Store>,
store: &Store,
) -> Result<InstanceHandle> {
let sig = ft.get_wasmtime_signature().clone();
@@ -270,7 +269,7 @@ pub fn create_handle_with_function(
create_handle(
module,
Some(store.borrow_mut()),
Some(store),
finished_functions,
Box::new(trampoline_state),
)

View File

@@ -12,7 +12,6 @@ use self::global::create_global;
use self::memory::create_handle_with_memory;
use self::table::create_handle_with_table;
use super::{Callable, FuncType, GlobalType, MemoryType, Store, TableType, Val};
use crate::r#ref::HostRef;
use anyhow::Result;
use std::rc::Rc;
@@ -22,7 +21,7 @@ pub use self::trap::take_api_trap;
pub fn generate_func_export(
ft: &FuncType,
func: &Rc<dyn Callable + 'static>,
store: &HostRef<Store>,
store: &Store,
) -> Result<(wasmtime_runtime::InstanceHandle, wasmtime_runtime::Export)> {
let mut instance = create_handle_with_function(ft, func, store)?;
let export = instance.lookup("trampoline").expect("trampoline export");

View File

@@ -197,7 +197,7 @@ impl From<RuntimeValue> for Val {
pub(crate) fn into_checked_anyfunc(
val: Val,
store: &HostRef<Store>,
store: &Store,
) -> wasmtime_runtime::VMCallerCheckedAnyfunc {
match val {
Val::AnyRef(AnyRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc {
@@ -215,7 +215,7 @@ pub(crate) fn into_checked_anyfunc(
} => (*vmctx, *address, signature),
_ => panic!("expected function export"),
};
let type_index = store.borrow_mut().register_wasmtime_signature(signature);
let type_index = store.register_wasmtime_signature(signature);
wasmtime_runtime::VMCallerCheckedAnyfunc {
func_ptr,
type_index,
@@ -228,16 +228,14 @@ pub(crate) fn into_checked_anyfunc(
pub(crate) fn from_checked_anyfunc(
item: &wasmtime_runtime::VMCallerCheckedAnyfunc,
store: &HostRef<Store>,
store: &Store,
) -> Val {
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
return Val::AnyRef(AnyRef::Null);
}
let signature = store
.borrow()
.lookup_wasmtime_signature(item.type_index)
.expect("signature")
.clone();
.expect("signature");
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
let export = wasmtime_runtime::Export::Function {
address: item.func_ptr,

View File

@@ -609,7 +609,7 @@ pub unsafe extern "C" fn wasm_func_new(
ty: *const wasm_functype_t,
callback: wasm_func_callback_t,
) -> *mut wasm_func_t {
let store = &(*store).store;
let store = &(*store).store.borrow();
let ty = (*ty).functype.clone();
let callback = Rc::new(callback);
let func = Box::new(wasm_func_t {
@@ -663,7 +663,7 @@ pub unsafe extern "C" fn wasm_instance_new(
imports: *const *const wasm_extern_t,
result: *mut *mut wasm_trap_t,
) -> *mut wasm_instance_t {
let store = &(*store).store;
let store = &(*store).store.borrow();
let mut externs: Vec<Extern> = Vec::with_capacity((*module).imports.len());
for i in 0..(*module).imports.len() {
let import = *imports.add(i);
@@ -731,7 +731,7 @@ pub unsafe extern "C" fn wasm_module_new(
binary: *const wasm_byte_vec_t,
) -> *mut wasm_module_t {
let binary = (*binary).as_slice();
let store = &(*store).store;
let store = &(*store).store.borrow();
let module = Module::new_unchecked(store, binary).expect("module");
let imports = module
.imports()
@@ -765,7 +765,7 @@ pub unsafe extern "C" fn wasm_module_validate(
binary: *const wasm_byte_vec_t,
) -> bool {
let binary = (*binary).as_slice();
let store = &(*store).store;
let store = &(*store).store.borrow();
Module::validate(store, binary).is_ok()
}
@@ -814,7 +814,7 @@ pub unsafe extern "C" fn wasm_func_new_with_env(
env: *mut std::ffi::c_void,
finalizer: std::option::Option<unsafe extern "C" fn(arg1: *mut std::ffi::c_void)>,
) -> *mut wasm_func_t {
let store = &(*store).store;
let store = &(*store).store.borrow();
let ty = (*ty).functype.clone();
let callback = Rc::new(CallbackWithEnv {
callback,
@@ -1337,7 +1337,7 @@ pub unsafe extern "C" fn wasm_global_new(
val: *const wasm_val_t,
) -> *mut wasm_global_t {
let global = HostRef::new(Global::new(
&(*store).store,
&(*store).store.borrow(),
(*gt).globaltype.clone(),
(*val).val(),
));
@@ -1456,7 +1456,10 @@ pub unsafe extern "C" fn wasm_memory_new(
store: *mut wasm_store_t,
mt: *const wasm_memorytype_t,
) -> *mut wasm_memory_t {
let memory = HostRef::new(Memory::new(&(*store).store, (*mt).memorytype.clone()));
let memory = HostRef::new(Memory::new(
&(*store).store.borrow(),
(*mt).memorytype.clone(),
));
let m = Box::new(wasm_memory_t { memory, ext: None });
Box::into_raw(m)
}
@@ -1549,7 +1552,11 @@ pub unsafe extern "C" fn wasm_table_new(
Val::AnyRef(AnyRef::Null)
};
let t = Box::new(wasm_table_t {
table: HostRef::new(Table::new(&(*store).store, (*tt).tabletype.clone(), init)),
table: HostRef::new(Table::new(
&(*store).store.borrow(),
(*tt).tabletype.clone(),
init,
)),
ext: None,
});
Box::into_raw(t)