Move crates/api to crates/wasmtime (#1693)

The `wasmtime` crate currently lives in `crates/api` for historical
reasons, because we once called it `wasmtime-api` crate. This creates a
stumbling block for new contributors.

As discussed on Zulip, rename the directory to `crates/wasmtime`.
This commit is contained in:
Josh Triplett
2020-05-13 14:04:31 -07:00
committed by GitHub
parent f5eab5225f
commit 08983bf39c
34 changed files with 14 additions and 14 deletions

View File

@@ -0,0 +1,49 @@
[package]
name = "wasmtime"
version = "0.16.0"
authors = ["The Wasmtime Project Developers"]
description = "High-level API to expose the Wasmtime runtime"
documentation = "https://docs.rs/wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/bytecodealliance/wasmtime"
readme = "README.md"
edition = "2018"
[dependencies]
wasmtime-runtime = { path = "../runtime", version = "0.16.0" }
wasmtime-environ = { path = "../environ", version = "0.16.0" }
wasmtime-jit = { path = "../jit", version = "0.16.0" }
wasmtime-profiling = { path = "../profiling", version = "0.16.0" }
wasmparser = "0.52.0"
target-lexicon = { version = "0.10.0", default-features = false }
anyhow = "1.0.19"
region = "2.0.0"
libc = "0.2"
cfg-if = "0.1.9"
backtrace = "0.3.42"
rustc-demangle = "0.1.16"
lazy_static = "1.4"
wat = { version = "1.0.10", optional = true }
[target.'cfg(target_os = "windows")'.dependencies]
winapi = "0.3.7"
[dev-dependencies]
tempfile = "3.0"
[badges]
maintenance = { status = "actively-developed" }
[features]
default = ['wat', 'jitdump']
# Enables experimental support for the lightbeam codegen backend, an alternative
# to cranelift. Requires Nightly Rust currently, and this is not enabled by
# default.
lightbeam = ["wasmtime-jit/lightbeam"]
# Enables support for the `perf` jitdump profiler
jitdump = ["wasmtime-jit/jitdump"]
# Enables support for the `VTune` profiler
vtune = ["wasmtime-jit/vtune"]

220
crates/wasmtime/LICENSE Normal file
View File

@@ -0,0 +1,220 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

View File

@@ -0,0 +1,8 @@
## Wasmtime Embedding API
The `wasmtime` crate is an embedding API of the `wasmtime` WebAssembly runtime.
This is intended to be used in Rust projects and provides a high-level API of
working with WebAssembly modules.
If you're interested in embedding `wasmtime` in other languages, you may wish to
take a look a the [C embedding API](../c-api) instead!

View File

@@ -0,0 +1,990 @@
use crate::trampoline::{
generate_global_export, generate_memory_export, generate_table_export, StoreInstanceHandle,
};
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
use crate::{ExternType, GlobalType, MemoryType, Mutability, TableType, ValType};
use crate::{Func, Store, Trap};
use anyhow::{anyhow, bail, Result};
use std::slice;
use wasmtime_environ::wasm;
use wasmtime_runtime::{self as runtime, InstanceHandle};
// Externals
/// An external item to a WebAssembly module, or a list of what can possibly be
/// exported from a wasm module.
///
/// This is both returned from [`Instance::exports`](crate::Instance::exports)
/// as well as required by [`Instance::new`](crate::Instance::new). In other
/// words, this is the type of extracted values from an instantiated module, and
/// it's also used to provide imported values when instantiating a module.
#[derive(Clone)]
pub enum Extern {
/// A WebAssembly `func` which can be called.
Func(Func),
/// A WebAssembly `global` which acts like a `Cell<T>` of sorts, supporting
/// `get` and `set` operations.
Global(Global),
/// A WebAssembly `table` which is an array of `Val` types.
Table(Table),
/// A WebAssembly linear memory.
Memory(Memory),
}
impl Extern {
/// Returns the underlying `Func`, if this external is a function.
///
/// Returns `None` if this is not a function.
pub fn into_func(self) -> Option<Func> {
match self {
Extern::Func(func) => Some(func),
_ => None,
}
}
/// Returns the underlying `Global`, if this external is a global.
///
/// Returns `None` if this is not a global.
pub fn into_global(self) -> Option<Global> {
match self {
Extern::Global(global) => Some(global),
_ => None,
}
}
/// Returns the underlying `Table`, if this external is a table.
///
/// Returns `None` if this is not a table.
pub fn into_table(self) -> Option<Table> {
match self {
Extern::Table(table) => Some(table),
_ => None,
}
}
/// Returns the underlying `Memory`, if this external is a memory.
///
/// Returns `None` if this is not a memory.
pub fn into_memory(self) -> Option<Memory> {
match self {
Extern::Memory(memory) => Some(memory),
_ => None,
}
}
/// Returns the type associated with this `Extern`.
pub fn ty(&self) -> ExternType {
match self {
Extern::Func(ft) => ExternType::Func(ft.ty()),
Extern::Memory(ft) => ExternType::Memory(ft.ty()),
Extern::Table(tt) => ExternType::Table(tt.ty()),
Extern::Global(gt) => ExternType::Global(gt.ty()),
}
}
pub(crate) fn get_wasmtime_export(&self) -> wasmtime_runtime::Export {
match self {
Extern::Func(f) => f.wasmtime_function().clone().into(),
Extern::Global(g) => g.wasmtime_export.clone().into(),
Extern::Memory(m) => m.wasmtime_export.clone().into(),
Extern::Table(t) => t.wasmtime_export.clone().into(),
}
}
pub(crate) fn from_wasmtime_export(
wasmtime_export: wasmtime_runtime::Export,
instance: StoreInstanceHandle,
) -> Extern {
match wasmtime_export {
wasmtime_runtime::Export::Function(f) => {
Extern::Func(Func::from_wasmtime_function(f, instance))
}
wasmtime_runtime::Export::Memory(m) => {
Extern::Memory(Memory::from_wasmtime_memory(m, instance))
}
wasmtime_runtime::Export::Global(g) => {
Extern::Global(Global::from_wasmtime_global(g, instance))
}
wasmtime_runtime::Export::Table(t) => {
Extern::Table(Table::from_wasmtime_table(t, instance))
}
}
}
pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
let my_store = match self {
Extern::Func(f) => f.store(),
Extern::Global(g) => &g.instance.store,
Extern::Memory(m) => &m.instance.store,
Extern::Table(t) => &t.instance.store,
};
Store::same(my_store, store)
}
}
impl From<Func> for Extern {
fn from(r: Func) -> Self {
Extern::Func(r)
}
}
impl From<Global> for Extern {
fn from(r: Global) -> Self {
Extern::Global(r)
}
}
impl From<Memory> for Extern {
fn from(r: Memory) -> Self {
Extern::Memory(r)
}
}
impl From<Table> for Extern {
fn from(r: Table) -> Self {
Extern::Table(r)
}
}
/// A WebAssembly `global` value which can be read and written to.
///
/// A `global` in WebAssembly is sort of like a global variable within an
/// [`Instance`](crate::Instance). The `global.get` and `global.set`
/// instructions will modify and read global values in a wasm module. Globals
/// can either be imported or exported from wasm modules.
///
/// If you're familiar with Rust already you can think of a `Global` as a sort
/// of `Rc<Cell<Val>>`, more or less.
///
/// # `Global` and `Clone`
///
/// Globals are internally reference counted so you can `clone` a `Global`. The
/// cloning process only performs a shallow clone, so two cloned `Global`
/// instances are equivalent in their functionality.
#[derive(Clone)]
pub struct Global {
instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportGlobal,
}
impl Global {
/// Creates a new WebAssembly `global` value with the provide type `ty` and
/// initial value `val`.
///
/// The `store` argument provided is used as a general global cache for
/// information, and otherwise the `ty` and `val` arguments are used to
/// initialize the global.
///
/// # Errors
///
/// Returns an error if the `ty` provided does not match the type of the
/// value `val`.
pub fn new(store: &Store, ty: GlobalType, val: Val) -> Result<Global> {
if !val.comes_from_same_store(store) {
bail!("cross-`Store` globals are not supported");
}
if val.ty() != *ty.content() {
bail!("value provided does not match the type of this global");
}
let (instance, wasmtime_export) = generate_global_export(store, &ty, val)?;
Ok(Global {
instance,
wasmtime_export,
})
}
/// Returns the underlying type of this `global`.
pub fn ty(&self) -> GlobalType {
// The original export is coming from wasmtime_runtime itself we should
// support all the types coming out of it, so assert such here.
GlobalType::from_wasmtime_global(&self.wasmtime_export.global)
.expect("core wasm global type should be supported")
}
/// Returns the value type of this `global`.
pub fn val_type(&self) -> ValType {
ValType::from_wasmtime_type(self.wasmtime_export.global.ty)
.expect("core wasm type should be supported")
}
/// Returns the underlying mutability of this `global`.
pub fn mutability(&self) -> Mutability {
if self.wasmtime_export.global.mutability {
Mutability::Var
} else {
Mutability::Const
}
}
/// Returns the current [`Val`] of this global.
pub fn get(&self) -> Val {
unsafe {
let definition = &mut *self.wasmtime_export.definition;
match self.val_type() {
ValType::I32 => Val::from(*definition.as_i32()),
ValType::I64 => Val::from(*definition.as_i64()),
ValType::F32 => Val::F32(*definition.as_u32()),
ValType::F64 => Val::F64(*definition.as_u64()),
ty => unimplemented!("Global::get for {:?}", ty),
}
}
}
/// Attempts to set the current value of this global to [`Val`].
///
/// # Errors
///
/// Returns an error if this global has a different type than `Val`, or if
/// it's not a mutable global.
pub fn set(&self, val: Val) -> Result<()> {
if self.mutability() != Mutability::Var {
bail!("immutable global cannot be set");
}
let ty = self.val_type();
if val.ty() != ty {
bail!("global of type {:?} cannot be set to {:?}", ty, val.ty());
}
if !val.comes_from_same_store(&self.instance.store) {
bail!("cross-`Store` values are not supported");
}
unsafe {
let definition = &mut *self.wasmtime_export.definition;
match val {
Val::I32(i) => *definition.as_i32_mut() = i,
Val::I64(i) => *definition.as_i64_mut() = i,
Val::F32(f) => *definition.as_u32_mut() = f,
Val::F64(f) => *definition.as_u64_mut() = f,
_ => unimplemented!("Global::set for {:?}", val.ty()),
}
}
Ok(())
}
pub(crate) fn from_wasmtime_global(
wasmtime_export: wasmtime_runtime::ExportGlobal,
instance: StoreInstanceHandle,
) -> Global {
Global {
instance,
wasmtime_export,
}
}
}
/// A WebAssembly `table`, or an array of values.
///
/// Like [`Memory`] a table is an indexed array of values, but unlike [`Memory`]
/// it's an array of WebAssembly values rather than bytes. One of the most
/// common usages of a table is a function table for wasm modules, where each
/// element has the `Func` type.
///
/// Tables, like globals, are not threadsafe and can only be used on one thread.
/// Tables can be grown in size and each element can be read/written.
///
/// # `Table` and `Clone`
///
/// Tables are internally reference counted so you can `clone` a `Table`. The
/// cloning process only performs a shallow clone, so two cloned `Table`
/// instances are equivalent in their functionality.
#[derive(Clone)]
pub struct Table {
instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportTable,
}
fn set_table_item(
instance: &InstanceHandle,
table_index: wasm::DefinedTableIndex,
item_index: u32,
item: wasmtime_runtime::VMCallerCheckedAnyfunc,
) -> Result<()> {
instance
.table_set(table_index, item_index, item)
.map_err(|()| anyhow!("table element index out of bounds"))
}
impl Table {
/// Creates a new `Table` with the given parameters.
///
/// * `store` - a global cache to store information in
/// * `ty` - the type of this table, containing both the element type as
/// well as the initial size and maximum size, if any.
/// * `init` - the initial value to fill all table entries with, if the
/// table starts with an initial size.
///
/// # Errors
///
/// Returns an error if `init` does not match the element type of the table.
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table> {
let item = into_checked_anyfunc(init, store)?;
let (instance, wasmtime_export) = generate_table_export(store, &ty)?;
// Initialize entries with the init value.
let definition = unsafe { &*wasmtime_export.definition };
let index = instance.table_index(definition);
for i in 0..definition.current_elements {
set_table_item(&instance, index, i, item.clone())?;
}
Ok(Table {
instance,
wasmtime_export,
})
}
/// Returns the underlying type of this table, including its element type as
/// well as the maximum/minimum lower bounds.
pub fn ty(&self) -> TableType {
TableType::from_wasmtime_table(&self.wasmtime_export.table.table)
}
fn wasmtime_table_index(&self) -> wasm::DefinedTableIndex {
unsafe { self.instance.table_index(&*self.wasmtime_export.definition) }
}
/// Returns the table element value at `index`.
///
/// Returns `None` if `index` is out of bounds.
pub fn get(&self, index: u32) -> Option<Val> {
let table_index = self.wasmtime_table_index();
let item = self.instance.table_get(table_index, index)?;
Some(from_checked_anyfunc(item, &self.instance.store))
}
/// Writes the `val` provided into `index` within this table.
///
/// # Errors
///
/// Returns an error if `index` is out of bounds or if `val` does not have
/// the right type to be stored in this table.
pub fn set(&self, index: u32, val: Val) -> Result<()> {
let table_index = self.wasmtime_table_index();
let item = into_checked_anyfunc(val, &self.instance.store)?;
set_table_item(&self.instance, table_index, index, item)
}
/// Returns the current size of this table.
pub fn size(&self) -> u32 {
unsafe { (*self.wasmtime_export.definition).current_elements }
}
/// Grows the size of this table by `delta` more elements, initialization
/// all new elements to `init`.
///
/// Returns the previous size of this table if successful.
///
/// # Errors
///
/// Returns an error if the table cannot be grown by `delta`, for example
/// if it would cause the table to exceed its maximum size. Also returns an
/// error if `init` is not of the right type.
pub fn grow(&self, delta: u32, init: Val) -> Result<u32> {
let index = self.wasmtime_table_index();
let item = into_checked_anyfunc(init, &self.instance.store)?;
if let Some(len) = self.instance.table_grow(index, delta) {
for i in 0..delta {
set_table_item(&self.instance, index, len + i, item.clone())?;
}
Ok(len)
} else {
bail!("failed to grow table by `{}`", delta)
}
}
/// Copy `len` elements from `src_table[src_index..]` into
/// `dst_table[dst_index..]`.
///
/// # Errors
///
/// Returns an error if the range is out of bounds of either the source or
/// destination tables.
pub fn copy(
dst_table: &Table,
dst_index: u32,
src_table: &Table,
src_index: u32,
len: u32,
) -> Result<()> {
if !Store::same(&dst_table.instance.store, &src_table.instance.store) {
bail!("cross-`Store` table copies are not supported");
}
// NB: We must use the `dst_table`'s `wasmtime_handle` for the
// `dst_table_index` and vice versa for `src_table` since each table can
// come from different modules.
let dst_table_index = dst_table.wasmtime_table_index();
let dst_table = dst_table.instance.get_defined_table(dst_table_index);
let src_table_index = src_table.wasmtime_table_index();
let src_table = src_table.instance.get_defined_table(src_table_index);
runtime::Table::copy(dst_table, src_table, dst_index, src_index, len)
.map_err(Trap::from_jit)?;
Ok(())
}
pub(crate) fn from_wasmtime_table(
wasmtime_export: wasmtime_runtime::ExportTable,
instance: StoreInstanceHandle,
) -> Table {
Table {
instance,
wasmtime_export,
}
}
}
/// A WebAssembly linear memory.
///
/// WebAssembly memories represent a contiguous array of bytes that have a size
/// that is always a multiple of the WebAssembly page size, currently 64
/// kilobytes.
///
/// WebAssembly memory is used for global data, statics in C/C++/Rust, shadow
/// stack memory, etc. Accessing wasm memory is generally quite fast!
///
/// # `Memory` and `Clone`
///
/// Memories are internally reference counted so you can `clone` a `Memory`. The
/// cloning process only performs a shallow clone, so two cloned `Memory`
/// instances are equivalent in their functionality.
///
/// # `Memory` and threads
///
/// It is intended that `Memory` is safe to share between threads. At this time
/// this is not implemented in `wasmtime`, however. This is planned to be
/// implemented though!
///
/// # `Memory` and Safety
///
/// Linear memory is a lynchpin of safety for WebAssembly, but it turns out
/// there are very few ways to safely inspect the contents of a memory from the
/// host (Rust). This is because memory safety is quite tricky when working with
/// a `Memory` and we're still working out the best idioms to encapsulate
/// everything safely where it's efficient and ergonomic. This section of
/// documentation, however, is intended to help educate a bit what is and isn't
/// safe when working with `Memory`.
///
/// For safety purposes you can think of a `Memory` as a glorified
/// `Rc<UnsafeCell<Vec<u8>>>`. There are a few consequences of this
/// interpretation:
///
/// * At any time someone else may have access to the memory (hence the `Rc`).
/// This could be a wasm instance, other host code, or a set of wasm instances
/// which all reference a `Memory`. When in doubt assume someone else has a
/// handle to your `Memory`.
///
/// * At any time, memory can be read from or written to (hence the
/// `UnsafeCell`). Anyone with a handle to a wasm memory can read/write to it.
/// Primarily other instances can execute the `load` and `store` family of
/// instructions, as well as any other which modifies or reads memory.
///
/// * At any time memory may grow (hence the `Vec<..>`). Growth may relocate the
/// base memory pointer (similar to how `vec.push(...)` can change the result
/// of `.as_ptr()`)
///
/// So given that we're working roughly with `Rc<UnsafeCell<Vec<u8>>>` that's a
/// lot to keep in mind! It's hopefully though sort of setting the stage as to
/// what you can safely do with memories.
///
/// Let's run through a few safe examples first of how you can use a `Memory`.
///
/// ```rust
/// use wasmtime::Memory;
///
/// fn safe_examples(mem: &Memory) {
/// // Just like wasm, it's safe to read memory almost at any time. The
/// // gotcha here is that we need to be sure to load from the correct base
/// // pointer and perform the bounds check correctly. So long as this is
/// // all self contained here (e.g. not arbitrary code in the middle) we're
/// // good to go.
/// let byte = unsafe { mem.data_unchecked()[0x123] };
///
/// // Short-lived borrows of memory are safe, but they must be scoped and
/// // not have code which modifies/etc `Memory` while the borrow is active.
/// // For example if you want to read a string from memory it is safe to do
/// // so:
/// let string_base = 0xdead;
/// let string_len = 0xbeef;
/// let string = unsafe {
/// let bytes = &mem.data_unchecked()[string_base..][..string_len];
/// match std::str::from_utf8(bytes) {
/// Ok(s) => s.to_string(), // copy out of wasm memory
/// Err(_) => panic!("not valid utf-8"),
/// }
/// };
///
/// // Additionally like wasm you can write to memory at any point in time,
/// // again making sure that after you get the unchecked slice you don't
/// // execute code which could read/write/modify `Memory`:
/// unsafe {
/// mem.data_unchecked_mut()[0x123] = 3;
/// }
///
/// // When working with *borrows* that point directly into wasm memory you
/// // need to be extremely careful. Any functionality that operates on a
/// // borrow into wasm memory needs to be thoroughly audited to effectively
/// // not touch the `Memory` at all
/// let data_base = 0xfeed;
/// let data_len = 0xface;
/// unsafe {
/// let data = &mem.data_unchecked()[data_base..][..data_len];
/// host_function_that_doesnt_touch_memory(data);
///
/// // effectively the same rules apply to mutable borrows
/// let data_mut = &mut mem.data_unchecked_mut()[data_base..][..data_len];
/// host_function_that_doesnt_touch_memory(data);
/// }
/// }
/// # fn host_function_that_doesnt_touch_memory(_: &[u8]){}
/// ```
///
/// It's worth also, however, covering some examples of **incorrect**,
/// **unsafe** usages of `Memory`. Do not do these things!
///
/// ```rust
/// # use anyhow::Result;
/// use wasmtime::Memory;
///
/// // NOTE: All code in this function is not safe to execute and may cause
/// // segfaults/undefined behavior at runtime. Do not copy/paste these examples
/// // into production code!
/// unsafe fn unsafe_examples(mem: &Memory) -> Result<()> {
/// // First and foremost, any borrow can be invalidated at any time via the
/// // `Memory::grow` function. This can relocate memory which causes any
/// // previous pointer to be possibly invalid now.
/// let pointer: &u8 = &mem.data_unchecked()[0x100];
/// mem.grow(1)?; // invalidates `pointer`!
/// // println!("{}", *pointer); // FATAL: use-after-free
///
/// // Note that the use-after-free also applies to slices, whether they're
/// // slices of bytes or strings.
/// let slice: &[u8] = &mem.data_unchecked()[0x100..0x102];
/// mem.grow(1)?; // invalidates `slice`!
/// // println!("{:?}", slice); // FATAL: use-after-free
///
/// // Due to the reference-counted nature of `Memory` note that literal
/// // calls to `Memory::grow` are not sufficient to audit for. You'll need
/// // to be careful that any mutation of `Memory` doesn't happen while
/// // you're holding an active borrow.
/// let slice: &[u8] = &mem.data_unchecked()[0x100..0x102];
/// some_other_function(); // may invalidate `slice` through another `mem` reference
/// // println!("{:?}", slice); // FATAL: maybe a use-after-free
///
/// // An especially subtle aspect of accessing a wasm instance's memory is
/// // that you need to be extremely careful about aliasing. Anyone at any
/// // time can call `data_unchecked()` or `data_unchecked_mut()`, which
/// // means you can easily have aliasing mutable references:
/// let ref1: &u8 = &mem.data_unchecked()[0x100];
/// let ref2: &mut u8 = &mut mem.data_unchecked_mut()[0x100];
/// // *ref2 = *ref1; // FATAL: violates Rust's aliasing rules
///
/// // Note that aliasing applies to strings as well, for example this is
/// // not valid because the slices overlap.
/// let slice1: &mut [u8] = &mut mem.data_unchecked_mut()[0x100..][..3];
/// let slice2: &mut [u8] = &mut mem.data_unchecked_mut()[0x102..][..4];
/// // println!("{:?} {:?}", slice1, slice2); // FATAL: aliasing mutable pointers
///
/// Ok(())
/// }
/// # fn some_other_function() {}
/// ```
///
/// Overall there's some general rules of thumb when working with `Memory` and
/// getting raw pointers inside of it:
///
/// * If you never have a "long lived" pointer into memory, you're likely in the
/// clear. Care still needs to be taken in threaded scenarios or when/where
/// data is read, but you'll be shielded from many classes of issues.
/// * Long-lived pointers must always respect Rust'a aliasing rules. It's ok for
/// shared borrows to overlap with each other, but mutable borrows must
/// overlap with nothing.
/// * Long-lived pointers are only valid if `Memory` isn't used in an unsafe way
/// while the pointer is valid. This includes both aliasing and growth.
///
/// At this point it's worth reiterating again that working with `Memory` is
/// pretty tricky and that's not great! Proposals such as [interface types] are
/// intended to prevent wasm modules from even needing to import/export memory
/// in the first place, which obviates the need for all of these safety caveats!
/// Additionally over time we're still working out the best idioms to expose in
/// `wasmtime`, so if you've got ideas or questions please feel free to [open an
/// issue]!
///
/// ## `Memory` Safety and Threads
///
/// Currently the `wasmtime` crate does not implement the wasm threads proposal,
/// but it is planned to do so. It's additionally worthwhile discussing how this
/// affects memory safety and what was previously just discussed as well.
///
/// Once threads are added into the mix, all of the above rules still apply.
/// There's an additional, rule, however, that all reads and writes can
/// happen *concurrently*. This effectively means that long-lived borrows into
/// wasm memory are virtually never safe to have.
///
/// Mutable pointers are fundamentally unsafe to have in a concurrent scenario
/// in the face of arbitrary wasm code. Only if you dynamically know for sure
/// that wasm won't access a region would it be safe to construct a mutable
/// pointer. Additionally even shared pointers are largely unsafe because their
/// underlying contents may change, so unless `UnsafeCell` in one form or
/// another is used everywhere there's no safety.
///
/// One important point about concurrency is that `Memory::grow` can indeed
/// happen concurrently. This, however, will never relocate the base pointer.
/// Shared memories must always have a maximum size and they will be
/// preallocated such that growth will never relocate the base pointer. The
/// maximum length of the memory, however, will change over time.
///
/// Overall the general rule of thumb for shared memories is that you must
/// atomically read and write everything. Nothing can be borrowed and everything
/// must be eagerly copied out.
///
/// [interface types]: https://github.com/webassembly/interface-types
/// [open an issue]: https://github.com/bytecodealliance/wasmtime/issues/new
#[derive(Clone)]
pub struct Memory {
instance: StoreInstanceHandle,
wasmtime_export: wasmtime_runtime::ExportMemory,
}
impl Memory {
/// Creates a new WebAssembly memory given the configuration of `ty`.
///
/// The `store` argument is a general location for cache information, and
/// otherwise the memory will immediately be allocated according to the
/// type's configuration. All WebAssembly memory is initialized to zero.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let store = Store::default();
///
/// let memory_ty = MemoryType::new(Limits::new(1, None));
/// let memory = Memory::new(&store, memory_ty);
///
/// let module = Module::new(&store, "(module (memory (import \"\" \"\") 1))")?;
/// let instance = Instance::new(&module, &[memory.into()])?;
/// // ...
/// # Ok(())
/// # }
/// ```
pub fn new(store: &Store, ty: MemoryType) -> Memory {
let (instance, wasmtime_export) =
generate_memory_export(store, &ty).expect("generated memory");
Memory {
instance,
wasmtime_export,
}
}
/// Returns the underlying type of this memory.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let store = Store::default();
/// let module = Module::new(&store, "(module (memory (export \"mem\") 1))")?;
/// let instance = Instance::new(&module, &[])?;
/// let memory = instance.get_memory("mem").unwrap();
/// let ty = memory.ty();
/// assert_eq!(ty.limits().min(), 1);
/// # Ok(())
/// # }
/// ```
pub fn ty(&self) -> MemoryType {
MemoryType::from_wasmtime_memory(&self.wasmtime_export.memory.memory)
}
/// Returns this memory as a slice view that can be read natively in Rust.
///
/// # Safety
///
/// This is an unsafe operation because there is no guarantee that the
/// following operations do not happen concurrently while the slice is in
/// use:
///
/// * Data could be modified by calling into a wasm module.
/// * Memory could be relocated through growth by calling into a wasm
/// module.
/// * When threads are supported, non-atomic reads will race with other
/// writes.
///
/// Extreme care need be taken when the data of a `Memory` is read. The
/// above invariants all need to be upheld at a bare minimum, and in
/// general you'll need to ensure that while you're looking at slice you're
/// the only one who can possibly look at the slice and read/write it.
///
/// Be sure to keep in mind that `Memory` is reference counted, meaning
/// that there may be other users of this `Memory` instance elsewhere in
/// your program. Additionally `Memory` can be shared and used in any number
/// of wasm instances, so calling any wasm code should be considered
/// dangerous while you're holding a slice of memory.
///
/// For more information and examples see the documentation on the
/// [`Memory`] type.
pub unsafe fn data_unchecked(&self) -> &[u8] {
self.data_unchecked_mut()
}
/// Returns this memory as a slice view that can be read and written
/// natively in Rust.
///
/// # Safety
///
/// All of the same safety caveats of [`Memory::data_unchecked`] apply
/// here, doubly so because this is returning a mutable slice! As a
/// double-extra reminder, remember that `Memory` is reference counted, so
/// you can very easily acquire two mutable slices by simply calling this
/// function twice. Extreme caution should be used when using this method,
/// and in general you probably want to result to unsafe accessors and the
/// `data` methods below.
///
/// For more information and examples see the documentation on the
/// [`Memory`] type.
pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] {
let definition = &*self.wasmtime_export.definition;
slice::from_raw_parts_mut(definition.base, definition.current_length)
}
/// Returns the base pointer, in the host's address space, that the memory
/// is located at.
///
/// When reading and manipulating memory be sure to read up on the caveats
/// of [`Memory::data_unchecked`] to make sure that you can safely
/// read/write the memory.
///
/// For more information and examples see the documentation on the
/// [`Memory`] type.
pub fn data_ptr(&self) -> *mut u8 {
unsafe { (*self.wasmtime_export.definition).base }
}
/// Returns the byte length of this memory.
///
/// The returned value will be a multiple of the wasm page size, 64k.
///
/// For more information and examples see the documentation on the
/// [`Memory`] type.
pub fn data_size(&self) -> usize {
unsafe { (*self.wasmtime_export.definition).current_length }
}
/// Returns the size, in pages, of this wasm memory.
pub fn size(&self) -> u32 {
(self.data_size() / wasmtime_environ::WASM_PAGE_SIZE as usize) as u32
}
/// Grows this WebAssembly memory by `delta` pages.
///
/// This will attempt to add `delta` more pages of memory on to the end of
/// this `Memory` instance. If successful this may relocate the memory and
/// cause [`Memory::data_ptr`] to return a new value. Additionally previous
/// slices into this memory may no longer be valid.
///
/// On success returns the number of pages this memory previously had
/// before the growth succeeded.
///
/// # Errors
///
/// Returns an error if memory could not be grown, for example if it exceeds
/// the maximum limits of this memory.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let store = Store::default();
/// let module = Module::new(&store, "(module (memory (export \"mem\") 1 2))")?;
/// let instance = Instance::new(&module, &[])?;
/// let memory = instance.get_memory("mem").unwrap();
///
/// assert_eq!(memory.size(), 1);
/// assert_eq!(memory.grow(1)?, 1);
/// assert_eq!(memory.size(), 2);
/// assert!(memory.grow(1).is_err());
/// assert_eq!(memory.size(), 2);
/// assert_eq!(memory.grow(0)?, 2);
/// # Ok(())
/// # }
/// ```
pub fn grow(&self, delta: u32) -> Result<u32> {
let index = self
.instance
.memory_index(unsafe { &*self.wasmtime_export.definition });
self.instance
.memory_grow(index, delta)
.ok_or_else(|| anyhow!("failed to grow memory"))
}
pub(crate) fn from_wasmtime_memory(
wasmtime_export: wasmtime_runtime::ExportMemory,
instance: StoreInstanceHandle,
) -> Memory {
Memory {
instance,
wasmtime_export,
}
}
}
/// A linear memory. This trait provides an interface for raw memory buffers which are used
/// by wasmtime, e.g. inside ['Memory']. Such buffers are in principle not thread safe.
/// By implementing this trait together with MemoryCreator,
/// one can supply wasmtime with custom allocated host managed memory.
///
/// # Safety
/// The memory should be page aligned and a multiple of page size.
/// To prevent possible silent overflows, the memory should be protected by a guard page.
/// Additionally the safety concerns explained in ['Memory'], for accessing the memory
/// apply here as well.
///
/// Note that this is a relatively new and experimental feature and it is recommended
/// to be familiar with wasmtime runtime code to use it.
pub unsafe trait LinearMemory {
/// Returns the number of allocated wasm pages.
fn size(&self) -> u32;
/// Grow memory by the specified amount of wasm pages.
///
/// Returns `None` if memory can't be grown by the specified amount
/// of wasm pages.
fn grow(&self, delta: u32) -> Option<u32>;
/// Return the allocated memory as a mutable pointer to u8.
fn as_ptr(&self) -> *mut u8;
}
/// A memory creator. Can be used to provide a memory creator
/// to wasmtime which supplies host managed memory.
///
/// # Safety
/// This trait is unsafe, as the memory safety depends on proper implementation of
/// memory management. Memories created by the MemoryCreator should always be treated
/// as owned by wasmtime instance, and any modification of them outside of wasmtime
/// invoked routines is unsafe and may lead to corruption.
///
/// Note that this is a relatively new and experimental feature and it is recommended
/// to be familiar with wasmtime runtime code to use it.
pub unsafe trait MemoryCreator: Send + Sync {
/// Create a new `LinearMemory` object from the specified parameters.
///
/// The type of memory being created is specified by `ty` which indicates
/// both the minimum and maximum size, in wasm pages.
///
/// The `reserved_size` value indicates the expected size of the
/// reservation that is to be made for this memory. If this value is `None`
/// than the implementation is free to allocate memory as it sees fit. If
/// the value is `Some`, however, then the implementation is expected to
/// reserve that many bytes for the memory's allocation, plus the guard
/// size at the end. Note that this reservation need only be a virtual
/// memory reservation, physical memory does not need to be allocated
/// immediately. In this case `grow` should never move the base pointer and
/// the maximum size of `ty` is guaranteed to fit within `reserved_size`.
///
/// The `guard_size` parameter indicates how many bytes of space, after the
/// memory allocation, is expected to be unmapped. JIT code will elide
/// bounds checks based on the `guard_size` provided, so for JIT code to
/// work correctly the memory returned will need to be properly guarded with
/// `guard_size` bytes left unmapped after the base allocation.
///
/// Note that the `reserved_size` and `guard_size` options are tuned from
/// the various [`Config`](crate::Config) methods about memory
/// sizes/guards. Additionally these two values are guaranteed to be
/// multiples of the system page size.
fn new_memory(
&self,
ty: MemoryType,
reserved_size: Option<u64>,
guard_size: u64,
) -> Result<Box<dyn LinearMemory>, String>;
}
#[cfg(test)]
mod tests {
use crate::*;
// Assert that creating a memory via `Memory::new` respects the limits/tunables
// in `Config`.
#[test]
fn respect_tunables() {
let mut cfg = Config::new();
cfg.static_memory_maximum_size(0)
.dynamic_memory_guard_size(0);
let store = Store::new(&Engine::new(&cfg));
let ty = MemoryType::new(Limits::new(1, None));
let mem = Memory::new(&store, ty);
assert_eq!(mem.wasmtime_export.memory.offset_guard_size, 0);
match mem.wasmtime_export.memory.style {
wasmtime_environ::MemoryStyle::Dynamic => {}
other => panic!("unexpected style {:?}", other),
}
}
}
// Exports
/// An exported WebAssembly value.
///
/// This type is primarily accessed from the
/// [`Instance::exports`](crate::Instance::exports) accessor and describes what
/// names and items are exported from a wasm instance.
#[derive(Clone)]
pub struct Export<'instance> {
/// The name of the export.
name: &'instance str,
/// The definition of the export.
definition: Extern,
}
impl<'instance> Export<'instance> {
/// Creates a new export which is exported with the given `name` and has the
/// given `definition`.
pub(crate) fn new(name: &'instance str, definition: Extern) -> Export<'instance> {
Export { name, definition }
}
/// Returns the name by which this export is known.
pub fn name(&self) -> &'instance str {
self.name
}
/// Return the `ExternType` of this export.
pub fn ty(&self) -> ExternType {
self.definition.ty()
}
/// Consume this `Export` and return the contained `Extern`.
pub fn into_extern(self) -> Extern {
self.definition
}
/// Consume this `Export` and return the contained `Func`, if it's a function,
/// or `None` otherwise.
pub fn into_func(self) -> Option<Func> {
self.definition.into_func()
}
/// Consume this `Export` and return the contained `Table`, if it's a table,
/// or `None` otherwise.
pub fn into_table(self) -> Option<Table> {
self.definition.into_table()
}
/// Consume this `Export` and return the contained `Memory`, if it's a memory,
/// or `None` otherwise.
pub fn into_memory(self) -> Option<Memory> {
self.definition.into_memory()
}
/// Consume this `Export` and return the contained `Global`, if it's a global,
/// or `None` otherwise.
pub fn into_global(self) -> Option<Global> {
self.definition.into_global()
}
}

