Rework imports

This commit is contained in:
Lachlan Sneff
2019-01-12 22:02:19 -05:00
parent f5c5f777c0
commit a7ffb44bbc
73 changed files with 477 additions and 496 deletions

View File

@ -1,4 +1,4 @@
use crate::{module::Module, types::FuncIndex, vm};
use crate::{module::ModuleInner, types::FuncIndex, vm};
use std::ptr::NonNull;
pub use crate::mmap::{Mmap, Protect};
@ -6,9 +6,9 @@ pub use crate::sig_registry::SigRegistry;
pub trait Compiler {
/// Compiles a `Module` from WebAssembly binary format
fn compile(&self, wasm: &[u8]) -> Result<Module, String>;
fn compile(&self, wasm: &[u8]) -> Result<ModuleInner, String>;
}
pub trait FuncResolver {
fn get(&self, module: &Module, index: FuncIndex) -> Option<NonNull<vm::Func>>;
fn get(&self, module: &ModuleInner, index: FuncIndex) -> Option<NonNull<vm::Func>>;
}

View File

@ -1,8 +1,8 @@
use crate::{
export::{Context, Export},
import::ImportResolver,
import::Imports,
memory::LinearMemory,
module::{ImportName, Module},
module::{ImportName, ModuleInner},
table::{TableBacking, TableElements},
types::{Initializer, MapIndex, Value},
vm,
@ -10,16 +10,26 @@ use crate::{
#[derive(Debug)]
pub struct LocalBacking {
pub memories: Box<[LinearMemory]>,
pub tables: Box<[TableBacking]>,
pub(crate) memories: Box<[LinearMemory]>,
pub(crate) tables: Box<[TableBacking]>,
pub vm_memories: Box<[vm::LocalMemory]>,
pub vm_tables: Box<[vm::LocalTable]>,
pub vm_globals: Box<[vm::LocalGlobal]>,
pub(crate) vm_memories: Box<[vm::LocalMemory]>,
pub(crate) vm_tables: Box<[vm::LocalTable]>,
pub(crate) vm_globals: Box<[vm::LocalGlobal]>,
}
impl LocalBacking {
pub fn new(module: &Module, imports: &ImportBacking, vmctx: *mut vm::Ctx) -> Self {
pub fn memory(&mut self, index: u32) -> &mut LinearMemory {
&mut self.memories[index as usize]
}
pub fn table(&mut self, index: u32) -> &mut TableBacking {
&mut self.tables[index as usize]
}
}
impl LocalBacking {
pub(crate) fn new(module: &ModuleInner, 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);
@ -38,7 +48,7 @@ impl LocalBacking {
}
}
fn generate_memories(module: &Module) -> Box<[LinearMemory]> {
fn generate_memories(module: &ModuleInner) -> Box<[LinearMemory]> {
let mut memories = Vec::with_capacity(module.memories.len());
for (_, mem) in &module.memories {
@ -59,7 +69,10 @@ impl LocalBacking {
memories.into_boxed_slice()
}
fn finalize_memories(module: &Module, memories: &mut [LinearMemory]) -> Box<[vm::LocalMemory]> {
fn finalize_memories(
module: &ModuleInner,
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());
@ -82,7 +95,7 @@ impl LocalBacking {
.into_boxed_slice()
}
fn generate_tables(module: &Module) -> Box<[TableBacking]> {
fn generate_tables(module: &ModuleInner) -> Box<[TableBacking]> {
let mut tables = Vec::with_capacity(module.tables.len());
for (_, table) in &module.tables {
@ -94,7 +107,7 @@ impl LocalBacking {
}
fn finalize_tables(
module: &Module,
module: &ModuleInner,
imports: &ImportBacking,
tables: &mut [TableBacking],
vmctx: *mut vm::Ctx,
@ -134,14 +147,14 @@ impl LocalBacking {
.into_boxed_slice()
}
fn generate_globals(module: &Module) -> Box<[vm::LocalGlobal]> {
fn generate_globals(module: &ModuleInner) -> Box<[vm::LocalGlobal]> {
let globals = vec![vm::LocalGlobal::null(); module.globals.len()];
globals.into_boxed_slice()
}
fn finalize_globals(
module: &Module,
module: &ModuleInner,
imports: &ImportBacking,
mut globals: Box<[vm::LocalGlobal]>,
) -> Box<[vm::LocalGlobal]> {
@ -171,8 +184,8 @@ pub struct ImportBacking {
impl ImportBacking {
pub fn new(
module: &Module,
imports: &dyn ImportResolver,
module: &ModuleInner,
imports: &Imports,
vmctx: *mut vm::Ctx,
) -> Result<Self, String> {
assert!(
@ -190,15 +203,17 @@ impl ImportBacking {
}
fn import_memories(
module: &Module,
imports: &dyn ImportResolver,
module: &ModuleInner,
imports: &Imports,
vmctx: *mut vm::Ctx,
) -> Result<Box<[vm::ImportedMemory]>, String> {
let mut memories = Vec::with_capacity(module.imported_memories.len());
for (_index, (ImportName { namespace, name }, expected_memory_desc)) in
&module.imported_memories
{
let memory_import = imports.get(namespace, name);
let memory_import = imports
.get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name));
match memory_import {
Some(Export::Memory {
local,
@ -232,15 +247,17 @@ fn import_memories(
}
fn import_functions(
module: &Module,
imports: &dyn ImportResolver,
module: &ModuleInner,
imports: &Imports,
vmctx: *mut vm::Ctx,
) -> Result<Box<[vm::ImportedFunc]>, String> {
let mut functions = Vec::with_capacity(module.imported_functions.len());
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(namespace, name);
let import = imports
.get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name));
match import {
Some(Export::Function {
func,
@ -274,12 +291,14 @@ fn import_functions(
}
fn import_globals(
module: &Module,
imports: &dyn ImportResolver,
module: &ModuleInner,
imports: &Imports,
) -> Result<Box<[vm::ImportedGlobal]>, String> {
let mut globals = Vec::with_capacity(module.imported_globals.len());
for (_, (ImportName { namespace, name }, global_desc)) in &module.imported_globals {
let import = imports.get(namespace, name);
let import = imports
.get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name));
match import {
Some(Export::Global { local, global }) => {
if &global == global_desc {

View File

@ -1,10 +1,6 @@
use crate::export::Export;
use hashbrown::{hash_map::Entry, HashMap};
pub trait ImportResolver {
fn get(&self, namespace: &str, name: &str) -> Option<Export>;
}
pub trait Namespace {
fn get_export(&self, name: &str) -> Option<Export>;
}
@ -31,11 +27,11 @@ impl Imports {
}
}
pub fn register(
&mut self,
name: impl Into<String>,
namespace: impl Namespace + 'static,
) -> Option<Box<dyn Namespace>> {
pub fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn Namespace>>
where
S: Into<String>,
N: Namespace + 'static,
{
match self.map.entry(name.into()) {
Entry::Vacant(empty) => {
empty.insert(Box::new(namespace));
@ -45,36 +41,7 @@ impl Imports {
}
}
// 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_name: &str, name: &str) -> Option<Export> {
let namespace = self.map.get(namespace_name)?;
namespace.get_export(name)
pub fn get_namespace(&self, namespace: &str) -> Option<&dyn Namespace> {
self.map.get(namespace).map(|namespace| &**namespace)
}
}

View File

@ -2,8 +2,8 @@ use crate::recovery::call_protected;
use crate::{
backing::{ImportBacking, LocalBacking},
export::{Context, Export, ExportIter, FuncPointer, MemoryPointer},
import::{ImportResolver, Namespace},
module::{ExportIndex, Module},
import::{Imports, Namespace},
module::{ExportIndex, Module, ModuleInner},
types::{FuncIndex, FuncSig, MapIndex, Memory, MemoryIndex, Type, Value},
vm,
};
@ -14,19 +14,17 @@ use std::{iter, mem};
struct InstanceInner {
#[allow(dead_code)]
pub(crate) backing: LocalBacking,
#[allow(dead_code)]
imports: Rc<dyn ImportResolver>,
import_backing: ImportBacking,
vmctx: Box<vm::Ctx>,
}
pub struct Instance {
pub module: Module,
pub(crate) module: Rc<ModuleInner>,
inner: Box<InstanceInner>,
}
impl Instance {
pub(crate) fn new(module: Module, imports: Rc<dyn ImportResolver>) -> Result<Instance, String> {
pub(crate) fn new(module: Rc<ModuleInner>, imports: &Imports) -> Result<Instance, String> {
// 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.
@ -38,16 +36,13 @@ impl Instance {
// When Pin is stablized, this will use `Box::pinned` instead of `Box::new`.
let mut inner = Box::new(InstanceInner {
backing,
imports,
import_backing,
vmctx,
});
// Initialize the vm::Ctx in-place after the backing
// has been boxed.
*inner.vmctx = unsafe {
vm::Ctx::new(&mut inner.backing, &mut inner.import_backing)
};
*inner.vmctx = unsafe { vm::Ctx::new(&mut inner.backing, &mut inner.import_backing) };
let mut instance = Instance { module, inner };
@ -83,6 +78,10 @@ impl Instance {
pub fn exports(&self) -> ExportIter {
ExportIter::new(self)
}
pub fn module(&self) -> Module {
Module::new(Rc::clone(&self.module))
}
}
impl Instance {

View File

@ -4,6 +4,7 @@ extern crate field_offset;
#[macro_use]
mod macros;
#[doc(hidden)]
pub mod backend;
mod backing;
pub mod export;
@ -18,11 +19,17 @@ mod sighandler;
pub mod table;
pub mod types;
pub mod vm;
#[doc(hidden)]
pub mod vmcalls;
pub use self::instance::Instance;
#[doc(inline)]
pub use self::module::Module;
use std::rc::Rc;
/// Compile a webassembly module using the provided compiler.
pub fn compile(wasm: &[u8], compiler: &dyn backend::Compiler) -> Result<module::Module, String> {
compiler.compile(wasm)
compiler
.compile(wasm)
.map(|inner| module::Module::new(Rc::new(inner)))
}

View File

@ -1,8 +1,3 @@
//! The webassembly::Memory() constructor creates a new Memory object which is
//! a structure that holds the raw bytes of memory accessed by a
//! webassembly::Instance.
//! A memory created by Rust or in WebAssembly code will be accessible and
//! mutable from both Rust and WebAssembly.
use std::ops::{Deref, DerefMut};
use crate::{
@ -40,7 +35,9 @@ pub struct LinearMemory {
impl LinearMemory {
pub(crate) const PAGE_SIZE: u32 = 65_536;
pub(crate) const MAX_PAGES: u32 = 65_536;
#[doc(hidden)]
pub const DEFAULT_HEAP_SIZE: usize = 1 << 32; // 4 GiB
#[doc(hidden)]
pub const DEFAULT_GUARD_SIZE: usize = 1 << 31; // 2 GiB
pub(crate) const DEFAULT_SIZE: usize = Self::DEFAULT_HEAP_SIZE + Self::DEFAULT_GUARD_SIZE; // 6 GiB

View File

@ -1,6 +1,6 @@
use crate::{
backend::FuncResolver,
import::ImportResolver,
import::Imports,
sig_registry::SigRegistry,
types::{
FuncIndex, Global, GlobalDesc, GlobalIndex, Map, MapIndex, Memory, MemoryIndex, SigIndex,
@ -9,10 +9,10 @@ use crate::{
Instance,
};
use hashbrown::HashMap;
use std::ops::Deref;
use std::rc::Rc;
/// This is used to instantiate a new webassembly module.
#[doc(hidden)]
pub struct ModuleInner {
pub func_resolver: Box<dyn FuncResolver>,
pub memories: Map<MemoryIndex, Memory>,
@ -37,14 +37,13 @@ pub struct ModuleInner {
pub struct Module(Rc<ModuleInner>);
impl Module {
#[inline]
pub fn new(inner: ModuleInner) -> Self {
Module(Rc::new(inner))
pub(crate) fn new(inner: Rc<ModuleInner>) -> Self {
Module(inner)
}
/// Instantiate a webassembly module with the provided imports.
pub fn instantiate(&self, imports: Rc<dyn ImportResolver>) -> Result<Instance, String> {
Instance::new(Module(Rc::clone(&self.0)), imports)
pub fn instantiate(&self, imports: &Imports) -> Result<Instance, String> {
Instance::new(Rc::clone(&self.0), imports)
}
}
@ -58,14 +57,7 @@ impl ModuleInner {
}
}
impl Deref for Module {
type Target = ModuleInner;
fn deref(&self) -> &ModuleInner {
&*self.0
}
}
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct ImportName {
pub namespace: String,

View File

@ -1,36 +1,40 @@
use crate::backing::{ImportBacking, LocalBacking};
use crate::backing::ImportBacking;
pub use crate::backing::LocalBacking;
use std::{mem, ptr};
#[derive(Debug)]
#[repr(C)]
pub struct Ctx {
/// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`.
pub memories: *mut LocalMemory,
pub(crate) memories: *mut LocalMemory,
/// A pointer to an array of locally-defined tables, indexed by `TableIndex`.
pub tables: *mut LocalTable,
pub(crate) tables: *mut LocalTable,
/// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`.
pub globals: *mut LocalGlobal,
pub(crate) globals: *mut LocalGlobal,
/// A pointer to an array of imported memories, indexed by `MemoryIndex,
pub imported_memories: *mut ImportedMemory,
pub(crate) imported_memories: *mut ImportedMemory,
/// A pointer to an array of imported tables, indexed by `TableIndex`.
pub imported_tables: *mut ImportedTable,
pub(crate) imported_tables: *mut ImportedTable,
/// A pointer to an array of imported globals, indexed by `GlobalIndex`.
pub imported_globals: *mut ImportedGlobal,
pub(crate) imported_globals: *mut ImportedGlobal,
/// A pointer to an array of imported functions, indexed by `FuncIndex`.
pub imported_funcs: *mut ImportedFunc,
pub(crate) imported_funcs: *mut ImportedFunc,
/// The parent instance.
pub local_backing: *mut LocalBacking,
}
impl Ctx {
pub unsafe fn new(local_backing: &mut LocalBacking, import_backing: &mut ImportBacking) -> Self {
pub unsafe fn new(
local_backing: &mut LocalBacking,
import_backing: &mut ImportBacking,
) -> Self {
Self {
memories: local_backing.vm_memories.as_mut_ptr(),
tables: local_backing.vm_tables.as_mut_ptr(),

View File

@ -5,7 +5,9 @@ pub unsafe extern "C" fn memory_grow_static(
by_pages: u32,
ctx: *mut vm::Ctx,
) -> i32 {
if let Some(old) = (*(*ctx).local_backing).memories[memory_index as usize].grow_static(by_pages)
if let Some(old) = (*(*ctx).local_backing)
.memory(memory_index)
.grow_static(by_pages)
{
// Store the new size back into the vmctx.
(*(*ctx).memories.add(memory_index as usize)).size =
@ -17,7 +19,7 @@ pub unsafe extern "C" fn memory_grow_static(
}
pub unsafe extern "C" fn memory_size(memory_index: u32, ctx: *mut vm::Ctx) -> u32 {
(*(*ctx).local_backing).memories[memory_index as usize].pages()
(*(*ctx).local_backing).memory(memory_index).pages()
}
pub unsafe extern "C" fn memory_grow_dynamic(
@ -25,8 +27,9 @@ 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)
.memory(memory_index)
.grow_dynamic(by_pages)
{
// Store the new size back into the vmctx.
(*(*ctx).memories.add(memory_index as usize)).size =