Merge branch 'master' into feat-c-api-version

This commit is contained in:
Ivan Enderlin
2020-12-14 10:31:57 +01:00
26 changed files with 550 additions and 294 deletions

View File

@@ -13,6 +13,7 @@
* [#1867](https://github.com/wasmerio/wasmer/pull/1867) Added `Metering::get_remaining_points` and `Metering::set_remaining_points` * [#1867](https://github.com/wasmerio/wasmer/pull/1867) Added `Metering::get_remaining_points` and `Metering::set_remaining_points`
* [#1881](https://github.com/wasmerio/wasmer/pull/1881) Added `UnsupportedTarget` error to `CompileError` * [#1881](https://github.com/wasmerio/wasmer/pull/1881) Added `UnsupportedTarget` error to `CompileError`
* [#1908](https://github.com/wasmerio/wasmer/pull/1908) Implemented `TryFrom<Value<T>>` for `i32`/`u32`/`i64`/`u64`/`f32`/`f64` * [#1908](https://github.com/wasmerio/wasmer/pull/1908) Implemented `TryFrom<Value<T>>` for `i32`/`u32`/`i64`/`u64`/`f32`/`f64`
* [#1927](https://github.com/wasmerio/wasmer/pull/1927) Added mmap support in `Engine::deserialize_from_file` to speed up artifact loading
### Changed ### Changed
@@ -30,7 +31,7 @@
### Added ### Added
- [#1839](https://github.com/wasmerio/wasmer/pull/1839) Added support for Metering Middleware - [#1839](https://github.com/wasmerio/wasmer/pull/1839) Added support for Metering Middleware
- [#1837](https://github.com/wasmerio/wasmer/pull/1837) It is now possible to use exports of an `Intance` even after the `Instance` has been freed - [#1837](https://github.com/wasmerio/wasmer/pull/1837) It is now possible to use exports of an `Instance` even after the `Instance` has been freed
- [#1831](https://github.com/wasmerio/wasmer/pull/1831) Added support for Apple Silicon chips (`arm64-apple-darwin`) - [#1831](https://github.com/wasmerio/wasmer/pull/1831) Added support for Apple Silicon chips (`arm64-apple-darwin`)
- [#1739](https://github.com/wasmerio/wasmer/pull/1739) Improved function environment setup via `WasmerEnv` proc macro. - [#1739](https://github.com/wasmerio/wasmer/pull/1739) Improved function environment setup via `WasmerEnv` proc macro.
- [#1649](https://github.com/wasmerio/wasmer/pull/1649) Add outline of migration to 1.0.0 docs. - [#1649](https://github.com/wasmerio/wasmer/pull/1649) Add outline of migration to 1.0.0 docs.

11
Cargo.lock generated
View File

@@ -1186,6 +1186,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "memmap2"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.6.1" version = "0.6.1"
@@ -2465,7 +2474,6 @@ version = "1.0.0-beta1"
dependencies = [ dependencies = [
"blake3", "blake3",
"hex", "hex",
"memmap",
"thiserror", "thiserror",
"wasmer", "wasmer",
] ]
@@ -2613,6 +2621,7 @@ dependencies = [
"backtrace", "backtrace",
"bincode", "bincode",
"lazy_static", "lazy_static",
"memmap2",
"more-asserts", "more-asserts",
"rustc-demangle", "rustc-demangle",
"serde", "serde",

View File

@@ -29,13 +29,13 @@ fn main() -> anyhow::Result<()> {
"#; "#;
let store = Store::default(); let store = Store::default();
let module = Module::new(&store, &module_wat); let module = Module::new(&store, &module_wat)?;
// The module doesn't import anything, so we create an empty import object. // The module doesn't import anything, so we create an empty import object.
let import_object = imports! {}; let import_object = imports! {};
let instance = Instance::new(&module, &import_object)?; let instance = Instance::new(&module, &import_object)?;
let add_one = instance.exports.get_function("add_one")?; let add_one = instance.exports.get_function("add_one")?;
let result = add_one.call([Value::I32(42)])?; let result = add_one.call(&[Value::I32(42)])?;
assert_eq!(result[0], Value::I32(43)); assert_eq!(result[0], Value::I32(43));
Ok(()) Ok(())

View File

@@ -59,6 +59,8 @@ pub enum ExportError {
/// Exports is a special kind of map that allows easily unwrapping /// Exports is a special kind of map that allows easily unwrapping
/// the types of instances. /// the types of instances.
///
/// TODO: add examples of using exports
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Exports { pub struct Exports {
map: Arc<IndexMap<String, Extern>>, map: Arc<IndexMap<String, Extern>>,

View File

@@ -52,7 +52,7 @@ pub enum FunctionDefinition {
/// The module instance is used to resolve references to other definitions /// The module instance is used to resolve references to other definitions
/// during execution of the function. /// during execution of the function.
/// ///
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#function-instances /// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances>
/// ///
/// # Panics /// # Panics
/// - Closures (functions with captured environments) are not currently supported /// - Closures (functions with captured environments) are not currently supported

View File

@@ -15,7 +15,7 @@ use wasmer_vm::{Global as RuntimeGlobal, VMExportGlobal};
/// A global instance is the runtime representation of a global variable. /// A global instance is the runtime representation of a global variable.
/// It consists of an individual value and a flag indicating whether it is mutable. /// It consists of an individual value and a flag indicating whether it is mutable.
/// ///
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#global-instances /// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances>
#[derive(Clone)] #[derive(Clone)]
pub struct Global { pub struct Global {
store: Store, store: Store,

View File

@@ -22,7 +22,7 @@ use wasmer_vm::{Memory as RuntimeMemory, MemoryError, VMExportMemory};
/// A memory created by the host or in WebAssembly code will be accessible and /// A memory created by the host or in WebAssembly code will be accessible and
/// mutable from both host and WebAssembly. /// mutable from both host and WebAssembly.
/// ///
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances /// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances>
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Memory { pub struct Memory {
store: Store, store: Store,

View File

@@ -22,7 +22,7 @@ use wasmer_engine::Export;
/// An `Extern` is the runtime representation of an entity that /// An `Extern` is the runtime representation of an entity that
/// can be imported or exported. /// can be imported or exported.
/// ///
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#external-values /// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#external-values>
#[derive(Clone)] #[derive(Clone)]
pub enum Extern { pub enum Extern {
/// A external [`Function`]. /// A external [`Function`].

View File

@@ -16,7 +16,7 @@ use wasmer_vm::{Table as RuntimeTable, VMCallerCheckedAnyfunc, VMExportTable};
/// A table created by the host or in WebAssembly code will be accessible and /// A table created by the host or in WebAssembly code will be accessible and
/// mutable from both host and WebAssembly. /// mutable from both host and WebAssembly.
/// ///
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#table-instances /// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#table-instances>
#[derive(Clone)] #[derive(Clone)]
pub struct Table { pub struct Table {
store: Store, store: Store,

View File

@@ -16,7 +16,7 @@ use wasmer_vm::{InstanceHandle, VMContext};
/// functions, memories, tables and globals that allow /// functions, memories, tables and globals that allow
/// interacting with WebAssembly. /// interacting with WebAssembly.
/// ///
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#module-instances /// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#module-instances>
#[derive(Clone)] #[derive(Clone)]
pub struct Instance { pub struct Instance {
handle: Arc<Mutex<InstanceHandle>>, handle: Arc<Mutex<InstanceHandle>>,

View File

@@ -1,4 +1,3 @@
//! Wasmer API
#![doc( #![doc(
html_logo_url = "https://github.com/wasmerio.png?size=200", html_logo_url = "https://github.com/wasmerio.png?size=200",
html_favicon_url = "https://wasmer.io/static/icons/favicon.ico" html_favicon_url = "https://wasmer.io/static/icons/favicon.ico"
@@ -28,6 +27,232 @@
) )
)] )]
//! This crate contains the `wasmer` API. The `wasmer` API facilitates the efficient,
//! sandboxed execution of [WebAssembly (Wasm)][wasm] modules.
//!
//! Here's an example of the `wasmer` API in action:
//! ```
//! use wasmer::{Store, Module, Instance, Value, imports};
//!
//! fn main() -> anyhow::Result<()> {
//! let module_wat = r#"
//! (module
//! (type $t0 (func (param i32) (result i32)))
//! (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32)
//! get_local $p0
//! i32.const 1
//! i32.add))
//! "#;
//!
//! let store = Store::default();
//! let module = Module::new(&store, &module_wat)?;
//! // The module doesn't import anything, so we create an empty import object.
//! let import_object = imports! {};
//! let instance = Instance::new(&module, &import_object)?;
//!
//! let add_one = instance.exports.get_function("add_one")?;
//! let result = add_one.call(&[Value::I32(42)])?;
//! assert_eq!(result[0], Value::I32(43));
//!
//! Ok(())
//! }
//! ```
//!
//! For more examples of using the `wasmer` API, check out the
//! [wasmer examples][wasmer-examples].
//!
//! ---------
//!
//! # Table of Contents
//!
//! - [Wasm Primitives](#wasm-primitives)
//! - [Externs](#externs)
//! - [Functions](#functions)
//! - [Memories](#memories)
//! - [Globals](#globals)
//! - [Tables](#tables)
//! - [Project Layout](#project-layout)
//! - [Engines](#engines)
//! - [Compilers](#compilers)
//! - [Features](#features)
//!
//!
//! # Wasm Primitives
//! In order to make use of the power of the `wasmer` API, it's important
//! to understand the primitives around which the API is built.
//!
//! Wasm only deals with a small number of core data types, these data
//! types can be found in the [`Value`] type.
//!
//! In addition to the core Wasm types, the core types of the API are
//! referred to as "externs".
//!
//! ## Externs
//! An [`Extern`] is a type that can be imported or exported from a Wasm
//! module.
//!
//! To import an extern, simply give it a namespace and a name with the
//! [`imports`] macro:
//!
//! ```
//! # use wasmer::{imports, Function, Memory, MemoryType, Store, ImportObject};
//! # fn imports_example(store: &Store) -> ImportObject {
//! let memory = Memory::new(&store, MemoryType::new(1, None, false)).unwrap();
//! imports! {
//! "env" => {
//! "my_function" => Function::new_native(store, || println!("Hello")),
//! "memory" => memory,
//! }
//! }
//! # }
//! ```
//!
//! And to access an exported extern, see the [`Exports`] API, accessible
//! from any instance via `instance.exports`:
//!
//! ```
//! # use wasmer::{imports, Instance, Function, Memory, NativeFunc};
//! # fn exports_example(instance: &Instance) -> anyhow::Result<()> {
//! let memory = instance.exports.get_memory("memory")?;
//! let memory: &Memory = instance.exports.get("some_other_memory")?;
//! let add: NativeFunc<(i32, i32), i32> = instance.exports.get_native_function("add")?;
//! let result = add.call(5, 37)?;
//! assert_eq!(result, 42);
//! # Ok(())
//! # }
//! ```
//!
//! These are the primary types that the `wasmer` API uses.
//!
//! ### Functions
//! There are 2 types of functions in `wasmer`:
//! 1. Wasm functions
//! 2. Host functions
//!
//! A Wasm function is a function defined in a WebAssembly module that can
//! only perform computation without side effects and call other functions.
//!
//! Wasm functions take 0 or more arguments and return 0 or more results.
//! Wasm functions can only deal with the primitive types defined in
//! [`Value`].
//!
//! A Host function is any function implemented on the host, in this case in
//! Rust.
//!
//! Host functions can optionally be created with an environment that
//! implements [`WasmerEnv`]. This environment is useful for maintaining
//! host state (for example the filesystem in WASI).
//!
//! Thus WebAssembly modules by themselves cannot do anything but computation
//! on the core types in [`Value`]. In order to make them more useful we
//! give them access to the outside world with [`imports`].
//!
//! If you're looking for a sandboxed, POSIX-like environment to execute Wasm
//! in, check out the [`wasmer-wasi`][] crate for our implementation of WASI,
//! the WebAssembly System Interface.
//!
//! In the `wasmer` API we support functions which take their arguments and
//! return their results dynamically, [`Function`], and functions which
//! take their arguments and return their results statically, [`NativeFunc`].
//!
//! ### Memories
//! Memories store data.
//!
//! In most Wasm programs, nearly all data will live in a [`Memory`].
//!
//! This data can be shared between the host and guest to allow for more
//! interesting programs.
//!
//! ### Globals
//! A [`Global`] is a type that may be either mutable or immutable, and
//! contains one of the core Wasm types defined in [`Value`].
//!
//! ### Tables
//! A [`Table`] is an indexed list of items.
//!
//!
//! ## Project Layout
//!
//! The Wasmer project is divided into a number of crates, below is a dependency
//! graph with transitive dependencies removed.
//!
//! <div>
//! <img src="https://raw.githubusercontent.com/wasmerio/wasmer/master/docs/deps_dedup.svg" />
//! </div>
//!
//! While this crate is the top level API, we also publish crates built
//! on top of this API that you may be interested in using, including:
//!
//! - [wasmer-cache][] for caching compiled Wasm modules.
//! - [wasmer-emscripten][] for running Wasm modules compiled to the
//! Emscripten ABI.
//! - [wasmer-wasi][] for running Wasm modules compiled to the WASI ABI.
//!
//! --------
//!
//! The Wasmer project has two major abstractions:
//! 1. [Engines][wasmer-engine]
//! 2. [Compilers][wasmer-compiler]
//!
//! These two abstractions have multiple options that can be enabled
//! with features.
//!
//! ### Engines
//!
//! An engine is a system that uses a compiler to make a WebAssembly
//! module executable.
//!
//! ### Compilers
//!
//! A compiler is a system that handles the details of making a Wasm
//! module executable. For example, by generating native machine code
//! for each Wasm function.
//!
//!
//! ## Features
//!
//! This crate's features can be broken down into 2 kinds, features that
//! enable new functionality and features that set defaults.
//!
//! The features that enable new functionality are:
//! - `jit` - enable the JIT engine. (See [wasmer-jit][])
//! - `native` - enable the native engine. (See [wasmer-native][])
//! - `cranelift` - enable Wasmer's Cranelift compiler. (See [wasmer-cranelift][])
//! - `llvm` - enable Wasmer's LLVM compiler. (See [wasmer-llvm][])
//! - `singlepass` - enable Wasmer's Singlepass compiler. (See [wasmer-singlepass][])
//! - `wat` - enable `wasmer` to parse the WebAssembly text format.
//!
//! The features that set defaults come in sets that are mutually exclusive.
//!
//! The first set is the default compiler set:
//! - `default-cranelift` - set Wasmer's Cranelift compiler as the default.
//! - `default-llvm` - set Wasmer's LLVM compiler as the default.
//! - `default-singlepass` - set Wasmer's Singlepass compiler as the default.
//!
//! The next set is the default engine set:
//! - `default-jit` - set the JIT engine as the default.
//! - `default-native` - set the native engine as the default.
//!
//! --------
//!
//! By default the `wat`, `default-cranelift`, and `default-jit` features
//! are enabled.
//!
//!
//!
//! [wasm]: https://webassembly.org/
//! [wasmer-examples]: https://github.com/wasmerio/wasmer/tree/master/exmples
//! [wasmer-cache]: https://docs.rs/wasmer-cache/*/wasmer_cache/
//! [wasmer-compiler]: https://docs.rs/wasmer-compiler/*/wasmer_compiler/
//! [wasmer-cranelift]: https://docs.rs/wasmer-cranelift/*/wasmer_cranelift/
//! [wasmer-emscripten]: https://docs.rs/wasmer-emscripten/*/wasmer_emscripten/
//! [wasmer-engine]: https://docs.rs/wasmer-engine/*/wasmer_engine/
//! [wasmer-jit]: https://docs.rs/wasmer-jit/*/wasmer_jit/
//! [wasmer-native]: https://docs.rs/wasmer-native/*/wasmer_native/
//! [wasmer-singlepass]: https://docs.rs/wasmer-singlepass/*/wasmer_singlepass/
//! [wasmer-llvm]: https://docs.rs/wasmer-llvm/*/wasmer_llvm/
//! [wasmer-wasi]: https://docs.rs/wasmer-wasi/*/wasmer_wasi/
mod env; mod env;
mod exports; mod exports;
mod externals; mod externals;
@@ -41,8 +266,12 @@ mod tunables;
mod types; mod types;
mod utils; mod utils;
/// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`.
///
/// See the [`WasmerEnv`] trait for more information.
pub use wasmer_derive::WasmerEnv; pub use wasmer_derive::WasmerEnv;
#[doc(hidden)]
pub mod internals { pub mod internals {
//! We use the internals module for exporting types that are only //! We use the internals module for exporting types that are only
//! intended to use in internal crates such as the compatibility crate //! intended to use in internal crates such as the compatibility crate
@@ -92,7 +321,7 @@ pub use wasmer_types::{
// TODO: should those be moved into wasmer::vm as well? // TODO: should those be moved into wasmer::vm as well?
pub use wasmer_vm::{raise_user_trap, MemoryError, VMExport}; pub use wasmer_vm::{raise_user_trap, MemoryError, VMExport};
pub mod vm { pub mod vm {
//! We use the vm module for re-exporting wasmer-vm types //! The vm module re-exports wasmer-vm types.
pub use wasmer_vm::{ pub use wasmer_vm::{
Memory, MemoryError, MemoryStyle, Table, TableStyle, VMMemoryDefinition, VMTableDefinition, Memory, MemoryError, MemoryStyle, Table, TableStyle, VMMemoryDefinition, VMTableDefinition,

View File

@@ -15,7 +15,7 @@ use wasmer_engine::Tunables as BaseTunables;
/// the Wasm bytes into a valid module artifact), in addition to the /// the Wasm bytes into a valid module artifact), in addition to the
/// [`Tunables`] (that are used to create the memories, tables and globals). /// [`Tunables`] (that are used to create the memories, tables and globals).
/// ///
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#store /// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#store>
#[derive(Clone)] #[derive(Clone)]
pub struct Store { pub struct Store {
engine: Arc<dyn Engine + Send + Sync>, engine: Arc<dyn Engine + Send + Sync>,

View File

@@ -13,7 +13,7 @@ pub use wasmer_types::{
/// * Floating-point (32 or 64 bit width) /// * Floating-point (32 or 64 bit width)
/// * Vectors (128 bits, with 32 or 64 bit lanes) /// * Vectors (128 bits, with 32 or 64 bit lanes)
/// ///
/// Spec: https://webassembly.github.io/spec/core/exec/runtime.html#values /// Spec: <https://webassembly.github.io/spec/core/exec/runtime.html#values>
pub type Val = Value<Function>; pub type Val = Value<Function>;
impl StoreObject for Val { impl StoreObject for Val {

View File

@@ -40,7 +40,7 @@ paste = "0.1"
# wasmer-wasm-c-api = { version = "1.0.0-beta1", path = "crates/wasm-c-api" } # wasmer-wasm-c-api = { version = "1.0.0-beta1", path = "crates/wasm-c-api" }
[dev-dependencies] [dev-dependencies]
inline-c = "0.1.2" inline-c = "0.1.4"
[features] [features]
default = [ default = [

View File

@@ -16,7 +16,6 @@ $(info * LDLIBS: $(LDLIBS))
CAPI_WASMER_TESTS = \ CAPI_WASMER_TESTS = \
test-early-exit test-memory test-wasi \ test-early-exit test-memory test-wasi \
test-wat2wasm
CAPI_BASE_TESTS = \ CAPI_BASE_TESTS = \
wasm-c-api/example/callback wasm-c-api/example/global wasm-c-api/example/hello \ wasm-c-api/example/callback wasm-c-api/example/global wasm-c-api/example/hello \

View File

@@ -1,53 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasmer_wasm.h"
#define own
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Getting Wasm.
printf("Compiling WAT to Wasm...\n");
wasm_byte_vec_t wat = {
.data = "(module)",
.size = 8,
};
wasm_byte_vec_t wasm;
wat2wasm(&wat, &wasm);
if (!wasm.data) {
printf("> Error compiling WAT to Wasm!\n");
return 1;
}
if (wasm.size != 8) {
printf("The Wasm size is incorrect!\n");
return 1;
}
if (!(wasm.data[0] == 0 &&
wasm.data[1] == 'a' &&
wasm.data[2] == 's' &&
wasm.data[3] == 'm' &&
wasm.data[4] == 1 &&
wasm.data[5] == 0 &&
wasm.data[6] == 0 &&
wasm.data[7] == 0)) {
printf("The Wasm data is incorrect!\n");
return 1;
}
wasm_byte_vec_delete(&wasm);
// All done.
printf("Done.\n");
return 0;
}

View File

@@ -12,7 +12,6 @@ edition = "2018"
[dependencies] [dependencies]
wasmer = { path = "../api", version = "1.0.0-beta1", default-features = false } wasmer = { path = "../api", version = "1.0.0-beta1", default-features = false }
memmap = "0.7"
hex = "0.4" hex = "0.4"
thiserror = "1" thiserror = "1"
blake3 = "0.3" blake3 = "0.3"

28
lib/cache/src/hash.rs vendored
View File

@@ -9,7 +9,9 @@ use std::string::ToString;
pub struct Hash([u8; 32]); pub struct Hash([u8; 32]);
impl Hash { impl Hash {
/// Creates a new hash. Has to be encodable as a hex format. /// Creates a new instance from 32 raw bytes.
/// Does not perform any hashing. In order to create a hash from data,
/// use `Hash::generate`.
pub fn new(bytes: [u8; 32]) -> Self { pub fn new(bytes: [u8; 32]) -> Self {
Self(bytes) Self(bytes)
} }
@@ -20,10 +22,8 @@ impl Hash {
Self::new(hash.into()) Self::new(hash.into())
} }
pub(crate) fn into_array(self) -> [u8; 32] { pub(crate) fn to_array(&self) -> [u8; 32] {
let mut total = [0u8; 32]; self.0
total[0..32].copy_from_slice(&self.0);
total
} }
} }
@@ -31,7 +31,7 @@ impl ToString for Hash {
/// Create the hexadecimal representation of the /// Create the hexadecimal representation of the
/// stored hash. /// stored hash.
fn to_string(&self) -> String { fn to_string(&self) -> String {
hex::encode(&self.into_array() as &[u8]) hex::encode(&self.to_array())
} }
} }
@@ -56,3 +56,19 @@ impl FromStr for Hash {
})?)) })?))
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_to_array_works() {
let original = [
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x65, 0xAA, 0xBB, 0xCC, 0xDD,
0xEE, 0xFF, 0x12, 0x65,
];
let hash = Hash::new(original);
assert_eq!(hash.to_array(), original);
}
}

View File

@@ -18,6 +18,7 @@ target-lexicon = { version = "0.11", default-features = false }
# flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" } # flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" }
backtrace = "0.3" backtrace = "0.3"
rustc-demangle = "0.1" rustc-demangle = "0.1"
memmap2 = "0.1.0"
more-asserts = "0.2" more-asserts = "0.2"
thiserror = "1.0" thiserror = "1.0"
serde = { version = "1.0", features = ["derive", "rc"] } serde = { version = "1.0", features = ["derive", "rc"] }

View File

@@ -12,8 +12,8 @@ use wasmer_types::{
SignatureIndex, TableIndex, SignatureIndex, TableIndex,
}; };
use wasmer_vm::{ use wasmer_vm::{
FunctionBodyPtr, InstanceHandle, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex, FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, ModuleInfo, TableStyle,
VMTrampoline, VMSharedSignatureIndex, VMTrampoline,
}; };
/// An `Artifact` is the product that the `Engine` /// An `Artifact` is the product that the `Engine`
@@ -95,7 +95,6 @@ pub trait Artifact: Send + Sync + Upcastable {
self.preinstantiate()?; self.preinstantiate()?;
let module = self.module(); let module = self.module();
let (instance_ptr, offsets) = InstanceHandle::allocate_instance(&module);
let (imports, import_initializers) = { let (imports, import_initializers) = {
let mut imports = resolve_imports( let mut imports = resolve_imports(
&module, &module,
@@ -114,15 +113,14 @@ pub trait Artifact: Send + Sync + Upcastable {
}; };
// Get pointers to where metadata about local memories should live in VM memory. // Get pointers to where metadata about local memories should live in VM memory.
let memory_definition_locations = // Get pointers to where metadata about local tables should live in VM memory.
InstanceHandle::memory_definition_locations(instance_ptr, &offsets);
let (allocator, memory_definition_locations, table_definition_locations) =
InstanceAllocator::new(&*module);
let finished_memories = tunables let finished_memories = tunables
.create_memories(&module, self.memory_styles(), &memory_definition_locations) .create_memories(&module, self.memory_styles(), &memory_definition_locations)
.map_err(InstantiationError::Link)? .map_err(InstantiationError::Link)?
.into_boxed_slice(); .into_boxed_slice();
// Get pointers to where metadata about local tables should live in VM memory.
let table_definition_locations =
InstanceHandle::table_definition_locations(instance_ptr, &offsets);
let finished_tables = tunables let finished_tables = tunables
.create_tables(&module, self.table_styles(), &table_definition_locations) .create_tables(&module, self.table_styles(), &table_definition_locations)
.map_err(InstantiationError::Link)? .map_err(InstantiationError::Link)?
@@ -135,8 +133,7 @@ pub trait Artifact: Send + Sync + Upcastable {
self.register_frame_info(); self.register_frame_info();
let handle = InstanceHandle::new( let handle = InstanceHandle::new(
instance_ptr, allocator,
offsets,
module, module,
self.finished_functions().clone(), self.finished_functions().clone(),
self.finished_function_call_trampolines().clone(), self.finished_function_call_trampolines().clone(),

View File

@@ -2,6 +2,7 @@
use crate::tunables::Tunables; use crate::tunables::Tunables;
use crate::{Artifact, DeserializeError}; use crate::{Artifact, DeserializeError};
use memmap2::Mmap;
use std::path::Path; use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::Arc; use std::sync::Arc;
@@ -51,8 +52,9 @@ pub trait Engine {
&self, &self,
file_ref: &Path, file_ref: &Path,
) -> Result<Arc<dyn Artifact>, DeserializeError> { ) -> Result<Arc<dyn Artifact>, DeserializeError> {
let bytes = std::fs::read(file_ref)?; let file = std::fs::File::open(file_ref)?;
self.deserialize(&bytes) let mmap = Mmap::map(&file)?;
self.deserialize(&mmap)
} }
/// A unique identifier for this object. /// A unique identifier for this object.

View File

@@ -2,7 +2,7 @@
// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md // Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md
use crate::global::Global; use crate::global::Global;
use crate::instance::InstanceAllocator; use crate::instance::InstanceRef;
use crate::memory::{Memory, MemoryStyle}; use crate::memory::{Memory, MemoryStyle};
use crate::table::{Table, TableStyle}; use crate::table::{Table, TableStyle};
use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline}; use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline};
@@ -49,8 +49,8 @@ pub struct VMExportFunction {
pub call_trampoline: Option<VMTrampoline>, pub call_trampoline: Option<VMTrampoline>,
/// A “reference” to the instance through the /// A “reference” to the instance through the
/// `InstanceAllocator`. `None` if it is a host function. /// `InstanceRef`. `None` if it is a host function.
pub instance_allocator: Option<InstanceAllocator>, pub instance_allocator: Option<InstanceRef>,
} }
/// # Safety /// # Safety
@@ -74,8 +74,8 @@ pub struct VMExportTable {
pub from: Arc<dyn Table>, pub from: Arc<dyn Table>,
/// A “reference” to the instance through the /// A “reference” to the instance through the
/// `InstanceAllocator`. `None` if it is a host function. /// `InstanceRef`. `None` if it is a host function.
pub instance_allocator: Option<InstanceAllocator>, pub instance_allocator: Option<InstanceRef>,
} }
/// # Safety /// # Safety
@@ -120,8 +120,8 @@ pub struct VMExportMemory {
pub from: Arc<dyn Memory>, pub from: Arc<dyn Memory>,
/// A “reference” to the instance through the /// A “reference” to the instance through the
/// `InstanceAllocator`. `None` if it is a host function. /// `InstanceRef`. `None` if it is a host function.
pub instance_allocator: Option<InstanceAllocator>, pub instance_allocator: Option<InstanceRef>,
} }
/// # Safety /// # Safety
@@ -166,8 +166,8 @@ pub struct VMExportGlobal {
pub from: Arc<Global>, pub from: Arc<Global>,
/// A “reference” to the instance through the /// A “reference” to the instance through the
/// `InstanceAllocator`. `None` if it is a host function. /// `InstanceRef`. `None` if it is a host function.
pub instance_allocator: Option<InstanceAllocator>, pub instance_allocator: Option<InstanceRef>,
} }
/// # Safety /// # Safety

View File

@@ -0,0 +1,197 @@
use super::{Instance, InstanceRef};
use crate::vmcontext::{VMMemoryDefinition, VMTableDefinition};
use crate::{ModuleInfo, VMOffsets};
use std::alloc::{self, Layout};
use std::convert::TryFrom;
use std::mem;
use std::ptr::{self, NonNull};
use wasmer_types::entity::EntityRef;
use wasmer_types::{LocalMemoryIndex, LocalTableIndex};
/// This is an intermediate type that manages the raw allocation and
/// metadata when creating an [`Instance`].
///
/// This type will free the allocated memory if it's dropped before
/// being used.
///
/// The [`InstanceAllocator::instance_layout`] computes the correct
/// layout to represent the wanted [`Instance`].
///
/// Then we use this layout to allocate an empty `Instance` properly.
pub struct InstanceAllocator {
/// The buffer that will contain the [`Instance`] and dynamic fields.
instance_ptr: NonNull<Instance>,
/// The layout of the `instance_ptr` buffer.
instance_layout: Layout,
/// Information about the offsets into the `instance_ptr` buffer for
/// the dynamic fields.
offsets: VMOffsets,
/// Whether or not this type has transferred ownership of the
/// `instance_ptr` buffer. If it has not when being dropped,
/// the buffer should be freed.
consumed: bool,
}
impl Drop for InstanceAllocator {
fn drop(&mut self) {
if !self.consumed {
// If `consumed` has not been set, then we still have ownership
// over the buffer and must free it.
let instance_ptr = self.instance_ptr.as_ptr();
unsafe {
std::alloc::dealloc(instance_ptr as *mut u8, self.instance_layout);
}
}
}
}
impl InstanceAllocator {
/// Allocates instance data for use with [`InstanceHandle::new`].
///
/// Returns a wrapper type around the allocation and 2 vectors of
/// pointers into the allocated buffer. These lists of pointers
/// correspond to the location in memory for the local memories and
/// tables respectively. These pointers should be written to before
/// calling [`InstanceHandle::new`].
pub fn new(
module: &ModuleInfo,
) -> (
InstanceAllocator,
Vec<NonNull<VMMemoryDefinition>>,
Vec<NonNull<VMTableDefinition>>,
) {
let offsets = VMOffsets::new(mem::size_of::<usize>() as u8, module);
let instance_layout = Self::instance_layout(&offsets);
#[allow(clippy::cast_ptr_alignment)]
let instance_ptr = unsafe { alloc::alloc(instance_layout) as *mut Instance };
let instance_ptr = if let Some(ptr) = NonNull::new(instance_ptr) {
ptr
} else {
alloc::handle_alloc_error(instance_layout);
};
let allocator = Self {
instance_ptr,
instance_layout,
offsets,
consumed: false,
};
// # Safety
// Both of these calls are safe because we allocate the pointer
// above with the same `offsets` that these functions use.
// Thus there will be enough valid memory for both of them.
let memories = unsafe { allocator.memory_definition_locations() };
let tables = unsafe { allocator.table_definition_locations() };
(allocator, memories, tables)
}
/// Calculate the appropriate layout for the [`Instance`].
fn instance_layout(offsets: &VMOffsets) -> Layout {
let vmctx_size = usize::try_from(offsets.size_of_vmctx())
.expect("Failed to convert the size of `vmctx` to a `usize`");
let instance_vmctx_layout =
Layout::array::<u8>(vmctx_size).expect("Failed to create a layout for `VMContext`");
let (instance_layout, _offset) = Layout::new::<Instance>()
.extend(instance_vmctx_layout)
.expect("Failed to extend to `Instance` layout to include `VMContext`");
instance_layout.pad_to_align()
}
/// Get the locations of where the local [`VMMemoryDefinition`]s should be stored.
///
/// This function lets us create `Memory` objects on the host with backing
/// memory in the VM.
///
/// # Safety
/// - `instance_ptr` must point to enough memory that all of the
/// offsets in `offsets` point to valid locations in memory,
/// i.e. `instance_ptr` must have been allocated by
/// `Self::new`.
unsafe fn memory_definition_locations(&self) -> Vec<NonNull<VMMemoryDefinition>> {
let num_memories = self.offsets.num_local_memories;
let num_memories = usize::try_from(num_memories).unwrap();
let mut out = Vec::with_capacity(num_memories);
// We need to do some pointer arithmetic now. The unit is `u8`.
let ptr = self.instance_ptr.cast::<u8>().as_ptr();
let base_ptr = ptr.add(mem::size_of::<Instance>());
for i in 0..num_memories {
let mem_offset = self
.offsets
.vmctx_vmmemory_definition(LocalMemoryIndex::new(i));
let mem_offset = usize::try_from(mem_offset).unwrap();
let new_ptr = NonNull::new_unchecked(base_ptr.add(mem_offset));
out.push(new_ptr.cast());
}
out
}
/// Get the locations of where the [`VMTableDefinition`]s should be stored.
///
/// This function lets us create [`Table`] objects on the host with backing
/// memory in the VM.
///
/// # Safety
/// - `instance_ptr` must point to enough memory that all of the
/// offsets in `offsets` point to valid locations in memory,
/// i.e. `instance_ptr` must have been allocated by
/// `Self::new`.
unsafe fn table_definition_locations(&self) -> Vec<NonNull<VMTableDefinition>> {
let num_tables = self.offsets.num_local_tables;
let num_tables = usize::try_from(num_tables).unwrap();
let mut out = Vec::with_capacity(num_tables);
// We need to do some pointer arithmetic now. The unit is `u8`.
let ptr = self.instance_ptr.cast::<u8>().as_ptr();
let base_ptr = ptr.add(std::mem::size_of::<Instance>());
for i in 0..num_tables {
let table_offset = self
.offsets
.vmctx_vmtable_definition(LocalTableIndex::new(i));
let table_offset = usize::try_from(table_offset).unwrap();
let new_ptr = NonNull::new_unchecked(base_ptr.add(table_offset));
out.push(new_ptr.cast());
}
out
}
/// Finish preparing by writing the [`Instance`] into memory.
pub(crate) fn write_instance(mut self, instance: Instance) -> InstanceRef {
// prevent the old state's drop logic from being called as we
// transition into the new state.
self.consumed = true;
unsafe {
// `instance` is moved at `instance_ptr`. This pointer has
// been allocated by `Self::allocate_instance` (so by
// `InstanceRef::allocate_instance`.
ptr::write(self.instance_ptr.as_ptr(), instance);
// Now `instance_ptr` is correctly initialized!
}
let instance = self.instance_ptr;
let instance_layout = self.instance_layout;
// This is correct because of the invariants of `Self` and
// because we write `Instance` to the pointer in this function.
unsafe { InstanceRef::new(instance, instance_layout) }
}
/// Get the [`VMOffsets`] for the allocated buffer.
pub(crate) fn offsets(&self) -> &VMOffsets {
&self.offsets
}
}

View File

@@ -3,9 +3,13 @@
//! An `Instance` contains all the runtime state used by execution of //! An `Instance` contains all the runtime state used by execution of
//! a WebAssembly module (except its callstack and register state). An //! a WebAssembly module (except its callstack and register state). An
//! `InstanceAllocator` is a wrapper around `Instance` that manages //! `InstanceRef` is a wrapper around `Instance` that manages
//! how it is allocated and deallocated. An `InstanceHandle` is a //! how it is allocated and deallocated. An `InstanceHandle` is a
//! wrapper around an `InstanceAllocator`. //! wrapper around an `InstanceRef`.
mod allocator;
pub use allocator::InstanceAllocator;
use crate::export::VMExport; use crate::export::VMExport;
use crate::global::Global; use crate::global::Global;
@@ -23,7 +27,7 @@ use crate::{FunctionBodyPtr, ModuleInfo, VMOffsets};
use crate::{VMExportFunction, VMExportGlobal, VMExportMemory, VMExportTable}; use crate::{VMExportFunction, VMExportGlobal, VMExportMemory, VMExportTable};
use memoffset::offset_of; use memoffset::offset_of;
use more_asserts::assert_lt; use more_asserts::assert_lt;
use std::alloc::{self, Layout}; use std::alloc::Layout;
use std::any::Any; use std::any::Any;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
@@ -688,15 +692,16 @@ impl Instance {
} }
} }
/// An `InstanceAllocator` is responsible to allocate, to deallocate, /// TODO: update these docs
/// An `InstanceRef` is responsible to allocate, to deallocate,
/// and to give access to an `Instance`, in such a way that `Instance` /// and to give access to an `Instance`, in such a way that `Instance`
/// is unique, can be shared, safely, across threads, without /// is unique, can be shared, safely, across threads, without
/// duplicating the pointer in multiple locations. `InstanceAllocator` /// duplicating the pointer in multiple locations. `InstanceRef`
/// must be the only “owner” of an `Instance`. /// must be the only “owner” of an `Instance`.
/// ///
/// Consequently, one must not share `Instance` but /// Consequently, one must not share `Instance` but
/// `InstanceAllocator`. It acts like an Atomically Reference Counted /// `InstanceRef`. It acts like an Atomically Reference Counted
/// to `Instance`. In short, `InstanceAllocator` is roughly a /// to `Instance`. In short, `InstanceRef` is roughly a
/// simplified version of `std::sync::Arc`. /// simplified version of `std::sync::Arc`.
/// ///
/// It is important to remind that `Instance` is dynamically-sized /// It is important to remind that `Instance` is dynamically-sized
@@ -707,31 +712,25 @@ impl Instance {
/// 1. Define the correct layout for `Instance` (size and alignment), /// 1. Define the correct layout for `Instance` (size and alignment),
/// 2. Allocate it properly. /// 2. Allocate it properly.
/// ///
/// The `InstanceAllocator::instance_layout` computes the correct /// This allocation must be freed with [`InstanceRef::deallocate_instance`]
/// layout to represent the wanted `Instance`. /// if and only if it has been set correctly. The `Drop` implementation of
/// [`InstanceRef`] calls its `deallocate_instance` method without
/// checking if this property holds, only when `Self.strong` is equal to 1.
/// ///
/// Then `InstanceAllocator::allocate_instance` will use this layout /// Note for the curious reader: [`InstanceAllocator::new`]
/// to allocate an empty `Instance` properly. This allocation must be /// and [`InstanceHandle::new`] will respectively allocate a proper
/// freed with `InstanceAllocator::deallocate_instance` if and only if
/// it has been set correctly. The `Drop` implementation of
/// `InstanceAllocator` calls its `deallocate_instance` method without
/// checking if this property holds, only when `Self.strong` is equal
/// to 1.
///
/// Note for the curious reader: `InstanceHandle::allocate_instance`
/// and `InstanceHandle::new` will respectively allocate a proper
/// `Instance` and will fill it correctly. /// `Instance` and will fill it correctly.
/// ///
/// A little bit of background: The initial goal was to be able to /// A little bit of background: The initial goal was to be able to
/// shared an `Instance` between an `InstanceHandle` and the module /// share an [`Instance`] between an [`InstanceHandle`] and the module
/// exports, so that one can drop a `InstanceHandle` but still being /// exports, so that one can drop a [`InstanceHandle`] but still being
/// able to use the exports properly. /// able to use the exports properly.
/// ///
/// This structure has a C representation because `Instance` is /// This structure has a C representation because `Instance` is
/// dynamically-sized, and the `instance` field must be last. /// dynamically-sized, and the `instance` field must be last.
#[derive(Debug)] #[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct InstanceAllocator { pub struct InstanceRef {
/// Number of `Self` in the nature. It increases when `Self` is /// Number of `Self` in the nature. It increases when `Self` is
/// cloned, and it decreases when `Self` is dropped. /// cloned, and it decreases when `Self` is dropped.
strong: Arc<atomic::AtomicUsize>, strong: Arc<atomic::AtomicUsize>,
@@ -740,7 +739,7 @@ pub struct InstanceAllocator {
instance_layout: Layout, instance_layout: Layout,
/// The `Instance` itself. It must be the last field of /// The `Instance` itself. It must be the last field of
/// `InstanceAllocator` since `Instance` is dyamically-sized. /// `InstanceRef` since `Instance` is dyamically-sized.
/// ///
/// `Instance` must not be dropped manually by Rust, because it's /// `Instance` must not be dropped manually by Rust, because it's
/// allocated manually with `alloc` and a specific layout (Rust /// allocated manually with `alloc` and a specific layout (Rust
@@ -752,14 +751,8 @@ pub struct InstanceAllocator {
instance: NonNull<Instance>, instance: NonNull<Instance>,
} }
impl InstanceAllocator { impl InstanceRef {
/// A soft limit on the amount of references that may be made to an `InstanceAllocator`. /// Create a new `InstanceRef`. It allocates nothing. It
///
/// Going above this limit will make the program to panic at exactly
/// `MAX_REFCOUNT` references.
const MAX_REFCOUNT: usize = std::usize::MAX - 1;
/// Create a new `InstanceAllocator`. It allocates nothing. It
/// fills nothing. The `Instance` must be already valid and /// fills nothing. The `Instance` must be already valid and
/// filled. `self_ptr` and `self_layout` must be the pointer and /// filled. `self_ptr` and `self_layout` must be the pointer and
/// the layout returned by `Self::allocate_self` used to build /// the layout returned by `Self::allocate_self` used to build
@@ -769,9 +762,10 @@ impl InstanceAllocator {
/// ///
/// `instance` must a non-null, non-dangling, properly aligned, /// `instance` must a non-null, non-dangling, properly aligned,
/// and correctly initialized pointer to `Instance`. See /// and correctly initialized pointer to `Instance`. See
/// `InstanceHandle::new` for an example of how to correctly use /// [`InstanceAllocator::new`] for an example of how to correctly use
/// this API. /// this API.
unsafe fn new(instance: NonNull<Instance>, instance_layout: Layout) -> Self { /// TODO: update docs
pub(crate) unsafe fn new(instance: NonNull<Instance>, instance_layout: Layout) -> Self {
Self { Self {
strong: Arc::new(atomic::AtomicUsize::new(1)), strong: Arc::new(atomic::AtomicUsize::new(1)),
instance_layout, instance_layout,
@@ -779,38 +773,11 @@ impl InstanceAllocator {
} }
} }
/// Calculate the appropriate layout for `Instance`. /// A soft limit on the amount of references that may be made to an `InstanceRef`.
fn instance_layout(offsets: &VMOffsets) -> Layout {
let vmctx_size = usize::try_from(offsets.size_of_vmctx())
.expect("Failed to convert the size of `vmctx` to a `usize`");
let instance_vmctx_layout =
Layout::array::<u8>(vmctx_size).expect("Failed to create a layout for `VMContext`");
let (instance_layout, _offset) = Layout::new::<Instance>()
.extend(instance_vmctx_layout)
.expect("Failed to extend to `Instance` layout to include `VMContext`");
instance_layout.pad_to_align()
}
/// Allocate `Instance` (it is an uninitialized pointer).
/// ///
/// `offsets` is used to compute the layout with `Self::instance_layout`. /// Going above this limit will make the program to panic at exactly
fn allocate_instance(offsets: &VMOffsets) -> (NonNull<Instance>, Layout) { /// `MAX_REFCOUNT` references.
let layout = Self::instance_layout(offsets); const MAX_REFCOUNT: usize = std::usize::MAX - 1;
#[allow(clippy::cast_ptr_alignment)]
let instance_ptr = unsafe { alloc::alloc(layout) as *mut Instance };
let ptr = if let Some(ptr) = NonNull::new(instance_ptr) {
ptr
} else {
alloc::handle_alloc_error(layout);
};
(ptr, layout)
}
/// Deallocate `Instance`. /// Deallocate `Instance`.
/// ///
@@ -826,7 +793,7 @@ impl InstanceAllocator {
} }
/// Get the number of strong references pointing to this /// Get the number of strong references pointing to this
/// `InstanceAllocator`. /// `InstanceRef`.
pub fn strong_count(&self) -> usize { pub fn strong_count(&self) -> usize {
self.strong.load(atomic::Ordering::SeqCst) self.strong.load(atomic::Ordering::SeqCst)
} }
@@ -847,13 +814,13 @@ impl InstanceAllocator {
} }
/// TODO: Review this super carefully. /// TODO: Review this super carefully.
unsafe impl Send for InstanceAllocator {} unsafe impl Send for InstanceRef {}
unsafe impl Sync for InstanceAllocator {} unsafe impl Sync for InstanceRef {}
impl Clone for InstanceAllocator { impl Clone for InstanceRef {
/// Makes a clone of `InstanceAllocator`. /// Makes a clone of `InstanceRef`.
/// ///
/// This creates another `InstanceAllocator` using the same /// This creates another `InstanceRef` using the same
/// `instance` pointer, increasing the strong reference count. /// `instance` pointer, increasing the strong reference count.
#[inline] #[inline]
fn clone(&self) -> Self { fn clone(&self) -> Self {
@@ -873,7 +840,7 @@ impl Clone for InstanceAllocator {
let old_size = self.strong.fetch_add(1, atomic::Ordering::Relaxed); let old_size = self.strong.fetch_add(1, atomic::Ordering::Relaxed);
// However we need to guard against massive refcounts in case // However we need to guard against massive refcounts in case
// someone is `mem::forget`ing `InstanceAllocator`. If we // someone is `mem::forget`ing `InstanceRef`. If we
// don't do this the count can overflow and users will // don't do this the count can overflow and users will
// use-after free. We racily saturate to `isize::MAX` on the // use-after free. We racily saturate to `isize::MAX` on the
// assumption that there aren't ~2 billion threads // assumption that there aren't ~2 billion threads
@@ -884,7 +851,7 @@ impl Clone for InstanceAllocator {
// and we don't care to support it. // and we don't care to support it.
if old_size > Self::MAX_REFCOUNT { if old_size > Self::MAX_REFCOUNT {
panic!("Too many references of `InstanceAllocator`"); panic!("Too many references of `InstanceRef`");
} }
Self { Self {
@@ -895,16 +862,16 @@ impl Clone for InstanceAllocator {
} }
} }
impl PartialEq for InstanceAllocator { impl PartialEq for InstanceRef {
/// Two `InstanceAllocator` are equal if and only if /// Two `InstanceRef` are equal if and only if
/// `Self.instance` points to the same location. /// `Self.instance` points to the same location.
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.instance == other.instance self.instance == other.instance
} }
} }
impl Drop for InstanceAllocator { impl Drop for InstanceRef {
/// Drop the `InstanceAllocator`. /// Drop the `InstanceRef`.
/// ///
/// This will decrement the strong reference count. If it reaches /// This will decrement the strong reference count. If it reaches
/// 1, then the `Self.instance` will be deallocated with /// 1, then the `Self.instance` will be deallocated with
@@ -938,39 +905,24 @@ impl Drop for InstanceAllocator {
// Now we can deallocate the instance. Note that we don't // Now we can deallocate the instance. Note that we don't
// check the pointer to `Instance` is correctly initialized, // check the pointer to `Instance` is correctly initialized,
// but the way `InstanceHandle` creates the // but the way `InstanceHandle` creates the
// `InstanceAllocator` ensures that. // `InstanceRef` ensures that.
unsafe { Self::deallocate_instance(self) }; unsafe { Self::deallocate_instance(self) };
} }
} }
/// A handle holding an `InstanceAllocator`, which holds an `Instance` /// A handle holding an `InstanceRef`, which holds an `Instance`
/// of a WebAssembly module. /// of a WebAssembly module.
/// ///
/// This is more or less a public facade of the private `Instance`, /// This is more or less a public facade of the private `Instance`,
/// providing useful higher-level API. /// providing useful higher-level API.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct InstanceHandle { pub struct InstanceHandle {
/// The `InstanceAllocator`. See its documentation to learn more. /// The `InstanceRef`. See its documentation to learn more.
instance: InstanceAllocator, instance: InstanceRef,
} }
impl InstanceHandle { impl InstanceHandle {
/// Allocates an instance for use with `InstanceHandle::new`. /// Create a new `InstanceHandle` pointing at a new `InstanceRef`.
///
/// Returns the instance pointer and the [`VMOffsets`] that describe the
/// memory buffer pointed to by the instance pointer.
///
/// It should ideally return `NonNull<Instance>` rather than
/// `NonNull<u8>`, however `Instance` is private, and we want to
/// keep it private.
pub fn allocate_instance(module: &ModuleInfo) -> (NonNull<u8>, VMOffsets) {
let offsets = VMOffsets::new(mem::size_of::<*const u8>() as u8, module);
let (instance_ptr, _instance_layout) = InstanceAllocator::allocate_instance(&offsets);
(instance_ptr.cast(), offsets)
}
/// Create a new `InstanceHandle` pointing at a new `InstanceAllocator`.
/// ///
/// # Safety /// # Safety
/// ///
@@ -987,17 +939,13 @@ impl InstanceHandle {
/// safety. /// safety.
/// ///
/// However the following must be taken care of before calling this function: /// However the following must be taken care of before calling this function:
/// - `instance_ptr` must point to valid memory sufficiently large
/// for the `Instance`. `instance_ptr` will be owned by
/// `InstanceAllocator`, see `InstanceAllocator` to learn more.
/// - The memory at `instance.tables_ptr()` must be initialized with data for /// - The memory at `instance.tables_ptr()` must be initialized with data for
/// all the local tables. /// all the local tables.
/// - The memory at `instance.memories_ptr()` must be initialized with data for /// - The memory at `instance.memories_ptr()` must be initialized with data for
/// all the local memories. /// all the local memories.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub unsafe fn new( pub unsafe fn new(
instance_ptr: NonNull<u8>, allocator: InstanceAllocator,
offsets: VMOffsets,
module: Arc<ModuleInfo>, module: Arc<ModuleInfo>,
finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>, finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>, finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
@@ -1009,10 +957,6 @@ impl InstanceHandle {
host_state: Box<dyn Any>, host_state: Box<dyn Any>,
import_initializers: ImportInitializerThunks, import_initializers: ImportInitializerThunks,
) -> Result<Self, Trap> { ) -> Result<Self, Trap> {
// `NonNull<u8>` here actually means `NonNull<Instance>`. See
// `Self::allocate_instance` to understand why.
let instance_ptr: NonNull<Instance> = instance_ptr.cast();
let vmctx_globals = finished_globals let vmctx_globals = finished_globals
.values() .values()
.map(|m| m.vmglobal()) .map(|m| m.vmglobal())
@@ -1021,7 +965,7 @@ impl InstanceHandle {
let passive_data = RefCell::new(module.passive_data.clone()); let passive_data = RefCell::new(module.passive_data.clone());
let handle = { let handle = {
let instance_layout = InstanceAllocator::instance_layout(&offsets); let offsets = allocator.offsets().clone();
// Create the `Instance`. The unique, the One. // Create the `Instance`. The unique, the One.
let instance = Instance { let instance = Instance {
module, module,
@@ -1039,20 +983,7 @@ impl InstanceHandle {
vmctx: VMContext {}, vmctx: VMContext {},
}; };
// `instance` is moved at `instance_ptr`. This pointer has let instance_allocator = allocator.write_instance(instance);
// been allocated by `Self::allocate_instance` (so by
// `InstanceAllocator::allocate_instance`.
ptr::write(instance_ptr.as_ptr(), instance);
// Now `instance_ptr` is correctly initialized!
// `instance_ptr` is passed to `InstanceAllocator`, which
// makes it the only “owner” (it doesn't own the value,
// it's just the semantics we define).
//
// SAFETY: `instance_ptr` fulfills all the requirement of
// `InstanceAllocator::new`.
let instance_allocator = InstanceAllocator::new(instance_ptr, instance_layout);
Self { Self {
instance: instance_allocator, instance: instance_allocator,
@@ -1110,85 +1041,10 @@ impl InstanceHandle {
} }
/// Return a reference to the contained `Instance`. /// Return a reference to the contained `Instance`.
pub(crate) fn instance(&self) -> &InstanceAllocator { pub(crate) fn instance(&self) -> &InstanceRef {
&self.instance &self.instance
} }
/// Get the locations of where the local `VMMemoryDefinition`s should be stored.
///
/// This function lets us create `Memory` objects on the host with backing
/// memory in the VM.
///
/// # Safety
/// - `instance_ptr` must point to enough memory that all of the
/// offsets in `offsets` point to valid locations in memory,
/// i.e. `instance_ptr` must have been allocated by
/// `InstanceHandle::allocate_instance`.
pub unsafe fn memory_definition_locations(
instance_ptr: NonNull<u8>,
offsets: &VMOffsets,
) -> Vec<NonNull<VMMemoryDefinition>> {
// `NonNull<u8>` here actually means `NonNull<Instance>`. See
// `Self::allocate_instance` to understand why.
let instance_ptr: NonNull<Instance> = instance_ptr.cast();
let num_memories = offsets.num_local_memories;
let num_memories = usize::try_from(num_memories).unwrap();
let mut out = Vec::with_capacity(num_memories);
// We need to do some pointer arithmetic now. The unit is `u8`.
let ptr = instance_ptr.cast::<u8>().as_ptr();
let base_ptr = ptr.add(mem::size_of::<Instance>());
for i in 0..num_memories {
let mem_offset = offsets.vmctx_vmmemory_definition(LocalMemoryIndex::new(i));
let mem_offset = usize::try_from(mem_offset).unwrap();
let new_ptr = NonNull::new_unchecked(base_ptr.add(mem_offset));
out.push(new_ptr.cast());
}
out
}
/// Get the locations of where the `VMTableDefinition`s should be stored.
///
/// This function lets us create `Table` objects on the host with backing
/// memory in the VM.
///
/// # Safety
/// - `instance_ptr` must point to enough memory that all of the
/// offsets in `offsets` point to valid locations in memory,
/// i.e. `instance_ptr` must have been allocated by
/// `InstanceHandle::allocate_instance`.
pub unsafe fn table_definition_locations(
instance_ptr: NonNull<u8>,
offsets: &VMOffsets,
) -> Vec<NonNull<VMTableDefinition>> {
// `NonNull<u8>` here actually means `NonNull<Instance>`. See
// `Self::allocate_instance` to understand why.
let instance_ptr: NonNull<Instance> = instance_ptr.cast();
let num_tables = offsets.num_local_tables;
let num_tables = usize::try_from(num_tables).unwrap();
let mut out = Vec::with_capacity(num_tables);
// We need to do some pointer arithmetic now. The unit is `u8`.
let ptr = instance_ptr.cast::<u8>().as_ptr();
let base_ptr = ptr.add(std::mem::size_of::<Instance>());
for i in 0..num_tables {
let table_offset = offsets.vmctx_vmtable_definition(LocalTableIndex::new(i));
let table_offset = usize::try_from(table_offset).unwrap();
let new_ptr = NonNull::new_unchecked(base_ptr.add(table_offset));
out.push(new_ptr.cast());
}
out
}
/// Finishes the instantiation process started by `Instance::new`. /// Finishes the instantiation process started by `Instance::new`.
/// ///
/// # Safety /// # Safety

View File

@@ -39,7 +39,7 @@ pub mod libcalls;
pub use crate::export::*; pub use crate::export::*;
pub use crate::global::*; pub use crate::global::*;
pub use crate::imports::Imports; pub use crate::imports::Imports;
pub use crate::instance::{ImportInitializerFuncPtr, InstanceHandle}; pub use crate::instance::{ImportInitializerFuncPtr, InstanceAllocator, InstanceHandle};
pub use crate::memory::{LinearMemory, Memory, MemoryError, MemoryStyle}; pub use crate::memory::{LinearMemory, Memory, MemoryError, MemoryStyle};
pub use crate::mmap::Mmap; pub use crate::mmap::Mmap;
pub use crate::module::{ExportsIterator, ImportsIterator, ModuleInfo}; pub use crate::module::{ExportsIterator, ImportsIterator, ModuleInfo};

View File

@@ -33,6 +33,7 @@ const fn align(offset: u32, width: u32) -> u32 {
/// related structs that JIT code accesses directly. /// related structs that JIT code accesses directly.
/// ///
/// [`VMContext`]: crate::vmcontext::VMContext /// [`VMContext`]: crate::vmcontext::VMContext
#[derive(Clone, Debug)]
pub struct VMOffsets { pub struct VMOffsets {
/// The size in bytes of a pointer on the target. /// The size in bytes of a pointer on the target.
pub pointer_size: u8, pub pointer_size: u8,