View File

@@ -0,0 +1,275 @@
use std::cmp;
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use wasmtime_environ::entity::EntityRef;
use wasmtime_environ::ir;
use wasmtime_environ::wasm::FuncIndex;
use wasmtime_environ::{FunctionAddressMap, Module, TrapInformation};
use wasmtime_jit::CompiledModule;
lazy_static::lazy_static! {
/// This is a global cache of backtrace frame information for all active
///
/// This global cache is used during `Trap` creation to symbolicate frames.
/// This is populated on module compilation, and it is cleared out whenever
/// all references to a module are dropped.
pub static ref FRAME_INFO: RwLock<GlobalFrameInfo> = Default::default();
}
#[derive(Default)]
pub struct GlobalFrameInfo {
/// An internal map that keeps track of backtrace frame information for
/// each module.
///
/// This map is morally a map of ranges to a map of information for that
/// module. Each module is expected to reside in a disjoint section of
/// contiguous memory. No modules can overlap.
///
/// The key of this map is the highest address in the module and the value
/// is the module's information, which also contains the start address.
ranges: BTreeMap<usize, ModuleFrameInfo>,
}
/// An RAII structure used to unregister a module's frame information when the
/// module is destroyed.
pub struct GlobalFrameInfoRegistration {
/// The key that will be removed from the global `ranges` map when this is
/// dropped.
key: usize,
}
struct ModuleFrameInfo {
start: usize,
functions: BTreeMap<usize, FunctionInfo>,
module: Arc<Module>,
}
struct FunctionInfo {
start: usize,
index: FuncIndex,
traps: Vec<TrapInformation>,
instr_map: FunctionAddressMap,
}
impl GlobalFrameInfo {
/// Fetches frame information about a program counter in a backtrace.
///
/// Returns an object if this `pc` is known to some previously registered
/// module, or returns `None` if no information can be found.
pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
let (module, func) = self.func(pc)?;
// Use our relative position from the start of the function to find the
// machine instruction that corresponds to `pc`, which then allows us to
// map that to a wasm original source location.
let rel_pos = pc - func.start;
let pos = match func
.instr_map
.instructions
.binary_search_by_key(&rel_pos, |map| map.code_offset)
{
// Exact hit!
Ok(pos) => Some(pos),
// This *would* be at the first slot in the array, so no
// instructions cover `pc`.
Err(0) => None,
// This would be at the `nth` slot, so check `n-1` to see if we're
// part of that instruction. This happens due to the minus one when
// this function is called form trap symbolication, where we don't
// always get called with a `pc` that's an exact instruction
// boundary.
Err(n) => {
let instr = &func.instr_map.instructions[n - 1];
if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len {
Some(n - 1)
} else {
None
}
}
};
// In debug mode for now assert that we found a mapping for `pc` within
// the function, because otherwise something is buggy along the way and
// not accounting for all the instructions. This isn't super critical
// though so we can omit this check in release mode.
debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc);
let instr = match pos {
Some(pos) => func.instr_map.instructions[pos].srcloc,
None => func.instr_map.start_srcloc,
};
Some(FrameInfo {
module_name: module.module.name.clone(),
func_index: func.index.index() as u32,
func_name: module.module.func_names.get(&func.index).cloned(),
instr,
func_start: func.instr_map.start_srcloc,
})
}
/// Fetches trap information about a program counter in a backtrace.
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
let (_module, func) = self.func(pc)?;
let idx = func
.traps
.binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset)
.ok()?;
Some(&func.traps[idx])
}
fn func(&self, pc: usize) -> Option<(&ModuleFrameInfo, &FunctionInfo)> {
let (end, info) = self.ranges.range(pc..).next()?;
if pc < info.start || *end < pc {
return None;
}
let (end, func) = info.functions.range(pc..).next()?;
if pc < func.start || *end < pc {
return None;
}
Some((info, func))
}
}
impl Drop for GlobalFrameInfoRegistration {
fn drop(&mut self) {
if let Ok(mut info) = FRAME_INFO.write() {
info.ranges.remove(&self.key);
}
}
}
/// Registers a new compiled module's frame information.
///
/// This function will register the `names` information for all of the
/// compiled functions within `module`. If the `module` has no functions
/// then `None` will be returned. Otherwise the returned object, when
/// dropped, will be used to unregister all name information from this map.
pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration> {
let mut min = usize::max_value();
let mut max = 0;
let mut functions = BTreeMap::new();
for (((i, allocated), traps), instrs) in module
.finished_functions()
.iter()
.zip(module.traps().values())
.zip(module.address_transform().values())
{
let (start, end) = unsafe {
let ptr = (**allocated).as_ptr();
let len = (**allocated).len();
(ptr as usize, ptr as usize + len)
};
min = cmp::min(min, start);
max = cmp::max(max, end);
let func = FunctionInfo {
start,
index: module.module().local.func_index(i),
traps: traps.to_vec(),
instr_map: (*instrs).clone(),
};
assert!(functions.insert(end, func).is_none());
}
if functions.len() == 0 {
return None;
}
let mut info = FRAME_INFO.write().unwrap();
// First up assert that our chunk of jit functions doesn't collide with
// any other known chunks of jit functions...
if let Some((_, prev)) = info.ranges.range(max..).next() {
assert!(prev.start > max);
}
if let Some((prev_end, _)) = info.ranges.range(..=min).next_back() {
assert!(*prev_end < min);
}
// ... then insert our range and assert nothing was there previously
let prev = info.ranges.insert(
max,
ModuleFrameInfo {
start: min,
functions,
module: module.module().clone(),
},
);
assert!(prev.is_none());
Some(GlobalFrameInfoRegistration { key: max })
}
/// Description of a frame in a backtrace for a [`Trap`].
///
/// Whenever a WebAssembly trap occurs an instance of [`Trap`] is created. Each
/// [`Trap`] has a backtrace of the WebAssembly frames that led to the trap, and
/// each frame is described by this structure.
///
/// [`Trap`]: crate::Trap
#[derive(Debug)]
pub struct FrameInfo {
module_name: Option<String>,
func_index: u32,
func_name: Option<String>,
func_start: ir::SourceLoc,
instr: ir::SourceLoc,
}
impl FrameInfo {
/// Returns the WebAssembly function index for this frame.
///
/// This function index is the index in the function index space of the
/// WebAssembly module that this frame comes from.
pub fn func_index(&self) -> u32 {
self.func_index
}
/// Returns the identifer of the module that this frame is for.
///
/// Module identifiers are present in the `name` section of a WebAssembly
/// binary, but this may not return the exact item in the `name` section.
/// Module names can be overwritten at construction time or perhaps inferred
/// from file names. The primary purpose of this function is to assist in
/// debugging and therefore may be tweaked over time.
///
/// This function returns `None` when no name can be found or inferred.
pub fn module_name(&self) -> Option<&str> {
self.module_name.as_deref()
}
/// Returns a descriptive name of the function for this frame, if one is
/// available.
///
/// The name of this function may come from the `name` section of the
/// WebAssembly binary, or wasmtime may try to infer a better name for it if
/// not available, for example the name of the export if it's exported.
///
/// This return value is primarily used for debugging and human-readable
/// purposes for things like traps. Note that the exact return value may be
/// tweaked over time here and isn't guaranteed to be something in
/// particular about a wasm module due to its primary purpose of assisting
/// in debugging.
///
/// This function returns `None` when no name could be inferred.
pub fn func_name(&self) -> Option<&str> {
self.func_name.as_deref()
}
/// Returns the offset within the original wasm module this frame's program
/// counter was at.
///
/// The offset here is the offset from the beginning of the original wasm
/// module to the instruction that this frame points to.
pub fn module_offset(&self) -> usize {
self.instr.bits() as usize
}
/// Returns the offset from the original wasm module's function to this
/// frame's program counter.
///
/// The offset here is the offset from the beginning of the defining
/// function of this frame (within the wasm module) to the instruction this
/// frame points to.
pub fn func_offset(&self) -> usize {
(self.instr.bits() - self.func_start.bits()) as usize
}
}

