Make Instance a refcounting handle around InstanceContents.

This commit is contained in:
Dan Gohman
2019-02-22 11:48:17 -08:00
parent a8cd5ef613
commit 077ee717a8
11 changed files with 205 additions and 189 deletions

View File

@@ -254,7 +254,11 @@ pub fn inspect_memory<'instance>(
/// Read a global in this `Instance` identified by an export name. /// Read a global in this `Instance` identified by an export name.
pub fn get(instance: &Instance, global_name: &str) -> Result<RuntimeValue, ActionError> { pub fn get(instance: &Instance, global_name: &str) -> Result<RuntimeValue, ActionError> {
let (definition, global) = match unsafe { instance.lookup_immutable(global_name) } { let (definition, global) = match unsafe { instance.lookup_immutable(global_name) } {
Some(Export::Global { definition, global }) => (definition, global), Some(Export::Global {
definition,
vmctx: _,
global,
}) => (definition, global),
Some(_) => { Some(_) => {
return Err(ActionError::Kind(format!( return Err(ActionError::Kind(format!(
"exported item \"{}\" is not a global variable", "exported item \"{}\" is not a global variable",

View File

@@ -1,6 +1,7 @@
use crate::action::{get, inspect_memory, invoke};
use crate::{ use crate::{
instantiate, ActionError, ActionOutcome, Compiler, Instance, InstanceIndex, Namespace, instantiate, ActionError, ActionOutcome, Compiler, Instance, Namespace, RuntimeValue,
RuntimeValue, SetupError, SetupError,
}; };
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use std::borrow::ToOwned; use std::borrow::ToOwned;
@@ -96,13 +97,10 @@ impl Context {
) )
} }
/// Return the instance index for the instance with the given name. /// Return the instance associated with the given name.
pub fn get_instance_index( pub fn get_instance(&mut self, instance_name: &str) -> Result<&mut Instance, UnknownInstance> {
&mut self,
instance_name: &str,
) -> Result<InstanceIndex, UnknownInstance> {
self.namespace self.namespace
.get_instance_index(instance_name) .get_instance(instance_name)
.ok_or_else(|| UnknownInstance { .ok_or_else(|| UnknownInstance {
instance_name: instance_name.to_string(), instance_name: instance_name.to_string(),
}) })
@@ -113,72 +111,84 @@ impl Context {
&mut self, &mut self,
instance_name: Option<String>, instance_name: Option<String>,
data: &[u8], data: &[u8],
) -> Result<InstanceIndex, ActionError> { ) -> Result<Instance, ActionError> {
let instance = self.instantiate(data).map_err(ActionError::Setup)?; let instance = self.instantiate(data).map_err(ActionError::Setup)?;
Ok(self.instance(instance_name, instance)) self.optionally_name_instance(instance_name, instance.clone());
Ok(instance)
} }
/// Install a new `Instance` in this `Namespace`, optionally with the /// If `name` isn't None, register it for the given instance.
/// given name, and return its index. pub fn optionally_name_instance(&mut self, name: Option<String>, instance: Instance) {
pub fn instance(&mut self, instance_name: Option<String>, instance: Instance) -> InstanceIndex { if let Some(name) = name {
self.namespace.instance(instance_name, instance) self.namespace.name_instance(name, instance);
}
}
/// Register a name for the given instance.
pub fn name_instance(&mut self, name: String, instance: Instance) {
self.namespace.name_instance(name, instance);
} }
/// Register an additional name for an existing registered instance. /// Register an additional name for an existing registered instance.
pub fn alias(&mut self, name: &str, as_name: String) -> Result<(), UnknownInstance> { pub fn alias(&mut self, name: &str, as_name: String) -> Result<(), UnknownInstance> {
let index = self.get_instance_index(&name)?; let instance = self.get_instance(&name)?.clone();
self.alias_for_indexed(index, as_name); self.name_instance(as_name, instance);
Ok(()) Ok(())
} }
/// Register an additional name for an existing registered instance. /// Invoke an exported function from a named instance.
pub fn alias_for_indexed(&mut self, index: InstanceIndex, as_name: String) { pub fn invoke_named(
self.namespace.alias_for_indexed(index, as_name)
}
/// Invoke an exported function from an instance.
pub fn invoke(
&mut self, &mut self,
instance_name: &str, instance_name: &str,
field: &str, field: &str,
args: &[RuntimeValue], args: &[RuntimeValue],
) -> Result<ActionOutcome, ContextError> { ) -> Result<ActionOutcome, ContextError> {
let index = self let mut instance = self
.get_instance_index(&instance_name) .get_instance(&instance_name)
.map_err(ContextError::Instance)?; .map_err(ContextError::Instance)?
self.invoke_indexed(index, field, args) .clone();
self.invoke(&mut instance, field, args)
.map_err(ContextError::Action) .map_err(ContextError::Action)
} }
/// Invoke an exported function from an instance. /// Invoke an exported function from an instance.
pub fn invoke_indexed( pub fn invoke(
&mut self, &mut self,
index: InstanceIndex, instance: &mut Instance,
field: &str, field: &str,
args: &[RuntimeValue], args: &[RuntimeValue],
) -> Result<ActionOutcome, ActionError> { ) -> Result<ActionOutcome, ActionError> {
self.namespace invoke(&mut *self.compiler, instance, field, &args)
.invoke(&mut *self.compiler, index, field, &args)
} }
/// Get the value of an exported global variable from an instance. /// Get the value of an exported global variable from an instance.
pub fn get(&mut self, instance_name: &str, field: &str) -> Result<ActionOutcome, ContextError> { pub fn get_named(
let index = self
.get_instance_index(&instance_name)
.map_err(ContextError::Instance)?;
self.get_indexed(index, field).map_err(ContextError::Action)
}
/// Get the value of an exported global variable from an instance.
pub fn get_indexed(
&mut self, &mut self,
index: InstanceIndex, instance_name: &str,
field: &str, field: &str,
) -> Result<ActionOutcome, ActionError> { ) -> Result<ActionOutcome, ContextError> {
self.namespace let mut instance = self
.get(index, field) .get_instance(&instance_name)
.map(|value| ActionOutcome::Returned { .map_err(ContextError::Instance)?
.clone();
self.get(&mut instance, field).map_err(ContextError::Action)
}
/// Get the value of an exported global variable from an instance.
pub fn get(&mut self, instance: &Instance, field: &str) -> Result<ActionOutcome, ActionError> {
get(instance, field).map(|value| ActionOutcome::Returned {
values: vec![value], values: vec![value],
}) })
} }
/// Get a slice of memory from an instance.
pub fn inspect_memory<'instance>(
&self,
instance: &'instance Instance,
field_name: &str,
start: usize,
len: usize,
) -> Result<&'instance [u8], ActionError> {
inspect_memory(instance, field_name, start, len)
}
} }

View File

@@ -56,7 +56,7 @@ pub use crate::compiler::Compiler;
pub use crate::context::{Context, ContextError, UnknownInstance}; pub use crate::context::{Context, ContextError, UnknownInstance};
pub use crate::instantiate::{instantiate, CompiledModule, SetupError}; pub use crate::instantiate::{instantiate, CompiledModule, SetupError};
pub use crate::link::link_module; pub use crate::link::link_module;
pub use crate::namespace::{InstanceIndex, Namespace}; pub use crate::namespace::Namespace;
pub use crate::resolver::{NullResolver, Resolver}; pub use crate::resolver::{NullResolver, Resolver};
pub use crate::target_tunables::target_tunables; pub use crate::target_tunables::target_tunables;

View File

@@ -5,14 +5,15 @@ use core::ptr::write_unaligned;
use cranelift_codegen::binemit::Reloc; use cranelift_codegen::binemit::Reloc;
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType}; use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType};
use std::collections::HashSet;
use std::vec::Vec; use std::vec::Vec;
use wasmtime_environ::{ use wasmtime_environ::{
MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan, MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan,
}; };
use wasmtime_runtime::libcalls; use wasmtime_runtime::libcalls;
use wasmtime_runtime::{ use wasmtime_runtime::{
Export, Imports, LinkError, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, Export, Imports, Instance, LinkError, VMFunctionBody, VMFunctionImport, VMGlobalImport,
VMTableImport, VMMemoryImport, VMTableImport,
}; };
/// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`. /// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`.
@@ -22,6 +23,8 @@ pub fn link_module(
relocations: Relocations, relocations: Relocations,
resolver: &mut dyn Resolver, resolver: &mut dyn Resolver,
) -> Result<Imports, LinkError> { ) -> Result<Imports, LinkError> {
let mut dependencies = HashSet::new();
let mut function_imports = PrimaryMap::with_capacity(module.imported_funcs.len()); let mut function_imports = PrimaryMap::with_capacity(module.imported_funcs.len());
for (index, (ref module_name, ref field)) in module.imported_funcs.iter() { for (index, (ref module_name, ref field)) in module.imported_funcs.iter() {
match resolver.resolve(module_name, field) { match resolver.resolve(module_name, field) {
@@ -41,6 +44,7 @@ pub fn link_module(
signature, import_signature) signature, import_signature)
)); ));
} }
dependencies.insert(Instance::from_vmctx(vmctx));
function_imports.push(VMFunctionImport { function_imports.push(VMFunctionImport {
body: address, body: address,
vmctx, vmctx,
@@ -78,6 +82,7 @@ pub fn link_module(
module_name, field, module_name, field,
))); )));
} }
dependencies.insert(Instance::from_vmctx(vmctx));
table_imports.push(VMTableImport { table_imports.push(VMTableImport {
from: definition, from: definition,
vmctx, vmctx,
@@ -131,6 +136,7 @@ pub fn link_module(
} }
assert!(memory.offset_guard_size >= import_memory.offset_guard_size); assert!(memory.offset_guard_size >= import_memory.offset_guard_size);
dependencies.insert(Instance::from_vmctx(vmctx));
memory_imports.push(VMMemoryImport { memory_imports.push(VMMemoryImport {
from: definition, from: definition,
vmctx, vmctx,
@@ -156,7 +162,17 @@ pub fn link_module(
for (index, (ref module_name, ref field)) in module.imported_globals.iter() { for (index, (ref module_name, ref field)) in module.imported_globals.iter() {
match resolver.resolve(module_name, field) { match resolver.resolve(module_name, field) {
Some(export_value) => match export_value { Some(export_value) => match export_value {
Export::Global { definition, global } => { Export::Table { .. } | Export::Memory { .. } | Export::Function { .. } => {
return Err(LinkError(format!(
"{}/{}: exported global incompatible with global import",
module_name, field
)));
}
Export::Global {
definition,
vmctx,
global,
} => {
let imported_global = module.globals[index]; let imported_global = module.globals[index];
if !is_global_compatible(&global, &imported_global) { if !is_global_compatible(&global, &imported_global) {
return Err(LinkError(format!( return Err(LinkError(format!(
@@ -164,14 +180,9 @@ pub fn link_module(
module_name, field module_name, field
))); )));
} }
dependencies.insert(Instance::from_vmctx(vmctx));
global_imports.push(VMGlobalImport { from: definition }); global_imports.push(VMGlobalImport { from: definition });
} }
Export::Table { .. } | Export::Memory { .. } | Export::Function { .. } => {
return Err(LinkError(format!(
"{}/{}: exported global incompatible with global import",
module_name, field
)));
}
}, },
None => { None => {
return Err(LinkError(format!( return Err(LinkError(format!(
@@ -186,6 +197,7 @@ pub fn link_module(
relocate(allocated_functions, relocations, module); relocate(allocated_functions, relocations, module);
Ok(Imports::new( Ok(Imports::new(
dependencies,
function_imports, function_imports,
table_imports, table_imports,
memory_imports, memory_imports,

View File

@@ -3,29 +3,17 @@
//! and resolve imports to exports among them. //! and resolve imports to exports among them.
use super::HashMap; use super::HashMap;
use crate::action::{get, inspect_memory, invoke};
use crate::action::{ActionError, ActionOutcome, RuntimeValue};
use crate::compiler::Compiler;
use crate::resolver::Resolver; use crate::resolver::Resolver;
use cranelift_entity::PrimaryMap;
use std::string::String; use std::string::String;
use wasmtime_runtime::{Export, Instance}; use wasmtime_runtime::{Export, Instance};
/// An opaque reference to an `Instance` within a `Namespace`.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct InstanceIndex(u32);
entity_impl!(InstanceIndex, "instance");
/// A namespace containing instances keyed by name. /// A namespace containing instances keyed by name.
/// ///
/// Note that `Namespace` implements the `Resolver` trait, so it can resolve /// Note that `Namespace` implements the `Resolver` trait, so it can resolve
/// imports using defined exports. /// imports using defined exports.
pub struct Namespace { pub struct Namespace {
/// Mapping from identifiers to indices in `self.instances`. /// Mapping from identifiers to indices in `self.instances`.
names: HashMap<String, InstanceIndex>, names: HashMap<String, Instance>,
/// The instances, available by index.
instances: PrimaryMap<InstanceIndex, Instance>,
} }
impl Namespace { impl Namespace {
@@ -33,62 +21,25 @@ impl Namespace {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
names: HashMap::new(), names: HashMap::new(),
instances: PrimaryMap::new(),
} }
} }
/// Install a new `Instance` in this `Namespace`, optionally with the /// Install a new `Instance` in this `Namespace`, optionally with the
/// given name, and return its index. /// given name, and return its index.
pub fn instance(&mut self, instance_name: Option<String>, instance: Instance) -> InstanceIndex { pub fn name_instance(&mut self, name: String, instance: Instance) {
let index = self.instances.push(instance); self.names.insert(name, instance);
if let Some(instance_name) = instance_name {
self.names.insert(instance_name, index);
}
index
} }
/// Get the instance index registered with the given `instance_name`. /// Get the instance index registered with the given `instance_name`.
pub fn get_instance_index(&mut self, instance_name: &str) -> Option<InstanceIndex> { pub fn get_instance(&mut self, name: &str) -> Option<&mut Instance> {
self.names.get_mut(instance_name).cloned() self.names.get_mut(name)
}
/// Register an additional name for an existing registered instance.
pub fn alias_for_indexed(&mut self, existing_index: InstanceIndex, new_name: String) {
self.names.insert(new_name, existing_index);
}
/// Invoke an exported function from an instance.
pub fn invoke(
&mut self,
compiler: &mut Compiler,
index: InstanceIndex,
field_name: &str,
args: &[RuntimeValue],
) -> Result<ActionOutcome, ActionError> {
invoke(compiler, &mut self.instances[index], field_name, args)
}
/// Get a slice of memory from an instance.
pub fn inspect_memory(
&self,
index: InstanceIndex,
field_name: &str,
start: usize,
len: usize,
) -> Result<&[u8], ActionError> {
inspect_memory(&self.instances[index], field_name, start, len)
}
/// Get the value of an exported global from an instance.
pub fn get(&self, index: InstanceIndex, field_name: &str) -> Result<RuntimeValue, ActionError> {
get(&self.instances[index], field_name)
} }
} }
impl Resolver for Namespace { impl Resolver for Namespace {
fn resolve(&mut self, instance: &str, field: &str) -> Option<Export> { fn resolve(&mut self, name: &str, field: &str) -> Option<Export> {
if let Some(index) = self.names.get(instance) { if let Some(instance) = self.names.get_mut(name) {
self.instances[*index].lookup(field) instance.lookup(field)
} else { } else {
None None
} }

View File

@@ -42,6 +42,8 @@ pub enum Export {
Global { Global {
/// The address of the global storage. /// The address of the global storage.
definition: *mut VMGlobalDefinition, definition: *mut VMGlobalDefinition,
/// Pointer to the containing VMContext.
vmctx: *mut VMContext,
/// The global declaration, used for compatibilty checking. /// The global declaration, used for compatibilty checking.
global: Global, global: Global,
}, },
@@ -88,7 +90,15 @@ impl Export {
} }
/// Construct a global export value. /// Construct a global export value.
pub fn global(definition: *mut VMGlobalDefinition, global: Global) -> Self { pub fn global(
Export::Global { definition, global } definition: *mut VMGlobalDefinition,
vmctx: *mut VMContext,
global: Global,
) -> Self {
Export::Global {
definition,
vmctx,
global,
}
} }
} }

View File

@@ -1,10 +1,15 @@
use crate::instance::Instance;
use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport}; use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
use cranelift_entity::{BoxedSlice, PrimaryMap}; use cranelift_entity::{BoxedSlice, PrimaryMap};
use cranelift_wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex}; use cranelift_wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
use std::collections::HashSet;
/// Resolved import pointers. /// Resolved import pointers.
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct Imports { pub struct Imports {
/// The set of instances that the imports depend on.
pub dependencies: HashSet<Instance>,
/// Resolved addresses for imported functions. /// Resolved addresses for imported functions.
pub functions: BoxedSlice<FuncIndex, VMFunctionImport>, pub functions: BoxedSlice<FuncIndex, VMFunctionImport>,
@@ -21,12 +26,14 @@ pub struct Imports {
impl Imports { impl Imports {
/// Construct a new `Imports` instance. /// Construct a new `Imports` instance.
pub fn new( pub fn new(
dependencies: HashSet<Instance>,
function_imports: PrimaryMap<FuncIndex, VMFunctionImport>, function_imports: PrimaryMap<FuncIndex, VMFunctionImport>,
table_imports: PrimaryMap<TableIndex, VMTableImport>, table_imports: PrimaryMap<TableIndex, VMTableImport>,
memory_imports: PrimaryMap<MemoryIndex, VMMemoryImport>, memory_imports: PrimaryMap<MemoryIndex, VMMemoryImport>,
global_imports: PrimaryMap<GlobalIndex, VMGlobalImport>, global_imports: PrimaryMap<GlobalIndex, VMGlobalImport>,
) -> Self { ) -> Self {
Self { Self {
dependencies,
functions: function_imports.into_boxed_slice(), functions: function_imports.into_boxed_slice(),
tables: table_imports.into_boxed_slice(), tables: table_imports.into_boxed_slice(),
memories: memory_imports.into_boxed_slice(), memories: memory_imports.into_boxed_slice(),
@@ -37,6 +44,7 @@ impl Imports {
/// Construct a new `Imports` instance with no imports. /// Construct a new `Imports` instance with no imports.
pub fn none() -> Self { pub fn none() -> Self {
Self { Self {
dependencies: HashSet::new(),
functions: PrimaryMap::new().into_boxed_slice(), functions: PrimaryMap::new().into_boxed_slice(),
tables: PrimaryMap::new().into_boxed_slice(), tables: PrimaryMap::new().into_boxed_slice(),
memories: PrimaryMap::new().into_boxed_slice(), memories: PrimaryMap::new().into_boxed_slice(),

View File

@@ -27,7 +27,7 @@ use cranelift_wasm::{
use indexmap; use indexmap;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::boxed::Box; use std::boxed::Box;
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::rc::Rc; use std::rc::Rc;
use std::string::{String, ToString}; use std::string::{String, ToString};
use wasmtime_environ::{DataInitializer, Module, TableElements, VMOffsets}; use wasmtime_environ::{DataInitializer, Module, TableElements, VMOffsets};
@@ -184,6 +184,17 @@ fn global_mut<'vmctx>(
/// FIXME: Should this be pub(crate)? /// FIXME: Should this be pub(crate)?
#[repr(C)] #[repr(C)]
pub struct InstanceContents { pub struct InstanceContents {
/// The number of references to this `InstanceContents`.
refcount: usize,
/// Instances from which this `InstanceContents` imports. These won't
/// create reference cycles because wasm instances can't cyclically
/// import from each other.
dependencies: HashSet<Instance>,
/// The allocated contents.
mmap: Mmap,
/// The `Module` this `Instance` was instantiated from. /// The `Module` this `Instance` was instantiated from.
module: Rc<Module>, module: Rc<Module>,
@@ -600,42 +611,13 @@ impl InstanceContents {
} }
} }
/// A wrapper around an `Mmap` holding an `InstanceContents`.
struct MmapField {
/// The allocated contents.
mmap: Mmap,
}
#[allow(clippy::cast_ptr_alignment)]
impl MmapField {
/// Return the contained contents.
fn contents(&self) -> &InstanceContents {
assert!(self.mmap.len() >= mem::size_of::<InstanceContents>());
unsafe { &*(self.mmap.as_ptr() as *const InstanceContents) }
}
/// Return the contained contents.
fn contents_mut(&mut self) -> &mut InstanceContents {
assert!(self.mmap.len() >= mem::size_of::<InstanceContents>());
unsafe { &mut *(self.mmap.as_mut_ptr() as *mut InstanceContents) }
}
}
impl Drop for MmapField {
fn drop(&mut self) {
/// Drop the `InstanceContents`.
assert!(self.mmap.len() >= mem::size_of::<InstanceContents>());
mem::drop(mem::replace(self.contents_mut(), unsafe { mem::zeroed() }));
}
}
/// An Instance of a WebAssembly module. /// An Instance of a WebAssembly module.
/// ///
/// Note that compiled wasm code passes around raw pointers to `Instance`, so /// Note that compiled wasm code passes around raw pointers to `Instance`, so
/// this shouldn't be moved. /// this shouldn't be moved.
#[derive(Hash, PartialEq, Eq)]
pub struct Instance { pub struct Instance {
/// The `Mmap` containing the contents of the instance. instance: *mut InstanceContents,
mmap_field: MmapField,
} }
impl Instance { impl Instance {
@@ -679,6 +661,9 @@ impl Instance {
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
let contents_ptr = contents_mmap.as_mut_ptr() as *mut InstanceContents; let contents_ptr = contents_mmap.as_mut_ptr() as *mut InstanceContents;
let contents = InstanceContents { let contents = InstanceContents {
refcount: 1,
dependencies: imports.dependencies,
mmap: contents_mmap,
module, module,
global_exports, global_exports,
offsets, offsets,
@@ -776,48 +761,52 @@ impl Instance {
// invoked automatically at instantiation time. // invoked automatically at instantiation time.
contents.invoke_start_function()?; contents.invoke_start_function()?;
Ok(Self { Ok(Self { instance: contents })
mmap_field: MmapField { }
mmap: contents_mmap,
}, /// Create a new `InstanceHandle` pointing at the instance
}) /// pointed to by the given `VMContext` pointer.
pub fn from_vmctx(vmctx: *mut VMContext) -> Self {
let instance = unsafe { (&mut *vmctx).instance_contents() };
instance.refcount += 1;
Self { instance }
} }
/// Return a reference to the vmctx used by compiled wasm code. /// Return a reference to the vmctx used by compiled wasm code.
pub fn vmctx(&self) -> &VMContext { pub fn vmctx(&self) -> &VMContext {
self.mmap_field.contents().vmctx() self.contents().vmctx()
} }
/// Return a raw pointer to the vmctx used by compiled wasm code. /// Return a raw pointer to the vmctx used by compiled wasm code.
pub fn vmctx_ptr(&self) -> *const VMContext { pub fn vmctx_ptr(&self) -> *const VMContext {
self.mmap_field.contents().vmctx_ptr() self.contents().vmctx_ptr()
} }
/// Return a mutable reference to the vmctx used by compiled wasm code. /// Return a mutable reference to the vmctx used by compiled wasm code.
pub fn vmctx_mut(&mut self) -> &mut VMContext { pub fn vmctx_mut(&mut self) -> &mut VMContext {
self.mmap_field.contents_mut().vmctx_mut() self.contents_mut().vmctx_mut()
} }
/// Return a mutable raw pointer to the vmctx used by compiled wasm code. /// Return a mutable raw pointer to the vmctx used by compiled wasm code.
pub fn vmctx_mut_ptr(&mut self) -> *mut VMContext { pub fn vmctx_mut_ptr(&mut self) -> *mut VMContext {
self.mmap_field.contents_mut().vmctx_mut_ptr() self.contents_mut().vmctx_mut_ptr()
} }
/// Lookup an export with the given name. /// Lookup an export with the given name.
pub fn lookup(&mut self, field: &str) -> Option<Export> { pub fn lookup(&mut self, field: &str) -> Option<Export> {
self.mmap_field.contents_mut().lookup(field) self.contents_mut().lookup(field)
} }
/// Lookup an export with the given name. This takes an immutable reference, /// Lookup an export with the given name. This takes an immutable reference,
/// and the result is an `Export` that the type system doesn't prevent from /// and the result is an `Export` that the type system doesn't prevent from
/// being used to mutate the instance, so this function is unsafe. /// being used to mutate the instance, so this function is unsafe.
pub unsafe fn lookup_immutable(&self, field: &str) -> Option<Export> { pub unsafe fn lookup_immutable(&self, field: &str) -> Option<Export> {
self.mmap_field.contents().lookup_immutable(field) self.contents().lookup_immutable(field)
} }
/// Lookup an export with the given export declaration. /// Lookup an export with the given export declaration.
pub fn lookup_by_declaration(&mut self, export: &wasmtime_environ::Export) -> Export { pub fn lookup_by_declaration(&mut self, export: &wasmtime_environ::Export) -> Export {
self.mmap_field.contents_mut().lookup_by_declaration(export) self.contents_mut().lookup_by_declaration(export)
} }
/// Lookup an export with the given export declaration. This takes an immutable /// Lookup an export with the given export declaration. This takes an immutable
@@ -827,9 +816,7 @@ impl Instance {
&self, &self,
export: &wasmtime_environ::Export, export: &wasmtime_environ::Export,
) -> Export { ) -> Export {
self.mmap_field self.contents().lookup_immutable_by_declaration(export)
.contents()
.lookup_immutable_by_declaration(export)
} }
/// Return an iterator over the exports of this instance. /// Return an iterator over the exports of this instance.
@@ -838,12 +825,45 @@ impl Instance {
/// are export names, and the values are export declarations which can be /// are export names, and the values are export declarations which can be
/// resolved `lookup_by_declaration`. /// resolved `lookup_by_declaration`.
pub fn exports(&self) -> indexmap::map::Iter<String, wasmtime_environ::Export> { pub fn exports(&self) -> indexmap::map::Iter<String, wasmtime_environ::Export> {
self.mmap_field.contents().exports() self.contents().exports()
} }
/// Return a reference to the custom state attached to this instance. /// Return a reference to the custom state attached to this instance.
pub fn host_state(&mut self) -> &mut Any { pub fn host_state(&mut self) -> &mut Any {
self.mmap_field.contents_mut().host_state() self.contents_mut().host_state()
}
}
impl Instance {
/// Return the contained contents.
pub fn contents(&self) -> &InstanceContents {
unsafe { &*(self.instance as *const InstanceContents) }
}
/// Return the contained contents.
pub fn contents_mut(&mut self) -> &mut InstanceContents {
unsafe { &mut *(self.instance as *mut InstanceContents) }
}
}
impl Clone for Instance {
fn clone(&self) -> Self {
unsafe { &mut *(self.instance as *mut InstanceContents) }.refcount += 1;
Instance {
instance: self.instance,
}
}
}
impl Drop for Instance {
fn drop(&mut self) {
let contents = self.contents_mut();
contents.refcount -= 1;
if contents.refcount == 0 {
let mmap = mem::replace(&mut contents.mmap, Mmap::new());
unsafe { ptr::drop_in_place(contents) };
mem::drop(mmap);
}
} }
} }
@@ -907,6 +927,7 @@ fn lookup_by_declaration(
} else { } else {
imported_global(vmctx, offsets, *index).from imported_global(vmctx, offsets, *index).from
}, },
vmctx,
global: module.globals[*index], global: module.globals[*index],
}, },
} }

View File

@@ -46,7 +46,7 @@ pub mod libcalls;
pub use crate::export::Export; pub use crate::export::Export;
pub use crate::imports::Imports; pub use crate::imports::Imports;
pub use crate::instance::{Instance, InstantiationError, LinkError}; pub use crate::instance::{Instance, InstanceContents, InstantiationError, LinkError};
pub use crate::mmap::Mmap; pub use crate::mmap::Mmap;
pub use crate::sig_registry::SignatureRegistry; pub use crate::sig_registry::SignatureRegistry;
pub use crate::signalhandlers::{wasmtime_init_eager, wasmtime_init_finish}; pub use crate::signalhandlers::{wasmtime_init_eager, wasmtime_init_finish};

View File

@@ -4,7 +4,7 @@ use std::path::Path;
use std::{fmt, fs, io, str}; use std::{fmt, fs, io, str};
use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value}; use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value};
use wasmtime_jit::{ use wasmtime_jit::{
ActionError, ActionOutcome, Compiler, Context, InstanceIndex, InstantiationError, RuntimeValue, ActionError, ActionOutcome, Compiler, Context, Instance, InstantiationError, RuntimeValue,
UnknownInstance, UnknownInstance,
}; };
@@ -71,7 +71,7 @@ pub struct WastFileError {
pub struct WastContext { pub struct WastContext {
/// Wast files have a concept of a "current" module, which is the most /// Wast files have a concept of a "current" module, which is the most
/// recently defined. /// recently defined.
current: Option<InstanceIndex>, current: Option<Instance>,
context: Context, context: Context,
} }
@@ -85,28 +85,24 @@ impl WastContext {
} }
} }
fn get_instance_index( fn get_instance(&mut self, instance_name: Option<&str>) -> Result<&mut Instance, WastError> {
&mut self, let instance = if let Some(instance_name) = instance_name {
instance_name: Option<&str>,
) -> Result<InstanceIndex, WastError> {
let index = if let Some(instance_name) = instance_name {
self.context self.context
.get_instance_index(instance_name) .get_instance(instance_name)
.map_err(WastError::Instance) .map_err(WastError::Instance)
} else { } else {
self.current self.current
.as_mut() .as_mut()
.cloned()
.ok_or_else(|| WastError::NoDefaultInstance) .ok_or_else(|| WastError::NoDefaultInstance)
}?; }?;
Ok(index) Ok(instance)
} }
/// Register "spectest" which is used by the spec testsuite. /// Register "spectest" which is used by the spec testsuite.
pub fn register_spectest(&mut self) -> Result<(), InstantiationError> { pub fn register_spectest(&mut self) -> Result<(), InstantiationError> {
let instance = instantiate_spectest()?; let instance = instantiate_spectest()?;
self.context.instance(Some("spectest".to_owned()), instance); self.context.name_instance("spectest".to_owned(), instance);
Ok(()) Ok(())
} }
@@ -140,8 +136,8 @@ impl WastContext {
/// Register an instance to make it available for performing actions. /// Register an instance to make it available for performing actions.
fn register(&mut self, name: Option<String>, as_name: String) -> Result<(), WastError> { fn register(&mut self, name: Option<String>, as_name: String) -> Result<(), WastError> {
let index = self.get_instance_index(name.as_ref().map(|x| &**x))?; let instance = self.get_instance(name.as_ref().map(|x| &**x))?.clone();
self.context.alias_for_indexed(index, as_name); self.context.name_instance(as_name, instance);
Ok(()) Ok(())
} }
@@ -156,9 +152,11 @@ impl WastContext {
.iter() .iter()
.map(|arg| runtime_value(*arg)) .map(|arg| runtime_value(*arg))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let index = self.get_instance_index(instance_name.as_ref().map(|x| &**x))?; let mut instance = self
.get_instance(instance_name.as_ref().map(|x| &**x))?
.clone();
self.context self.context
.invoke_indexed(index, field, &value_args) .invoke(&mut instance, field, &value_args)
.map_err(WastError::Action) .map_err(WastError::Action)
} }
@@ -168,9 +166,11 @@ impl WastContext {
instance_name: Option<String>, instance_name: Option<String>,
field: &str, field: &str,
) -> Result<ActionOutcome, WastError> { ) -> Result<ActionOutcome, WastError> {
let index = self.get_instance_index(instance_name.as_ref().map(|x| &**x))?; let instance = self
.get_instance(instance_name.as_ref().map(|x| &**x))?
.clone();
self.context self.context
.get_indexed(index, field) .get(&instance, field)
.map_err(WastError::Action) .map_err(WastError::Action)
} }

View File

@@ -132,8 +132,8 @@ fn main() {
let mut context = Context::with_isa(isa); let mut context = Context::with_isa(isa);
// Make spectest available by default. // Make spectest available by default.
context.instance( context.name_instance(
Some("spectest".to_owned()), "spectest".to_owned(),
instantiate_spectest().expect("instantiating spectest"), instantiate_spectest().expect("instantiating spectest"),
); );
@@ -155,14 +155,14 @@ fn handle_module(context: &mut Context, args: &Args, path: &Path) -> Result<(),
let data = read_wasm(path.to_path_buf())?; let data = read_wasm(path.to_path_buf())?;
// Create a new `Instance` by compiling and instantiating a wasm module. // Create a new `Instance` by compiling and instantiating a wasm module.
let index = context let mut instance = context
.instantiate_module(None, &data) .instantiate_module(None, &data)
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
// If a function to invoke was given, invoke it. // If a function to invoke was given, invoke it.
if let Some(ref f) = args.flag_invoke { if let Some(ref f) = args.flag_invoke {
match context match context
.invoke_indexed(index, f, &[]) .invoke(&mut instance, f, &[])
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
{ {
ActionOutcome::Returned { .. } => {} ActionOutcome::Returned { .. } => {}