Remove wasmer-{artifact,engine} and merge it into wasmer-compiler and wasmer-vm

This commit is contained in:
Manos Pitsidianakis
2022-06-13 12:39:45 +03:00
parent 8f539d134b
commit a6173f9746
61 changed files with 136 additions and 776 deletions

View File

@@ -22,6 +22,15 @@ serde_bytes = { version = "0.11", optional = true }
smallvec = "1.6"
rkyv = { version = "0.7.38", features = ["indexmap"] }
backtrace = "0.3"
rustc-demangle = "0.1"
memmap2 = "0.5"
more-asserts = "0.2"
lazy_static = "1.4"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
wasmer-vm = { path = "../vm", version = "=2.3.0" }
[features]
default = ["std", "enable-serde" ]
# This feature is for compiler implementors, it enables using `Compiler` and

View File

@@ -0,0 +1,166 @@
//! Generic Artifact abstraction for Wasmer Engines.
use crate::{CpuFeature, Features};
use enumset::EnumSet;
use std::any::Any;
use std::convert::TryInto;
use std::path::Path;
use std::sync::Arc;
use std::{fs, mem};
use wasmer_types::entity::PrimaryMap;
use wasmer_types::{DeserializeError, SerializeError};
use wasmer_types::{
MemoryIndex, MemoryStyle, ModuleInfo, OwnedDataInitializer, TableIndex, TableStyle,
};
/// An `Artifact` is the product that the `Engine`
/// implementation produce and use.
///
/// The `Artifact` contains the compiled data for a given
/// module as well as extra information needed to run the
/// module at runtime, such as [`ModuleInfo`] and [`Features`].
pub trait ArtifactCreate: Send + Sync + Upcastable {
/// Return a reference-counted pointer to the module
fn module(&self) -> Arc<ModuleInfo>;
/// Return a pointer to a module.
fn module_ref(&self) -> &ModuleInfo;
/// Gets a mutable reference to the info.
///
/// Note: this will return `None` if the module is already instantiated.
fn module_mut(&mut self) -> Option<&mut ModuleInfo>;
/// Returns the features for this Artifact
fn features(&self) -> &Features;
/// Returns the CPU features for this Artifact
fn cpu_features(&self) -> EnumSet<CpuFeature>;
/// Returns the memory styles associated with this `Artifact`.
fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle>;
/// Returns the table plans associated with this `Artifact`.
fn table_styles(&self) -> &PrimaryMap<TableIndex, TableStyle>;
/// Returns data initializers to pass to `InstanceHandle::initialize`
fn data_initializers(&self) -> &[OwnedDataInitializer];
/// Serializes an artifact into bytes
fn serialize(&self) -> Result<Vec<u8>, SerializeError>;
/// Serializes an artifact into a file path
fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> {
let serialized = self.serialize()?;
fs::write(&path, serialized)?;
Ok(())
}
}
// Implementation of `Upcastable` taken from https://users.rust-lang.org/t/why-does-downcasting-not-work-for-subtraits/33286/7 .
/// Trait needed to get downcasting of `Engine`s to work.
pub trait Upcastable {
/// upcast ref
fn upcast_any_ref(&'_ self) -> &'_ dyn Any;
/// upcast mut ref
fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any;
/// upcast boxed dyn
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
}
impl<T: Any + Send + Sync + 'static> Upcastable for T {
#[inline]
fn upcast_any_ref(&'_ self) -> &'_ dyn Any {
self
}
#[inline]
fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any {
self
}
#[inline]
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
self
}
}
impl dyn ArtifactCreate + 'static {
/// Try to downcast the artifact into a given type.
#[inline]
pub fn downcast_ref<T: 'static>(&'_ self) -> Option<&'_ T> {
self.upcast_any_ref().downcast_ref::<T>()
}
/// Try to downcast the artifact into a given type mutably.
#[inline]
pub fn downcast_mut<T: 'static>(&'_ mut self) -> Option<&'_ mut T> {
self.upcast_any_mut().downcast_mut::<T>()
}
}
/// Metadata header which holds an ABI version and the length of the remaining
/// metadata.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct MetadataHeader {
magic: [u8; 8],
version: u32,
len: u32,
}
impl MetadataHeader {
/// Current ABI version. Increment this any time breaking changes are made
/// to the format of the serialized data.
const CURRENT_VERSION: u32 = 1;
/// Magic number to identify wasmer metadata.
const MAGIC: [u8; 8] = *b"WASMER\0\0";
/// Length of the metadata header.
pub const LEN: usize = 16;
/// Alignment of the metadata.
pub const ALIGN: usize = 16;
/// Creates a new header for metadata of the given length.
pub fn new(len: usize) -> Self {
Self {
magic: Self::MAGIC,
version: Self::CURRENT_VERSION,
len: len.try_into().expect("metadata exceeds maximum length"),
}
}
/// Convert the header into its bytes representation.
pub fn into_bytes(self) -> [u8; 16] {
unsafe { mem::transmute(self) }
}
/// Parses the header and returns the length of the metadata following it.
pub fn parse(bytes: &[u8]) -> Result<usize, DeserializeError> {
if bytes.as_ptr() as usize % 16 != 0 {
return Err(DeserializeError::CorruptedBinary(
"misaligned metadata".to_string(),
));
}
let bytes: [u8; 16] = bytes
.get(..16)
.ok_or_else(|| {
DeserializeError::CorruptedBinary("invalid metadata header".to_string())
})?
.try_into()
.unwrap();
let header: Self = unsafe { mem::transmute(bytes) };
if header.magic != Self::MAGIC {
return Err(DeserializeError::Incompatible(
"The provided bytes were not serialized by Wasmer".to_string(),
));
}
if header.version != Self::CURRENT_VERSION {
return Err(DeserializeError::Incompatible(
"The provided bytes were serialized by an incompatible version of Wasmer"
.to_string(),
));
}
Ok(header.len as usize)
}
}

View File

