mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-21 20:51:32 +00:00
Rewrite exports and imports
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
use crate::{module::Module, types::FuncIndex, vm};
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub use crate::sig_registry::SigRegistry;
|
||||
pub use crate::mmap::{Mmap, Protect};
|
||||
pub use crate::sig_registry::SigRegistry;
|
||||
|
||||
pub trait Compiler {
|
||||
/// Compiles a `Module` from WebAssembly binary format
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
instance::{Import, ImportResolver},
|
||||
export::{Context, Export},
|
||||
import::ImportResolver,
|
||||
memory::LinearMemory,
|
||||
module::{ImportName, Module},
|
||||
table::{TableBacking, TableElements},
|
||||
@ -18,13 +19,13 @@ pub struct LocalBacking {
|
||||
}
|
||||
|
||||
impl LocalBacking {
|
||||
pub fn new(module: &Module, imports: &ImportBacking) -> Self {
|
||||
pub fn new(module: &Module, imports: &ImportBacking, vmctx: *mut vm::Ctx) -> Self {
|
||||
let mut memories = Self::generate_memories(module);
|
||||
let mut tables = Self::generate_tables(module);
|
||||
let globals = Self::generate_globals(module);
|
||||
|
||||
let vm_memories = Self::finalize_memories(module, &mut memories[..]);
|
||||
let vm_tables = Self::finalize_tables(module, imports, &mut tables[..]);
|
||||
let vm_tables = Self::finalize_tables(module, imports, &mut tables[..], vmctx);
|
||||
let vm_globals = Self::finalize_globals(module, imports, globals);
|
||||
|
||||
Self {
|
||||
@ -61,9 +62,7 @@ impl LocalBacking {
|
||||
fn finalize_memories(module: &Module, memories: &mut [LinearMemory]) -> Box<[vm::LocalMemory]> {
|
||||
for init in &module.data_initializers {
|
||||
assert!(init.base.is_none(), "global base not supported yet");
|
||||
assert!(
|
||||
init.offset + init.data.len() <= memories[init.memory_index.index()].size()
|
||||
);
|
||||
assert!(init.offset + init.data.len() <= memories[init.memory_index.index()].size());
|
||||
let offset = init.offset;
|
||||
let mem: &mut LinearMemory = &mut memories[init.memory_index.index()];
|
||||
// let end_of_init = offset + init.data.len();
|
||||
@ -98,6 +97,7 @@ impl LocalBacking {
|
||||
module: &Module,
|
||||
imports: &ImportBacking,
|
||||
tables: &mut [TableBacking],
|
||||
vmctx: *mut vm::Ctx,
|
||||
) -> Box<[vm::LocalTable]> {
|
||||
for init in &module.table_initializers {
|
||||
assert!(init.base.is_none(), "global base not supported yet");
|
||||
@ -106,7 +106,8 @@ impl LocalBacking {
|
||||
TableElements::Anyfunc(ref mut elements) => {
|
||||
for (i, &func_index) in init.elements.iter().enumerate() {
|
||||
let sig_index = module.func_assoc[func_index];
|
||||
let vm_sig_id = vm::SigId(sig_index.index() as u32);
|
||||
let sig_id = vm::SigId(sig_index.index() as u32);
|
||||
|
||||
let func_data = if module.is_imported_function(func_index) {
|
||||
imports.functions[func_index.index()].clone()
|
||||
} else {
|
||||
@ -116,13 +117,13 @@ impl LocalBacking {
|
||||
.get(module, func_index)
|
||||
.unwrap()
|
||||
.as_ptr(),
|
||||
vmctx,
|
||||
}
|
||||
};
|
||||
|
||||
elements[init.offset + i] = vm::Anyfunc {
|
||||
func_data,
|
||||
sig_id: vm_sig_id,
|
||||
};
|
||||
println!("func_data: {:#?}", func_data);
|
||||
|
||||
elements[init.offset + i] = vm::Anyfunc { func_data, sig_id };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,7 +153,9 @@ impl LocalBacking {
|
||||
Initializer::Const(Value::I64(x)) => x as u64,
|
||||
Initializer::Const(Value::F32(x)) => x.to_bits() as u64,
|
||||
Initializer::Const(Value::F64(x)) => x.to_bits(),
|
||||
Initializer::GetGlobal(index) => (imports.globals[index.index()].global).data,
|
||||
Initializer::GetGlobal(index) => unsafe {
|
||||
(*imports.globals[index.index()].global).data
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -169,7 +172,11 @@ pub struct ImportBacking {
|
||||
}
|
||||
|
||||
impl ImportBacking {
|
||||
pub fn new(module: &Module, imports: &dyn ImportResolver) -> Result<Self, String> {
|
||||
pub fn new(
|
||||
module: &Module,
|
||||
imports: &dyn ImportResolver,
|
||||
vmctx: *mut vm::Ctx,
|
||||
) -> Result<Self, String> {
|
||||
assert!(
|
||||
module.imported_memories.len() == 0,
|
||||
"imported memories not yet supported"
|
||||
@ -180,7 +187,7 @@ impl ImportBacking {
|
||||
);
|
||||
|
||||
Ok(ImportBacking {
|
||||
functions: import_functions(module, imports)?,
|
||||
functions: import_functions(module, imports, vmctx)?,
|
||||
memories: vec![].into_boxed_slice(),
|
||||
tables: vec![].into_boxed_slice(),
|
||||
globals: import_globals(module, imports)?,
|
||||
@ -192,89 +199,71 @@ impl ImportBacking {
|
||||
|
||||
// }
|
||||
|
||||
fn import_functions(module: &Module, imports: &dyn ImportResolver) -> Result<Box<[vm::ImportedFunc]>, String> {
|
||||
fn import_functions(
|
||||
module: &Module,
|
||||
imports: &dyn ImportResolver,
|
||||
vmctx: *mut vm::Ctx,
|
||||
) -> Result<Box<[vm::ImportedFunc]>, String> {
|
||||
let mut functions = Vec::with_capacity(module.imported_functions.len());
|
||||
for (
|
||||
index,
|
||||
ImportName {
|
||||
module: mod_name,
|
||||
name: item_name,
|
||||
},
|
||||
) in &module.imported_functions
|
||||
{
|
||||
for (index, ImportName { namespace, name }) in &module.imported_functions {
|
||||
let sig_index = module.func_assoc[index];
|
||||
let expected_sig = module.sig_registry.lookup_func_sig(sig_index);
|
||||
let import = imports.get(mod_name, item_name);
|
||||
let import = imports.get(namespace, name);
|
||||
match import {
|
||||
Some(&Import::Func(ref func, ref signature)) => {
|
||||
if expected_sig == signature {
|
||||
Some(Export::Function {
|
||||
func,
|
||||
ctx,
|
||||
signature,
|
||||
}) => {
|
||||
if expected_sig == &signature {
|
||||
functions.push(vm::ImportedFunc {
|
||||
func: func.inner(),
|
||||
// vmctx: ptr::null_mut(),
|
||||
vmctx: match ctx {
|
||||
Context::External(ctx) => ctx,
|
||||
Context::Internal => vmctx,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return Err(format!(
|
||||
"unexpected signature for {:?}:{:?}",
|
||||
mod_name, item_name
|
||||
namespace, name
|
||||
));
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
return Err(format!(
|
||||
"incorrect import type for {}:{}",
|
||||
mod_name, item_name
|
||||
));
|
||||
return Err(format!("incorrect import type for {}:{}", namespace, name));
|
||||
}
|
||||
None => {
|
||||
return Err(format!("import not found: {}:{}", mod_name, item_name));
|
||||
return Err(format!("import not found: {}:{}", namespace, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(functions.into_boxed_slice())
|
||||
}
|
||||
|
||||
fn import_globals(module: &Module, imports: &dyn ImportResolver) -> Result<Box<[vm::ImportedGlobal]>, String> {
|
||||
fn import_globals(
|
||||
module: &Module,
|
||||
imports: &dyn ImportResolver,
|
||||
) -> Result<Box<[vm::ImportedGlobal]>, String> {
|
||||
let mut globals = Vec::with_capacity(module.imported_globals.len());
|
||||
for (
|
||||
_,
|
||||
(
|
||||
ImportName {
|
||||
module: mod_name,
|
||||
name: item_name,
|
||||
},
|
||||
global_desc,
|
||||
),
|
||||
) in &module.imported_globals
|
||||
{
|
||||
let import = imports.get(mod_name, item_name);
|
||||
for (_, (ImportName { namespace, name }, global_desc)) in &module.imported_globals {
|
||||
let import = imports.get(namespace, name);
|
||||
match import {
|
||||
Some(Import::Global(val)) => {
|
||||
if val.ty() == global_desc.ty {
|
||||
globals.push(vm::ImportedGlobal {
|
||||
global: vm::LocalGlobal {
|
||||
data: match val {
|
||||
Value::I32(n) => *n as u64,
|
||||
Value::I64(n) => *n as u64,
|
||||
Value::F32(n) => (*n).to_bits() as u64,
|
||||
Value::F64(n) => (*n).to_bits(),
|
||||
},
|
||||
},
|
||||
});
|
||||
Some(Export::Global { local, global }) => {
|
||||
if &global == global_desc {
|
||||
globals.push(vm::ImportedGlobal { global: local });
|
||||
} else {
|
||||
return Err(format!(
|
||||
"unexpected global type for {:?}:{:?}",
|
||||
mod_name, item_name
|
||||
"unexpected global description for {:?}:{:?}",
|
||||
namespace, name
|
||||
));
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
return Err(format!(
|
||||
"incorrect import type for {}:{}",
|
||||
mod_name, item_name
|
||||
));
|
||||
return Err(format!("incorrect import type for {}:{}", namespace, name));
|
||||
}
|
||||
None => {
|
||||
return Err(format!("import not found: {}:{}", mod_name, item_name));
|
||||
return Err(format!("import not found: {}:{}", namespace, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
lib/runtime/src/export.rs
Normal file
34
lib/runtime/src/export.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::{
|
||||
instance::FuncRef,
|
||||
types::{FuncSig, GlobalDesc, Memory, Table},
|
||||
vm,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Context {
|
||||
External(*mut vm::Ctx),
|
||||
Internal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Export {
|
||||
Function {
|
||||
func: FuncRef,
|
||||
ctx: Context,
|
||||
signature: FuncSig,
|
||||
},
|
||||
Memory {
|
||||
local: *mut vm::LocalMemory,
|
||||
ctx: Context,
|
||||
memory: Memory,
|
||||
},
|
||||
Table {
|
||||
local: *mut vm::LocalTable,
|
||||
ctx: Context,
|
||||
table: Table,
|
||||
},
|
||||
Global {
|
||||
local: *mut vm::LocalGlobal,
|
||||
global: GlobalDesc,
|
||||
},
|
||||
}
|
58
lib/runtime/src/import.rs
Normal file
58
lib/runtime/src/import.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use crate::{export::Export, Instance};
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
|
||||
pub trait ImportResolver {
|
||||
fn get(&self, namespace: &str, name: &str) -> Option<Export>;
|
||||
}
|
||||
|
||||
enum Namespace {
|
||||
Instance(Box<Instance>),
|
||||
UserSupplied(HashMap<String, Export>),
|
||||
}
|
||||
|
||||
pub struct Imports {
|
||||
map: HashMap<String, Namespace>,
|
||||
}
|
||||
|
||||
impl Imports {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_instance(&mut self, namespace: impl Into<String>, instance: Box<Instance>) {
|
||||
match self.map.entry(namespace.into()) {
|
||||
Entry::Vacant(empty) => empty.insert(Namespace::Instance(instance)),
|
||||
Entry::Occupied(_) => {
|
||||
panic!("cannot register an instance in a namespace that already exists")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn register_export(
|
||||
&mut self,
|
||||
namespace: impl Into<String>,
|
||||
name: impl Into<String>,
|
||||
export: Export,
|
||||
) {
|
||||
let namespace_item = self
|
||||
.map
|
||||
.entry(namespace.into())
|
||||
.or_insert_with(|| Namespace::UserSupplied(HashMap::new()));
|
||||
|
||||
match namespace_item {
|
||||
Namespace::UserSupplied(ref mut map) => map.insert(name.into(), export),
|
||||
Namespace::Instance(_) => panic!("cannot register an export in a namespace that has already been used to register an instance"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportResolver for Imports {
|
||||
fn get(&self, namespace: &str, name: &str) -> Option<Export> {
|
||||
match self.map.get(namespace)? {
|
||||
Namespace::UserSupplied(map) => map.get(name).cloned(),
|
||||
Namespace::Instance(instance) => instance.get_export(name).ok(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +1,54 @@
|
||||
use crate::recovery::call_protected;
|
||||
use crate::{
|
||||
backing::{ImportBacking, LocalBacking},
|
||||
memory::LinearMemory,
|
||||
module::{Export, Module},
|
||||
table::TableBacking,
|
||||
types::{FuncIndex, FuncSig, Memory, Table, Type, Value, MapIndex},
|
||||
export::{Context, Export},
|
||||
import::ImportResolver,
|
||||
module::{ExportIndex, Module},
|
||||
types::{FuncIndex, FuncSig, MapIndex, Type, Value},
|
||||
vm,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use hashbrown::hash_map;
|
||||
use libffi::high::{arg as libffi_arg, call as libffi_call, CodePtr};
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
use std::{iter, mem};
|
||||
|
||||
pub struct Instance {
|
||||
pub(crate) backing: LocalBacking,
|
||||
import_backing: ImportBacking,
|
||||
pub module: Module,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) backing: LocalBacking,
|
||||
#[allow(dead_code)]
|
||||
imports: Rc<dyn ImportResolver>,
|
||||
import_backing: ImportBacking,
|
||||
vmctx: Box<vm::Ctx>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub(crate) fn new(
|
||||
module: Module,
|
||||
imports: &dyn ImportResolver,
|
||||
imports: Rc<dyn ImportResolver>,
|
||||
) -> Result<Box<Instance>, String> {
|
||||
let import_backing = ImportBacking::new(&module, imports)?;
|
||||
let backing = LocalBacking::new(&module, &import_backing);
|
||||
// We need the backing and import_backing to create a vm::Ctx, but we need
|
||||
// a vm::Ctx to create a backing and an import_backing. The solution is to create an
|
||||
// uninitialized vm::Ctx and then initialize it in-place.
|
||||
let mut vmctx = unsafe { Box::new(mem::uninitialized()) };
|
||||
|
||||
let start_func = module.start_func;
|
||||
let import_backing = ImportBacking::new(&module, &*imports, &mut *vmctx)?;
|
||||
let backing = LocalBacking::new(&module, &import_backing, &mut *vmctx);
|
||||
|
||||
// When Pin is stablized, this will use `Box::pinned` instead of `Box::new`.
|
||||
let mut instance = Box::new(Instance {
|
||||
backing,
|
||||
import_backing,
|
||||
module,
|
||||
backing,
|
||||
imports,
|
||||
import_backing,
|
||||
vmctx,
|
||||
});
|
||||
|
||||
if let Some(start_index) = start_func {
|
||||
// Initialize the vm::Ctx in-place after the import_backing
|
||||
// has been boxed.
|
||||
*instance.vmctx = vm::Ctx::new(&mut instance.backing, &mut instance.import_backing);
|
||||
|
||||
if let Some(start_index) = instance.module.start_func {
|
||||
instance.call_with_index(start_index, &[])?;
|
||||
}
|
||||
|
||||
@ -48,15 +62,17 @@ impl Instance {
|
||||
/// This will eventually return `Result<Option<Vec<Value>>, String>` in
|
||||
/// order to support multi-value returns.
|
||||
pub fn call(&mut self, name: &str, args: &[Value]) -> Result<Option<Value>, String> {
|
||||
let func_index = *self
|
||||
let export_index = self
|
||||
.module
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| "there is no export with that name".to_string())
|
||||
.and_then(|export| match export {
|
||||
Export::Func(func_index) => Ok(func_index),
|
||||
_ => Err("that export is not a function".to_string()),
|
||||
})?;
|
||||
.ok_or_else(|| format!("there is no export with that name: {}", name))?;
|
||||
|
||||
let func_index = if let ExportIndex::Func(func_index) = export_index {
|
||||
*func_index
|
||||
} else {
|
||||
return Err("that export is not a function".to_string());
|
||||
};
|
||||
|
||||
self.call_with_index(func_index, args)
|
||||
}
|
||||
@ -66,31 +82,25 @@ impl Instance {
|
||||
func_index: FuncIndex,
|
||||
args: &[Value],
|
||||
) -> Result<Option<Value>, String> {
|
||||
// Check the function signature.
|
||||
let sig_index = *self
|
||||
.module
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
|
||||
{
|
||||
let signature = self.module.sig_registry.lookup_func_sig(sig_index);
|
||||
let (func_ref, ctx, signature) = self.get_func_from_index(func_index);
|
||||
|
||||
assert!(
|
||||
signature.returns.len() <= 1,
|
||||
"multi-value returns not yet supported"
|
||||
);
|
||||
println!("func_ref: {:?}", func_ref);
|
||||
|
||||
if !signature.check_sig(args) {
|
||||
return Err("incorrect signature".to_string());
|
||||
}
|
||||
let func_ptr = CodePtr::from_ptr(func_ref.inner() as _);
|
||||
let vmctx_ptr = match ctx {
|
||||
Context::External(vmctx) => vmctx,
|
||||
Context::Internal => &mut *self.vmctx,
|
||||
};
|
||||
|
||||
assert!(
|
||||
signature.returns.len() <= 1,
|
||||
"multi-value returns not yet supported"
|
||||
);
|
||||
|
||||
if !signature.check_sig(args) {
|
||||
return Err("incorrect signature".to_string());
|
||||
}
|
||||
|
||||
// the vmctx will be located at the same place on the stack the entire time that this
|
||||
// wasm function is running.
|
||||
let mut vmctx = vm::Ctx::new(&mut self.backing, &mut self.import_backing);
|
||||
let vmctx_ptr = &mut vmctx as *mut vm::Ctx;
|
||||
|
||||
let libffi_args: Vec<_> = args
|
||||
.iter()
|
||||
.map(|val| match val {
|
||||
@ -102,22 +112,8 @@ impl Instance {
|
||||
.chain(iter::once(libffi_arg(&vmctx_ptr)))
|
||||
.collect();
|
||||
|
||||
let func_ptr = CodePtr::from_ptr(if self.module.is_imported_function(func_index) {
|
||||
let imported_func = &self.import_backing.functions[func_index.index()];
|
||||
imported_func.func as *const _
|
||||
} else {
|
||||
self.module
|
||||
.func_resolver
|
||||
.get(&self.module, func_index)
|
||||
.expect("broken invariant, func resolver not synced with module.exports")
|
||||
.cast()
|
||||
.as_ptr()
|
||||
});
|
||||
|
||||
call_protected(|| {
|
||||
self.module
|
||||
.sig_registry
|
||||
.lookup_func_sig(sig_index)
|
||||
signature
|
||||
.returns
|
||||
.first()
|
||||
.map(|ty| match ty {
|
||||
@ -135,9 +131,75 @@ impl Instance {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_export(&self, name: &str) -> Result<Export, String> {
|
||||
let export_index = self
|
||||
.module
|
||||
.exports
|
||||
.get(name)
|
||||
.ok_or_else(|| format!("there is no export with that name: {}", name))?;
|
||||
|
||||
Ok(self.get_export_from_index(export_index))
|
||||
}
|
||||
|
||||
pub fn exports(&self) -> ExportIter {
|
||||
ExportIter::new(self)
|
||||
}
|
||||
|
||||
fn get_export_from_index(&self, export_index: &ExportIndex) -> Export {
|
||||
match export_index {
|
||||
ExportIndex::Func(func_index) => {
|
||||
let (func, ctx, signature) = self.get_func_from_index(*func_index);
|
||||
|
||||
Export::Function {
|
||||
func,
|
||||
ctx: match ctx {
|
||||
Context::Internal => {
|
||||
Context::External(&*self.vmctx as *const vm::Ctx as *mut vm::Ctx)
|
||||
}
|
||||
ctx @ Context::External(_) => ctx,
|
||||
},
|
||||
signature,
|
||||
}
|
||||
}
|
||||
ExportIndex::Memory(_memory_index) => unimplemented!(),
|
||||
ExportIndex::Global(_global_index) => unimplemented!(),
|
||||
ExportIndex::Table(_table_index) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_func_from_index(&self, func_index: FuncIndex) -> (FuncRef, Context, FuncSig) {
|
||||
let sig_index = *self
|
||||
.module
|
||||
.func_assoc
|
||||
.get(func_index)
|
||||
.expect("broken invariant, incorrect func index");
|
||||
|
||||
let (func_ptr, ctx) = if self.module.is_imported_function(func_index) {
|
||||
let imported_func = &self.import_backing.functions[func_index.index()];
|
||||
(
|
||||
imported_func.func as *const _,
|
||||
Context::External(imported_func.vmctx),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
self.module
|
||||
.func_resolver
|
||||
.get(&self.module, func_index)
|
||||
.expect("broken invariant, func resolver not synced with module.exports")
|
||||
.cast()
|
||||
.as_ptr() as *const _,
|
||||
Context::Internal,
|
||||
)
|
||||
};
|
||||
|
||||
let signature = self.module.sig_registry.lookup_func_sig(sig_index).clone();
|
||||
|
||||
(FuncRef(func_ptr), ctx, signature)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FuncRef(*const vm::Func);
|
||||
|
||||
impl FuncRef {
|
||||
@ -153,47 +215,31 @@ impl FuncRef {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Import {
|
||||
Func(FuncRef, FuncSig),
|
||||
Table(Arc<TableBacking>, Table),
|
||||
Memory(Arc<LinearMemory>, Memory),
|
||||
Global(Value),
|
||||
pub struct ExportIter<'a> {
|
||||
instance: &'a Instance,
|
||||
iter: hash_map::Iter<'a, String, ExportIndex>,
|
||||
}
|
||||
|
||||
pub struct Imports {
|
||||
map: HashMap<String, HashMap<String, Import>>,
|
||||
}
|
||||
|
||||
impl Imports {
|
||||
pub fn new() -> Self {
|
||||
impl<'a> ExportIter<'a> {
|
||||
fn new(instance: &'a Instance) -> Self {
|
||||
Self {
|
||||
map: HashMap::new(),
|
||||
instance,
|
||||
iter: instance.module.exports.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, module: impl Into<String>, name: impl Into<String>, import: Import) {
|
||||
self.map
|
||||
.entry(module.into())
|
||||
.or_insert_with(|| HashMap::new())
|
||||
.insert(name.into(), import);
|
||||
}
|
||||
|
||||
pub fn get(&self, module: &str, name: &str) -> Option<&Import> {
|
||||
self.map.get(module).and_then(|m| m.get(name))
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportResolver for Imports {
|
||||
fn get(&self, module: &str, name: &str) -> Option<&Import> {
|
||||
self.get(module, name)
|
||||
impl<'a> Iterator for ExportIter<'a> {
|
||||
type Item = (String, Export);
|
||||
fn next(&mut self) -> Option<(String, Export)> {
|
||||
let (name, export_index) = self.iter.next()?;
|
||||
Some((
|
||||
name.clone(),
|
||||
self.instance.get_export_from_index(export_index),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ImportResolver {
|
||||
fn get(&self, module: &str, name: &str) -> Option<&Import>;
|
||||
}
|
||||
|
||||
// TODO Remove this later, only needed for compilation till emscripten is updated
|
||||
impl Instance {
|
||||
pub fn memory_offset_addr(&self, _index: usize, _offset: usize) -> *const usize {
|
||||
|
@ -4,21 +4,23 @@ extern crate field_offset;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod backend;
|
||||
mod backing;
|
||||
pub mod export;
|
||||
pub mod import;
|
||||
mod instance;
|
||||
mod memory;
|
||||
mod recovery;
|
||||
mod sighandler;
|
||||
mod sig_registry;
|
||||
mod mmap;
|
||||
pub mod module;
|
||||
pub mod backend;
|
||||
mod recovery;
|
||||
mod sig_registry;
|
||||
mod sighandler;
|
||||
pub mod table;
|
||||
pub mod types;
|
||||
pub mod vm;
|
||||
pub mod vmcalls;
|
||||
|
||||
pub use self::instance::{Import, ImportResolver, Imports, FuncRef, Instance};
|
||||
pub use self::instance::{FuncRef, Instance};
|
||||
pub use self::memory::LinearMemory;
|
||||
|
||||
/// Compile a webassembly module using the provided compiler.
|
||||
|
@ -52,21 +52,22 @@ impl LinearMemory {
|
||||
assert!(mem.max.is_none() || mem.max.unwrap() <= Self::MAX_PAGES);
|
||||
debug!("Instantiate LinearMemory(mem: {:?})", mem);
|
||||
|
||||
let (mmap_size, initial_pages, offset_guard_size, requires_signal_catch) =
|
||||
if /*mem.is_static_heap()*/ true {
|
||||
(Self::DEFAULT_SIZE, mem.min, Self::DEFAULT_GUARD_SIZE, true)
|
||||
// This is a static heap
|
||||
} else {
|
||||
// this is a dynamic heap
|
||||
assert!(!mem.shared, "shared memories must have a maximum size.");
|
||||
let (mmap_size, initial_pages, offset_guard_size, requires_signal_catch) = if
|
||||
/*mem.is_static_heap()*/
|
||||
true {
|
||||
(Self::DEFAULT_SIZE, mem.min, Self::DEFAULT_GUARD_SIZE, true)
|
||||
// This is a static heap
|
||||
} else {
|
||||
// this is a dynamic heap
|
||||
assert!(!mem.shared, "shared memories must have a maximum size.");
|
||||
|
||||
(
|
||||
mem.min as usize * Self::PAGE_SIZE as usize,
|
||||
mem.min,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
};
|
||||
(
|
||||
mem.min as usize * Self::PAGE_SIZE as usize,
|
||||
mem.min,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
let mut mmap = Mmap::with_size(mmap_size).unwrap();
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
use crate::{
|
||||
backend::FuncResolver,
|
||||
import::ImportResolver,
|
||||
sig_registry::SigRegistry,
|
||||
types::{
|
||||
FuncIndex, Global, GlobalDesc, GlobalIndex, Map, MapIndex, Memory, MemoryIndex, SigIndex,
|
||||
Table, TableIndex,
|
||||
},
|
||||
ImportResolver, Instance,
|
||||
Instance,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// This is used to instantiate a new webassembly module.
|
||||
pub struct ModuleInner {
|
||||
@ -23,7 +24,7 @@ pub struct ModuleInner {
|
||||
pub imported_tables: Map<TableIndex, (ImportName, Table)>,
|
||||
pub imported_globals: Map<GlobalIndex, (ImportName, GlobalDesc)>,
|
||||
|
||||
pub exports: HashMap<String, Export>,
|
||||
pub exports: HashMap<String, ExportIndex>,
|
||||
|
||||
pub data_initializers: Vec<DataInitializer>,
|
||||
pub table_initializers: Vec<TableInitializer>,
|
||||
@ -33,17 +34,17 @@ pub struct ModuleInner {
|
||||
pub sig_registry: SigRegistry,
|
||||
}
|
||||
|
||||
pub struct Module(Arc<ModuleInner>);
|
||||
pub struct Module(Rc<ModuleInner>);
|
||||
|
||||
impl Module {
|
||||
#[inline]
|
||||
pub fn new(inner: ModuleInner) -> Self {
|
||||
Module(Arc::new(inner))
|
||||
Module(Rc::new(inner))
|
||||
}
|
||||
|
||||
/// Instantiate a webassembly module with the provided imports.
|
||||
pub fn instantiate(&self, imports: &dyn ImportResolver) -> Result<Box<Instance>, String> {
|
||||
Instance::new(Module(Arc::clone(&self.0)), imports)
|
||||
pub fn instantiate(&self, imports: Rc<dyn ImportResolver>) -> Result<Box<Instance>, String> {
|
||||
Instance::new(Module(Rc::clone(&self.0)), imports)
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,21 +64,21 @@ impl Deref for Module {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImportName {
|
||||
pub module: String,
|
||||
pub namespace: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<(String, String)> for ImportName {
|
||||
fn from(n: (String, String)) -> Self {
|
||||
ImportName {
|
||||
module: n.0,
|
||||
namespace: n.0,
|
||||
name: n.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Export {
|
||||
pub enum ExportIndex {
|
||||
Func(FuncIndex),
|
||||
Memory(MemoryIndex),
|
||||
Global(GlobalIndex),
|
||||
|
@ -1,6 +1,4 @@
|
||||
use crate::{
|
||||
types::{FuncSig, Map, SigIndex},
|
||||
};
|
||||
use crate::types::{FuncSig, Map, SigIndex};
|
||||
use hashbrown::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1,9 +1,8 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::{
|
||||
iter,
|
||||
iter, mem,
|
||||
ops::{Index, IndexMut},
|
||||
slice,
|
||||
mem,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -354,7 +353,7 @@ impl<T> MonoVec<T> {
|
||||
0 | 1 => Self::new(),
|
||||
_ => Self {
|
||||
inner: MonoVecInner::Heap(Vec::with_capacity(capacity)),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,16 +361,12 @@ impl<T> MonoVec<T> {
|
||||
let uninit = unsafe { mem::uninitialized() };
|
||||
let prev = mem::replace(&mut self.inner, uninit);
|
||||
let next = match prev {
|
||||
MonoVecInner::None => {
|
||||
MonoVecInner::Inline(item)
|
||||
},
|
||||
MonoVecInner::Inline(previous_item) => {
|
||||
MonoVecInner::Heap(vec![previous_item, item])
|
||||
},
|
||||
MonoVecInner::None => MonoVecInner::Inline(item),
|
||||
MonoVecInner::Inline(previous_item) => MonoVecInner::Heap(vec![previous_item, item]),
|
||||
MonoVecInner::Heap(mut v) => {
|
||||
v.push(item);
|
||||
MonoVecInner::Heap(v)
|
||||
},
|
||||
}
|
||||
};
|
||||
let uninit = mem::replace(&mut self.inner, next);
|
||||
mem::forget(uninit);
|
||||
@ -379,51 +374,35 @@ impl<T> MonoVec<T> {
|
||||
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
match self.inner {
|
||||
MonoVecInner::None => {
|
||||
None
|
||||
},
|
||||
MonoVecInner::None => None,
|
||||
MonoVecInner::Inline(ref mut item) => {
|
||||
let uninit = unsafe { mem::uninitialized() };
|
||||
let item = mem::replace(item, uninit);
|
||||
let uninit = mem::replace(&mut self.inner, MonoVecInner::None);
|
||||
mem::forget(uninit);
|
||||
Some(item)
|
||||
},
|
||||
MonoVecInner::Heap(ref mut v) => {
|
||||
v.pop()
|
||||
},
|
||||
}
|
||||
MonoVecInner::Heap(ref mut v) => v.pop(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
match self.inner {
|
||||
MonoVecInner::None => {
|
||||
unsafe {
|
||||
slice::from_raw_parts(mem::align_of::<T>() as *const T, 0)
|
||||
}
|
||||
},
|
||||
MonoVecInner::Inline(ref item) => {
|
||||
slice::from_ref(item)
|
||||
},
|
||||
MonoVecInner::Heap(ref v) => {
|
||||
&v[..]
|
||||
MonoVecInner::None => unsafe {
|
||||
slice::from_raw_parts(mem::align_of::<T>() as *const T, 0)
|
||||
},
|
||||
MonoVecInner::Inline(ref item) => slice::from_ref(item),
|
||||
MonoVecInner::Heap(ref v) => &v[..],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice_mut(&mut self) -> &mut [T] {
|
||||
match self.inner {
|
||||
MonoVecInner::None => {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(mem::align_of::<T>() as *mut T, 0)
|
||||
}
|
||||
},
|
||||
MonoVecInner::Inline(ref mut item) => {
|
||||
slice::from_mut(item)
|
||||
},
|
||||
MonoVecInner::Heap(ref mut v) => {
|
||||
&mut v[..]
|
||||
MonoVecInner::None => unsafe {
|
||||
slice::from_raw_parts_mut(mem::align_of::<T>() as *mut T, 0)
|
||||
},
|
||||
MonoVecInner::Inline(ref mut item) => slice::from_mut(item),
|
||||
MonoVecInner::Heap(ref mut v) => &mut v[..],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl Ctx {
|
||||
imported_tables: import_backing.tables.as_mut_ptr(),
|
||||
imported_globals: import_backing.globals.as_mut_ptr(),
|
||||
imported_funcs: import_backing.functions.as_mut_ptr(),
|
||||
|
||||
|
||||
local_backing,
|
||||
}
|
||||
}
|
||||
@ -88,6 +88,7 @@ pub enum Func {}
|
||||
#[repr(C)]
|
||||
pub struct ImportedFunc {
|
||||
pub func: *const Func,
|
||||
pub vmctx: *mut Ctx,
|
||||
}
|
||||
|
||||
impl ImportedFunc {
|
||||
@ -95,6 +96,10 @@ impl ImportedFunc {
|
||||
0 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
pub fn offset_vmctx() -> u8 {
|
||||
1 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
pub fn size() -> u8 {
|
||||
mem::size_of::<Self>() as u8
|
||||
}
|
||||
@ -164,12 +169,17 @@ impl LocalMemory {
|
||||
pub struct ImportedMemory {
|
||||
/// A pointer to the memory definition.
|
||||
pub memory: *mut LocalMemory,
|
||||
pub vmctx: *mut Ctx,
|
||||
}
|
||||
|
||||
impl ImportedMemory {
|
||||
pub fn offset_memory() -> u8 {
|
||||
0 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
pub fn offset_vmctx() -> u8 {
|
||||
1 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
/// Definition of a global used by the VM.
|
||||
@ -192,11 +202,11 @@ impl LocalGlobal {
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct ImportedGlobal {
|
||||
pub global: LocalGlobal,
|
||||
pub global: *mut LocalGlobal,
|
||||
}
|
||||
|
||||
impl ImportedGlobal {
|
||||
pub fn offset_data() -> u8 {
|
||||
pub fn offset_global() -> u8 {
|
||||
0 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
}
|
||||
@ -216,7 +226,10 @@ pub struct Anyfunc {
|
||||
impl Anyfunc {
|
||||
pub fn null() -> Self {
|
||||
Self {
|
||||
func_data: ImportedFunc { func: ptr::null() },
|
||||
func_data: ImportedFunc {
|
||||
func: ptr::null(),
|
||||
vmctx: ptr::null_mut(),
|
||||
},
|
||||
sig_id: SigId(u32::max_value()),
|
||||
}
|
||||
}
|
||||
@ -225,12 +238,12 @@ impl Anyfunc {
|
||||
0 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
// pub fn offset_vmctx() -> u8 {
|
||||
// 1 * (mem::size_of::<usize>() as u8)
|
||||
// }
|
||||
pub fn offset_vmctx() -> u8 {
|
||||
1 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
pub fn offset_sig_id() -> u8 {
|
||||
1 * (mem::size_of::<usize>() as u8)
|
||||
2 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,10 +299,10 @@ mod vm_offset_tests {
|
||||
offset_of!(ImportedFunc => func).get_byte_offset(),
|
||||
);
|
||||
|
||||
// assert_eq!(
|
||||
// ImportedFunc::offset_vmctx() as usize,
|
||||
// offset_of!(ImportedFunc => vmctx).get_byte_offset(),
|
||||
// );
|
||||
assert_eq!(
|
||||
ImportedFunc::offset_vmctx() as usize,
|
||||
offset_of!(ImportedFunc => vmctx).get_byte_offset(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -337,6 +350,11 @@ mod vm_offset_tests {
|
||||
ImportedMemory::offset_memory() as usize,
|
||||
offset_of!(ImportedMemory => memory).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ImportedMemory::offset_vmctx() as usize,
|
||||
offset_of!(ImportedMemory => vmctx).get_byte_offset(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -350,8 +368,8 @@ mod vm_offset_tests {
|
||||
#[test]
|
||||
fn imported_global() {
|
||||
assert_eq!(
|
||||
ImportedGlobal::offset_data() as usize,
|
||||
offset_of!(ImportedGlobal => global: LocalGlobal => data).get_byte_offset(),
|
||||
ImportedGlobal::offset_global() as usize,
|
||||
offset_of!(ImportedGlobal => global).get_byte_offset(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -362,10 +380,10 @@ mod vm_offset_tests {
|
||||
offset_of!(Anyfunc => func_data: ImportedFunc => func).get_byte_offset(),
|
||||
);
|
||||
|
||||
// assert_eq!(
|
||||
// Anyfunc::offset_vmctx() as usize,
|
||||
// offset_of!(Anyfunc => func_data: ImportedFunc => vmctx).get_byte_offset(),
|
||||
// );
|
||||
assert_eq!(
|
||||
Anyfunc::offset_vmctx() as usize,
|
||||
offset_of!(Anyfunc => func_data: ImportedFunc => vmctx).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Anyfunc::offset_sig_id() as usize,
|
||||
|
@ -25,7 +25,8 @@ pub unsafe extern "C" fn memory_grow_dynamic(
|
||||
by_pages: u32,
|
||||
ctx: *mut vm::Ctx,
|
||||
) -> i32 {
|
||||
if let Some(old) = (*(*ctx).local_backing).memories[memory_index as usize].grow_dynamic(by_pages)
|
||||
if let Some(old) =
|
||||
(*(*ctx).local_backing).memories[memory_index as usize].grow_dynamic(by_pages)
|
||||
{
|
||||
// Store the new size back into the vmctx.
|
||||
(*(*ctx).memories.add(memory_index as usize)).size =
|
||||
@ -34,4 +35,4 @@ pub unsafe extern "C" fn memory_grow_dynamic(
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user