1177
crates/wasmtime/src/func.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,243 @@
use crate::trampoline::StoreInstanceHandle;
use crate::{Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
use anyhow::{bail, Error, Result};
use std::any::Any;
use std::mem;
use wasmtime_environ::EntityIndex;
use wasmtime_jit::{CompiledModule, Resolver};
use wasmtime_runtime::{InstantiationError, SignatureRegistry, VMContext, VMFunctionBody};
struct SimpleResolver<'a> {
imports: &'a [Extern],
}
impl Resolver for SimpleResolver<'_> {
fn resolve(&mut self, idx: u32, _name: &str, _field: &str) -> Option<wasmtime_runtime::Export> {
self.imports
.get(idx as usize)
.map(|i| i.get_wasmtime_export())
}
}
fn instantiate(
store: &Store,
compiled_module: &CompiledModule,
imports: &[Extern],
sig_registry: &SignatureRegistry,
host: Box<dyn Any>,
) -> Result<StoreInstanceHandle, Error> {
let mut resolver = SimpleResolver { imports };
unsafe {
let config = store.engine().config();
let instance = compiled_module.instantiate(
&mut resolver,
sig_registry,
config.memory_creator.as_ref().map(|a| a as _),
host,
)?;
// After we've created the `InstanceHandle` we still need to run
// initialization to set up data/elements/etc. We do this after adding
// the `InstanceHandle` to the store though. This is required for safety
// because the start function (for example) may trap, but element
// initializers may have run which placed elements into other instance's
// tables. This means that from this point on, regardless of whether
// initialization is successful, we need to keep the instance alive.
let instance = store.add_instance(instance);
instance
.initialize(
config.validating_config.operator_config.enable_bulk_memory,
&compiled_module.data_initializers(),
)
.map_err(|e| -> Error {
match e {
InstantiationError::StartTrap(trap) | InstantiationError::Trap(trap) => {
Trap::from_jit(trap).into()
}
other => other.into(),
}
})?;
// If a start function is present, now that we've got our compiled
// instance we can invoke it. Make sure we use all the trap-handling
// configuration in `store` as well.
if let Some(start) = instance.module().start_func {
let f = match instance.lookup_by_declaration(&EntityIndex::Function(start)) {
wasmtime_runtime::Export::Function(f) => f,
_ => unreachable!(), // valid modules shouldn't hit this
};
super::func::catch_traps(instance.vmctx_ptr(), store, || {
mem::transmute::<
*const VMFunctionBody,
unsafe extern "C" fn(*mut VMContext, *mut VMContext),
>(f.address)(f.vmctx, instance.vmctx_ptr())
})?;
}
Ok(instance)
}
}
/// An instantiated WebAssembly module.
///
/// This type represents the instantiation of a [`Module`]. Once instantiated
/// you can access the [`exports`](Instance::exports) which are of type
/// [`Extern`] and provide the ability to call functions, set globals, read
/// memory, etc. This is where all the fun stuff happens!
///
/// An [`Instance`] is created from two inputs, a [`Module`] and a list of
/// imports, provided as a list of [`Extern`] values. The [`Module`] is the wasm
/// code that was compiled and we're instantiating, and the [`Extern`] imports
/// are how we're satisfying the imports of the module provided. On successful
/// instantiation an [`Instance`] will automatically invoke the wasm `start`
/// function.
///
/// When interacting with any wasm code you'll want to make an [`Instance`] to
/// call any code or execute anything!
#[derive(Clone)]
pub struct Instance {
pub(crate) handle: StoreInstanceHandle,
module: Module,
}
impl Instance {
/// Creates a new [`Instance`] from the previously compiled [`Module`] and
/// list of `imports` specified.
///
/// This method instantiates the `module` provided with the `imports`,
/// following the procedure in the [core specification][inst] to
/// instantiate. Instantiation can fail for a number of reasons (many
/// specified below), but if successful the `start` function will be
/// automatically run (if provided) and then the [`Instance`] will be
/// returned.
///
/// ## Providing Imports
///
/// The `imports` array here is a bit tricky. The entries in the list of
/// `imports` are intended to correspond 1:1 with the list of imports
/// returned by [`Module::imports`]. Before calling [`Instance::new`] you'll
/// want to inspect the return value of [`Module::imports`] and, for each
/// import type, create an [`Extern`] which corresponds to that type.
/// These [`Extern`] values are all then collected into a list and passed to
/// this function.
///
/// Note that this function is intentionally relatively low level. It is the
/// intention that we'll soon provide a [higher level API][issue] which will
/// be much more ergonomic for instantiating modules. If you need the full
/// power of customization of imports, though, this is the method for you!
///
/// ## Errors
///
/// This function can fail for a number of reasons, including, but not
/// limited to:
///
/// * The number of `imports` provided doesn't match the number of imports
/// returned by the `module`'s [`Module::imports`] method.
/// * The type of any [`Extern`] doesn't match the corresponding
/// [`ExternType`] entry that it maps to.
/// * The `start` function in the instance, if present, traps.
/// * Module/instance resource limits are exceeded.
///
/// When instantiation fails it's recommended to inspect the return value to
/// see why it failed, or bubble it upwards. If you'd like to specifically
/// check for trap errors, you can use `error.downcast::<Trap>()`.
///
/// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation
/// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727
/// [`ExternType`]: crate::ExternType
pub fn new(module: &Module, imports: &[Extern]) -> Result<Instance, Error> {
let store = module.store();
// For now we have a restriction that the `Store` that we're working
// with is the same for everything involved here.
for import in imports {
if !import.comes_from_same_store(store) {
bail!("cross-`Store` instantiation is not currently supported");
}
}
if imports.len() != module.imports().len() {
bail!(
"wrong number of imports provided, {} != {}",
imports.len(),
module.imports().len()
);
}
let info = module.register_frame_info();
let handle = instantiate(
store,
module.compiled_module(),
imports,
store.compiler().signatures(),
Box::new(info),
)?;
Ok(Instance {
handle,
module: module.clone(),
})
}
/// Returns the associated [`Store`] that this `Instance` is compiled into.
///
/// This is the [`Store`] that generally serves as a sort of global cache
/// for various instance-related things.
pub fn store(&self) -> &Store {
self.module.store()
}
/// Returns the list of exported items from this [`Instance`].
pub fn exports<'instance>(
&'instance self,
) -> impl ExactSizeIterator<Item = Export<'instance>> + 'instance {
self.handle.exports().map(move |(name, entity_index)| {
let export = self.handle.lookup_by_declaration(entity_index);
let extern_ = Extern::from_wasmtime_export(export, self.handle.clone());
Export::new(name, extern_)
})
}
/// Looks up an exported [`Extern`] value by name.
///
/// This method will search the module for an export named `name` and return
/// the value, if found.
///
/// Returns `None` if there was no export named `name`.
pub fn get_export(&self, name: &str) -> Option<Extern> {
let export = self.handle.lookup(&name)?;
Some(Extern::from_wasmtime_export(export, self.handle.clone()))
}
/// Looks up an exported [`Func`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a function.
pub fn get_func(&self, name: &str) -> Option<Func> {
self.get_export(name)?.into_func()
}
/// Looks up an exported [`Table`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a table.
pub fn get_table(&self, name: &str) -> Option<Table> {
self.get_export(name)?.into_table()
}
/// Looks up an exported [`Memory`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a memory.
pub fn get_memory(&self, name: &str) -> Option<Memory> {
self.get_export(name)?.into_memory()
}
/// Looks up an exported [`Global`] value by name.
///
/// Returns `None` if there was no export named `name`, or if there was but
/// it wasn't a global.
pub fn get_global(&self, name: &str) -> Option<Global> {
self.get_export(name)?.into_global()
}
}

