Merge branch 'master' into wasix-merge

This commit is contained in:
Christoph Herzog
2022-12-06 13:22:09 +01:00
135 changed files with 13867 additions and 7713 deletions

View File

@@ -132,7 +132,11 @@ impl Imports {
/// Resolve and return a vector of imports in the order they are defined in the `module`'s source code.
///
/// This means the returned `Vec<Extern>` might be a subset of the imports contained in `self`.
pub fn imports_for_module(&self, module: &Module) -> Result<Vec<Extern>, LinkError> {
pub fn imports_for_module(
&self,
module: &Module,
_store: &mut impl AsStoreMut,
) -> Result<Vec<Extern>, LinkError> {
let mut ret = vec![];
for import in module.imports() {
if let Some(imp) = self

View File

@@ -54,7 +54,7 @@ pub use crate::js::function_env::{FunctionEnv, FunctionEnvMut};
pub use crate::js::imports::Imports;
pub use crate::js::instance::Instance;
pub use crate::js::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter};
pub use crate::js::module::{Module, ModuleTypeHints};
pub use crate::js::module::{IoCompileError, Module, ModuleTypeHints};
pub use crate::js::native::TypedFunction;
pub use crate::js::native_type::NativeWasmTypeInto;
pub use crate::js::ptr::{Memory32, Memory64, MemorySize, WasmPtr, WasmPtr64};
@@ -73,7 +73,6 @@ pub use crate::js::value::Value as Val;
pub mod vm {
//! The `vm` module re-exports wasmer-vm types.
pub use crate::js::export::VMMemory;
}

View File

@@ -26,6 +26,7 @@ use wasmer_types::{
Pages, TableType, Type,
};
/// IO Error on a Module Compilation
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
pub enum IoCompileError {

View File

@@ -58,16 +58,26 @@ impl Imports {
/// import_object.get_export("module", "name");
/// ```
pub fn get_export(&self, module: &str, name: &str) -> Option<Extern> {
if self
.map
.contains_key(&(module.to_string(), name.to_string()))
{
if self.exists(module, name) {
let ext = &self.map[&(module.to_string(), name.to_string())];
return Some(ext.clone());
}
None
}
/// Returns if an export exist for a given module and name.
///
/// # Usage
/// ```no_run
/// # use wasmer::Imports;
/// let mut import_object = Imports::new();
/// import_object.exists("module", "name");
/// ```
pub fn exists(&self, module: &str, name: &str) -> bool {
self.map
.contains_key(&(module.to_string(), name.to_string()))
}
/// Returns true if the Imports contains namespace with the provided name.
pub fn contains_namespace(&self, name: &str) -> bool {
self.map.keys().any(|(k, _)| (k == name))

View File

@@ -23,7 +23,7 @@ pub use crate::sys::function_env::{FunctionEnv, FunctionEnvMut};
pub use crate::sys::imports::Imports;
pub use crate::sys::instance::{Instance, InstantiationError};
pub use crate::sys::mem_access::{MemoryAccessError, WasmRef, WasmSlice, WasmSliceIter};
pub use crate::sys::module::Module;
pub use crate::sys::module::{IoCompileError, Module};
pub use crate::sys::native::TypedFunction;
pub use crate::sys::native_type::NativeWasmTypeInto;
pub use crate::sys::store::{AsStoreMut, AsStoreRef, StoreMut, StoreRef};
@@ -74,9 +74,9 @@ pub use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel};
#[cfg(feature = "llvm")]
pub use wasmer_compiler_llvm::{LLVMOptLevel, LLVM};
pub use wasmer_compiler::Engine;
#[cfg(feature = "compiler")]
pub use wasmer_compiler::{Artifact, EngineBuilder};
pub use wasmer_compiler::{AsEngineRef, Engine, EngineRef};
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -1,14 +1,16 @@
use std::fmt;
use std::io;
#[cfg(feature = "compiler")]
use std::path::Path;
use std::sync::Arc;
use thiserror::Error;
#[cfg(feature = "compiler")]
use wasmer_compiler::Artifact;
use wasmer_compiler::ArtifactCreate;
use wasmer_compiler::AsEngineRef;
#[cfg(feature = "wat")]
use wasmer_types::WasmError;
use wasmer_types::{CompileError, ExportsIterator, ImportsIterator, ModuleInfo};
use wasmer_types::{
CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError,
};
use wasmer_types::{ExportType, ImportType};
#[cfg(feature = "compiler")]
@@ -16,6 +18,7 @@ use crate::{sys::InstantiationError, AsStoreMut, AsStoreRef, IntoBytes};
#[cfg(feature = "compiler")]
use wasmer_vm::InstanceHandle;
/// IO Error on a Module Compilation
#[derive(Error, Debug)]
pub enum IoCompileError {
/// An IO error
@@ -50,8 +53,7 @@ pub struct Module {
//
// In the future, this code should be refactored to properly describe the
// ownership of the code and its metadata.
#[cfg(feature = "compiler")]
artifact: Arc<wasmer_compiler::Artifact>,
artifact: Arc<Artifact>,
module_info: Arc<ModuleInfo>,
}
@@ -116,33 +118,38 @@ impl Module {
/// # Ok(())
/// # }
/// ```
/// # Example of loading a module using just an `Engine` and no `Store`
///
/// ```
/// # use wasmer::*;
/// #
/// # let compiler = Cranelift::default();
/// # let engine = EngineBuilder::new(compiler).engine();
///
/// let module = Module::from_file(&engine, "path/to/foo.wasm");
/// ```
#[allow(unreachable_code)]
pub fn new(store: &impl AsStoreRef, bytes: impl IntoBytes) -> Result<Self, CompileError> {
#[allow(unused_mut)]
let mut bytes = bytes.into_bytes();
pub fn new(engine: &impl AsEngineRef, bytes: impl AsRef<[u8]>) -> Result<Self, CompileError> {
#[cfg(feature = "wat")]
if bytes.starts_with(b"\0asm") == false {
let parsed_bytes = wat::parse_bytes(&bytes[..]).map_err(|e| {
CompileError::Wasm(WasmError::Generic(format!(
"Error when converting wat: {}",
e
)))
})?;
bytes = bytes::Bytes::from(parsed_bytes.to_vec());
}
Self::from_binary(store, bytes.as_ref())
let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| {
CompileError::Wasm(WasmError::Generic(format!(
"Error when converting wat: {}",
e
)))
})?;
Self::from_binary(engine, bytes.as_ref())
}
#[cfg(feature = "compiler")]
/// Creates a new WebAssembly module from a file path.
pub fn from_file(
store: &impl AsStoreRef,
engine: &impl AsEngineRef,
file: impl AsRef<Path>,
) -> Result<Self, IoCompileError> {
let file_ref = file.as_ref();
let canonical = file_ref.canonicalize()?;
let wasm_bytes = std::fs::read(file_ref)?;
let mut module = Self::new(store, &wasm_bytes)?;
let mut module = Self::new(engine, &wasm_bytes)?;
// Set the module name to the absolute path of the filename.
// This is useful for debugging the stack traces.
let filename = canonical.as_path().to_str().unwrap();
@@ -156,9 +163,9 @@ impl Module {
/// Opposed to [`Module::new`], this function is not compatible with
/// the WebAssembly text format (if the "wat" feature is enabled for
/// this crate).
pub fn from_binary(store: &impl AsStoreRef, binary: &[u8]) -> Result<Self, CompileError> {
Self::validate(store, binary)?;
unsafe { Self::from_binary_unchecked(store, binary) }
pub fn from_binary(engine: &impl AsEngineRef, binary: &[u8]) -> Result<Self, CompileError> {
Self::validate(engine, binary)?;
unsafe { Self::from_binary_unchecked(engine, binary) }
}
#[cfg(feature = "compiler")]
@@ -170,11 +177,10 @@ impl Module {
/// in environments where the WebAssembly modules are trusted and validated
/// beforehand.
pub unsafe fn from_binary_unchecked(
store: &impl AsStoreRef,
binary: impl IntoBytes,
engine: &impl AsEngineRef,
binary: &[u8],
) -> Result<Self, CompileError> {
let binary = binary.into_bytes();
let module = Self::compile(store, binary)?;
let module = Self::compile(engine, binary)?;
Ok(module)
}
@@ -185,24 +191,18 @@ impl Module {
/// This validation is normally pretty fast and checks the enabled
/// WebAssembly features in the Store Engine to assure deterministic
/// validation of the Module.
pub fn validate(store: &impl AsStoreRef, binary: impl IntoBytes) -> Result<(), CompileError> {
let binary = binary.into_bytes();
store.as_store_ref().engine().validate(&binary[..])
pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> {
engine.as_engine_ref().engine().validate(binary)
}
#[cfg(feature = "compiler")]
fn compile(store: &impl AsStoreRef, binary: impl IntoBytes) -> Result<Self, CompileError> {
let binary = binary.into_bytes();
let artifact = store
.as_store_ref()
.engine()
.compile(&binary[..], store.as_store_ref().tunables())?;
fn compile(engine: &impl AsEngineRef, binary: &[u8]) -> Result<Self, CompileError> {
let artifact = engine.as_engine_ref().engine().compile(binary)?;
Ok(Self::from_artifact(artifact))
}
/// Serializes a module into a binary representation that the `Engine`
/// can later process via
#[cfg(feature = "enable-rkyv")]
#[cfg_attr(feature = "compiler", doc = "[`Module::deserialize`].")]
#[cfg_attr(not(feature = "compiler"), doc = "`Module::deserialize`.")]
///
@@ -217,13 +217,12 @@ impl Module {
/// # Ok(())
/// # }
/// ```
pub fn serialize(&self) -> Result<bytes::Bytes, wasmer_types::SerializeError> {
pub fn serialize(&self) -> Result<Bytes, SerializeError> {
self.artifact.serialize().map(|bytes| bytes.into())
}
/// Serializes a module into a file that the `Engine`
/// can later process via
#[cfg(feature = "enable-rkyv")]
#[cfg_attr(feature = "compiler", doc = "[`Module::deserialize_from_file`].")]
#[cfg_attr(not(feature = "compiler"), doc = "`Module::deserialize_from_file`.")]
///
@@ -238,14 +237,10 @@ impl Module {
/// # Ok(())
/// # }
/// ```
pub fn serialize_to_file(
&self,
path: impl AsRef<Path>,
) -> Result<(), wasmer_types::SerializeError> {
pub fn serialize_to_file(&self, path: impl AsRef<Path>) -> Result<(), SerializeError> {
self.artifact.serialize_to_file(path.as_ref())
}
#[cfg(feature = "enable-rkyv")]
#[cfg(feature = "compiler")]
/// Deserializes a serialized Module binary into a `Module`.
/// > Note: the module has to be serialized before with the `serialize` method.
@@ -273,13 +268,12 @@ impl Module {
pub unsafe fn deserialize(
store: &impl AsStoreRef,
bytes: impl IntoBytes,
) -> Result<Self, wasmer_types::DeserializeError> {
let bytes = bytes.into_bytes().to_vec();
) -> Result<Self, DeserializeError> {
let bytes = bytes.into_bytes();
let artifact = store.as_store_ref().engine().deserialize(&bytes)?;
Ok(Self::from_artifact(artifact))
}
#[cfg(feature = "enable-rkyv")]
#[cfg(feature = "compiler")]
/// Deserializes a a serialized Module located in a `Path` into a `Module`.
/// > Note: the module has to be serialized before with the `serialize` method.
@@ -301,7 +295,7 @@ impl Module {
pub unsafe fn deserialize_from_file(
store: &impl AsStoreRef,
path: impl AsRef<Path>,
) -> Result<Self, wasmer_types::DeserializeError> {
) -> Result<Self, DeserializeError> {
let artifact = store
.as_store_ref()
.engine()
@@ -309,8 +303,7 @@ impl Module {
Ok(Self::from_artifact(artifact))
}
#[cfg(feature = "compiler")]
fn from_artifact(artifact: Arc<wasmer_compiler::Artifact>) -> Self {
fn from_artifact(artifact: Arc<Artifact>) -> Self {
Self {
module_info: Arc::new(artifact.create_module_info()),
artifact,

View File

@@ -1,13 +1,10 @@
#[cfg(feature = "compiler")]
use crate::sys::tunables::BaseTunables;
use derivative::Derivative;
use std::fmt;
#[cfg(feature = "compiler")]
use wasmer_compiler::{Engine, EngineBuilder, Tunables};
use wasmer_compiler::{AsEngineRef, Engine, EngineBuilder, EngineRef, Tunables};
use wasmer_vm::{init_traps, StoreId, TrapHandler, TrapHandlerFn};
use wasmer_types::{OnCalledAction, StoreSnapshot};
#[cfg(feature = "compiler")]
use wasmer_vm::init_traps;
use wasmer_vm::{StoreId, TrapHandler, TrapHandlerFn};
use wasmer_vm::StoreObjects;
@@ -22,9 +19,6 @@ pub(crate) struct StoreInner {
#[cfg(feature = "compiler")]
pub(crate) engine: Engine,
#[derivative(Debug = "ignore")]
#[cfg(feature = "compiler")]
pub(crate) tunables: Box<dyn Tunables + Send + Sync>,
#[derivative(Debug = "ignore")]
pub(crate) trap_handler: Option<Box<TrapHandlerFn<'static>>>,
#[derivative(Debug = "ignore")]
pub(crate) on_called: Option<
@@ -55,10 +49,7 @@ impl StoreInner {
/// have been allocated during the lifetime of the abstract machine.
///
/// The `Store` holds the engine (that is —amongst many things— used to compile
/// the Wasm bytes into a valid module artifact), in addition to the
#[cfg_attr(feature = "compiler", doc = "[`Tunables`]")]
#[cfg_attr(not(feature = "compiler"), doc = "`Tunables`")]
/// (that are used to create the memories, tables and globals).
/// the Wasm bytes into a valid module artifact).
///
/// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#store>
pub struct Store {
@@ -72,8 +63,21 @@ impl Store {
/// Creates a new `Store` with a specific [`Engine`].
pub fn new(engine: impl Into<Engine>) -> Self {
let engine = engine.into();
let target = engine.target().clone();
Self::new_with_tunables(engine, BaseTunables::for_target(&target))
// Make sure the signal handlers are installed.
// This is required for handling traps.
init_traps();
Self {
inner: Box::new(StoreInner {
objects: Default::default(),
engine: engine.cloned(),
trap_handler: None,
on_called: None,
}),
engine: engine.cloned(),
trap_handler: Arc::new(RwLock::new(None)),
}
}
#[cfg(feature = "compiler")]
@@ -97,28 +101,16 @@ impl Store {
engine: impl Into<Engine>,
tunables: impl Tunables + Send + Sync + 'static,
) -> Self {
let engine = engine.into();
let mut engine = engine.into();
engine.set_tunables(tunables);
// Make sure the signal handlers are installed.
// This is required for handling traps.
init_traps();
Self {
inner: Box::new(StoreInner {
objects: Default::default(),
engine: engine.cloned(),
tunables: Box::new(tunables),
trap_handler: None,
on_called: None,
}),
engine: engine.cloned(),
}
Self::new(engine)
}
#[cfg(feature = "compiler")]
/// Returns the [`Tunables`].
pub fn tunables(&self) -> &dyn Tunables {
self.inner.tunables.as_ref()
self.engine.tunables()
}
#[cfg(feature = "compiler")]
@@ -236,6 +228,34 @@ impl AsStoreMut for Store {
}
}
#[cfg(feature = "compiler")]
impl AsEngineRef for Store {
fn as_engine_ref(&self) -> EngineRef<'_> {
EngineRef::new(&self.engine)
}
}
#[cfg(feature = "compiler")]
impl AsEngineRef for &Store {
fn as_engine_ref(&self) -> EngineRef<'_> {
EngineRef::new(&self.engine)
}
}
#[cfg(feature = "compiler")]
impl AsEngineRef for StoreRef<'_> {
fn as_engine_ref(&self) -> EngineRef<'_> {
EngineRef::new(&self.inner.engine)
}
}
#[cfg(feature = "compiler")]
impl AsEngineRef for StoreMut<'_> {
fn as_engine_ref(&self) -> EngineRef<'_> {
EngineRef::new(&self.inner.engine)
}
}
impl fmt::Debug for Store {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Store").finish()
@@ -255,7 +275,7 @@ impl<'a> StoreRef<'a> {
#[cfg(feature = "compiler")]
/// Returns the [`Tunables`].
pub fn tunables(&self) -> &dyn Tunables {
self.inner.tunables.as_ref()
self.inner.engine.tunables()
}
#[cfg(feature = "compiler")]
@@ -296,7 +316,7 @@ impl<'a> StoreMut<'a> {
/// Returns the [`Tunables`].
#[cfg(feature = "compiler")]
pub fn tunables(&self) -> &dyn Tunables {
self.inner.tunables.as_ref()
self.inner.engine.tunables()
}
/// Returns the [`Engine`].
@@ -325,10 +345,9 @@ impl<'a> StoreMut<'a> {
#[cfg(feature = "compiler")]
pub(crate) fn tunables_and_objects_mut(&mut self) -> (&dyn Tunables, &mut StoreObjects) {
(self.inner.tunables.as_ref(), &mut self.inner.objects)
(self.inner.engine.tunables(), &mut self.inner.objects)
}
#[cfg(feature = "compiler")]
pub(crate) fn as_raw(&self) -> *mut StoreInner {
self.inner as *const StoreInner as *mut StoreInner
}

View File

@@ -1,140 +1,20 @@
use crate::sys::{MemoryType, Pages, TableType};
use std::ptr::NonNull;
use wasmer_compiler::Tunables;
use wasmer_types::{PointerWidth, Target};
use wasmer_vm::MemoryError;
use wasmer_vm::{
MemoryStyle, TableStyle, VMMemory, VMMemoryDefinition, VMTable, VMTableDefinition,
};
pub use wasmer_compiler::BaseTunables;
/// Tunable parameters for WebAssembly compilation.
/// This is the reference implementation of the `Tunables` trait,
/// used by default.
///
/// You can use this as a template for creating a custom Tunables
/// implementation or use composition to wrap your Tunables around
/// this one. The later approach is demonstrated in the
/// tunables-limit-memory example.
#[derive(Clone)]
pub struct BaseTunables {
/// For static heaps, the size in wasm pages of the heap protected by bounds checking.
pub static_memory_bound: Pages,
/// The size in bytes of the offset guard for static heaps.
pub static_memory_offset_guard_size: u64,
/// The size in bytes of the offset guard for dynamic heaps.
pub dynamic_memory_offset_guard_size: u64,
}
impl BaseTunables {
/// Get the `BaseTunables` for a specific Target
pub fn for_target(target: &Target) -> Self {
let triple = target.triple();
let pointer_width: PointerWidth = triple.pointer_width().unwrap();
let (static_memory_bound, static_memory_offset_guard_size): (Pages, u64) =
match pointer_width {
PointerWidth::U16 => (0x400.into(), 0x1000),
PointerWidth::U32 => (0x4000.into(), 0x1_0000),
// Static Memory Bound:
// Allocating 4 GiB of address space let us avoid the
// need for explicit bounds checks.
// Static Memory Guard size:
// Allocating 2 GiB of address space lets us translate wasm
// offsets into x86 offsets as aggressively as we can.
PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000),
};
// Allocate a small guard to optimize common cases but without
// wasting too much memory.
// The Windows memory manager seems more laxed than the other ones
// And a guard of just 1 page may not be enough is some borderline cases
// So using 2 pages for guard on this platform
#[cfg(target_os = "windows")]
let dynamic_memory_offset_guard_size: u64 = 0x2_0000;
#[cfg(not(target_os = "windows"))]
let dynamic_memory_offset_guard_size: u64 = 0x1_0000;
Self {
static_memory_bound,
static_memory_offset_guard_size,
dynamic_memory_offset_guard_size,
}
}
}
impl Tunables for BaseTunables {
/// Get a `MemoryStyle` for the provided `MemoryType`
fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
// A heap with a maximum that doesn't exceed the static memory bound specified by the
// tunables make it static.
//
// If the module doesn't declare an explicit maximum treat it as 4GiB.
let maximum = memory.maximum.unwrap_or_else(Pages::max_value);
if maximum <= self.static_memory_bound {
MemoryStyle::Static {
// Bound can be larger than the maximum for performance reasons
bound: self.static_memory_bound,
offset_guard_size: self.static_memory_offset_guard_size,
}
} else {
MemoryStyle::Dynamic {
offset_guard_size: self.dynamic_memory_offset_guard_size,
}
}
}
/// Get a [`TableStyle`] for the provided [`TableType`].
fn table_style(&self, _table: &TableType) -> TableStyle {
TableStyle::CallerChecksSignature
}
/// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`].
fn create_host_memory(
&self,
ty: &MemoryType,
style: &MemoryStyle,
) -> Result<VMMemory, MemoryError> {
VMMemory::new(ty, style)
}
/// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
///
/// # Safety
/// - `vm_definition_location` must point to a valid, owned `VMMemoryDefinition`,
/// for example in `VMContext`.
unsafe fn create_vm_memory(
&self,
ty: &MemoryType,
style: &MemoryStyle,
vm_definition_location: NonNull<VMMemoryDefinition>,
) -> Result<VMMemory, MemoryError> {
VMMemory::from_definition(ty, style, vm_definition_location)
}
/// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String> {
VMTable::new(ty, style)
}
/// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
///
/// # Safety
/// - `vm_definition_location` must point to a valid, owned `VMTableDefinition`,
/// for example in `VMContext`.
unsafe fn create_vm_table(
&self,
ty: &TableType,
style: &TableStyle,
vm_definition_location: NonNull<VMTableDefinition>,
) -> Result<VMTable, String> {
VMTable::from_definition(ty, style, vm_definition_location)
}
}
// All BaseTunable definition now is in wasmer_compile crate
// Tests are still here
#[cfg(test)]
mod tests {
use super::*;
use crate::sys::TableType;
use std::cell::UnsafeCell;
use std::ptr::NonNull;
use wasmer_compiler::Tunables;
use wasmer_types::{MemoryType, Pages, WASM_PAGE_SIZE};
use wasmer_vm::{
LinearMemory, MemoryError, MemoryStyle, TableStyle, VMMemory, VMMemoryDefinition, VMTable,
VMTableDefinition,
};
#[test]
fn memory_style() {
@@ -175,11 +55,6 @@ mod tests {
}
}
use std::cell::UnsafeCell;
use std::ptr::NonNull;
use wasmer_types::{MemoryError, MemoryStyle, MemoryType, Pages, WASM_PAGE_SIZE};
use wasmer_vm::LinearMemory;
#[derive(Debug)]
struct VMTinyMemory {
mem: Vec<u8>,