mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-11 07:08:21 +00:00
Merge branch 'master' into feat-c-api-version
This commit is contained in:
@@ -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
11
Cargo.lock
generated
@@ -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",
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
|||||||
@@ -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>>,
|
||||||
|
|||||||
2
lib/api/src/externals/function.rs
vendored
2
lib/api/src/externals/function.rs
vendored
@@ -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
|
||||||
|
|||||||
2
lib/api/src/externals/global.rs
vendored
2
lib/api/src/externals/global.rs
vendored
@@ -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,
|
||||||
|
|||||||
2
lib/api/src/externals/memory.rs
vendored
2
lib/api/src/externals/memory.rs
vendored
@@ -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,
|
||||||
|
|||||||
2
lib/api/src/externals/mod.rs
vendored
2
lib/api/src/externals/mod.rs
vendored
@@ -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`].
|
||||||
|
|||||||
2
lib/api/src/externals/table.rs
vendored
2
lib/api/src/externals/table.rs
vendored
@@ -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,
|
||||||
|
|||||||
@@ -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>>,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
1
lib/cache/Cargo.toml
vendored
1
lib/cache/Cargo.toml
vendored
@@ -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
28
lib/cache/src/hash.rs
vendored
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"] }
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
197
lib/vm/src/instance/allocator.rs
Normal file
197
lib/vm/src/instance/allocator.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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};
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user