View File

@@ -0,0 +1,46 @@
//! Wasmtime's embedding API
//!
//! This crate contains a high-level API used to interact with WebAssembly
//! modules. The API here is intended to mirror the proposed [WebAssembly C
//! API](https://github.com/WebAssembly/wasm-c-api), with small extensions here
//! and there to implement Rust idioms. This crate also defines the actual C API
//! itself for consumption from other languages.
#![deny(missing_docs, intra_doc_link_resolution_failure)]
#![doc(test(attr(deny(warnings))))]
#![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))]
mod externals;
mod frame_info;
mod func;
mod instance;
mod linker;
mod module;
mod r#ref;
mod runtime;
mod trampoline;
mod trap;
mod types;
mod values;
pub use crate::externals::*;
pub use crate::frame_info::FrameInfo;
pub use crate::func::*;
pub use crate::instance::Instance;
pub use crate::linker::*;
pub use crate::module::Module;
pub use crate::r#ref::{AnyRef, HostRef};
pub use crate::runtime::*;
pub use crate::trap::Trap;
pub use crate::types::*;
pub use crate::values::*;
cfg_if::cfg_if! {
if #[cfg(unix)] {
pub mod unix;
} else if #[cfg(windows)] {
pub mod windows;
} else {
// ... unknown os!
}
}

View File

@@ -0,0 +1,488 @@
use crate::{
Extern, ExternType, Func, FuncType, GlobalType, ImportType, Instance, IntoFunc, Module, Store,
};
use anyhow::{anyhow, bail, Result};
use std::collections::hash_map::{Entry, HashMap};
use std::rc::Rc;
/// Structure used to link wasm modules/instances together.
///
/// This structure is used to assist in instantiating a [`Module`]. A `Linker`
/// is a way of performing name resolution to make instantiating a module easier
/// (as opposed to calling [`Instance::new`]). `Linker` is a name-based resolver
/// where names are dynamically defined and then used to instantiate a
/// [`Module`]. The goal of a `Linker` is to have a one-argument method,
/// [`Linker::instantiate`], which takes a [`Module`] and produces an
/// [`Instance`]. This method will automatically select all the right imports
/// for the [`Module`] to be instantiated, and will otherwise return an error
/// if an import isn't satisfied.
///
/// ## Name Resolution
///
/// As mentioned previously, `Linker` is a form of name resolver. It will be
/// using the string-based names of imports on a module to attempt to select a
/// matching item to hook up to it. This name resolution has two-levels of
/// namespaces, a module level and a name level. Each item is defined within a
/// module and then has its own name. This basically follows the wasm standard
/// for modularization.
///
/// Names in a `Linker` can be defined twice, but only for different signatures
/// of items. This means that every item defined in a `Linker` has a unique
/// name/type pair. For example you can define two functions with the module
/// name `foo` and item name `bar`, so long as they have different function
/// signatures. Currently duplicate memories and tables are not allowed, only
/// one-per-name is allowed.
///
/// Note that allowing duplicates by shadowing the previous definition can be
/// controlled with the [`Linker::allow_shadowing`] method as well.
pub struct Linker {
store: Store,
string2idx: HashMap<Rc<str>, usize>,
strings: Vec<Rc<str>>,
map: HashMap<ImportKey, Extern>,
allow_shadowing: bool,
}
#[derive(Hash, PartialEq, Eq)]
struct ImportKey {
name: usize,
module: usize,
kind: ImportKind,
}
#[derive(Hash, PartialEq, Eq, Debug)]
enum ImportKind {
Func(FuncType),
Global(GlobalType),
Memory,
Table,
}
impl Linker {
/// Creates a new [`Linker`].
///
/// This function will create a new [`Linker`] which is ready to start
/// linking modules. All items defined in this linker and produced by this
/// linker will be connected with `store` and must come from the same
/// `store`.
///
/// # Examples
///
/// ```
/// use wasmtime::{Linker, Store};
///
/// let store = Store::default();
/// let mut linker = Linker::new(&store);
/// // ...
/// ```
pub fn new(store: &Store) -> Linker {
Linker {
store: store.clone(),
map: HashMap::new(),
string2idx: HashMap::new(),
strings: Vec::new(),
allow_shadowing: false,
}
}
/// Configures whether this [`Linker`] will shadow previous duplicate
/// definitions of the same signature.
///
/// By default a [`Linker`] will disallow duplicate definitions of the same
/// signature. This method, however, can be used to instead allow duplicates
/// and have the latest definition take precedence when linking modules.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let mut linker = Linker::new(&store);
/// linker.func("", "", || {})?;
///
/// // by default, duplicates are disallowed
/// assert!(linker.func("", "", || {}).is_err());
///
/// // but shadowing can be configured to be allowed as well
/// linker.allow_shadowing(true);
/// linker.func("", "", || {})?;
/// # Ok(())
/// # }
/// ```
pub fn allow_shadowing(&mut self, allow: bool) -> &mut Linker {
self.allow_shadowing = allow;
self
}
/// Defines a new item in this [`Linker`].
///
/// This method will add a new definition, by name, to this instance of
/// [`Linker`]. The `module` and `name` provided are what to name the
/// `item`.
///
/// # Errors
///
/// Returns an error if the `module` and `name` already identify an item
/// of the same type as the `item` provided and if shadowing is disallowed.
/// For more information see the documentation on [`Linker`].
///
/// Also returns an error if `item` comes from a different store than this
/// [`Linker`] was created with.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let mut linker = Linker::new(&store);
/// let ty = GlobalType::new(ValType::I32, Mutability::Const);
/// let global = Global::new(&store, ty, Val::I32(0x1234))?;
/// linker.define("host", "offset", global)?;
///
/// let wat = r#"
/// (module
/// (import "host" "offset" (global i32))
/// (memory 1)
/// (data (global.get 0) "foo")
/// )
/// "#;
/// let module = Module::new(&store, wat)?;
/// linker.instantiate(&module)?;
/// # Ok(())
/// # }
/// ```
pub fn define(
&mut self,
module: &str,
name: &str,
item: impl Into<Extern>,
) -> Result<&mut Self> {
self._define(module, name, item.into())
}
fn _define(&mut self, module: &str, name: &str, item: Extern) -> Result<&mut Self> {
if !item.comes_from_same_store(&self.store) {
bail!("all linker items must be from the same store");
}
self.insert(module, name, item)?;
Ok(self)
}
/// Convenience wrapper to define a function import.
///
/// This method is a convenience wrapper around [`Linker::define`] which
/// internally delegates to [`Func::wrap`].
///
/// # Errors
///
/// Returns an error if the `module` and `name` already identify an item
/// of the same type as the `item` provided and if shadowing is disallowed.
/// For more information see the documentation on [`Linker`].
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let mut linker = Linker::new(&store);
/// linker.func("host", "double", |x: i32| x * 2)?;
/// linker.func("host", "log_i32", |x: i32| println!("{}", x))?;
/// linker.func("host", "log_str", |caller: Caller, ptr: i32, len: i32| {
/// // ...
/// })?;
///
/// let wat = r#"
/// (module
/// (import "host" "double" (func (param i32) (result i32)))
/// (import "host" "log_i32" (func (param i32)))
/// (import "host" "log_str" (func (param i32 i32)))
/// )
/// "#;
/// let module = Module::new(&store, wat)?;
/// linker.instantiate(&module)?;
/// # Ok(())
/// # }
/// ```
pub fn func<Params, Args>(
&mut self,
module: &str,
name: &str,
func: impl IntoFunc<Params, Args>,
) -> Result<&mut Self> {
self._define(module, name, Func::wrap(&self.store, func).into())
}
/// Convenience wrapper to define an entire [`Instance`] in this linker.
///
/// This function is a convenience wrapper around [`Linker::define`] which
/// will define all exports on `instance` into this linker. The module name
/// for each export is `module_name`, and the name for each export is the
/// name in the instance itself.
///
/// # Errors
///
/// Returns an error if the any item is redefined twice in this linker (for
/// example the same `module_name` was already defined) and shadowing is
/// disallowed, or if `instance` comes from a different [`Store`] than this
/// [`Linker`] originally was created with.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let mut linker = Linker::new(&store);
///
/// // Instantiate a small instance...
/// let wat = r#"(module (func (export "run") ))"#;
/// let module = Module::new(&store, wat)?;
/// let instance = linker.instantiate(&module)?;
///
/// // ... and inform the linker that the name of this instance is
/// // `instance1`. This defines the `instance1::run` name for our next
/// // module to use.
/// linker.instance("instance1", &instance)?;
///
/// let wat = r#"
/// (module
/// (import "instance1" "run" (func $instance1_run))
/// (func (export "run")
/// call $instance1_run
/// )
/// )
/// "#;
/// let module = Module::new(&store, wat)?;
/// let instance = linker.instantiate(&module)?;
/// # Ok(())
/// # }
/// ```
pub fn instance(&mut self, module_name: &str, instance: &Instance) -> Result<&mut Self> {
if !Store::same(&self.store, instance.store()) {
bail!("all linker items must be from the same store");
}
for export in instance.exports() {
self.insert(module_name, export.name(), export.into_extern())?;
}
Ok(self)
}
/// Aliases one module's name as another.
///
/// This method will alias all currently defined under `module` to also be
/// defined under the name `as_module` too.
///
/// # Errors
///
/// Returns an error if any shadowing violations happen while defining new
/// items.
pub fn alias(&mut self, module: &str, as_module: &str) -> Result<()> {
let items = self
.iter()
.filter(|(m, _, _)| *m == module)
.map(|(_, name, item)| (name.to_string(), item))
.collect::<Vec<_>>();
for (name, item) in items {
self.define(as_module, &name, item)?;
}
Ok(())
}
fn insert(&mut self, module: &str, name: &str, item: Extern) -> Result<()> {
let key = self.import_key(module, name, item.ty());
match self.map.entry(key) {
Entry::Occupied(o) if !self.allow_shadowing => bail!(
"import of `{}::{}` with kind {:?} defined twice",
module,
name,
o.key().kind,
),
Entry::Occupied(mut o) => {
o.insert(item);
}
Entry::Vacant(v) => {
v.insert(item);
}
}
Ok(())
}
fn import_key(&mut self, module: &str, name: &str, ty: ExternType) -> ImportKey {
ImportKey {
module: self.intern_str(module),
name: self.intern_str(name),
kind: self.import_kind(ty),
}
}
fn import_kind(&self, ty: ExternType) -> ImportKind {
match ty {
ExternType::Func(f) => ImportKind::Func(f),
ExternType::Global(f) => ImportKind::Global(f),
ExternType::Memory(_) => ImportKind::Memory,
ExternType::Table(_) => ImportKind::Table,
}
}
fn intern_str(&mut self, string: &str) -> usize {
if let Some(idx) = self.string2idx.get(string) {
return *idx;
}
let string: Rc<str> = string.into();
let idx = self.strings.len();
self.strings.push(string.clone());
self.string2idx.insert(string, idx);
idx
}
/// Attempts to instantiate the `module` provided.
///
/// This method will attempt to assemble a list of imports that correspond
/// to the imports required by the [`Module`] provided. This list
/// of imports is then passed to [`Instance::new`] to continue the
/// instantiation process.
///
/// Each import of `module` will be looked up in this [`Linker`] and must
/// have previously been defined. If it was previously defined with an
/// incorrect signature or if it was not prevoiusly defined then an error
/// will be returned because the import can not be satisfied.
///
/// # Errors
///
/// This method can fail because an import may not be found, or because
/// instantiation itself may fail. For information on instantiation
/// failures see [`Instance::new`].
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let mut linker = Linker::new(&store);
/// linker.func("host", "double", |x: i32| x * 2)?;
///
/// let wat = r#"
/// (module
/// (import "host" "double" (func (param i32) (result i32)))
/// )
/// "#;
/// let module = Module::new(&store, wat)?;
/// linker.instantiate(&module)?;
/// # Ok(())
/// # }
/// ```
pub fn instantiate(&self, module: &Module) -> Result<Instance> {
let mut imports = Vec::new();
for import in module.imports() {
if let Some(item) = self.get(&import) {
imports.push(item);
continue;
}
let mut options = String::new();
for i in self.map.keys() {
if &*self.strings[i.module] != import.module()
|| &*self.strings[i.name] != import.name()
{
continue;
}
options.push_str(" * ");
options.push_str(&format!("{:?}", i.kind));
options.push_str("\n");
}
if options.len() == 0 {
bail!(
"unknown import: `{}::{}` has not been defined",
import.module(),
import.name()
)
}
bail!(
"incompatible import type for `{}::{}` specified\n\
desired signature was: {:?}\n\
signatures available:\n\n{}",
import.module(),
import.name(),
import.ty(),
options,
)
}
Instance::new(module, &imports)
}
/// Returns the [`Store`] that this linker is connected to.
pub fn store(&self) -> &Store {
&self.store
}
/// Returns an iterator over all items defined in this `Linker`.
///
/// The iterator returned will yield 3-tuples where the first two elements
/// are the module name and item name for the external item, and the third
/// item is the item itself that is defined.
///
/// Note that multiple `Extern` items may be defined for the same
/// module/name pair.
pub fn iter(&self) -> impl Iterator<Item = (&str, &str, Extern)> {
self.map.iter().map(move |(key, item)| {
(
&*self.strings[key.module],
&*self.strings[key.name],
item.clone(),
)
})
}
/// Looks up a value in this `Linker` which matches the `import` type
/// provided.
///
/// Returns `None` if no match was found.
pub fn get(&self, import: &ImportType) -> Option<Extern> {
let key = ImportKey {
module: *self.string2idx.get(import.module())?,
name: *self.string2idx.get(import.name())?,
kind: self.import_kind(import.ty()),
};
self.map.get(&key).cloned()
}
/// Returns all items defined for the `module` and `name` pair.
///
/// This may return an empty iterator, but it may also return multiple items
/// if the module/name have been defined twice.
pub fn get_by_name<'a: 'p, 'p>(
&'a self,
module: &'p str,
name: &'p str,
) -> impl Iterator<Item = &'a Extern> + 'p {
self.map
.iter()
.filter(move |(key, _item)| {
&*self.strings[key.module] == module && &*self.strings[key.name] == name
})
.map(|(_, item)| item)
}
/// Returns the single item defined for the `module` and `name` pair.
///
/// Unlike the similar [`Linker::get_by_name`] method this function returns
/// a single `Extern` item. If the `module` and `name` pair isn't defined
/// in this linker then an error is returned. If more than one value exists
/// for the `module` and `name` pairs, then an error is returned as well.
pub fn get_one_by_name(&self, module: &str, name: &str) -> Result<Extern> {
let mut items = self.get_by_name(module, name);
let ret = items
.next()
.ok_or_else(|| anyhow!("no item named `{}` in `{}`", name, module))?;
if items.next().is_some() {
bail!("too many items named `{}` in `{}`", name, module);
}
Ok(ret.clone())
}
}

