Make Instance a refcounting handle around InstanceContents.
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 { .. } => {}
|
||||||
|
|||||||
Reference in New Issue
Block a user