@@ -0,0 +1,162 @@
use crate::CpuFeature;
use crate::{resolve_imports, Export, InstantiationError, RuntimeError, Tunables};
use crate::{ArtifactCreate, Upcastable};
use std::any::Any;
use wasmer_types::entity::BoxedSlice;
use wasmer_types::{DataInitializer, FunctionIndex, LocalFunctionIndex, SignatureIndex};
use wasmer_vm::{
FuncDataRegistry, FunctionBodyPtr, InstanceAllocator, InstanceHandle, TrapHandler,
VMSharedSignatureIndex, VMTrampoline,
};
/// An `Artifact` is the product that the `Engine`
/// implementation produce and use.
///
/// An `Artifact` is the product that the `Engine`
/// implementation produce and use.
///
/// The `ArtifactRun` contains the extra information needed to run the
/// module at runtime, such as [`ModuleInfo`] and [`Features`].
pub trait Artifact: Send + Sync + Upcastable + ArtifactCreate {
/// Register thie `Artifact` stack frame information into the global scope.
///
/// This is required to ensure that any traps can be properly symbolicated.
fn register_frame_info(&self);
/// Returns the functions allocated in memory or this `Artifact`
/// ready to be run.
fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>;
/// Returns the function call trampolines allocated in memory of this
/// `Artifact`, ready to be run.
fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline>;
/// Returns the dynamic function trampolines allocated in memory
/// of this `Artifact`, ready to be run.
fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr>;
/// Returns the associated VM signatures for this `Artifact`.
fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex>;
/// Get the func data registry
fn func_data_registry(&self) -> &FuncDataRegistry;
/// Do preinstantiation logic that is executed before instantiating
fn preinstantiate(&self) -> Result<(), InstantiationError> {
Ok(())
}
/// Crate an `Instance` from this `Artifact`.
///
/// # Safety
///
/// See [`InstanceHandle::new`].
unsafe fn instantiate(
&self,
tunables: &dyn Tunables,
imports: &[Export],
host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> {
// Validate the CPU features this module was compiled with against the
// host CPU features.
let host_cpu_features = CpuFeature::for_host();
if !host_cpu_features.is_superset(self.cpu_features()) {
return Err(InstantiationError::CpuFeature(format!(
"{:?}",
self.cpu_features().difference(host_cpu_features)
)));
}
self.preinstantiate()?;
let module = self.module();
let (imports, import_function_envs) = {
let mut imports = resolve_imports(
&module,
imports,
self.finished_dynamic_function_trampolines(),
self.memory_styles(),
self.table_styles(),
)
.map_err(InstantiationError::Link)?;
// Get the `WasmerEnv::init_with_instance` function pointers and the pointers
// to the envs to call it on.
let import_function_envs = imports.get_imported_function_envs();
(imports, import_function_envs)
};
// Get pointers to where metadata about local memories should live in VM memory.
// Get pointers to where metadata about local tables should live in VM memory.
let (allocator, memory_definition_locations, table_definition_locations) =
InstanceAllocator::new(&*module);
let finished_memories = tunables
.create_memories(&module, self.memory_styles(), &memory_definition_locations)
.map_err(InstantiationError::Link)?
.into_boxed_slice();
let finished_tables = tunables
.create_tables(&module, self.table_styles(), &table_definition_locations)
.map_err(InstantiationError::Link)?
.into_boxed_slice();
let finished_globals = tunables
.create_globals(&module)
.map_err(InstantiationError::Link)?
.into_boxed_slice();
self.register_frame_info();
let handle = InstanceHandle::new(
allocator,
module,
self.finished_functions().clone(),
self.finished_function_call_trampolines().clone(),
finished_memories,
finished_tables,
finished_globals,
imports,
self.signatures().clone(),
host_state,
import_function_envs,
)
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))?;
Ok(handle)
}
/// Finishes the instantiation of a just created `InstanceHandle`.
///
/// # Safety
///
/// See [`InstanceHandle::finish_instantiation`].
unsafe fn finish_instantiation(
&self,
trap_handler: &(dyn TrapHandler + 'static),
handle: &InstanceHandle,
) -> Result<(), InstantiationError> {
let data_initializers = self
.data_initializers()
.iter()
.map(|init| DataInitializer {
location: init.location.clone(),
data: &*init.data,
})
.collect::<Vec<_>>();
handle
.finish_instantiation(trap_handler, &data_initializers)
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
}
}
impl dyn Artifact + 'static {
/// Try to downcast the artifact into a given type.
#[inline]
pub fn downcast_ref<T: 'static>(&'_ self) -> Option<&'_ T> {
self.upcast_any_ref().downcast_ref::<T>()
}
/// Try to downcast the artifact into a given type mutably.
#[inline]
pub fn downcast_mut<T: 'static>(&'_ mut self) -> Option<&'_ mut T> {
self.upcast_any_mut().downcast_mut::<T>()
}
}

View File