View File

@@ -0,0 +1,498 @@
use crate::frame_info::GlobalFrameInfoRegistration;
use crate::runtime::Store;
use crate::types::{EntityType, ExportType, ImportType};
use anyhow::{Error, Result};
use std::path::Path;
use std::sync::{Arc, Mutex};
use wasmparser::validate;
use wasmtime_jit::CompiledModule;
/// A compiled WebAssembly module, ready to be instantiated.
///
/// A `Module` is a compiled in-memory representation of an input WebAssembly
/// binary. A `Module` is then used to create an [`Instance`](crate::Instance)
/// through an instantiation process. You cannot call functions or fetch
/// globals, for example, on a `Module` because it's purely a code
/// representation. Instead you'll need to create an
/// [`Instance`](crate::Instance) to interact with the wasm module.
///
/// Creating a `Module` currently involves compiling code, meaning that it can
/// be an expensive operation. All `Module` instances are compiled according to
/// the configuration in [`Config`], but typically they're JIT-compiled. If
/// you'd like to instantiate a module multiple times you can do so with
/// compiling the original wasm module only once with a single [`Module`]
/// instance.
///
/// ## Modules and `Clone`
///
/// Using `clone` on a `Module` is a cheap operation. It will not create an
/// entirely new module, but rather just a new reference to the existing module.
/// In other words it's a shallow copy, not a deep copy.
///
/// ## Examples
///
/// There are a number of ways you can create a `Module`, for example pulling
/// the bytes from a number of locations. One example is loading a module from
/// the filesystem:
///
/// ```no_run
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let store = Store::default();
/// let module = Module::from_file(&store, "path/to/foo.wasm")?;
/// # Ok(())
/// # }
/// ```
///
/// You can also load the wasm text format if more convenient too:
///
/// ```no_run
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let store = Store::default();
/// // Now we're using the WebAssembly text extension: `.wat`!
/// let module = Module::from_file(&store, "path/to/foo.wat")?;
/// # Ok(())
/// # }
/// ```
///
/// And if you've already got the bytes in-memory you can use the
/// [`Module::new`] constructor:
///
/// ```no_run
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let store = Store::default();
/// # let wasm_bytes: Vec<u8> = Vec::new();
/// let module = Module::new(&store, &wasm_bytes)?;
///
/// // It also works with the text format!
/// let module = Module::new(&store, "(module (func))")?;
/// # Ok(())
/// # }
/// ```
///
/// [`Config`]: crate::Config
#[derive(Clone)]
pub struct Module {
inner: Arc<ModuleInner>,
}
struct ModuleInner {
store: Store,
compiled: CompiledModule,
frame_info_registration: Mutex<Option<Option<Arc<GlobalFrameInfoRegistration>>>>,
}
impl Module {
/// Creates a new WebAssembly `Module` from the given in-memory `bytes`.
///
/// The `bytes` provided must be in one of two formats:
///
/// * It can be a [binary-encoded][binary] WebAssembly module. This
/// is always supported.
/// * It may also be a [text-encoded][text] instance of the WebAssembly
/// text format. This is only supported when the `wat` feature of this
/// crate is enabled. If this is supplied then the text format will be
/// parsed before validation. Note that the `wat` feature is enabled by
/// default.
///
/// The data for the wasm module must be loaded in-memory if it's present
/// elsewhere, for example on disk. This requires that the entire binary is
/// loaded into memory all at once, this API does not support streaming
/// compilation of a module.
///
/// The WebAssembly binary will be decoded and validated. It will also be
/// compiled according to the configuration of the provided `store` and
/// cached in this type.
///
/// The provided `store` is a global cache for compiled resources as well as
/// configuration for what wasm features are enabled. It's recommended to
/// share a `store` among modules if possible.
///
/// # Errors
///
/// This function may fail and return an error. Errors may include
/// situations such as:
///
/// * The binary provided could not be decoded because it's not a valid
/// WebAssembly binary
/// * The WebAssembly binary may not validate (e.g. contains type errors)
/// * Implementation-specific limits were exceeded with a valid binary (for
/// example too many locals)
/// * The wasm binary may use features that are not enabled in the
/// configuration of `store`
/// * If the `wat` feature is enabled and the input is text, then it may be
/// rejected if it fails to parse.
///
/// The error returned should contain full information about why module
/// creation failed if one is returned.
///
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html
/// [text]: https://webassembly.github.io/spec/core/text/index.html
///
/// # Examples
///
/// The `new` function can be invoked with a in-memory array of bytes:
///
/// ```no_run
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// # let wasm_bytes: Vec<u8> = Vec::new();
/// let module = Module::new(&store, &wasm_bytes)?;
/// # Ok(())
/// # }
/// ```
///
/// Or you can also pass in a string to be parsed as the wasm text
/// format:
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let module = Module::new(&store, "(module (func))")?;
/// # Ok(())
/// # }
/// ```
pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Module> {
#[cfg(feature = "wat")]
let bytes = wat::parse_bytes(bytes.as_ref())?;
Module::from_binary(store, bytes.as_ref())
}
/// Creates a new WebAssembly `Module` from the given in-memory `binary`
/// data. The provided `name` will be used in traps/backtrace details.
///
/// See [`Module::new`] for other details.
pub fn new_with_name(store: &Store, bytes: impl AsRef<[u8]>, name: &str) -> Result<Module> {
let mut module = Module::new(store, bytes.as_ref())?;
let inner = Arc::get_mut(&mut module.inner).unwrap();
Arc::get_mut(inner.compiled.module_mut()).unwrap().name = Some(name.to_string());
Ok(module)
}
/// Creates a new WebAssembly `Module` from the contents of the given
/// `file` on disk.
///
/// This is a convenience function that will read the `file` provided and
/// pass the bytes to the [`Module::new`] function. For more information
/// see [`Module::new`]
///
/// # Examples
///
/// ```no_run
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// let store = Store::default();
/// let module = Module::from_file(&store, "./path/to/foo.wasm")?;
/// # Ok(())
/// # }
/// ```
///
/// The `.wat` text format is also supported:
///
/// ```no_run
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let module = Module::from_file(&store, "./path/to/foo.wat")?;
/// # Ok(())
/// # }
/// ```
pub fn from_file(store: &Store, file: impl AsRef<Path>) -> Result<Module> {
#[cfg(feature = "wat")]
let wasm = wat::parse_file(file)?;
#[cfg(not(feature = "wat"))]
let wasm = std::fs::read(file)?;
Module::new(store, &wasm)
}
/// Creates a new WebAssembly `Module` from the given in-memory `binary`
/// data.
///
/// This is similar to [`Module::new`] except that it requires that the
/// `binary` input is a WebAssembly binary, the text format is not supported
/// by this function. It's generally recommended to use [`Module::new`],
/// but if it's required to not support the text format this function can be
/// used instead.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let wasm = b"\0asm\x01\0\0\0";
/// let module = Module::from_binary(&store, wasm)?;
/// # Ok(())
/// # }
/// ```
///
/// Note that the text format is **not** accepted by this function:
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// assert!(Module::from_binary(&store, b"(module)").is_err());
/// # Ok(())
/// # }
/// ```
pub fn from_binary(store: &Store, binary: &[u8]) -> Result<Module> {
Module::validate(store, binary)?;
// Note that the call to `from_binary_unchecked` here should be ok
// because we previously validated the binary, meaning we're guaranteed
// to pass a valid binary for `store`.
unsafe { Module::from_binary_unchecked(store, binary) }
}
/// Creates a new WebAssembly `Module` from the given in-memory `binary`
/// data, skipping validation and asserting that `binary` is a valid
/// WebAssembly module.
///
/// This function is the same as [`Module::new`] except that it skips the
/// call to [`Module::validate`] and it does not support the text format of
/// WebAssembly. The WebAssembly binary is not validated for
/// correctness and it is simply assumed as valid.
///
/// For more information about creation of a module and the `store` argument
/// see the documentation of [`Module::new`].
///
/// # Unsafety
///
/// This function is `unsafe` due to the unchecked assumption that the input
/// `binary` is valid. If the `binary` is not actually a valid wasm binary it
/// may cause invalid machine code to get generated, cause panics, etc.
///
/// It is only safe to call this method if [`Module::validate`] succeeds on
/// the same arguments passed to this function.
///
/// # Errors
///
/// This function may fail for many of the same reasons as [`Module::new`].
/// While this assumes that the binary is valid it still needs to actually
/// be somewhat valid for decoding purposes, and the basics of decoding can
/// still fail.
pub unsafe fn from_binary_unchecked(store: &Store, binary: &[u8]) -> Result<Module> {
Module::compile(store, binary)
}
/// Validates `binary` input data as a WebAssembly binary given the
/// configuration in `store`.
///
/// This function will perform a speedy validation of the `binary` input
/// WebAssembly module (which is in [binary form][binary], the text format
/// is not accepted by this function) and return either `Ok` or `Err`
/// depending on the results of validation. The `store` argument indicates
/// configuration for WebAssembly features, for example, which are used to
/// indicate what should be valid and what shouldn't be.
///
/// Validation automatically happens as part of [`Module::new`], but is a
/// requirement for [`Module::from_binary_unchecked`] to be safe.
///
/// # Errors
///
/// If validation fails for any reason (type check error, usage of a feature
/// that wasn't enabled, etc) then an error with a description of the
/// validation issue will be returned.
///
/// [binary]: https://webassembly.github.io/spec/core/binary/index.html
pub fn validate(store: &Store, binary: &[u8]) -> Result<()> {
let config = store.engine().config().validating_config.clone();
validate(binary, Some(config)).map_err(Error::new)
}
unsafe fn compile(store: &Store, binary: &[u8]) -> Result<Self> {
let compiled = CompiledModule::new(
&mut store.compiler_mut(),
binary,
&*store.engine().config().profiler,
)?;
Ok(Module {
inner: Arc::new(ModuleInner {
store: store.clone(),
compiled,
frame_info_registration: Mutex::new(None),
}),
})
}
pub(crate) fn compiled_module(&self) -> &CompiledModule {
&self.inner.compiled
}
/// Returns identifier/name that this [`Module`] has. This name
/// is used in traps/backtrace details.
///
/// Note that most LLVM/clang/Rust-produced modules do not have a name
/// associated with them, but other wasm tooling can be used to inject or
/// add a name.
///
/// # Examples
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let module = Module::new(&store, "(module $foo)")?;
/// assert_eq!(module.name(), Some("foo"));
///
/// let module = Module::new(&store, "(module)")?;
/// assert_eq!(module.name(), None);
///
/// let module = Module::new_with_name(&store, "(module)", "bar")?;
/// assert_eq!(module.name(), Some("bar"));
/// # Ok(())
/// # }
/// ```
pub fn name(&self) -> Option<&str> {
self.inner.compiled.module().name.as_deref()
}
/// Returns the list of imports that this [`Module`] has and must be
/// satisfied.
///
/// This function returns the list of imports that the wasm module has, but
/// only the types of each import. The type of each import is used to
/// typecheck the [`Instance::new`](crate::Instance::new) method's `imports`
/// argument. The arguments to that function must match up 1-to-1 with the
/// entries in the array returned here.
///
/// The imports returned reflect the order of the imports in the wasm module
/// itself, and note that no form of deduplication happens.
///
/// # Examples
///
/// Modules with no imports return an empty list here:
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let module = Module::new(&store, "(module)")?;
/// assert_eq!(module.imports().len(), 0);
/// # Ok(())
/// # }
/// ```
///
/// and modules with imports will have a non-empty list:
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let wat = r#"
/// (module
/// (import "host" "foo" (func))
/// )
/// "#;
/// let module = Module::new(&store, wat)?;
/// assert_eq!(module.imports().len(), 1);
/// let import = module.imports().next().unwrap();
/// assert_eq!(import.module(), "host");
/// assert_eq!(import.name(), "foo");
/// match import.ty() {
/// ExternType::Func(_) => { /* ... */ }
/// _ => panic!("unexpected import type!"),
/// }
/// # Ok(())
/// # }
/// ```
pub fn imports<'module>(
&'module self,
) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module {
let module = self.inner.compiled.module_ref();
module
.imports
.iter()
.map(move |(module_name, name, entity_index)| {
let r#type = EntityType::new(entity_index, module);
ImportType::new(module_name, name, r#type)
})
}
/// Returns the list of exports that this [`Module`] has and will be
/// available after instantiation.
///
/// This function will return the type of each item that will be returned
/// from [`Instance::exports`](crate::Instance::exports). Each entry in this
/// list corresponds 1-to-1 with that list, and the entries here will
/// indicate the name of the export along with the type of the export.
///
/// # Examples
///
/// Modules might not have any exports:
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let module = Module::new(&store, "(module)")?;
/// assert!(module.exports().next().is_none());
/// # Ok(())
/// # }
/// ```
///
/// When the exports are not empty, you can inspect each export:
///
/// ```
/// # use wasmtime::*;
/// # fn main() -> anyhow::Result<()> {
/// # let store = Store::default();
/// let wat = r#"
/// (module
/// (func (export "foo"))
/// (memory (export "memory") 1)
/// )
/// "#;
/// let module = Module::new(&store, wat)?;
/// assert_eq!(module.exports().len(), 2);
///
/// let mut exports = module.exports();
/// let foo = exports.next().unwrap();
/// assert_eq!(foo.name(), "foo");
/// match foo.ty() {
/// ExternType::Func(_) => { /* ... */ }
/// _ => panic!("unexpected export type!"),
/// }
///
/// let memory = exports.next().unwrap();
/// assert_eq!(memory.name(), "memory");
/// match memory.ty() {
/// ExternType::Memory(_) => { /* ... */ }
/// _ => panic!("unexpected export type!"),
/// }
/// # Ok(())
/// # }
/// ```
pub fn exports<'module>(
&'module self,
) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module {
let module = self.inner.compiled.module_ref();
module.exports.iter().map(move |(name, entity_index)| {
let r#type = EntityType::new(entity_index, module);
ExportType::new(name, r#type)
})
}
/// Returns the [`Store`] that this [`Module`] was compiled into.
pub fn store(&self) -> &Store {
&self.inner.store
}
/// Register this module's stack frame information into the global scope.
///
/// This is required to ensure that any traps can be properly symbolicated.
pub(crate) fn register_frame_info(&self) -> Option<Arc<GlobalFrameInfoRegistration>> {
let mut info = self.inner.frame_info_registration.lock().unwrap();
if let Some(info) = &*info {
return info.clone();
}
let ret = super::frame_info::register(&self.inner.compiled).map(Arc::new);
*info = Some(ret.clone());
return ret;
}
}

223
crates/wasmtime/src/ref.rs Normal file
View File

