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:
49
crates/wasmtime/Cargo.toml
Normal file
49
crates/wasmtime/Cargo.toml
Normal 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
220
crates/wasmtime/LICENSE
Normal 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.
|
||||
|
||||
8
crates/wasmtime/README.md
Normal file
8
crates/wasmtime/README.md
Normal 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!
|
||||
990
crates/wasmtime/src/externals.rs
Normal file
990
crates/wasmtime/src/externals.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
275
crates/wasmtime/src/frame_info.rs
Normal file
275
crates/wasmtime/src/frame_info.rs
Normal 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
1177
crates/wasmtime/src/func.rs
Normal file
File diff suppressed because it is too large
Load Diff
243
crates/wasmtime/src/instance.rs
Normal file
243
crates/wasmtime/src/instance.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
46
crates/wasmtime/src/lib.rs
Normal file
46
crates/wasmtime/src/lib.rs
Normal 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!
|
||||
}
|
||||
}
|
||||
488
crates/wasmtime/src/linker.rs
Normal file
488
crates/wasmtime/src/linker.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
498
crates/wasmtime/src/module.rs
Normal file
498
crates/wasmtime/src/module.rs
Normal 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
223
crates/wasmtime/src/ref.rs
Normal 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, ")")
|
||||
}
|
||||
}
|
||||
1036
crates/wasmtime/src/runtime.rs
Normal file
1036
crates/wasmtime/src/runtime.rs
Normal file
File diff suppressed because it is too large
Load Diff
52
crates/wasmtime/src/trampoline/create_handle.rs
Normal file
52
crates/wasmtime/src/trampoline/create_handle.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
298
crates/wasmtime/src/trampoline/func.rs
Normal file
298
crates/wasmtime/src/trampoline/func.rs
Normal 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)
|
||||
}
|
||||
40
crates/wasmtime/src/trampoline/global.rs
Normal file
40
crates/wasmtime/src/trampoline/global.rs
Normal 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)
|
||||
}
|
||||
78
crates/wasmtime/src/trampoline/memory.rs
Normal file
78
crates/wasmtime/src/trampoline/memory.rs
Normal 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>)
|
||||
}
|
||||
}
|
||||
113
crates/wasmtime/src/trampoline/mod.rs
Normal file
113
crates/wasmtime/src/trampoline/mod.rs
Normal 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!(),
|
||||
}
|
||||
}
|
||||
35
crates/wasmtime/src/trampoline/table.rs
Normal file
35
crates/wasmtime/src/trampoline/table.rs
Normal 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
184
crates/wasmtime/src/trap.rs
Normal 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 {}
|
||||
536
crates/wasmtime/src/types.rs
Normal file
536
crates/wasmtime/src/types.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
31
crates/wasmtime/src/unix.rs
Normal file
31
crates/wasmtime/src/unix.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
230
crates/wasmtime/src/values.rs
Normal file
230
crates/wasmtime/src/values.rs
Normal 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)
|
||||
}
|
||||
31
crates/wasmtime/src/windows.rs
Normal file
31
crates/wasmtime/src/windows.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user