@@ -0,0 +1,49 @@
//! The WebAssembly possible errors
use crate::engine::trap::RuntimeError;
use thiserror::Error;
pub use wasmer_types::{DeserializeError, ImportError, SerializeError};
/// The WebAssembly.LinkError object indicates an error during
/// module instantiation (besides traps from the start function).
///
/// This is based on the [link error][link-error] API.
///
/// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError
#[derive(Error, Debug)]
#[error("Link error: {0}")]
pub enum LinkError {
/// An error occurred when checking the import types.
#[error("Error while importing {0:?}.{1:?}: {2}")]
Import(String, String, ImportError),
/// A trap ocurred during linking.
#[error("RuntimeError occurred during linking: {0}")]
Trap(#[source] RuntimeError),
/// Insufficient resources available for linking.
#[error("Insufficient resources: {0}")]
Resource(String),
}
/// An error while instantiating a module.
///
/// This is not a common WebAssembly error, however
/// we need to differentiate from a `LinkError` (an error
/// that happens while linking, on instantiation) and a
/// Trap that occurs when calling the WebAssembly module
/// start function.
#[derive(Error, Debug)]
pub enum InstantiationError {
/// A linking ocurred during instantiation.
#[error(transparent)]
Link(LinkError),
/// The module was compiled with a CPU feature that is not available on
/// the current host.
#[error("module compiled with CPU feature that is missing from host")]
CpuFeature(String),
/// A runtime error occured while invoking the start function
#[error(transparent)]
Start(RuntimeError),
}

View File

@@ -0,0 +1,166 @@
use std::sync::Arc;
use wasmer_vm::{ImportInitializerFuncPtr, VMExtern, VMFunction, VMGlobal, VMMemory, VMTable};
/// The value of an export passed from one instance to another.
#[derive(Debug, Clone)]
pub enum Export {
/// A function export value.
Function(ExportFunction),
/// A table export value.
Table(VMTable),
/// A memory export value.
Memory(VMMemory),
/// A global export value.
Global(VMGlobal),
}
impl From<Export> for VMExtern {
fn from(other: Export) -> Self {
match other {
Export::Function(ExportFunction { vm_function, .. }) => Self::Function(vm_function),
Export::Memory(vm_memory) => Self::Memory(vm_memory),
Export::Table(vm_table) => Self::Table(vm_table),
Export::Global(vm_global) => Self::Global(vm_global),
}
}
}
impl From<VMExtern> for Export {
fn from(other: VMExtern) -> Self {
match other {
VMExtern::Function(vm_function) => Self::Function(ExportFunction {
vm_function,
metadata: None,
}),
VMExtern::Memory(vm_memory) => Self::Memory(vm_memory),
VMExtern::Table(vm_table) => Self::Table(vm_table),
VMExtern::Global(vm_global) => Self::Global(vm_global),
}
}
}
/// Extra metadata about `ExportFunction`s.
///
/// The metadata acts as a kind of manual virtual dispatch. We store the
/// user-supplied `WasmerEnv` as a void pointer and have methods on it
/// that have been adapted to accept a void pointer.
///
/// This struct owns the original `host_env`, thus when it gets dropped
/// it calls the `drop` function on it.
#[derive(Debug, PartialEq)]
pub struct ExportFunctionMetadata {
/// This field is stored here to be accessible by `Drop`.
///
/// At the time it was added, it's not accessed anywhere outside of
/// the `Drop` implementation. This field is the "master copy" of the env,
/// that is, the original env passed in by the user. Every time we create
/// an `Instance` we clone this with the `host_env_clone_fn` field.
///
/// Thus, we only bother to store the master copy at all here so that
/// we can free it.
///
/// See `wasmer_vm::export::VMFunction::vmctx` for the version of
/// this pointer that is used by the VM when creating an `Instance`.
pub(crate) host_env: *mut std::ffi::c_void,
/// Function pointer to `WasmerEnv::init_with_instance(&mut self, instance: &Instance)`.
///
/// This function is called to finish setting up the environment after
/// we create the `api::Instance`.
// This one is optional for now because dynamic host envs need the rest
// of this without the init fn
pub(crate) import_init_function_ptr: Option<ImportInitializerFuncPtr>,
/// A function analogous to `Clone::clone` that returns a leaked `Box`.
pub(crate) host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void,
/// The destructor to free the host environment.
///
/// # Safety
/// - This function should only be called in when properly synchronized.
/// For example, in the `Drop` implementation of this type.
pub(crate) host_env_drop_fn: unsafe fn(*mut std::ffi::c_void),
}
/// This can be `Send` because `host_env` comes from `WasmerEnv` which is
/// `Send`. Therefore all operations should work on any thread.
unsafe impl Send for ExportFunctionMetadata {}
/// This data may be shared across threads, `drop` is an unsafe function
/// pointer, so care must be taken when calling it.
unsafe impl Sync for ExportFunctionMetadata {}
impl ExportFunctionMetadata {
/// Create an `ExportFunctionMetadata` type with information about
/// the exported function.
///
/// # Safety
/// - the `host_env` must be `Send`.
/// - all function pointers must work on any thread.
pub unsafe fn new(
host_env: *mut std::ffi::c_void,
import_init_function_ptr: Option<ImportInitializerFuncPtr>,
host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void,
host_env_drop_fn: fn(*mut std::ffi::c_void),
) -> Self {
Self {
host_env,
import_init_function_ptr,
host_env_clone_fn,
host_env_drop_fn,
}
}
}
// We have to free `host_env` here because we always clone it before using it
// so all the `host_env`s freed at the `Instance` level won't touch the original.
impl Drop for ExportFunctionMetadata {
fn drop(&mut self) {
if !self.host_env.is_null() {
// # Safety
// - This is correct because we know no other references
// to this data can exist if we're dropping it.
unsafe {
(self.host_env_drop_fn)(self.host_env);
}
}
}
}
/// A function export value with an extra function pointer to initialize
/// host environments.
#[derive(Debug, Clone, PartialEq)]
pub struct ExportFunction {
/// The VM function, containing most of the data.
pub vm_function: VMFunction,
/// Contains functions necessary to create and initialize host envs
/// with each `Instance` as well as being responsible for the
/// underlying memory of the host env.
pub metadata: Option<Arc<ExportFunctionMetadata>>,
}
impl From<ExportFunction> for Export {
fn from(func: ExportFunction) -> Self {
Self::Function(func)
}
}
impl From<VMTable> for Export {
fn from(table: VMTable) -> Self {
Self::Table(table)
}
}
impl From<VMMemory> for Export {
fn from(memory: VMMemory) -> Self {
Self::Memory(memory)
}
}
impl From<VMGlobal> for Export {
fn from(global: VMGlobal) -> Self {
Self::Global(global)
}
}

View File

@@ -0,0 +1,101 @@
//! Engine trait and associated types.
use crate::engine::tunables::Tunables;
use crate::Artifact;
use crate::Target;
use memmap2::Mmap;
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::Arc;
use wasmer_types::{CompileError, DeserializeError, FunctionType};
use wasmer_vm::{VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex};
/// A unimplemented Wasmer `Engine`.
///
/// This trait is used by implementors to implement custom engines
/// such as: Universal or Native.
///
/// The product that an `Engine` produces and consumes is the [`Artifact`].
pub trait Engine {
/// Gets the target
fn target(&self) -> &Target;
/// Register a signature
fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex;
/// Register a function's data.
fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef;
/// Lookup a signature
fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType>;
/// Validates a WebAssembly module
fn validate(&self, binary: &[u8]) -> Result<(), CompileError>;
/// Compile a WebAssembly binary
fn compile(
&self,
binary: &[u8],
tunables: &dyn Tunables,
) -> Result<Arc<dyn Artifact>, CompileError>;
/// Deserializes a WebAssembly module
///
/// # Safety
///
/// The serialized content must represent a serialized WebAssembly module.
unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError>;
/// Deserializes a WebAssembly module from a path
///
/// # Safety
///
/// The file's content must represent a serialized WebAssembly module.
unsafe fn deserialize_from_file(
&self,
file_ref: &Path,
) -> Result<Arc<dyn Artifact>, DeserializeError> {
let file = std::fs::File::open(file_ref)?;
let mmap = Mmap::map(&file)?;
self.deserialize(&mmap)
}
/// A unique identifier for this object.
///
/// This exists to allow us to compare two Engines for equality. Otherwise,
/// comparing two trait objects unsafely relies on implementation details
/// of trait representation.
fn id(&self) -> &EngineId;
/// Clone the engine
fn cloned(&self) -> Arc<dyn Engine + Send + Sync>;
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
/// A unique identifier for an Engine.
pub struct EngineId {
id: usize,
}
impl EngineId {
/// Format this identifier as a string.
pub fn id(&self) -> String {
format!("{}", &self.id)
}
}
impl Clone for EngineId {
fn clone(&self) -> Self {
Self::default()
}
}
impl Default for EngineId {
fn default() -> Self {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
Self {
id: NEXT_ID.fetch_add(1, SeqCst),
}
}
}

View File

@@ -0,0 +1,17 @@
//! Generic Engine abstraction for Wasmer Engines.
mod artifact;
mod error;
mod export;
mod inner;
mod resolver;
mod trap;
mod tunables;
pub use self::artifact::Artifact;
pub use self::error::{InstantiationError, LinkError};
pub use self::export::{Export, ExportFunction, ExportFunctionMetadata};
pub use self::inner::{Engine, EngineId};
pub use self::resolver::resolve_imports;
pub use self::trap::*;
pub use self::tunables::Tunables;

View File

@@ -0,0 +1,218 @@
//! Custom resolution for external references.
use crate::{Export, ExportFunctionMetadata, LinkError};
use more_asserts::assert_ge;
use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasmer_types::{
ExternType, FunctionIndex, ImportError, ImportIndex, MemoryIndex, ModuleInfo, TableIndex,
};
use wasmer_vm::{
FunctionBodyPtr, ImportFunctionEnv, Imports, MemoryStyle, TableStyle, VMFunctionBody,
VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalImport, VMMemoryImport,
VMTableImport,
};
/// Get an `ExternType` given a import index.
fn get_extern_from_import(module: &ModuleInfo, import_index: &ImportIndex) -> ExternType {
match import_index {
ImportIndex::Function(index) => {
let func = module.signatures[module.functions[*index]].clone();
ExternType::Function(func)
}
ImportIndex::Table(index) => {
let table = module.tables[*index];
ExternType::Table(table)
}
ImportIndex::Memory(index) => {
let memory = module.memories[*index];
ExternType::Memory(memory)
}
ImportIndex::Global(index) => {
let global = module.globals[*index];
ExternType::Global(global)
}
}
}
/// Get an `ExternType` given an export (and Engine signatures in case is a function).
fn get_extern_from_export(_module: &ModuleInfo, export: &Export) -> ExternType {
match export {
Export::Function(ref f) => ExternType::Function(f.vm_function.signature.clone()),
Export::Table(ref t) => ExternType::Table(*t.ty()),
Export::Memory(ref m) => ExternType::Memory(m.ty()),
Export::Global(ref g) => {
let global = g.from.ty();
ExternType::Global(*global)
}
}
}
/// This function allows to match all imports of a `ModuleInfo` with concrete definitions provided by
/// a `Resolver`.
///
/// If all imports are satisfied returns an `Imports` instance required for a module instantiation.
pub fn resolve_imports(
module: &ModuleInfo,
imports: &[Export],
finished_dynamic_function_trampolines: &BoxedSlice<FunctionIndex, FunctionBodyPtr>,
memory_styles: &PrimaryMap<MemoryIndex, MemoryStyle>,
_table_styles: &PrimaryMap<TableIndex, TableStyle>,
) -> Result<Imports, LinkError> {
let mut function_imports = PrimaryMap::with_capacity(module.num_imported_functions);
let mut host_function_env_initializers =
PrimaryMap::with_capacity(module.num_imported_functions);
let mut table_imports = PrimaryMap::with_capacity(module.num_imported_tables);
let mut memory_imports = PrimaryMap::with_capacity(module.num_imported_memories);
let mut global_imports = PrimaryMap::with_capacity(module.num_imported_globals);
for ((module_name, field, import_idx), import_index) in module.imports.iter() {
let import_extern = get_extern_from_import(module, import_index);
let resolved = if let Some(r) = imports.get(*import_idx as usize) {
r
} else {
return Err(LinkError::Import(
module_name.to_string(),
field.to_string(),
ImportError::UnknownImport(import_extern),
));
};
let export_extern = get_extern_from_export(module, resolved);
if !export_extern.is_compatible_with(&import_extern) {
return Err(LinkError::Import(
module_name.to_string(),
field.to_string(),
ImportError::IncompatibleType(import_extern, export_extern),
));
}
match resolved {
Export::Function(ref f) => {
let address = match f.vm_function.kind {
VMFunctionKind::Dynamic => {
// If this is a dynamic imported function,
// the address of the function is the address of the
// reverse trampoline.
let index = FunctionIndex::new(function_imports.len());
finished_dynamic_function_trampolines[index].0 as *mut VMFunctionBody as _
// TODO: We should check that the f.vmctx actually matches
// the shape of `VMDynamicFunctionImportContext`
}
VMFunctionKind::Static => f.vm_function.address,
};
// Clone the host env for this `Instance`.
let env = if let Some(ExportFunctionMetadata {
host_env_clone_fn: clone,
..
}) = f.metadata.as_deref()
{
// TODO: maybe start adding asserts in all these
// unsafe blocks to prevent future changes from
// horribly breaking things.
unsafe {
assert!(!f.vm_function.vmctx.host_env.is_null());
(clone)(f.vm_function.vmctx.host_env)
}
} else {
// No `clone` function means we're dealing with some
// other kind of `vmctx`, not a host env of any
// kind.
unsafe { f.vm_function.vmctx.host_env }
};
function_imports.push(VMFunctionImport {
body: address,
environment: VMFunctionEnvironment { host_env: env },
});
let initializer = f.metadata.as_ref().and_then(|m| m.import_init_function_ptr);
let clone = f.metadata.as_ref().map(|m| m.host_env_clone_fn);
let destructor = f.metadata.as_ref().map(|m| m.host_env_drop_fn);
let import_function_env =
if let (Some(clone), Some(destructor)) = (clone, destructor) {
ImportFunctionEnv::Env {
env,
clone,
initializer,
destructor,
}
} else {
ImportFunctionEnv::NoEnv
};
host_function_env_initializers.push(import_function_env);
}
Export::Table(ref t) => match import_index {
ImportIndex::Table(index) => {
let import_table_ty = t.from.ty();
let expected_table_ty = &module.tables[*index];
if import_table_ty.ty != expected_table_ty.ty {
return Err(LinkError::Import(
module_name.to_string(),
field.to_string(),
ImportError::IncompatibleType(import_extern, export_extern),
));
}
table_imports.push(VMTableImport {
definition: t.from.vmtable(),
from: t.from.clone(),
});
}
_ => {
unreachable!("Table resolution did not match");
}
},
Export::Memory(ref m) => {
match import_index {
ImportIndex::Memory(index) => {
// Sanity-check: Ensure that the imported memory has at least
// guard-page protections the importing module expects it to have.
let export_memory_style = m.style();
let import_memory_style = &memory_styles[*index];
if let (
MemoryStyle::Static { bound, .. },
MemoryStyle::Static {
bound: import_bound,
..
},
) = (export_memory_style.clone(), &import_memory_style)
{
assert_ge!(bound, *import_bound);
}
assert_ge!(
export_memory_style.offset_guard_size(),
import_memory_style.offset_guard_size()
);
}
_ => {
// This should never be reached, as we did compatibility
// checks before
panic!("Memory resolution didn't matched");
}
}
memory_imports.push(VMMemoryImport {
definition: m.from.vmmemory(),
from: m.from.clone(),
});
}
Export::Global(ref g) => {
global_imports.push(VMGlobalImport {
definition: g.from.vmglobal(),
from: g.from.clone(),
});
}
}
}
Ok(Imports::new(
function_imports,
host_function_env_initializers,
table_imports,
memory_imports,
global_imports,
))
}

View File

@@ -0,0 +1,275 @@
use super::frame_info::{FrameInfo, GlobalFrameInfo, FRAME_INFO};
use backtrace::Backtrace;
use std::error::Error;
use std::fmt;
use std::sync::Arc;
use wasmer_vm::{raise_user_trap, Trap, TrapCode};
/// A struct representing an aborted instruction execution, with a message
/// indicating the cause.
#[derive(Clone)]
pub struct RuntimeError {
inner: Arc<RuntimeErrorInner>,
}
/// The source of the `RuntimeError`.
#[derive(Debug)]
enum RuntimeErrorSource {
Generic(String),
OutOfMemory,
User(Box<dyn Error + Send + Sync>),
Trap(TrapCode),
}
impl fmt::Display for RuntimeErrorSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Generic(s) => write!(f, "{}", s),
Self::User(s) => write!(f, "{}", s),
Self::OutOfMemory => write!(f, "Wasmer VM out of memory"),
Self::Trap(s) => write!(f, "{}", s.message()),
}
}
}
struct RuntimeErrorInner {
/// The source error (this can be a custom user `Error` or a [`TrapCode`])
source: RuntimeErrorSource,
/// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`).
wasm_trace: Vec<FrameInfo>,
/// The native backtrace
native_trace: Backtrace,
}
fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) {
(t, t)
}
impl RuntimeError {
/// Creates a new generic `RuntimeError` with the given `message`.
///
/// # Example
/// ```
/// let trap = wasmer_engine::RuntimeError::new("unexpected error");
/// assert_eq!("unexpected error", trap.message());
/// ```
pub fn new<I: Into<String>>(message: I) -> Self {
let info = FRAME_INFO.read().unwrap();
let msg = message.into();
Self::new_with_trace(
&info,
None,
RuntimeErrorSource::Generic(msg),
Backtrace::new_unresolved(),
)
}
/// Create a new RuntimeError from a Trap.
pub fn from_trap(trap: Trap) -> Self {
let info = FRAME_INFO.read().unwrap();
match trap {
// A user error
Trap::User(error) => {
match error.downcast::<Self>() {
// The error is already a RuntimeError, we return it directly
Ok(runtime_error) => *runtime_error,
Err(e) => Self::new_with_trace(
&info,
None,
RuntimeErrorSource::User(e),
Backtrace::new_unresolved(),
),
}
}
// A trap caused by the VM being Out of Memory
Trap::OOM { backtrace } => {
Self::new_with_trace(&info, None, RuntimeErrorSource::OutOfMemory, backtrace)
}
// A trap caused by an error on the generated machine code for a Wasm function
Trap::Wasm {
pc,
signal_trap,
backtrace,
} => {
let code = info
.lookup_trap_info(pc)
.map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| {
info.trap_code
});
Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace)
}
// A trap triggered manually from the Wasmer runtime
Trap::Lib {
trap_code,
backtrace,
} => Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace),
}
}
/// Raises a custom user Error
#[deprecated(since = "2.1.1", note = "return a Result from host functions instead")]
pub fn raise(error: Box<dyn Error + Send + Sync>) -> ! {
unsafe { raise_user_trap(error) }
}
/// Creates a custom user Error.
///
/// This error object can be passed through Wasm frames and later retrieved
/// using the `downcast` method.
pub fn user(error: Box<dyn Error + Send + Sync>) -> Self {
match error.downcast::<Self>() {
// The error is already a RuntimeError, we return it directly
Ok(runtime_error) => *runtime_error,
Err(error) => {
let info = FRAME_INFO.read().unwrap();
Self::new_with_trace(
&info,
None,
RuntimeErrorSource::User(error),
Backtrace::new_unresolved(),
)
}
}
}
fn new_with_trace(
info: &GlobalFrameInfo,
trap_pc: Option<usize>,
source: RuntimeErrorSource,
native_trace: Backtrace,
) -> Self {
// Let's construct the trace
let wasm_trace = native_trace
.frames()
.iter()
.filter_map(|frame| {
let pc = frame.ip() as usize;
if pc == 0 {
None
} else {
// Note that we need to be careful about the pc we pass in here to
// lookup frame information. This program counter is used to
// translate back to an original source location in the origin wasm
// module. If this pc is the exact pc that the trap happened at,
// then we look up that pc precisely. Otherwise backtrace
// information typically points at the pc *after* the call
// instruction (because otherwise it's likely a call instruction on
// the stack). In that case we want to lookup information for the
// previous instruction (the call instruction) so we subtract one as
// the lookup.
let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
Some(pc_to_lookup)
}
})
.filter_map(|pc| info.lookup_frame_info(pc))
.collect::<Vec<_>>();
Self {
inner: Arc::new(RuntimeErrorInner {
source,
wasm_trace,
native_trace,
}),
}
}
/// Returns a reference the `message` stored in `Trap`.
pub fn message(&self) -> String {
self.inner.source.to_string()
}
/// Returns a list of function frames in WebAssembly code that led to this
/// trap happening.
pub fn trace(&self) -> &[FrameInfo] {
&self.inner.wasm_trace
}
/// Attempts to downcast the `RuntimeError` to a concrete type.
pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
match Arc::try_unwrap(self.inner) {
// We only try to downcast user errors
Ok(RuntimeErrorInner {
source: RuntimeErrorSource::User(err),
..
}) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
Ok(inner) => Err(Self {
inner: Arc::new(inner),
}),
Err(inner) => Err(Self { inner }),
}
}
/// Returns trap code, if it's a Trap
pub fn to_trap(self) -> Option<TrapCode> {
if let RuntimeErrorSource::Trap(trap_code) = self.inner.source {
Some(trap_code)
} else {
None
}
}
/// Returns true if the `RuntimeError` is the same as T
pub fn is<T: Error + 'static>(&self) -> bool {
match &self.inner.source {
RuntimeErrorSource::User(err) => err.is::<T>(),
_ => false,
}
}
}
impl fmt::Debug for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RuntimeError")
.field("source", &self.inner.source)
.field("wasm_trace", &self.inner.wasm_trace)
.field("native_trace", &self.inner.native_trace)
.finish()
}
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RuntimeError: {}", self.message())?;
let trace = self.trace();
if trace.is_empty() {
return Ok(());
}
for frame in self.trace().iter() {
let name = frame.module_name();
let func_index = frame.func_index();
writeln!(f)?;
write!(f, " at ")?;
match frame.function_name() {
Some(name) => match rustc_demangle::try_demangle(name) {
Ok(name) => write!(f, "{}", name)?,
Err(_) => write!(f, "{}", name)?,
},
None => write!(f, "<unnamed>")?,
}
write!(
f,
" ({}[{}]:0x{:x})",
name,
func_index,
frame.module_offset()
)?;
}
Ok(())
}
}
impl std::error::Error for RuntimeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.inner.source {
RuntimeErrorSource::User(err) => Some(&**err),
RuntimeErrorSource::Trap(err) => Some(err),
_ => None,
}
}
}
impl From<Trap> for RuntimeError {
fn from(trap: Trap) -> Self {
Self::from_trap(trap)
}
}