@@ -0,0 +1,223 @@
#![allow(missing_docs)]
use std::any::Any;
use std::cell::{self, RefCell};
use std::fmt;
use std::rc::{Rc, Weak};
trait InternalRefBase: Any {
fn as_any(&self) -> &dyn Any;
fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;
fn set_host_info(&self, info: Option<Box<dyn Any>>);
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;
}
#[derive(Clone)]
pub struct InternalRef(Rc<dyn InternalRefBase>);
impl InternalRef {
pub fn is_ref<T: 'static>(&self) -> bool {
let r = self.0.as_any();
Any::is::<HostRef<T>>(r)
}
pub fn get_ref<T: 'static>(&self) -> HostRef<T> {
let r = self.0.as_any();
r.downcast_ref::<HostRef<T>>()
.expect("reference is not T type")
.clone()
}
}
struct AnyAndHostInfo {
any: Box<dyn Any>,
host_info: Option<Box<dyn Any>>,
}
#[derive(Clone)]
pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);
/// Represents an opaque reference to any data within WebAssembly.
#[derive(Clone)]
pub enum AnyRef {
/// A reference to no data.
Null,
/// A reference to data stored internally in `wasmtime`.
Ref(InternalRef),
/// A reference to data located outside of `wasmtime`.
Other(OtherRef),
}
impl AnyRef {
/// Creates a new instance of `AnyRef` from `Box<dyn Any>`.
pub fn new(data: Box<dyn Any>) -> Self {
let info = AnyAndHostInfo {
any: data,
host_info: None,
};
AnyRef::Other(OtherRef(Rc::new(RefCell::new(info))))
}
/// Creates a `Null` reference.
pub fn null() -> Self {
AnyRef::Null
}
/// Returns the data stored in the reference if available.
/// # Panics
/// Panics if the variant isn't `AnyRef::Other`.
pub fn data(&self) -> cell::Ref<Box<dyn Any>> {
match self {
AnyRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
_ => panic!("expected AnyRef::Other"),
}
}
/// Returns true if the two `AnyRef<T>`'s point to the same value (not just
/// values that compare as equal).
pub fn ptr_eq(&self, other: &AnyRef) -> bool {
match (self, other) {
(AnyRef::Null, AnyRef::Null) => true,
(AnyRef::Ref(InternalRef(ref a)), AnyRef::Ref(InternalRef(ref b))) => {
a.ptr_eq(b.as_ref())
}
(AnyRef::Other(OtherRef(ref a)), AnyRef::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b),
_ => false,
}
}
/// Returns a mutable reference to the host information if available.
/// # Panics
/// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`.
pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {
match self {
AnyRef::Null => panic!("null"),
AnyRef::Ref(r) => r.0.host_info(),
AnyRef::Other(r) => {
let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);
if info.is_none() {
return None;
}
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
}
}
}
/// Sets the host information for an `AnyRef`.
/// # Panics
/// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`.
pub fn set_host_info(&self, info: Option<Box<dyn Any>>) {
match self {
AnyRef::Null => panic!("null"),
AnyRef::Ref(r) => r.0.set_host_info(info),
AnyRef::Other(r) => {
r.0.borrow_mut().host_info = info;
}
}
}
}
impl fmt::Debug for AnyRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AnyRef::Null => write!(f, "null"),
AnyRef::Ref(_) => write!(f, "anyref"),
AnyRef::Other(_) => write!(f, "other ref"),
}
}
}
struct ContentBox<T> {
content: T,
host_info: Option<Box<dyn Any>>,
anyref_data: Weak<dyn InternalRefBase>,
}
/// Represents a piece of data located in the host environment.
pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>);
impl<T: 'static> HostRef<T> {
/// Creates a new `HostRef<T>` from `T`.
pub fn new(item: T) -> HostRef<T> {
let anyref_data: Weak<HostRef<T>> = Weak::new();
let content = ContentBox {
content: item,
host_info: None,
anyref_data,
};
HostRef(Rc::new(RefCell::new(content)))
}
/// Immutably borrows the wrapped data.
/// # Panics
/// Panics if the value is currently mutably borrowed.
pub fn borrow(&self) -> cell::Ref<T> {
cell::Ref::map(self.0.borrow(), |b| &b.content)
}
/// Mutably borrows the wrapped data.
/// # Panics
/// Panics if the `HostRef<T>` is already borrowed.
pub fn borrow_mut(&self) -> cell::RefMut<T> {
cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content)
}
/// Returns true if the two `HostRef<T>`'s point to the same value (not just
/// values that compare as equal).
pub fn ptr_eq(&self, other: &HostRef<T>) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
/// Returns an opaque reference to the wrapped data in the form of
/// an `AnyRef`.
/// # Panics
/// Panics if `HostRef<T>` is already mutably borrowed.
pub fn anyref(&self) -> AnyRef {
let r = self.0.borrow_mut().anyref_data.upgrade();
if let Some(r) = r {
return AnyRef::Ref(InternalRef(r));
}
let anyref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
self.0.borrow_mut().anyref_data = Rc::downgrade(&anyref_data);
AnyRef::Ref(InternalRef(anyref_data))
}
}
impl<T: 'static> InternalRefBase for HostRef<T> {
fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool {
if let Some(other) = other.as_any().downcast_ref() {
self.ptr_eq(other)
} else {
false
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {
let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info);
if info.is_none() {
return None;
}
Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))
}
fn set_host_info(&self, info: Option<Box<dyn Any>>) {
self.0.borrow_mut().host_info = info;
}
}
impl<T> Clone for HostRef<T> {
fn clone(&self) -> HostRef<T> {
HostRef(self.0.clone())
}
}
impl<T: fmt::Debug> fmt::Debug for HostRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ref(")?;
self.0.borrow().content.fmt(f)?;
write!(f, ")")
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
//! Support for a calling of an imported function.
use crate::trampoline::StoreInstanceHandle;
use crate::Store;
use anyhow::Result;
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::DefinedFuncIndex;
use wasmtime_environ::Module;
use wasmtime_runtime::{
Imports, InstanceHandle, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
};
pub(crate) fn create_handle(
module: Module,
store: &Store,
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
state: Box<dyn Any>,
) -> Result<StoreInstanceHandle> {
let imports = Imports::new(
PrimaryMap::new(),
PrimaryMap::new(),
PrimaryMap::new(),
PrimaryMap::new(),
);
// Compute indices into the shared signature table.
let signatures = module
.local
.signatures
.values()
.map(|sig| store.compiler().signatures().register(sig))
.collect::<PrimaryMap<_, _>>();
unsafe {
let handle = InstanceHandle::new(
Arc::new(module),
finished_functions.into_boxed_slice(),
trampolines,
imports,
store.memory_creator(),
signatures.into_boxed_slice(),
None,
state,
store.compiler().interrupts().clone(),
)?;
Ok(store.add_instance(handle))
}
}

View File

@@ -0,0 +1,298 @@
//! Support for a calling of an imported function.
use super::create_handle::create_handle;
use crate::trampoline::StoreInstanceHandle;
use crate::{FuncType, Store, Trap};
use anyhow::{bail, Result};
use std::any::Any;
use std::cmp;
use std::collections::HashMap;
use std::mem;
use std::panic::{self, AssertUnwindSafe};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::{ir, settings, CompiledFunction, EntityIndex, Module};
use wasmtime_jit::trampoline::ir::{
ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind,
};
use wasmtime_jit::trampoline::{
binemit, pretty_error, Context, FunctionBuilder, FunctionBuilderContext,
};
use wasmtime_jit::{native, CodeMemory};
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody, VMTrampoline};
struct TrampolineState {
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
#[allow(dead_code)]
code_memory: CodeMemory,
}
unsafe extern "C" fn stub_fn(
vmctx: *mut VMContext,
caller_vmctx: *mut VMContext,
values_vec: *mut u128,
) {
// Here we are careful to use `catch_unwind` to ensure Rust panics don't
// unwind past us. The primary reason for this is that Rust considers it UB
// to unwind past an `extern "C"` function. Here we are in an `extern "C"`
// function and the cross into wasm was through an `extern "C"` function at
// the base of the stack as well. We'll need to wait for assorted RFCs and
// language features to enable this to be done in a sound and stable fashion
// before avoiding catching the panic here.
//
// Also note that there are intentionally no local variables on this stack
// frame. The reason for that is that some of the "raise" functions we have
// below will trigger a longjmp, which won't run local destructors if we
// have any. To prevent leaks we avoid having any local destructors by
// avoiding local variables.
let result = panic::catch_unwind(AssertUnwindSafe(|| {
call_stub(vmctx, caller_vmctx, values_vec)
}));
match result {
Ok(Ok(())) => {}
// If a trap was raised (an error returned from the imported function)
// then we smuggle the trap through `Box<dyn Error>` through to the
// call-site, which gets unwrapped in `Trap::from_jit` later on as we
// convert from the internal `Trap` type to our own `Trap` type in this
// crate.
Ok(Err(trap)) => wasmtime_runtime::raise_user_trap(Box::new(trap)),
// And finally if the imported function panicked, then we trigger the
// form of unwinding that's safe to jump over wasm code on all
// platforms.
Err(panic) => wasmtime_runtime::resume_panic(panic),
}
unsafe fn call_stub(
vmctx: *mut VMContext,
caller_vmctx: *mut VMContext,
values_vec: *mut u128,
) -> Result<(), Trap> {
let instance = InstanceHandle::from_vmctx(vmctx);
let state = &instance
.host_state()
.downcast_ref::<TrampolineState>()
.expect("state");
(state.func)(caller_vmctx, values_vec)
}
}
/// Create a trampoline for invoking a function.
fn make_trampoline(
isa: &dyn TargetIsa,
code_memory: &mut CodeMemory,
fn_builder_ctx: &mut FunctionBuilderContext,
signature: &ir::Signature,
) -> *mut [VMFunctionBody] {
// Mostly reverse copy of the similar method from wasmtime's
// wasmtime-jit/src/compiler.rs.
let pointer_type = isa.pointer_type();
let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
// Add the caller/callee `vmctx` parameters.
stub_sig.params.push(ir::AbiParam::special(
pointer_type,
ir::ArgumentPurpose::VMContext,
));
// Add the caller `vmctx` parameter.
stub_sig.params.push(ir::AbiParam::new(pointer_type));
// Add the `values_vec` parameter.
stub_sig.params.push(ir::AbiParam::new(pointer_type));
// Compute the size of the values vector. The vmctx and caller vmctx are passed separately.
let value_size = mem::size_of::<u128>();
let values_vec_len = ((value_size as usize)
* cmp::max(signature.params.len() - 2, signature.returns.len()))
as u32;
let mut context = Context::new();
context.func = Function::with_name_signature(ExternalName::user(0, 0), signature.clone());
let ss = context.func.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
values_vec_len,
));
{
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
let block0 = builder.create_block();
builder.append_block_params_for_function_params(block0);
builder.switch_to_block(block0);
builder.seal_block(block0);
let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
let mflags = MemFlags::trusted();
for i in 2..signature.params.len() {
if i == 0 {
continue;
}
let val = builder.func.dfg.block_params(block0)[i];
builder.ins().store(
mflags,
val,
values_vec_ptr_val,
((i - 2) * value_size) as i32,
);
}
let block_params = builder.func.dfg.block_params(block0);
let vmctx_ptr_val = block_params[0];
let caller_vmctx_ptr_val = block_params[1];
let callee_args = vec![vmctx_ptr_val, caller_vmctx_ptr_val, values_vec_ptr_val];
let new_sig = builder.import_signature(stub_sig);
let callee_value = builder
.ins()
.iconst(pointer_type, stub_fn as *const VMFunctionBody as i64);
builder
.ins()
.call_indirect(new_sig, callee_value, &callee_args);
let mflags = MemFlags::trusted();
let mut results = Vec::new();
for (i, r) in signature.returns.iter().enumerate() {
let load = builder.ins().load(
r.value_type,
mflags,
values_vec_ptr_val,
(i * value_size) as i32,
);
results.push(load);
}
builder.ins().return_(&results);
builder.finalize()
}
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = binemit::TrampolineRelocSink {};
let mut trap_sink = binemit::NullTrapSink {};
let mut stackmap_sink = binemit::NullStackmapSink {};
context
.compile_and_emit(
isa,
&mut code_buf,
&mut reloc_sink,
&mut trap_sink,
&mut stackmap_sink,
)
.map_err(|error| pretty_error(&context.func, Some(isa), error))
.expect("compile_and_emit");
let unwind_info = context
.create_unwind_info(isa)
.map_err(|error| pretty_error(&context.func, Some(isa), error))
.expect("create unwind information");
code_memory
.allocate_for_function(&CompiledFunction {
body: code_buf,
jt_offsets: context.func.jt_offsets,
unwind_info,
})
.expect("allocate_for_function")
}
pub fn create_handle_with_function(
ft: &FuncType,
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
store: &Store,
) -> Result<(StoreInstanceHandle, VMTrampoline)> {
let isa = {
let isa_builder = native::builder();
let flag_builder = settings::builder();
isa_builder.finish(settings::Flags::new(flag_builder))
};
let pointer_type = isa.pointer_type();
let sig = match ft.get_wasmtime_signature(pointer_type) {
Some(sig) => sig,
None => bail!("not a supported core wasm signature {:?}", ft),
};
let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut module = Module::new();
let mut finished_functions = PrimaryMap::new();
let mut trampolines = HashMap::new();
let mut code_memory = CodeMemory::new();
// First up we manufacture a trampoline which has the ABI specified by `ft`
// and calls into `stub_fn`...
let sig_id = module.local.signatures.push(sig.clone());
let func_id = module.local.functions.push(sig_id);
module
.exports
.insert("trampoline".to_string(), EntityIndex::Function(func_id));
let trampoline = make_trampoline(isa.as_ref(), &mut code_memory, &mut fn_builder_ctx, &sig);
finished_functions.push(trampoline);
// ... and then we also need a trampoline with the standard "trampoline ABI"
// which enters into the ABI specified by `ft`. Note that this is only used
// if `Func::call` is called on an object created by `Func::new`.
let (trampoline, relocations) = wasmtime_jit::make_trampoline(
&*isa,
&mut code_memory,
&mut fn_builder_ctx,
&sig,
mem::size_of::<u128>(),
)?;
assert!(relocations.is_empty());
let sig_id = store.compiler().signatures().register(&sig);
trampolines.insert(sig_id, trampoline);
// Next up we wrap everything up into an `InstanceHandle` by publishing our
// code memory (makes it executable) and ensuring all our various bits of
// state make it into the instance constructors.
code_memory.publish(isa.as_ref());
let trampoline_state = TrampolineState { func, code_memory };
create_handle(
module,
store,
finished_functions,
trampolines,
Box::new(trampoline_state),
)
.map(|instance| (instance, trampoline))
}
pub unsafe fn create_handle_with_raw_function(
ft: &FuncType,
func: *mut [VMFunctionBody],
trampoline: VMTrampoline,
store: &Store,
state: Box<dyn Any>,
) -> Result<StoreInstanceHandle> {
let isa = {
let isa_builder = native::builder();
let flag_builder = settings::builder();
isa_builder.finish(settings::Flags::new(flag_builder))
};
let pointer_type = isa.pointer_type();
let sig = match ft.get_wasmtime_signature(pointer_type) {
Some(sig) => sig,
None => bail!("not a supported core wasm signature {:?}", ft),
};
let mut module = Module::new();
let mut finished_functions = PrimaryMap::new();
let mut trampolines = HashMap::new();
let sig_id = module.local.signatures.push(sig.clone());
let func_id = module.local.functions.push(sig_id);
module
.exports
.insert("trampoline".to_string(), EntityIndex::Function(func_id));
finished_functions.push(func);
let sig_id = store.compiler().signatures().register(&sig);
trampolines.insert(sig_id, trampoline);
create_handle(module, store, finished_functions, trampolines, state)
}

View File

@@ -0,0 +1,40 @@
use super::create_handle::create_handle;
use crate::trampoline::StoreInstanceHandle;
use crate::Store;
use crate::{GlobalType, Mutability, Val};
use anyhow::{bail, Result};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, EntityIndex, Module};
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
let global = wasm::Global {
ty: match gt.content().get_wasmtime_type() {
Some(t) => t,
None => bail!("cannot support {:?} as a wasm global type", gt.content()),
},
mutability: match gt.mutability() {
Mutability::Const => false,
Mutability::Var => true,
},
initializer: match val {
Val::I32(i) => wasm::GlobalInit::I32Const(i),
Val::I64(i) => wasm::GlobalInit::I64Const(i),
Val::F32(f) => wasm::GlobalInit::F32Const(f),
Val::F64(f) => wasm::GlobalInit::F64Const(f),
_ => unimplemented!("create_global for {:?}", gt),
},
};
let mut module = Module::new();
let global_id = module.local.globals.push(global);
module
.exports
.insert("global".to_string(), EntityIndex::Global(global_id));
let handle = create_handle(
module,
store,
PrimaryMap::new(),
Default::default(),
Box::new(()),
)?;
Ok(handle)
}

View File