View File

@@ -0,0 +1,318 @@
//! This module is used for having backtraces in the Wasm runtime.
//! Once the Compiler has compiled the ModuleInfo, and we have a set of
//! compiled functions (addresses and function index) and a module,
//! then we can use this to set a backtrace for that module.
//!
//! # Example
//! ```ignore
//! use wasmer_vm::{FRAME_INFO};
//! use wasmer_types::ModuleInfo;
//!
//! let module: ModuleInfo = ...;
//! FRAME_INFO.register(module, compiled_functions);
//! ```
use std::cmp;
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasmer_types::{CompiledFunctionFrameInfo, SourceLoc, TrapInformation};
use wasmer_types::{LocalFunctionIndex, ModuleInfo};
use wasmer_vm::FunctionBodyPtr;
lazy_static::lazy_static! {
/// This is a global cache of backtrace frame information for all active
///
/// This global cache is used during `Trap` creation to symbolicate frames.
/// This is populated on module compilation, and it is cleared out whenever
/// all references to a module are dropped.
pub static ref FRAME_INFO: RwLock<GlobalFrameInfo> = Default::default();
}
#[derive(Default)]
pub struct GlobalFrameInfo {
/// An internal map that keeps track of backtrace frame information for
/// each module.
///
/// This map is morally a map of ranges to a map of information for that
/// module. Each module is expected to reside in a disjoint section of
/// contiguous memory. No modules can overlap.
///
/// The key of this map is the highest address in the module and the value
/// is the module's information, which also contains the start address.
ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
}
/// An RAII structure used to unregister a module's frame information when the
/// module is destroyed.
pub struct GlobalFrameInfoRegistration {
/// The key that will be removed from the global `ranges` map when this is
/// dropped.
key: usize,
}
#[derive(Debug)]
struct ModuleInfoFrameInfo {
start: usize,
functions: BTreeMap<usize, FunctionInfo>,
module: Arc<ModuleInfo>,
frame_infos: PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>,
}
impl ModuleInfoFrameInfo {
fn function_debug_info(&self, local_index: LocalFunctionIndex) -> &CompiledFunctionFrameInfo {
self.frame_infos.get(local_index).unwrap()
}
/// Gets a function given a pc
fn function_info(&self, pc: usize) -> Option<&FunctionInfo> {
let (end, func) = self.functions.range(pc..).next()?;
if func.start <= pc && pc <= *end {
Some(func)
} else {
None
}
}
}
#[derive(Debug)]
struct FunctionInfo {
start: usize,
local_index: LocalFunctionIndex,
}
impl GlobalFrameInfo {
/// Fetches frame information about a program counter in a backtrace.
///
/// Returns an object if this `pc` is known to some previously registered
/// module, or returns `None` if no information can be found.
pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
// Use our relative position from the start of the function to find the
// machine instruction that corresponds to `pc`, which then allows us to
// map that to a wasm original source location.
let rel_pos = pc - func.start;
let instr_map = &module.function_debug_info(func.local_index).address_map;
let pos = match instr_map
.instructions
.binary_search_by_key(&rel_pos, |map| map.code_offset)
{
// Exact hit!
Ok(pos) => Some(pos),
// This *would* be at the first slot in the array, so no
// instructions cover `pc`.
Err(0) => None,
// This would be at the `nth` slot, so check `n-1` to see if we're
// part of that instruction. This happens due to the minus one when
// this function is called form trap symbolication, where we don't
// always get called with a `pc` that's an exact instruction
// boundary.
Err(n) => {
let instr = &instr_map.instructions[n - 1];
if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len {
Some(n - 1)
} else {
None
}
}
};
let instr = match pos {
Some(pos) => instr_map.instructions[pos].srcloc,
// Some compilers don't emit yet the full trap information for each of
// the instructions (such as LLVM).
// In case no specific instruction is found, we return by default the
// start offset of the function.
None => instr_map.start_srcloc,
};
let func_index = module.module.func_index(func.local_index);
Some(FrameInfo {
module_name: module.module.name(),
func_index: func_index.index() as u32,
function_name: module.module.function_names.get(&func_index).cloned(),
instr,
func_start: instr_map.start_srcloc,
})
}
/// Fetches trap information about a program counter in a backtrace.
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
let traps = &module.function_debug_info(func.local_index).traps;
let idx = traps
.binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset)
.ok()?;
Some(&traps[idx])
}
/// Gets a module given a pc
fn module_info(&self, pc: usize) -> Option<&ModuleInfoFrameInfo> {
let (end, module_info) = self.ranges.range(pc..).next()?;
if module_info.start <= pc && pc <= *end {
Some(module_info)
} else {
None
}
}
}
impl Drop for GlobalFrameInfoRegistration {
fn drop(&mut self) {
if let Ok(mut info) = FRAME_INFO.write() {
info.ranges.remove(&self.key);
}
}
}
/// Represents a continuous region of executable memory starting with a function
/// entry point.
#[derive(Debug)]
#[repr(C)]
pub struct FunctionExtent {
/// Entry point for normal entry of the function. All addresses in the
/// function lie after this address.
pub ptr: FunctionBodyPtr,
/// Length in bytes.
pub length: usize,
}
/// Registers a new compiled module's frame information.
///
/// This function will register the `names` information for all of the
/// compiled functions within `module`. If the `module` has no functions
/// then `None` will be returned. Otherwise the returned object, when
/// dropped, will be used to unregister all name information from this map.
pub fn register(
module: Arc<ModuleInfo>,
finished_functions: &BoxedSlice<LocalFunctionIndex, FunctionExtent>,
frame_infos: PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>,
) -> Option<GlobalFrameInfoRegistration> {
let mut min = usize::max_value();
let mut max = 0;
let mut functions = BTreeMap::new();
for (
i,
FunctionExtent {
ptr: start,
length: len,
},
) in finished_functions.iter()
{
let start = **start as usize;
let end = start + len;
min = cmp::min(min, start);
max = cmp::max(max, end);
let func = FunctionInfo {
start,
local_index: i,
};
assert!(functions.insert(end, func).is_none());
}
if functions.is_empty() {
return None;
}
let mut info = FRAME_INFO.write().unwrap();
// First up assert that our chunk of jit functions doesn't collide with
// any other known chunks of jit functions...
if let Some((_, prev)) = info.ranges.range(max..).next() {
assert!(prev.start > max);
}
if let Some((prev_end, _)) = info.ranges.range(..=min).next_back() {
assert!(*prev_end < min);
}
// ... then insert our range and assert nothing was there previously
let prev = info.ranges.insert(
max,
ModuleInfoFrameInfo {
start: min,
functions,
module,
frame_infos,
},
);
assert!(prev.is_none());
Some(GlobalFrameInfoRegistration { key: max })
}
/// Description of a frame in a backtrace for a [`RuntimeError::trace`](crate::RuntimeError::trace).
///
/// Whenever a WebAssembly trap occurs an instance of [`RuntimeError`]
/// is created. Each [`RuntimeError`] has a backtrace of the
/// WebAssembly frames that led to the trap, and each frame is
/// described by this structure.
///
/// [`RuntimeError`]: crate::RuntimeError
#[derive(Debug, Clone)]
pub struct FrameInfo {
module_name: String,
func_index: u32,
function_name: Option<String>,
func_start: SourceLoc,
instr: SourceLoc,
}
impl FrameInfo {
/// Returns the WebAssembly function index for this frame.
///
/// This function index is the index in the function index space of the
/// WebAssembly module that this frame comes from.
pub fn func_index(&self) -> u32 {
self.func_index
}
/// Returns the identifer of the module that this frame is for.
///
/// ModuleInfo identifiers are present in the `name` section of a WebAssembly
/// binary, but this may not return the exact item in the `name` section.
/// ModuleInfo names can be overwritten at construction time or perhaps inferred
/// from file names. The primary purpose of this function is to assist in
/// debugging and therefore may be tweaked over time.
///
/// This function returns `None` when no name can be found or inferred.
pub fn module_name(&self) -> &str {
&self.module_name
}
/// Returns a descriptive name of the function for this frame, if one is
/// available.
///
/// The name of this function may come from the `name` section of the
/// WebAssembly binary, or wasmer may try to infer a better name for it if
/// not available, for example the name of the export if it's exported.
///
/// This return value is primarily used for debugging and human-readable
/// purposes for things like traps. Note that the exact return value may be
/// tweaked over time here and isn't guaranteed to be something in
/// particular about a wasm module due to its primary purpose of assisting
/// in debugging.
///
/// This function returns `None` when no name could be inferred.
pub fn function_name(&self) -> Option<&str> {
self.function_name.as_deref()
}
/// Returns the offset within the original wasm module this frame's program
/// counter was at.
///
/// The offset here is the offset from the beginning of the original wasm
/// module to the instruction that this frame points to.
pub fn module_offset(&self) -> usize {
self.instr.bits() as usize
}
/// Returns the offset from the original wasm module's function to this
/// frame's program counter.
///
/// The offset here is the offset from the beginning of the defining
/// function of this frame (within the wasm module) to the instruction this
/// frame points to.
pub fn func_offset(&self) -> usize {
(self.instr.bits() - self.func_start.bits()) as usize
}
}