@@ -0,0 +1,78 @@
use super::create_handle::create_handle;
use crate::externals::{LinearMemory, MemoryCreator};
use crate::trampoline::StoreInstanceHandle;
use crate::Store;
use crate::{Limits, MemoryType};
use anyhow::Result;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, EntityIndex, MemoryPlan, MemoryStyle, Module, WASM_PAGE_SIZE};
use wasmtime_runtime::{RuntimeLinearMemory, RuntimeMemoryCreator, VMMemoryDefinition};
use std::sync::Arc;
pub fn create_handle_with_memory(
store: &Store,
memory: &MemoryType,
) -> Result<StoreInstanceHandle> {
let mut module = Module::new();
let memory = wasm::Memory {
minimum: memory.limits().min(),
maximum: memory.limits().max(),
shared: false, // TODO
};
let memory_plan =
wasmtime_environ::MemoryPlan::for_memory(memory, &store.engine().config().tunables);
let memory_id = module.local.memory_plans.push(memory_plan);
module
.exports
.insert("memory".to_string(), EntityIndex::Memory(memory_id));
create_handle(
module,
store,
PrimaryMap::new(),
Default::default(),
Box::new(()),
)
}
struct LinearMemoryProxy {
mem: Box<dyn LinearMemory>,
}
impl RuntimeLinearMemory for LinearMemoryProxy {
fn size(&self) -> u32 {
self.mem.size()
}
fn grow(&self, delta: u32) -> Option<u32> {
self.mem.grow(delta)
}
fn vmmemory(&self) -> VMMemoryDefinition {
VMMemoryDefinition {
base: self.mem.as_ptr(),
current_length: self.mem.size() as usize * WASM_PAGE_SIZE as usize,
}
}
}
#[derive(Clone)]
pub(crate) struct MemoryCreatorProxy {
pub(crate) mem_creator: Arc<dyn MemoryCreator>,
}
impl RuntimeMemoryCreator for MemoryCreatorProxy {
fn new_memory(&self, plan: &MemoryPlan) -> Result<Box<dyn RuntimeLinearMemory>, String> {
let ty = MemoryType::new(Limits::new(plan.memory.minimum, plan.memory.maximum));
let reserved_size = match plan.style {
MemoryStyle::Static { bound } => Some(bound.into()),
MemoryStyle::Dynamic => None,
};
self.mem_creator
.new_memory(ty, reserved_size, plan.offset_guard_size)
.map(|mem| Box::new(LinearMemoryProxy { mem }) as Box<dyn RuntimeLinearMemory>)
}
}

View File

@@ -0,0 +1,113 @@
//! Utility module to create trampolines in/out WebAssembly module.
mod create_handle;
mod func;
mod global;
mod memory;
mod table;
pub(crate) use memory::MemoryCreatorProxy;
use self::func::create_handle_with_function;
use self::global::create_global;
use self::memory::create_handle_with_memory;
use self::table::create_handle_with_table;
use crate::{FuncType, GlobalType, MemoryType, Store, TableType, Trap, Val};
use anyhow::Result;
use std::any::Any;
use std::ops::Deref;
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody, VMTrampoline};
/// A wrapper around `wasmtime_runtime::InstanceHandle` which pairs it with the
/// `Store` that it's rooted within. The instance is deallocated when `Store` is
/// deallocated, so this is a safe handle in terms of memory management for the
/// `Store`.
pub struct StoreInstanceHandle {
pub store: Store,
pub handle: InstanceHandle,
}
impl Clone for StoreInstanceHandle {
fn clone(&self) -> StoreInstanceHandle {
StoreInstanceHandle {
store: self.store.clone(),
// Note should be safe because the lifetime of the instance handle
// is tied to the `Store` which this is paired with.
handle: unsafe { self.handle.clone() },
}
}
}
impl Deref for StoreInstanceHandle {
type Target = InstanceHandle;
fn deref(&self) -> &InstanceHandle {
&self.handle
}
}
pub fn generate_func_export(
ft: &FuncType,
func: Box<dyn Fn(*mut VMContext, *mut u128) -> Result<(), Trap>>,
store: &Store,
) -> Result<(
StoreInstanceHandle,
wasmtime_runtime::ExportFunction,
VMTrampoline,
)> {
let (instance, trampoline) = create_handle_with_function(ft, func, store)?;
match instance.lookup("trampoline").expect("trampoline export") {
wasmtime_runtime::Export::Function(f) => Ok((instance, f, trampoline)),
_ => unreachable!(),
}
}
/// Note that this is `unsafe` since `func` must be a valid function pointer and
/// have a signature which matches `ft`, otherwise the returned
/// instance/export/etc may exhibit undefined behavior.
pub unsafe fn generate_raw_func_export(
ft: &FuncType,
func: *mut [VMFunctionBody],
trampoline: VMTrampoline,
store: &Store,
state: Box<dyn Any>,
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportFunction)> {
let instance = func::create_handle_with_raw_function(ft, func, trampoline, store, state)?;
match instance.lookup("trampoline").expect("trampoline export") {
wasmtime_runtime::Export::Function(f) => Ok((instance, f)),
_ => unreachable!(),
}
}
pub fn generate_global_export(
store: &Store,
gt: &GlobalType,
val: Val,
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportGlobal)> {
let instance = create_global(store, gt, val)?;
match instance.lookup("global").expect("global export") {
wasmtime_runtime::Export::Global(g) => Ok((instance, g)),
_ => unreachable!(),
}
}
pub fn generate_memory_export(
store: &Store,
m: &MemoryType,
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportMemory)> {
let instance = create_handle_with_memory(store, m)?;
match instance.lookup("memory").expect("memory export") {
wasmtime_runtime::Export::Memory(m) => Ok((instance, m)),
_ => unreachable!(),
}
}
pub fn generate_table_export(
store: &Store,
t: &TableType,
) -> Result<(StoreInstanceHandle, wasmtime_runtime::ExportTable)> {
let instance = create_handle_with_table(store, t)?;
match instance.lookup("table").expect("table export") {
wasmtime_runtime::Export::Table(t) => Ok((instance, t)),
_ => unreachable!(),
}
}

View File

@@ -0,0 +1,35 @@
use super::create_handle::create_handle;
use crate::trampoline::StoreInstanceHandle;
use crate::Store;
use crate::{TableType, ValType};
use anyhow::{bail, Result};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, EntityIndex, Module};
pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<StoreInstanceHandle> {
let mut module = Module::new();
let table = wasm::Table {
minimum: table.limits().min(),
maximum: table.limits().max(),
ty: match table.element() {
ValType::FuncRef => wasm::TableElementType::Func,
_ => bail!("cannot support {:?} as a table element", table.element()),
},
};
let tunable = Default::default();
let table_plan = wasmtime_environ::TablePlan::for_table(table, &tunable);
let table_id = module.local.table_plans.push(table_plan);
module
.exports
.insert("table".to_string(), EntityIndex::Table(table_id));
create_handle(
module,
store,
PrimaryMap::new(),
Default::default(),
Box::new(()),
)
}

184
crates/wasmtime/src/trap.rs Normal file
View File

@@ -0,0 +1,184 @@
use crate::frame_info::{GlobalFrameInfo, FRAME_INFO};
use crate::FrameInfo;
use backtrace::Backtrace;
use std::fmt;
use std::sync::Arc;
use wasmtime_environ::ir::TrapCode;
/// A struct representing an aborted instruction execution, with a message
/// indicating the cause.
#[derive(Clone)]
pub struct Trap {
inner: Arc<TrapInner>,
}
struct TrapInner {
message: String,
wasm_trace: Vec<FrameInfo>,
native_trace: Backtrace,
}
fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) {
(t, t)
}
impl Trap {
/// Creates a new `Trap` with `message`.
/// # Example
/// ```
/// let trap = wasmtime::Trap::new("unexpected error");
/// assert_eq!("unexpected error", trap.message());
/// ```
pub fn new<I: Into<String>>(message: I) -> Self {
let info = FRAME_INFO.read().unwrap();
Trap::new_with_trace(&info, None, message.into(), Backtrace::new_unresolved())
}
pub(crate) fn from_jit(jit: wasmtime_runtime::Trap) -> Self {
let info = FRAME_INFO.read().unwrap();
match jit {
wasmtime_runtime::Trap::User(error) => {
// Since we're the only one using the wasmtime internals (in
// theory) we should only see user errors which were originally
// created from our own `Trap` type (see the trampoline module
// with functions).
//
// If this unwrap trips for someone we'll need to tweak the
// return type of this function to probably be `anyhow::Error`
// or something like that.
*error
.downcast()
.expect("only `Trap` user errors are supported")
}
wasmtime_runtime::Trap::Jit {
pc,
backtrace,
maybe_interrupted,
} => {
let mut code = info
.lookup_trap_info(pc)
.map(|info| info.trap_code)
.unwrap_or(TrapCode::StackOverflow);
if maybe_interrupted && code == TrapCode::StackOverflow {
code = TrapCode::Interrupt;
}
Trap::new_wasm(&info, Some(pc), code, backtrace)
}
wasmtime_runtime::Trap::Wasm {
trap_code,
backtrace,
} => Trap::new_wasm(&info, None, trap_code, backtrace),
wasmtime_runtime::Trap::OOM { backtrace } => {
Trap::new_with_trace(&info, None, "out of memory".to_string(), backtrace)
}
}
}
fn new_wasm(
info: &GlobalFrameInfo,
trap_pc: Option<usize>,
code: TrapCode,
backtrace: Backtrace,
) -> Self {
use wasmtime_environ::ir::TrapCode::*;
let desc = match code {
StackOverflow => "call stack exhausted",
HeapOutOfBounds => "out of bounds memory access",
TableOutOfBounds => "undefined element: out of bounds table access",
OutOfBounds => "out of bounds",
IndirectCallToNull => "uninitialized element",
BadSignature => "indirect call type mismatch",
IntegerOverflow => "integer overflow",
IntegerDivisionByZero => "integer divide by zero",
BadConversionToInteger => "invalid conversion to integer",
UnreachableCodeReached => "unreachable",
Interrupt => "interrupt",
User(_) => unreachable!(),
};
let msg = format!("wasm trap: {}", desc);
Trap::new_with_trace(info, trap_pc, msg, backtrace)
}
fn new_with_trace(
info: &GlobalFrameInfo,
trap_pc: Option<usize>,
message: String,
native_trace: Backtrace,
) -> Self {
let mut wasm_trace = Vec::new();
for frame in native_trace.frames() {
let pc = frame.ip() as usize;
if pc == 0 {
continue;
}
// Note that we need to be careful about the pc we pass in here to
// lookup frame information. This program counter is used to
// translate back to an original source location in the origin wasm
// module. If this pc is the exact pc that the trap happened at,
// then we look up that pc precisely. Otherwise backtrace
// information typically points at the pc *after* the call
// instruction (because otherwise it's likely a call instruction on
// the stack). In that case we want to lookup information for the
// previous instruction (the call instruction) so we subtract one as
// the lookup.
let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
if let Some(info) = info.lookup_frame_info(pc_to_lookup) {
wasm_trace.push(info);
}
}
Trap {
inner: Arc::new(TrapInner {
message,
wasm_trace,
native_trace,
}),
}
}
/// Returns a reference the `message` stored in `Trap`.
pub fn message(&self) -> &str {
&self.inner.message
}
/// Returns a list of function frames in WebAssembly code that led to this
/// trap happening.
pub fn trace(&self) -> &[FrameInfo] {
&self.inner.wasm_trace
}
}
impl fmt::Debug for Trap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Trap")
.field("message", &self.inner.message)
.field("wasm_trace", &self.inner.wasm_trace)
.field("native_trace", &self.inner.native_trace)
.finish()
}
}
impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner.message)?;
let trace = self.trace();
if trace.is_empty() {
return Ok(());
}
writeln!(f, "\nwasm backtrace:")?;
for (i, frame) in self.trace().iter().enumerate() {
let name = frame.module_name().unwrap_or("<unknown>");
write!(f, " {}: {:#6x} - {}!", i, frame.module_offset(), name)?;
match frame.func_name() {
Some(name) => match rustc_demangle::try_demangle(name) {
Ok(name) => write!(f, "{}", name)?,
Err(_) => write!(f, "{}", name)?,
},
None => write!(f, "<wasm function {}>", frame.func_index())?,
}
writeln!(f, "")?;
}
Ok(())
}
}
impl std::error::Error for Trap {}

View File

@@ -0,0 +1,536 @@
use std::fmt;
use wasmtime_environ::{ir, wasm, EntityIndex};
// Type Representations
// Type attributes
/// Indicator of whether a global is mutable or not
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum Mutability {
/// The global is constant and its value does not change
Const,
/// The value of the global can change over time
Var,
}
/// Limits of tables/memories where the units of the limits are defined by the
/// table/memory types.
///
/// A minimum is always available but the maximum may not be present.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct Limits {
min: u32,
max: Option<u32>,
}
impl Limits {
/// Creates a new set of limits with the minimum and maximum both specified.
pub fn new(min: u32, max: Option<u32>) -> Limits {
Limits { min, max }
}
/// Creates a new `Limits` with the `min` specified and no maximum specified.
pub fn at_least(min: u32) -> Limits {
Limits::new(min, None)
}
/// Returns the minimum amount for these limits.
pub fn min(&self) -> u32 {
self.min
}
/// Returns the maximum amount for these limits, if specified.
pub fn max(&self) -> Option<u32> {
self.max
}
}
// Value Types
/// A list of all possible value types in WebAssembly.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum ValType {
/// Signed 32 bit integer.
I32,
/// Signed 64 bit integer.
I64,
/// Floating point 32 bit integer.
F32,
/// Floating point 64 bit integer.
F64,
/// A 128 bit number.
V128,
/// A reference to opaque data in the Wasm instance.
AnyRef, /* = 128 */
/// A reference to a Wasm function.
FuncRef,
}
impl ValType {
/// Returns true if `ValType` matches any of the numeric types. (e.g. `I32`,
/// `I64`, `F32`, `F64`).
pub fn is_num(&self) -> bool {
match self {
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
_ => false,
}
}
/// Returns true if `ValType` matches either of the reference types.
pub fn is_ref(&self) -> bool {
match self {
ValType::AnyRef | ValType::FuncRef => true,
_ => false,
}
}
pub(crate) fn get_wasmtime_type(&self) -> Option<ir::Type> {
match self {
ValType::I32 => Some(ir::types::I32),
ValType::I64 => Some(ir::types::I64),
ValType::F32 => Some(ir::types::F32),
ValType::F64 => Some(ir::types::F64),
ValType::V128 => Some(ir::types::I8X16),
_ => None,
}
}
pub(crate) fn from_wasmtime_type(ty: ir::Type) -> Option<ValType> {
match ty {
ir::types::I32 => Some(ValType::I32),
ir::types::I64 => Some(ValType::I64),
ir::types::F32 => Some(ValType::F32),
ir::types::F64 => Some(ValType::F64),
ir::types::I8X16 => Some(ValType::V128),
_ => None,
}
}
}
// External Types
/// A list of all possible types which can be externally referenced from a
/// WebAssembly module.
///
/// This list can be found in [`ImportType`] or [`ExportType`], so these types
/// can either be imported or exported.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum ExternType {
/// This external type is the type of a WebAssembly function.
Func(FuncType),
/// This external type is the type of a WebAssembly global.
Global(GlobalType),
/// This external type is the type of a WebAssembly table.
Table(TableType),
/// This external type is the type of a WebAssembly memory.
Memory(MemoryType),
}
macro_rules! accessors {
($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
/// Attempt to return the underlying type of this external type,
/// returning `None` if it is a different type.
pub fn $get(&self) -> Option<&$ty> {
if let ExternType::$variant(e) = self {
Some(e)
} else {
None
}
}
/// Returns the underlying descriptor of this [`ExternType`], panicking
/// if it is a different type.
///
/// # Panics
///
/// Panics if `self` is not of the right type.
pub fn $unwrap(&self) -> &$ty {
self.$get().expect(concat!("expected ", stringify!($ty)))
}
)*)
}
impl ExternType {
accessors! {
(Func(FuncType) func unwrap_func)
(Global(GlobalType) global unwrap_global)
(Table(TableType) table unwrap_table)
(Memory(MemoryType) memory unwrap_memory)
}
}
impl From<FuncType> for ExternType {
fn from(ty: FuncType) -> ExternType {
ExternType::Func(ty)
}
}
impl From<GlobalType> for ExternType {
fn from(ty: GlobalType) -> ExternType {
ExternType::Global(ty)
}
}
impl From<MemoryType> for ExternType {
fn from(ty: MemoryType) -> ExternType {
ExternType::Memory(ty)
}
}
impl From<TableType> for ExternType {
fn from(ty: TableType) -> ExternType {
ExternType::Table(ty)
}
}
// Function Types
fn from_wasmtime_abiparam(param: &ir::AbiParam) -> Option<ValType> {
assert_eq!(param.purpose, ir::ArgumentPurpose::Normal);
ValType::from_wasmtime_type(param.value_type)
}
/// A descriptor for a function in a WebAssembly module.
///
/// WebAssembly functions can have 0 or more parameters and results.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct FuncType {
params: Box<[ValType]>,
results: Box<[ValType]>,
}
impl FuncType {
/// Creates a new function descriptor from the given parameters and results.
///
/// The function descriptor returned will represent a function which takes
/// `params` as arguments and returns `results` when it is finished.
pub fn new(params: Box<[ValType]>, results: Box<[ValType]>) -> FuncType {
FuncType { params, results }
}
/// Returns the list of parameter types for this function.
pub fn params(&self) -> &[ValType] {
&self.params
}
/// Returns the list of result types for this function.
pub fn results(&self) -> &[ValType] {
&self.results
}
/// Returns `Some` if this function signature was compatible with cranelift,
/// or `None` if one of the types/results wasn't supported or compatible
/// with cranelift.
pub(crate) fn get_wasmtime_signature(&self, pointer_type: ir::Type) -> Option<ir::Signature> {
use wasmtime_environ::ir::{AbiParam, ArgumentPurpose, Signature};
use wasmtime_jit::native;
let call_conv = native::call_conv();
let mut params = self
.params
.iter()
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
.collect::<Option<Vec<_>>>()?;
let returns = self
.results
.iter()
.map(|p| p.get_wasmtime_type().map(AbiParam::new))
.collect::<Option<Vec<_>>>()?;
params.insert(
0,
AbiParam::special(pointer_type, ArgumentPurpose::VMContext),
);
params.insert(1, AbiParam::new(pointer_type));
Some(Signature {
params,
returns,
call_conv,
})
}
/// Returns `None` if any types in the signature can't be converted to the
/// types in this crate, but that should very rarely happen and largely only
/// indicate a bug in our cranelift integration.
pub(crate) fn from_wasmtime_signature(signature: &ir::Signature) -> Option<FuncType> {
let params = signature
.params
.iter()
.skip(2) // skip the caller/callee vmctx
.map(|p| from_wasmtime_abiparam(p))
.collect::<Option<Vec<_>>>()?;
let results = signature
.returns
.iter()
.map(|p| from_wasmtime_abiparam(p))
.collect::<Option<Vec<_>>>()?;
Some(FuncType {
params: params.into_boxed_slice(),
results: results.into_boxed_slice(),
})
}
}
// Global Types
/// A WebAssembly global descriptor.
///
/// This type describes an instance of a global in a WebAssembly module. Globals
/// are local to an [`Instance`](crate::Instance) and are either immutable or
/// mutable.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct GlobalType {
content: ValType,
mutability: Mutability,
}
impl GlobalType {
/// Creates a new global descriptor of the specified `content` type and
/// whether or not it's mutable.
pub fn new(content: ValType, mutability: Mutability) -> GlobalType {
GlobalType {
content,
mutability,
}
}
/// Returns the value type of this global descriptor.
pub fn content(&self) -> &ValType {
&self.content
}
/// Returns whether or not this global is mutable.
pub fn mutability(&self) -> Mutability {
self.mutability
}
/// Returns `None` if the wasmtime global has a type that we can't
/// represent, but that should only very rarely happen and indicate a bug.
pub(crate) fn from_wasmtime_global(global: &wasm::Global) -> Option<GlobalType> {
let ty = ValType::from_wasmtime_type(global.ty)?;
let mutability = if global.mutability {
Mutability::Var
} else {
Mutability::Const
};
Some(GlobalType::new(ty, mutability))
}
}
// Table Types
/// A descriptor for a table in a WebAssembly module.
///
/// Tables are contiguous chunks of a specific element, typically a `funcref` or
/// an `anyref`. The most common use for tables is a function table through
/// which `call_indirect` can invoke other functions.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct TableType {
element: ValType,
limits: Limits,
}
impl TableType {
/// Creates a new table descriptor which will contain the specified
/// `element` and have the `limits` applied to its length.
pub fn new(element: ValType, limits: Limits) -> TableType {
TableType { element, limits }
}
/// Returns the element value type of this table.
pub fn element(&self) -> &ValType {
&self.element
}
/// Returns the limits, in units of elements, of this table.
pub fn limits(&self) -> &Limits {
&self.limits
}
pub(crate) fn from_wasmtime_table(table: &wasm::Table) -> TableType {
assert!(if let wasm::TableElementType::Func = table.ty {
true
} else {
false
});
let ty = ValType::FuncRef;
let limits = Limits::new(table.minimum, table.maximum);
TableType::new(ty, limits)
}
}
// Memory Types
/// A descriptor for a WebAssembly memory type.
///
/// Memories are described in units of pages (64KB) and represent contiguous
/// chunks of addressable memory.
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MemoryType {
limits: Limits,
}
impl MemoryType {
/// Creates a new descriptor for a WebAssembly memory given the specified
/// limits of the memory.
pub fn new(limits: Limits) -> MemoryType {
MemoryType { limits }
}
/// Returns the limits (in pages) that are configured for this memory.
pub fn limits(&self) -> &Limits {
&self.limits
}
pub(crate) fn from_wasmtime_memory(memory: &wasm::Memory) -> MemoryType {
MemoryType::new(Limits::new(memory.minimum, memory.maximum))
}
}
// Entity Types
#[derive(Clone, Hash, Eq, PartialEq)]
pub(crate) enum EntityType<'module> {
Function(&'module ir::Signature),
Table(&'module wasm::Table),
Memory(&'module wasm::Memory),
Global(&'module wasm::Global),
}
impl<'module> EntityType<'module> {
/// Translate from a `EntityIndex` into an `ExternType`.
pub(crate) fn new(
entity_index: &EntityIndex,
module: &'module wasmtime_environ::Module,
) -> EntityType<'module> {
match entity_index {
EntityIndex::Function(func_index) => {
let sig = module.local.func_signature(*func_index);
EntityType::Function(&sig)
}
EntityIndex::Table(table_index) => {
EntityType::Table(&module.local.table_plans[*table_index].table)
}
EntityIndex::Memory(memory_index) => {
EntityType::Memory(&module.local.memory_plans[*memory_index].memory)
}
EntityIndex::Global(global_index) => {
EntityType::Global(&module.local.globals[*global_index])
}
}
}
fn extern_type(&self) -> ExternType {
match self {
EntityType::Function(sig) => FuncType::from_wasmtime_signature(sig)
.expect("core wasm function type should be supported")
.into(),
EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),
EntityType::Memory(memory) => MemoryType::from_wasmtime_memory(memory).into(),
EntityType::Global(global) => GlobalType::from_wasmtime_global(global)
.expect("core wasm global type should be supported")
.into(),
}
}
}
// Import Types
/// A descriptor for an imported value into a wasm module.
///
/// This type is primarily accessed from the
/// [`Module::imports`](crate::Module::imports) API. Each [`ImportType`]
/// describes an import into the wasm module with the module/name that it's
/// imported from as well as the type of item that's being imported.
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ImportType<'module> {
/// The module of the import.
module: &'module str,
/// The field of the import.
name: &'module str,
/// The type of the import.
ty: EntityType<'module>,
}
impl<'module> ImportType<'module> {
/// Creates a new import descriptor which comes from `module` and `name` and
/// is of type `ty`.
pub(crate) fn new(
module: &'module str,
name: &'module str,
ty: EntityType<'module>,
) -> ImportType<'module> {
ImportType { module, name, ty }
}
/// Returns the module name that this import is expected to come from.
pub fn module(&self) -> &'module str {
self.module
}
/// Returns the field name of the module that this import is expected to
/// come from.
pub fn name(&self) -> &'module str {
self.name
}
/// Returns the expected type of this import.
pub fn ty(&self) -> ExternType {
self.ty.extern_type()
}
}
impl<'module> fmt::Debug for ImportType<'module> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImportType")
.field("module", &self.module().to_owned())
.field("name", &self.name().to_owned())
.field("ty", &self.ty())
.finish()
}
}
// Export Types
/// A descriptor for an exported WebAssembly value.
///
/// This type is primarily accessed from the
/// [`Module::exports`](crate::Module::exports) accessor and describes what
/// names are exported from a wasm module and the type of the item that is
/// exported.
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ExportType<'module> {
/// The name of the export.
name: &'module str,
/// The type of the export.
ty: EntityType<'module>,
}
impl<'module> ExportType<'module> {
/// Creates a new export which is exported with the given `name` and has the
/// given `ty`.
pub(crate) fn new(name: &'module str, ty: EntityType<'module>) -> ExportType<'module> {
ExportType { name, ty }
}
/// Returns the name by which this export is known.
pub fn name(&self) -> &'module str {
self.name
}
/// Returns the type of this export.
pub fn ty(&self) -> ExternType {
self.ty.extern_type()
}
}
impl<'module> fmt::Debug for ExportType<'module> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExportType")
.field("name", &self.name().to_owned())
.field("ty", &self.ty())
.finish()
}
}

View File

@@ -0,0 +1,31 @@
//! Unix-specific extension for the `wasmtime` crate.
//!
//! This module is only available on Unix targets, for example Linux and macOS.
//! It is not available on Windows, for example. Note that the import path for
//! this module is `wasmtime::unix::...`, which is intended to emphasize that it
//! is platform-specific.
//!
//! The traits contained in this module are intended to extend various types
//! throughout the `wasmtime` crate with extra functionality that's only
//! available on Unix.
use crate::Store;
/// Extensions for the [`Store`] type only available on Unix.
pub trait StoreExt {
// TODO: needs more docs?
/// The signal handler must be
/// [async-signal-safe](http://man7.org/linux/man-pages/man7/signal-safety.7.html).
unsafe fn set_signal_handler<H>(&self, handler: H)
where
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool;
}
impl StoreExt for Store {
unsafe fn set_signal_handler<H>(&self, handler: H)
where
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool,
{
*self.signal_handler_mut() = Some(Box::new(handler));
}
}

View File

@@ -0,0 +1,230 @@
use crate::r#ref::AnyRef;
use crate::{Func, Store, ValType};
use anyhow::{bail, Result};
use std::ptr;
/// Possible runtime values that a WebAssembly module can either consume or
/// produce.
#[derive(Debug, Clone)]
pub enum Val {
/// A 32-bit integer
I32(i32),
/// A 64-bit integer
I64(i64),
/// A 32-bit float.
///
/// Note that the raw bits of the float are stored here, and you can use
/// `f32::from_bits` to create an `f32` value.
F32(u32),
/// A 64-bit float.
///
/// Note that the raw bits of the float are stored here, and you can use
/// `f64::from_bits` to create an `f64` value.
F64(u64),
/// An `anyref` value which can hold opaque data to the wasm instance itself.
///
/// Note that this is a nullable value as well.
AnyRef(AnyRef),
/// A first-class reference to a WebAssembly function.
FuncRef(Func),
/// A 128-bit number
V128(u128),
}
macro_rules! accessors {
($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($(
/// Attempt to access the underlying value of this `Val`, returning
/// `None` if it is not the correct type.
pub fn $get(&self) -> Option<$ty> {
if let Val::$variant($bind) = self {
Some($cvt)
} else {
None
}
}
/// Returns the underlying value of this `Val`, panicking if it's the
/// wrong type.
///
/// # Panics
///
/// Panics if `self` is not of the right type.
pub fn $unwrap(&self) -> $ty {
self.$get().expect(concat!("expected ", stringify!($ty)))
}
)*)
}
impl Val {
/// Returns a null `anyref` value.
pub fn null() -> Val {
Val::AnyRef(AnyRef::null())
}
/// Returns the corresponding [`ValType`] for this `Val`.
pub fn ty(&self) -> ValType {
match self {
Val::I32(_) => ValType::I32,
Val::I64(_) => ValType::I64,
Val::F32(_) => ValType::F32,
Val::F64(_) => ValType::F64,
Val::AnyRef(_) => ValType::AnyRef,
Val::FuncRef(_) => ValType::FuncRef,
Val::V128(_) => ValType::V128,
}
}
pub(crate) unsafe fn write_value_to(&self, p: *mut u128) {
match self {
Val::I32(i) => ptr::write(p as *mut i32, *i),
Val::I64(i) => ptr::write(p as *mut i64, *i),
Val::F32(u) => ptr::write(p as *mut u32, *u),
Val::F64(u) => ptr::write(p as *mut u64, *u),
Val::V128(b) => ptr::write(p as *mut u128, *b),
_ => unimplemented!("Val::write_value_to"),
}
}
pub(crate) unsafe fn read_value_from(p: *const u128, ty: &ValType) -> Val {
match ty {
ValType::I32 => Val::I32(ptr::read(p as *const i32)),
ValType::I64 => Val::I64(ptr::read(p as *const i64)),
ValType::F32 => Val::F32(ptr::read(p as *const u32)),
ValType::F64 => Val::F64(ptr::read(p as *const u64)),
ValType::V128 => Val::V128(ptr::read(p as *const u128)),
_ => unimplemented!("Val::read_value_from"),
}
}
accessors! {
e
(I32(i32) i32 unwrap_i32 *e)
(I64(i64) i64 unwrap_i64 *e)
(F32(f32) f32 unwrap_f32 f32::from_bits(*e))
(F64(f64) f64 unwrap_f64 f64::from_bits(*e))
(FuncRef(&Func) funcref unwrap_funcref e)
(V128(u128) v128 unwrap_v128 *e)
}
/// Attempt to access the underlying value of this `Val`, returning
/// `None` if it is not the correct type.
///
/// This will return `Some` for both the `AnyRef` and `FuncRef` types.
pub fn anyref(&self) -> Option<AnyRef> {
match self {
Val::AnyRef(e) => Some(e.clone()),
_ => None,
}
}
/// Returns the underlying value of this `Val`, panicking if it's the
/// wrong type.
///
/// # Panics
///
/// Panics if `self` is not of the right type.
pub fn unwrap_anyref(&self) -> AnyRef {
self.anyref().expect("expected anyref")
}
pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
match self {
Val::FuncRef(f) => Store::same(store, f.store()),
// TODO: need to implement this once we actually finalize what
// `anyref` will look like and it's actually implemented to pass it
// to compiled wasm as well.
Val::AnyRef(AnyRef::Ref(_)) | Val::AnyRef(AnyRef::Other(_)) => false,
Val::AnyRef(AnyRef::Null) => true,
// Integers have no association with any particular store, so
// they're always considered as "yes I came from that store",
Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true,
}
}
}
impl From<i32> for Val {
fn from(val: i32) -> Val {
Val::I32(val)
}
}
impl From<i64> for Val {
fn from(val: i64) -> Val {
Val::I64(val)
}
}
impl From<f32> for Val {
fn from(val: f32) -> Val {
Val::F32(val.to_bits())
}
}
impl From<f64> for Val {
fn from(val: f64) -> Val {
Val::F64(val.to_bits())
}
}
impl From<AnyRef> for Val {
fn from(val: AnyRef) -> Val {
Val::AnyRef(val)
}
}
impl From<Func> for Val {
fn from(val: Func) -> Val {
Val::FuncRef(val)
}
}
pub(crate) fn into_checked_anyfunc(
val: Val,
store: &Store,
) -> Result<wasmtime_runtime::VMCallerCheckedAnyfunc> {
if !val.comes_from_same_store(store) {
bail!("cross-`Store` values are not supported");
}
Ok(match val {
Val::AnyRef(AnyRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc {
func_ptr: ptr::null(),
type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
vmctx: ptr::null_mut(),
},
Val::FuncRef(f) => {
let f = f.wasmtime_function();
wasmtime_runtime::VMCallerCheckedAnyfunc {
func_ptr: f.address,
type_index: f.signature,
vmctx: f.vmctx,
}
}
_ => bail!("val is not funcref"),
})
}
pub(crate) fn from_checked_anyfunc(
item: wasmtime_runtime::VMCallerCheckedAnyfunc,
store: &Store,
) -> Val {
if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
return Val::AnyRef(AnyRef::Null);
}
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
let export = wasmtime_runtime::ExportFunction {
address: item.func_ptr,
signature: item.type_index,
vmctx: item.vmctx,
};
let instance = store.existing_instance_handle(instance_handle);
let f = Func::from_wasmtime_function(export, instance);
Val::FuncRef(f)
}

View File

@@ -0,0 +1,31 @@
//! windows-specific extension for the `wasmtime` crate.
//!
//! This module is only available on Windows targets.
//! It is not available on Linux or macOS, for example. Note that the import path for
//! this module is `wasmtime::windows::...`, which is intended to emphasize that it
//! is platform-specific.
//!
//! The traits contained in this module are intended to extend various types
//! throughout the `wasmtime` crate with extra functionality that's only
//! available on Windows.
use crate::Store;
/// Extensions for the [`Store`] type only available on Windows.
pub trait StoreExt {
/// Configures a custom signal handler to execute.
///
/// TODO: needs more documentation.
unsafe fn set_signal_handler<H>(&self, handler: H)
where
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool;
}
impl StoreExt for Store {
unsafe fn set_signal_handler<H>(&self, handler: H)
where
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool,
{
*self.signal_handler_mut() = Some(Box::new(handler));
}
}