View File

@@ -0,0 +1,7 @@
mod error;
mod frame_info;
pub use error::RuntimeError;
pub use frame_info::{
register as register_frame_info, FrameInfo, FunctionExtent, GlobalFrameInfoRegistration,
FRAME_INFO,
};

View File

@@ -0,0 +1,143 @@
use crate::engine::error::LinkError;
use std::ptr::NonNull;
use std::sync::Arc;
use wasmer_types::entity::{EntityRef, PrimaryMap};
use wasmer_types::{
GlobalType, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType,
ModuleInfo, TableIndex, TableType,
};
use wasmer_vm::MemoryError;
use wasmer_vm::{Global, Memory, Table};
use wasmer_vm::{MemoryStyle, TableStyle};
use wasmer_vm::{VMMemoryDefinition, VMTableDefinition};
/// An engine delegates the creation of memories, tables, and globals
/// to a foreign implementor of this trait.
pub trait Tunables {
/// Construct a `MemoryStyle` for the provided `MemoryType`
fn memory_style(&self, memory: &MemoryType) -> MemoryStyle;
/// Construct a `TableStyle` for the provided `TableType`
fn table_style(&self, table: &TableType) -> TableStyle;
/// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`].
fn create_host_memory(
&self,
ty: &MemoryType,
style: &MemoryStyle,
) -> Result<Arc<dyn Memory>, MemoryError>;
/// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
///
/// # Safety
/// - `vm_definition_location` must point to a valid location in VM memory.
unsafe fn create_vm_memory(
&self,
ty: &MemoryType,
style: &MemoryStyle,
vm_definition_location: NonNull<VMMemoryDefinition>,
) -> Result<Arc<dyn Memory>, MemoryError>;
/// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
fn create_host_table(
&self,
ty: &TableType,
style: &TableStyle,
) -> Result<Arc<dyn Table>, String>;
/// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
///
/// # Safety
/// - `vm_definition_location` must point to a valid location in VM memory.
unsafe fn create_vm_table(
&self,
ty: &TableType,
style: &TableStyle,
vm_definition_location: NonNull<VMTableDefinition>,
) -> Result<Arc<dyn Table>, String>;
/// Create a global with an unset value.
fn create_global(&self, ty: GlobalType) -> Result<Arc<Global>, String> {
Ok(Arc::new(Global::new(ty)))
}
/// Allocate memory for just the memories of the current module.
///
/// # Safety
/// - `memory_definition_locations` must point to a valid locations in VM memory.
unsafe fn create_memories(
&self,
module: &ModuleInfo,
memory_styles: &PrimaryMap<MemoryIndex, MemoryStyle>,
memory_definition_locations: &[NonNull<VMMemoryDefinition>],
) -> Result<PrimaryMap<LocalMemoryIndex, Arc<dyn Memory>>, LinkError> {
let num_imports = module.num_imported_memories;
let mut memories: PrimaryMap<LocalMemoryIndex, _> =
PrimaryMap::with_capacity(module.memories.len() - num_imports);
for (index, mdl) in memory_definition_locations
.iter()
.enumerate()
.take(module.memories.len())
.skip(num_imports)
{
let mi = MemoryIndex::new(index);
let ty = &module.memories[mi];
let style = &memory_styles[mi];
memories.push(
self.create_vm_memory(ty, style, *mdl)
.map_err(|e| LinkError::Resource(format!("Failed to create memory: {}", e)))?,
);
}
Ok(memories)
}
/// Allocate memory for just the tables of the current module.
///
/// # Safety
///
/// To be done
unsafe fn create_tables(
&self,
module: &ModuleInfo,
table_styles: &PrimaryMap<TableIndex, TableStyle>,
table_definition_locations: &[NonNull<VMTableDefinition>],
) -> Result<PrimaryMap<LocalTableIndex, Arc<dyn Table>>, LinkError> {
let num_imports = module.num_imported_tables;
let mut tables: PrimaryMap<LocalTableIndex, _> =
PrimaryMap::with_capacity(module.tables.len() - num_imports);
for (index, tdl) in table_definition_locations
.iter()
.enumerate()
.take(module.tables.len())
.skip(num_imports)
{
let ti = TableIndex::new(index);
let ty = &module.tables[ti];
let style = &table_styles[ti];
tables.push(
self.create_vm_table(ty, style, *tdl)
.map_err(LinkError::Resource)?,
);
}
Ok(tables)
}
/// Allocate memory for just the globals of the current module,
/// with initializers applied.
fn create_globals(
&self,
module: &ModuleInfo,
) -> Result<PrimaryMap<LocalGlobalIndex, Arc<Global>>, LinkError> {
let num_imports = module.num_imported_globals;
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
for &global_type in module.globals.values().skip(num_imports) {
vmctx_globals.push(
self.create_global(global_type)
.map_err(LinkError::Resource)?,
);
}
Ok(vmctx_globals)
}
}

View File

@@ -9,7 +9,10 @@
#![warn(unused_import_braces)]
#![cfg_attr(feature = "std", deny(unstable_features))]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
#![cfg_attr(
feature = "cargo-clippy",
allow(clippy::new_without_default, clippy::upper_case_acronyms)
)]
#![cfg_attr(
feature = "cargo-clippy",
warn(
@@ -48,6 +51,14 @@ mod lib {
}
}
mod artifact;
#[cfg(not(target_arch = "wasm32"))]
mod engine;
pub use crate::artifact::*;
#[cfg(not(target_arch = "wasm32"))]
pub use crate::engine::*;
#[cfg(feature = "translator")]
mod compiler;
mod target;