Implement RFC 11: Redesigning Wasmtime's APIs (#2897)

Implement Wasmtime's new API as designed by RFC 11. This is quite a large commit which has had lots of discussion externally, so for more information it's best to read the RFC thread and the PR thread.
This commit is contained in:
Alex Crichton
2021-06-03 09:10:53 -05:00
committed by GitHub
parent a5a28b1c5b
commit 7a1b7cdf92
233 changed files with 13349 additions and 11997 deletions

View File

@@ -61,7 +61,9 @@ jobs:
toolchain: nightly-2021-04-11
# Build C API documentation
- run: sudo apt-get update && sudo apt-get install -y doxygen git
- run: sudo apt-get update -y && sudo apt-get install -y libclang1-9 libclang-cpp9
- run: curl -L https://doxygen.nl/files/doxygen-1.9.1.linux.bin.tar.gz | tar xzf -
- run: echo "`pwd`/doxygen-1.9.1/bin" >> $GITHUB_PATH
- run: cd crates/c-api && doxygen doxygen.conf
# install mdbook, build the docs, and test the docs
@@ -73,7 +75,7 @@ jobs:
echo "${{ runner.tool_cache }}/mdbook/bin" >> $GITHUB_PATH
cargo install --root ${{ runner.tool_cache }}/mdbook --version ${{ env.CARGO_MDBOOK_VERSION }} mdbook
- run: (cd docs && mdbook build)
- run: cargo build -p wasmtime
- run: cargo build -p wasmtime-wasi --features wasmtime/wat
- run: (cd docs && mdbook test -L ../target/debug/deps)
# Build Rust API documentation

37
Cargo.lock generated
View File

@@ -3804,7 +3804,6 @@ dependencies = [
"wasi-common",
"wasi-tokio",
"wasmtime",
"wasmtime-wiggle",
"wiggle",
]
@@ -3815,7 +3814,6 @@ dependencies = [
"anyhow",
"wasi-crypto",
"wasmtime",
"wasmtime-wiggle",
"wiggle",
]
@@ -3831,7 +3829,6 @@ dependencies = [
"wasmtime",
"wasmtime-runtime",
"wasmtime-wasi",
"wasmtime-wiggle",
"wiggle",
]
@@ -3844,30 +3841,6 @@ dependencies = [
"wast 35.0.2",
]
[[package]]
name = "wasmtime-wiggle"
version = "0.27.0"
dependencies = [
"anyhow",
"proptest",
"wasmtime",
"wasmtime-wiggle-macro",
"wiggle",
"wiggle-borrow",
"witx",
]
[[package]]
name = "wasmtime-wiggle-macro"
version = "0.27.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wiggle-generate",
"witx",
]
[[package]]
name = "wast"
version = "33.0.0"
@@ -3909,23 +3882,18 @@ dependencies = [
name = "wiggle"
version = "0.27.0"
dependencies = [
"anyhow",
"async-trait",
"bitflags",
"proptest",
"thiserror",
"tracing",
"wasmtime",
"wiggle-macro",
"wiggle-test",
"witx",
]
[[package]]
name = "wiggle-borrow"
version = "0.27.0"
dependencies = [
"wiggle",
]
[[package]]
name = "wiggle-generate"
version = "0.27.0"
@@ -3961,7 +3929,6 @@ dependencies = [
"tracing",
"tracing-subscriber",
"wiggle",
"wiggle-borrow",
]
[[package]]

View File

@@ -77,7 +77,6 @@ members = [
"crates/wiggle",
"crates/wiggle/generate",
"crates/wiggle/macro",
"crates/wiggle/wasmtime",
"crates/wasi-common",
"crates/wasi-common/cap-std-sync",
"crates/wasi-common/tokio",

View File

@@ -3,21 +3,14 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use rayon::{prelude::*, ThreadPoolBuilder};
use std::{path::PathBuf, process::Command};
use wasmtime::*;
use wasmtime_wasi::{sync::WasiCtxBuilder, Wasi};
use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx};
fn instantiate(module: &Module) -> Result<Instance> {
let store = Store::new(&module.engine());
fn instantiate(linker: &Linker<WasiCtx>, module: &Module) -> Result<()> {
let wasi = WasiCtxBuilder::new().build();
let mut store = Store::new(module.engine(), wasi);
let _instance = linker.instantiate(&mut store, module)?;
// As we don't actually invoke Wasm code in this benchmark, we still add
// the WASI context to the store as it is considered part of getting a
// module that depends on WASI "ready to run".
Wasi::set_context(&store, WasiCtxBuilder::new().build())
.map_err(|_| anyhow::anyhow!("wasi set_context failed"))?;
let linker = Linker::new(&store);
let instance = linker.instantiate(module)?;
Ok(instance)
Ok(())
}
fn benchmark_name<'a>(strategy: &InstanceAllocationStrategy) -> &'static str {
@@ -46,15 +39,16 @@ fn bench_sequential(c: &mut Criterion, modules: &[&str]) {
path.push(file_name);
let mut config = Config::default();
Wasi::add_to_config(&mut config);
config.allocation_strategy(strategy.clone());
let engine = Engine::new(&config).expect("failed to create engine");
let module = Module::from_file(&engine, &path)
.expect(&format!("failed to load benchmark `{}`", path.display()));
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap();
group.bench_function(BenchmarkId::new(benchmark_name(strategy), file_name), |b| {
b.iter(|| instantiate(&module).expect("failed to instantiate module"));
b.iter(|| instantiate(&linker, &module).expect("failed to instantiate module"));
});
}
}
@@ -74,12 +68,13 @@ fn bench_parallel(c: &mut Criterion) {
InstanceAllocationStrategy::pooling(),
] {
let mut config = Config::default();
Wasi::add_to_config(&mut config);
config.allocation_strategy(strategy.clone());
let engine = Engine::new(&config).expect("failed to create engine");
let module = Module::from_file(&engine, "benches/instantiation/wasi.wasm")
.expect("failed to load WASI example module");
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |cx| cx).unwrap();
for threads in 1..=num_cpus::get_physical() {
let pool = ThreadPoolBuilder::new()
@@ -101,7 +96,8 @@ fn bench_parallel(c: &mut Criterion) {
b.iter(|| {
pool.install(|| {
(0..PARALLEL_INSTANCES).into_par_iter().for_each(|_| {
instantiate(&module).expect("failed to instantiate module");
instantiate(&linker, &module)
.expect("failed to instantiate module");
})
})
});

View File

@@ -218,10 +218,17 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
_ => (),
},
"Cranelift" => match (testsuite, testname) {
// Skip all reference types tests on the old backend. The modern
// implementation of reference types uses atomic instructions
// for reference counts on `externref`, but the old backend does not
// implement atomic instructions.
("reference_types", _) if cfg!(feature = "old-x86-backend") => return true,
// Skip all SIMD tests on old backend, there are instructions not
// implemented there and support is generally not maintained.
("simd", _) if cfg!(feature = "old-x86-backend") => return true,
// No simd support yet for s390x.
("simd", _) if platform_is_s390x() => return true,
("simd", _) if cfg!(feature = "old-x86-backend") => return true, // skip all SIMD tests on old backend.
// These are new instructions that are not really implemented in any backend.
("simd", "simd_i8x16_arith2")
| ("simd", "simd_conversions")

View File

@@ -34,8 +34,8 @@ mkdir tmp/$api_pkgname/include
mkdir tmp/$bin_pkgname
cp LICENSE README.md tmp/$api_pkgname
cp LICENSE README.md tmp/$bin_pkgname
cp -r crates/c-api/include tmp/$api_pkgname
cp crates/c-api/wasm-c-api/include/wasm.h tmp/$api_pkgname/include
cp crates/c-api/include/{wasmtime,wasi}.h tmp/$api_pkgname/include
fmt=tar
if [ "$platform" = "x86_64-windows" ]; then

View File

@@ -42,7 +42,8 @@ impl<'a> Interpreter<'a> {
self.call_by_index(index, arguments)
}
/// Call a function by its index in the [FunctionStore]; this is a proxy for [Interpreter::call].
/// Call a function by its index in the [FunctionStore]; this is a proxy for
/// `Interpreter::call`.
pub fn call_by_index(
&mut self,
index: FuncIndex,
@@ -287,7 +288,7 @@ mod tests {
v1 = iadd_imm v0, -1
return v1
}
function %parent(i32) -> i32 {
fn42 = %child(i32) -> i32
block0(v0: i32):

View File

@@ -138,11 +138,8 @@ use anyhow::{anyhow, Context, Result};
use std::os::raw::{c_int, c_void};
use std::slice;
use std::{env, path::PathBuf};
use wasmtime::{Config, Engine, FuncType, Instance, Linker, Module, Store};
use wasmtime_wasi::{
sync::{Wasi, WasiCtxBuilder},
WasiCtx,
};
use wasmtime::{Config, Engine, Instance, Linker, Module, Store};
use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx};
pub type ExitCode = c_int;
pub const OK: ExitCode = 0;
@@ -372,7 +369,7 @@ fn to_exit_code<T>(result: impl Into<Result<T>>) -> ExitCode {
/// This structure contains the actual Rust implementation of the state required
/// to manage the Wasmtime engine between calls.
struct BenchState {
engine: Engine,
linker: Linker<HostState>,
compilation_timer: *mut u8,
compilation_start: extern "C" fn(*mut u8),
compilation_end: extern "C" fn(*mut u8),
@@ -381,7 +378,16 @@ struct BenchState {
instantiation_end: extern "C" fn(*mut u8),
make_wasi_cx: Box<dyn FnMut() -> Result<WasiCtx>>,
module: Option<Module>,
instance: Option<Instance>,
store_and_instance: Option<(Store<HostState>, Instance)>,
}
struct HostState {
wasi: WasiCtx,
#[cfg(feature = "wasi-nn")]
wasi_nn: wasmtime_wasi_nn::WasiNnCtx,
#[cfg(feature = "wasi-crypto")]
wasi_crypto: wasmtime_wasi_crypto::WasiCryptoCtx,
}
impl BenchState {
@@ -400,7 +406,8 @@ impl BenchState {
// NB: do not configure a code cache.
let mut config = Config::new();
config.wasm_simd(true);
Wasi::add_to_config(&mut config);
let engine = Engine::new(&config)?;
let mut linker = Linker::<HostState>::new(&engine);
// Define the benchmarking start/end functions.
let execution_timer = unsafe {
@@ -408,29 +415,25 @@ impl BenchState {
// are only ever called from a single thread.
UnsafeSendSync::new(execution_timer)
};
config.define_host_func(
"bench",
"start",
FuncType::new(vec![], vec![]),
move |_, _, _| {
execution_start(*execution_timer.get());
Ok(())
},
);
config.define_host_func(
"bench",
"end",
FuncType::new(vec![], vec![]),
move |_, _, _| {
execution_end(*execution_timer.get());
Ok(())
},
);
linker.func_wrap("bench", "start", move || {
execution_start(*execution_timer.get());
Ok(())
})?;
linker.func_wrap("bench", "end", move || {
execution_end(*execution_timer.get());
Ok(())
})?;
let engine = Engine::new(&config)?;
wasmtime_wasi::add_to_linker(&mut linker, |cx| &mut cx.wasi)?;
#[cfg(feature = "wasi-nn")]
wasmtime_wasi_nn::add_to_linker(&mut linker, |cx| &mut cx.wasi_nn)?;
#[cfg(feature = "wasi-crypto")]
wasmtime_wasi_crypto::add_to_linker(&mut linker, |cx| &mut cx.wasi_crypto)?;
Ok(Self {
engine,
linker,
compilation_timer,
compilation_start,
compilation_end,
@@ -439,7 +442,7 @@ impl BenchState {
instantiation_end,
make_wasi_cx: Box::new(make_wasi_cx) as _,
module: None,
instance: None,
store_and_instance: None,
})
}
@@ -450,7 +453,7 @@ impl BenchState {
);
(self.compilation_start)(self.compilation_timer);
let module = Module::from_binary(&self.engine, bytes)?;
let module = Module::from_binary(self.linker.engine(), bytes)?;
(self.compilation_end)(self.compilation_timer);
self.module = Some(module);
@@ -463,60 +466,34 @@ impl BenchState {
.as_ref()
.expect("compile the module before instantiating it");
let wasi_cx = (self.make_wasi_cx)().context("failed to create a WASI context")?;
let host = HostState {
wasi: (self.make_wasi_cx)().context("failed to create a WASI context")?,
#[cfg(feature = "wasi-nn")]
wasi_nn: wasmtime_wasi_nn::WasiNnCtx::new()?,
#[cfg(feature = "wasi-crypto")]
wasi_crypto: wasmtime_wasi_nn::WasiCryptoCtx::new(),
};
// NB: Start measuring instantiation time *after* we've created the WASI
// context, since that needs to do file I/O to setup
// stdin/stdout/stderr.
(self.instantiation_start)(self.instantiation_timer);
let store = Store::new(&self.engine);
assert!(Wasi::set_context(&store, wasi_cx).is_ok());
let linker = Linker::new(&store);
#[cfg(feature = "wasi-nn")]
{
use std::cell::RefCell;
use std::rc::Rc;
use wasmtime_wasi_nn::{WasiNn, WasiNnCtx};
let wasi_nn = WasiNn::new(linker.store(), Rc::new(RefCell::new(WasiNnCtx::new()?)));
wasi_nn.add_to_linker(&mut linker)?;
}
#[cfg(feature = "wasi-crypto")]
{
use std::cell::RefCell;
use std::rc::Rc;
use wasmtime_wasi_crypto::{
WasiCryptoAsymmetricCommon, WasiCryptoCommon, WasiCryptoCtx, WasiCryptoSignatures,
WasiCryptoSymmetric,
};
let cx_crypto = Rc::new(RefCell::new(WasiCryptoCtx::new()));
WasiCryptoCommon::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?;
WasiCryptoAsymmetricCommon::new(linker.store(), cx_crypto.clone())
.add_to_linker(linker)?;
WasiCryptoSignatures::new(linker.store(), cx_crypto.clone()).add_to_linker(linker)?;
WasiCryptoSymmetric::new(linker.store(), cx_crypto).add_to_linker(linker)?;
}
let instance = linker.instantiate(&module)?;
let mut store = Store::new(self.linker.engine(), host);
let instance = self.linker.instantiate(&mut store, &module)?;
(self.instantiation_end)(self.instantiation_timer);
self.instance = Some(instance);
self.store_and_instance = Some((store, instance));
Ok(())
}
fn execute(&mut self) -> Result<()> {
let instance = self
.instance
let (mut store, instance) = self
.store_and_instance
.take()
.expect("instantiate the module before executing it");
let start_func = instance.get_typed_func::<(), ()>("_start")?;
match start_func.call(()) {
let start_func = instance.get_typed_func::<(), (), _>(&mut store, "_start")?;
match start_func.call(&mut store, ()) {
Ok(_) => Ok(()),
Err(trap) => {
// Since _start will likely return by using the system `exit` call, we must

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.8.17
# Doxyfile 1.9.1
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "Wasmtime"
PROJECT_NAME = Wasmtime
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
@@ -227,6 +227,14 @@ QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
# By default Python docstrings are displayed as preformatted text and doxygen's
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
# doxygen's special commands can be used and the contents of the docstring
# documentation blocks is shown as doxygen documentation.
# The default value is: YES.
PYTHON_DOCSTRING = YES
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
@@ -263,12 +271,6 @@ TAB_SIZE = 4
ALIASES =
# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.
TCL_SUBST =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@@ -310,18 +312,21 @@ OPTIMIZE_OUTPUT_SLICE = NO
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
# .inc files as Fortran files (default is PHP), and .f files as C (default is
# Fortran), use: inc=Fortran f=C.
# default for Fortran type files). For instance to make doxygen treat .inc files
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen.
# the files are not read by doxygen. When specifying no_extension you should add
# * to the FILE_PATTERNS.
#
# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
@@ -455,6 +460,19 @@ TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which efficively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -518,6 +536,13 @@ EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
# If this flag is set to YES, the name of an unnamed parameter in a declaration
# will be determined by the corresponding definition. By default unnamed
# parameters remain unnamed in the output.
# The default value is: YES.
RESOLVE_UNNAMED_PARAMS = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
@@ -555,11 +580,18 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# (including Cygwin) ands Mac users are advised to set this option to NO.
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
# able to match the capabilities of the underlying filesystem. In case the
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
@@ -798,7 +830,10 @@ WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered.
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = YES
@@ -829,13 +864,13 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT =
INPUT = include wasm-c-api/include
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
# possible encodings.
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
@@ -848,13 +883,15 @@ INPUT_ENCODING = UTF-8
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
# *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h
@@ -899,13 +936,14 @@ EXCLUDE_PATTERNS =
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS = assertions \
WASM_DECLARE_* \
WASM_API_EXTERN \
WASI_API_EXTERN \
WASMTIME_DECLARE_OWN \
WASI_DECLARE_OWN \
WASMTIME_CONFIG_PROP \
own
WASM_DECLARE_* \
WASM_API_EXTERN \
WASI_API_EXTERN \
WASMTIME_DECLARE_OWN \
WASI_DECLARE_OWN \
WASMTIME_CONFIG_PROP \
own \
wasm_*_enum
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
@@ -1075,35 +1113,6 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
# were built. This is equivalent to specifying the "-p" option to a clang tool,
# such as clang-check. These options will then be passed to the parser.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
@@ -1115,13 +1124,6 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
@@ -1292,10 +1294,11 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in
# environment (see:
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
# create a documentation set, doxygen will generate a Makefile in the HTML
# output directory. Running make will produce the docset in that directory and
# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
@@ -1337,8 +1340,8 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# Windows.
# (see:
# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@@ -1368,7 +1371,7 @@ CHM_FILE =
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the master .chm file (NO).
# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@@ -1413,7 +1416,8 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1421,8 +1425,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# folders).
# Folders (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1430,16 +1434,16 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
@@ -1451,9 +1455,9 @@ QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location of Qt's
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
# generated .qhp file.
# The QHG_LOCATION tag can be used to specify the location (absolute path
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
@@ -1504,7 +1508,7 @@ DISABLE_INDEX = NO
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NO
GENERATE_TREEVIEW = YES
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
@@ -1530,6 +1534,17 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
# the HTML output. These images will generally look nicer at scaled resolutions.
# Possible values are: png (the default) and svg (looks nicer but requires the
# pdf2svg or inkscape tool).
# The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FORMULA_FORMAT = png
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
@@ -1569,7 +1584,7 @@ USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
# http://docs.mathjax.org/en/latest/output.html) for more details.
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG.
# The default value is: HTML-CSS.
@@ -1585,7 +1600,7 @@ MATHJAX_FORMAT = HTML-CSS
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment.
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
@@ -1599,7 +1614,8 @@ MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
# (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1646,7 +1662,8 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: https://xapian.org/).
# Xapian (see:
# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@@ -1659,8 +1676,9 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: https://xapian.org/). See the section "External Indexing and
# Searching" for details.
# Xapian (see:
# https://xapian.org/). See the section "External Indexing and Searching" for
# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
@@ -1824,9 +1842,11 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
# higher quality PDF documentation.
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
# files. Set this option to YES, to get a higher quality PDF documentation.
#
# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -2142,7 +2162,8 @@ SEARCH_INCLUDES = YES
# preprocessor.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH = wasm-c-api/include include
INCLUDE_PATH = wasm-c-api/include \
include
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
@@ -2150,7 +2171,7 @@ INCLUDE_PATH = wasm-c-api/include include
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
INCLUDE_FILE_PATTERNS = *.h
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
@@ -2258,9 +2279,9 @@ HIDE_UNDOC_RELATIONS = YES
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: YES.
# The default value is: NO.
HAVE_DOT = YES
HAVE_DOT = NO
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
@@ -2337,10 +2358,32 @@ UML_LOOK = NO
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES.
# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
# tag is set to YES, doxygen will add type and arguments for attributes and
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
# will not generate fields with class member information in the UML graphs. The
# class diagrams will look similar to the default class diagrams but using UML
# notation for the relationships.
# Possible values are: NO, YES and NONE.
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_WRAP_THRESHOLD = 17
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
@@ -2414,9 +2457,7 @@ DIRECTORY_GRAPH = YES
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
@@ -2532,9 +2573,11 @@ DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc and
# plantuml temporary files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES

File diff suppressed because it is too large Load Diff

View File

@@ -32,9 +32,9 @@ extern "C" {
* \brief Convenience alias for #wasi_config_t
*
* \struct wasi_config_t
* \brief Opaque type used to create a #wasi_instance_t.
* \brief TODO
*
* \fn void wasi_config_delete(own wasi_config_t *);
* \fn void wasi_config_delete(wasi_config_t *);
* \brief Deletes a configuration object.
*/
WASI_DECLARE_OWN(config)
@@ -146,64 +146,6 @@ WASI_API_EXTERN void wasi_config_inherit_stderr(wasi_config_t* config);
*/
WASI_API_EXTERN bool wasi_config_preopen_dir(wasi_config_t* config, const char* path, const char* guest_path);
/**
* \typedef wasi_instance_t
* \brief Convenience alias for #wasi_instance_t
*
* \struct wasi_instance_t
* \brief Opaque type representing a WASI instance.
*
* \fn void wasi_instance_delete(own wasi_instance_t *);
* \brief Deletes an instance object.
*/
WASI_DECLARE_OWN(instance)
/**
* \brief Creates a new WASI instance from the specified configuration.
*
* \param store the store which functions will be attached to
* \param name the WASI module name that is being instantiated, currently either
* `wasi_unstable` or `wasi_snapshot_preview`.
* \param config the configuration object which has settings for how WASI APIs
* will behave.
* \param trap a location, if `NULL` is returned, that contains information
* about why instantiation failed.
*
* \return a #wasi_instance_t owned by the caller on success or `NULL` on
* failure.
*
* Note that this function takes ownership of the `config` argument whether this
* function succeeds or not. Ownership of the #wasi_instance_t and #wasm_trap_t
* are transferred to the caller.
*
* With a #wasi_instance_t you'll likely call either
* #wasmtime_linker_define_wasi or #wasi_instance_bind_import afterwards.
*/
WASI_API_EXTERN own wasi_instance_t* wasi_instance_new(
wasm_store_t* store,
const char* name,
own wasi_config_t* config,
own wasm_trap_t** trap
);
/**
* \brief Extracts a matching item for the given import from a #wasi_instance_t.
*
* \param instance the WASI instance an export is extracted from
* \param import the desired import type that is being extracted, typically
* acquired from #wasm_module_imports.
*
* \return a #wasm_extern_t which can be used to satisfy the `import`
* requested, or `NULL` if the provided `instance` cannot satisfy `import`.
*
* This function does not take ownership of its arguments, and the lifetime of
* the #wasm_extern_t is tied to the #wasi_instance_t argument.
*/
WASI_API_EXTERN const wasm_extern_t* wasi_instance_bind_import(
const wasi_instance_t* instance,
const wasm_importtype_t* import
);
#undef own
#ifdef __cplusplus

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
/**
* \file wasmtime/config.h
*
* \brief Wasmtime-specific extensions to #wasm_config_t
*/
#ifndef WASMTIME_CONFIG_H
#define WASMTIME_CONFIG_H
#include <wasm.h>
#include <wasmtime/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Specifier for how Wasmtime will compile code, values are in
* #wasmtime_strategy_enum
*/
typedef uint8_t wasmtime_strategy_t;
/**
* \brief Different ways that Wasmtime can compile WebAssembly
*
* The default value is #WASMTIME_STRATEGY_AUTO.
*/
enum wasmtime_strategy_enum { // Strategy
/// Wasmtime will automatically determine whether to use Cranelift or
/// Lightbeam, and currently it will always pick Cranelift. This default may
/// change over time though.
WASMTIME_STRATEGY_AUTO,
/// Indicates that Cranelift will unconditionally use Cranelift to compile
/// WebAssembly code.
WASMTIME_STRATEGY_CRANELIFT,
/// Indicates that Cranelift will unconditionally use Lightbeam to compile
/// WebAssembly code. Note that Lightbeam isn't always enabled at compile
/// time, and if that's the case an error will be returned.
WASMTIME_STRATEGY_LIGHTBEAM,
};
/**
* \brief Specifier of what optimization level to use for generated JIT code.
*
* See #wasmtime_opt_level_enum for possible values.
*/
typedef uint8_t wasmtime_opt_level_t;
/**
* \brief Different ways Wasmtime can optimize generated code.
*
* The default value is #WASMTIME_OPT_LEVEL_SPEED.
*/
enum wasmtime_opt_level_enum { // OptLevel
/// Generated code will not be optimized at all.
WASMTIME_OPT_LEVEL_NONE,
/// Generated code will be optimized purely for speed.
WASMTIME_OPT_LEVEL_SPEED,
/// Generated code will be optimized, but some speed optimizations are
/// disabled if they cause the generated code to be significantly larger.
WASMTIME_OPT_LEVEL_SPEED_AND_SIZE,
};
/**
* \brief Different ways wasmtime can enable profiling JIT code.
*
* See #wasmtime_profiling_strategy_enum for possible values.
*/
typedef uint8_t wasmtime_profiling_strategy_t;
/**
* \brief Different ways to profile JIT code.
*
* The default is #WASMTIME_PROFILING_STRATEGY_NONE.
*/
enum wasmtime_profiling_strategy_enum { // ProfilingStrategy
/// No profiling is enabled at runtime.
WASMTIME_PROFILING_STRATEGY_NONE,
/// Linux's "jitdump" support in `perf` is enabled and when Wasmtime is run
/// under `perf` necessary calls will be made to profile generated JIT code.
WASMTIME_PROFILING_STRATEGY_JITDUMP,
/// Support for VTune will be enabled and the VTune runtime will be informed,
/// at runtime, about JIT code.
///
/// Note that this isn't always enabled at build time.
WASMTIME_PROFILING_STRATEGY_VTUNE,
};
#define WASMTIME_CONFIG_PROP(ret, name, ty) \
WASM_API_EXTERN ret wasmtime_config_##name##_set(wasm_config_t*, ty);
/**
* \brief Configures whether DWARF debug information is constructed at runtime
* to describe JIT code.
*
* This setting is `false` by default. When enabled it will attempt to inform
* native debuggers about DWARF debugging information for JIT code to more
* easily debug compiled WebAssembly via native debuggers. This can also
* sometimes improve the quality of output when profiling is enabled.
*/
WASMTIME_CONFIG_PROP(void, debug_info, bool)
/**
* \brief Enables WebAssembly code to be interrupted.
*
* This setting is `false` by default. When enabled it will enable getting an
* interrupt handle via #wasmtime_interrupt_handle_new which can be used to
* interrupt currently-executing WebAssembly code.
*/
WASMTIME_CONFIG_PROP(void, interruptable, bool)
/**
* \brief Whether or not fuel is enabled for generated code.
*
* This setting is `false` by default. When enabled it will enable fuel counting
* meaning that fuel will be consumed every time a wasm instruction is executed,
* and trap when reaching zero.
*/
WASMTIME_CONFIG_PROP(void, consume_fuel, bool)
/**
* \brief Configures the maximum stack size, in bytes, that JIT code can use.
*
* This setting is 2MB by default. Configuring this setting will limit the
* amount of native stack space that JIT code can use while it is executing. If
* you're hitting stack overflow you can try making this setting larger, or if
* you'd like to limit wasm programs to less stack you can also configure this.
*
* Note that this setting is not interpreted with 100% precision. Additionally
* the amount of stack space that wasm takes is always relative to the first
* invocation of wasm on the stack, so recursive calls with host frames in the
* middle will all need to fit within this setting.
*/
WASMTIME_CONFIG_PROP(void, max_wasm_stack, size_t)
/**
* \brief Configures whether the WebAssembly threading proposal is enabled.
*
* This setting is `false` by default.
*
* Note that threads are largely unimplemented in Wasmtime at this time.
*/
WASMTIME_CONFIG_PROP(void, wasm_threads, bool)
/**
* \brief Configures whether the WebAssembly reference types proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_reference_types, bool)
/**
* \brief Configures whether the WebAssembly SIMD proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_simd, bool)
/**
* \brief Configures whether the WebAssembly bulk memory proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_bulk_memory, bool)
/**
* \brief Configures whether the WebAssembly multi value proposal is
* enabled.
*
* This setting is `true` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_multi_value, bool)
/**
* \brief Configures whether the WebAssembly module linking proposal is
* enabled.
*
* This setting is `false` by default.
*/
WASMTIME_CONFIG_PROP(void, wasm_module_linking, bool)
/**
* \brief Configures how JIT code will be compiled.
*
* This setting is #WASMTIME_STRATEGY_AUTO by default.
*
* If the compilation strategy selected could not be enabled then an error is
* returned.
*/
WASMTIME_CONFIG_PROP(wasmtime_error_t*, strategy, wasmtime_strategy_t)
/**
* \brief Configures whether Cranelift's debug verifier is enabled.
*
* This setting in `false` by default.
*
* When cranelift is used for compilation this enables expensive debug checks
* within Cranelift itself to verify it's correct.
*/
WASMTIME_CONFIG_PROP(void, cranelift_debug_verifier, bool)
/**
* \brief Configures Cranelift's optimization level for JIT code.
*
* This setting in #WASMTIME_OPT_LEVEL_SPEED by default.
*/
WASMTIME_CONFIG_PROP(void, cranelift_opt_level, wasmtime_opt_level_t)
/**
* \brief Configures the profiling strategy used for JIT code.
*
* This setting in #WASMTIME_PROFILING_STRATEGY_NONE by default.
*/
WASMTIME_CONFIG_PROP(wasmtime_error_t*, profiler, wasmtime_profiling_strategy_t)
/**
* \brief Configures the maximum size for memory to be considered "static"
*
* For more information see the Rust documentation at
* https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_maximum_size.
*/
WASMTIME_CONFIG_PROP(void, static_memory_maximum_size, uint64_t)
/**
* \brief Configures the guard region size for "static" memory.
*
* For more information see the Rust documentation at
* https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_guard_size.
*/
WASMTIME_CONFIG_PROP(void, static_memory_guard_size, uint64_t)
/**
* \brief Configures the guard region size for "dynamic" memory.
*
* For more information see the Rust documentation at
* https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.dynamic_memory_guard_size.
*/
WASMTIME_CONFIG_PROP(void, dynamic_memory_guard_size, uint64_t)
/**
* \brief Enables Wasmtime's cache and loads configuration from the specified
* path.
*
* By default the Wasmtime compilation cache is disabled. The configuration path
* here can be `NULL` to use the default settings, and otherwise the argument
* here must be a file on the filesystem with TOML configuration -
* https://bytecodealliance.github.io/wasmtime/cli-cache.html.
*
* An error is returned if the cache configuration could not be loaded or if the
* cache could not be enabled.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_config_cache_config_load(wasm_config_t*, const char*);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_CONFIG_H

View File

@@ -0,0 +1,55 @@
/**
* \file wasmtime/error.h
*
* \brief Definition and accessors of #wasmtime_error_t
*/
#ifndef WASMTIME_ERROR_H
#define WASMTIME_ERROR_H
#include <wasm.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_error_t
* \brief Convenience alias for #wasmtime_error
*
* \struct wasmtime_error
* \brief Errors generated by Wasmtime.
* \headerfile wasmtime/error.h
*
* This opaque type represents an error that happened as part of one of the
* functions below. Errors primarily have an error message associated with them
* at this time, which you can acquire by calling #wasmtime_error_message.
*
* Errors are safe to share across threads and must be deleted with
* #wasmtime_error_delete.
*/
typedef struct wasmtime_error wasmtime_error_t;
/**
* \brief Deletes an error.
*/
WASM_API_EXTERN void wasmtime_error_delete(wasmtime_error_t *error);
/**
* \brief Returns the string description of this error.
*
* This will "render" the error to a string and then return the string
* representation of the error to the caller. The `message` argument should be
* uninitialized before this function is called and the caller is responsible
* for deallocating it with #wasm_byte_vec_delete afterwards.
*/
WASM_API_EXTERN void wasmtime_error_message(
const wasmtime_error_t *error,
wasm_name_t *message
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_ERROR_H

View File

@@ -0,0 +1,172 @@
/**
* \file wasmtime/extern.h
*
* \brief Definition of #wasmtime_extern_t and external items.
*/
#ifndef WASMTIME_EXTERN_H
#define WASMTIME_EXTERN_H
#include <wasmtime/module.h>
#include <wasmtime/store.h>
#ifdef __cplusplus
extern "C" {
#endif
/// \brief Representation of a function in Wasmtime.
///
/// Functions are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Functions cannot
/// interoperate between #wasmtime_store_t instances and if the wrong function
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_func {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_func_t;
/// \brief Representation of a table in Wasmtime.
///
/// Tables are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Tables cannot
/// interoperate between #wasmtime_store_t instances and if the wrong table
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_table {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_table_t;
/// \brief Representation of a memory in Wasmtime.
///
/// Memories are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Memories cannot
/// interoperate between #wasmtime_store_t instances and if the wrong memory
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_memory {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_memory_t;
/// \brief Representation of a instance in Wasmtime.
///
/// Instances are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Instances cannot
/// interoperate between #wasmtime_store_t instances and if the wrong instance
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_instance {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_instance_t;
/// \brief Representation of a global in Wasmtime.
///
/// Globals are represented with a 64-bit identifying integer in Wasmtime.
/// They do not have any destructor associated with them. Globals cannot
/// interoperate between #wasmtime_store_t instances and if the wrong global
/// is passed to the wrong store then it may trigger an assertion to abort the
/// process.
typedef struct wasmtime_global {
/// Internal identifier of what store this belongs to, never zero.
uint64_t store_id;
/// Internal index within the store.
size_t index;
} wasmtime_global_t;
/// \brief Disciminant of #wasmtime_extern_t
typedef uint8_t wasmtime_extern_kind_t;
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
/// function
#define WASMTIME_EXTERN_FUNC 0
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
/// global
#define WASMTIME_EXTERN_GLOBAL 1
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
/// table
#define WASMTIME_EXTERN_TABLE 2
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
/// memory
#define WASMTIME_EXTERN_MEMORY 3
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is
/// an instance
#define WASMTIME_EXTERN_INSTANCE 4
/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is
/// a module
#define WASMTIME_EXTERN_MODULE 5
/**
* \typedef wasmtime_extern_union_t
* \brief Convenience alias for #wasmtime_extern_union
*
* \union wasmtime_extern_union
* \brief Container for different kinds of extern items.
*
* This type is contained in #wasmtime_extern_t and contains the payload for the
* various kinds of items an extern wasm item can be.
*/
typedef union wasmtime_extern_union {
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_FUNC
wasmtime_func_t func;
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_GLOBAL
wasmtime_global_t global;
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_TABLE
wasmtime_table_t table;
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY
wasmtime_memory_t memory;
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_INSTANCE
wasmtime_instance_t instance;
/// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MODULE
///
/// Note that this may be an owned pointer depending on the ownership of the
/// #wasmtime_extern_t container value.
wasmtime_module_t *module;
} wasmtime_extern_union_t;
/**
* \typedef wasmtime_extern_t
* \brief Convenience alias for #wasmtime_extern_t
*
* \union wasmtime_extern
* \brief Container for different kinds of extern items.
*
* Note that this structure may contain an owned value, namely
* #wasmtime_module_t, depending on the context in which this is used. APIs
* which consume a #wasmtime_extern_t do not take ownership, but APIs that
* return #wasmtime_extern_t require that #wasmtime_extern_delete is called to
* deallocate the value.
*/
typedef struct wasmtime_extern {
/// Discriminant of which field of #of is valid.
wasmtime_extern_kind_t kind;
/// Container for the extern item's value.
wasmtime_extern_union_t of;
} wasmtime_extern_t;
/// \brief Deletes a #wasmtime_extern_t.
void wasmtime_extern_delete(wasmtime_extern_t *val);
/// \brief Returns the type of the #wasmtime_extern_t defined within the given
/// store.
///
/// Does not take ownership of `context` or `val`, but the returned
/// #wasm_externtype_t is an owned value that needs to be deleted.
wasm_externtype_t *wasmtime_extern_type(wasmtime_context_t *context, wasmtime_extern_t *val);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_EXTERN_H

View File

@@ -0,0 +1,179 @@
/**
* \file wasmtime/func.h
*
* Wasmtime definitions of how to interact with host and wasm functions.
*/
#ifndef WASMTIME_FUNC_H
#define WASMTIME_FUNC_H
#include <wasm.h>
#include <wasmtime/val.h>
#include <wasmtime/store.h>
#include <wasmtime/extern.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_caller_t
* \brief Alias to #wasmtime_caller
*
* \brief Structure used to learn about the caller of a host-defined function.
* \struct wasmtime_caller
*
* This structure is an argument to #wasmtime_func_callback_t. The purpose
* of this structure is acquire a #wasmtime_context_t pointer to interact with
* objects, but it can also be used for inspect the state of the caller (such as
* getting memories and functions) with #wasmtime_caller_export_get.
*
* This object is never owned and does not need to be deleted.
*/
typedef struct wasmtime_caller wasmtime_caller_t;
/**
* \brief Callback signature for #wasmtime_func_new.
*
* This is the function signature for host functions that can be made accessible
* to WebAssembly. The arguments to this function are:
*
* \param env user-provided argument passed to #wasmtime_func_new
* \param caller a temporary object that can only be used during this function
* call. Used to acquire #wasmtime_context_t or caller's state
* \param args the arguments provided to this function invocation
* \param nargs how many arguments are provided
* \param results where to write the results of this function
* \param nresults how many results must be produced
*
* Callbacks are guaranteed to get called with the right types of arguments, but
* they must produce the correct number and types of results. Failure to do so
* will cause traps to get raised on the wasm side.
*
* This callback can optionally return a #wasm_trap_t indicating that a trap
* should be raised in WebAssembly. It's expected that in this case the caller
* relinquishes ownership of the trap and it is passed back to the engine.
*/
typedef wasm_trap_t* (*wasmtime_func_callback_t)(
void *env,
wasmtime_caller_t* caller,
const wasmtime_val_t *args,
size_t nargs,
wasmtime_val_t *results,
size_t nresults);
/**
* \brief Creates a new host-defined function.
*
* Inserts a host-defined function into the `store` provided which can be used
* to then instantiate a module with or define within a #wasmtime_linker_t.
*
* \param store the store in which to create the function
* \param type the wasm type of the function that's being created
* \param callback the host-defined callback to invoke
* \param env host-specific data passed to the callback invocation, can be
* `NULL`
* \param finalizer optional finalizer for `env`, can be `NULL`
* \param ret the #wasmtime_func_t return value to be filled in.
*
* The returned function can only be used with the specified `store`.
*/
WASM_API_EXTERN void wasmtime_func_new(
wasmtime_context_t *store,
const wasm_functype_t* type,
wasmtime_func_callback_t callback,
void *env,
void (*finalizer)(void*),
wasmtime_func_t *ret
);
/**
* \brief Returns the type of the function specified
*
* The returned #wasm_functype_t is owned by the caller.
*/
WASM_API_EXTERN wasm_functype_t* wasmtime_func_type(
const wasmtime_context_t *store,
const wasmtime_func_t *func
);
/**
* \brief Call a WebAssembly function.
*
* This function is used to invoke a function defined within a store. For
* example this might be used after extracting a function from a
* #wasmtime_instance_t.
*
* \param store the store which owns `func`
* \param func the function to call
* \param args the arguments to the function call
* \param nargs the number of arguments provided
* \param results where to write the results of the function call
* \param nresults the number of results expected
* \param trap where to store a trap, if one happens.
*
* There are three possible return states from this function:
*
* 1. The returned error is non-null. This means `results`
* wasn't written to and `trap` will have `NULL` written to it. This state
* means that programmer error happened when calling the function, for
* example when the size of the arguments/results was wrong, the types of the
* arguments were wrong, or arguments may come from the wrong store.
* 2. The trap pointer is filled in. This means the returned error is `NULL` and
* `results` was not written to. This state means that the function was
* executing but hit a wasm trap while executing.
* 3. The error and trap returned are both `NULL` and `results` are written to.
* This means that the function call succeeded and the specified results were
* produced.
*
* The `trap` pointer cannot be `NULL`. The `args` and `results` pointers may be
* `NULL` if the corresponding length is zero.
*
* Does not take ownership of #wasmtime_val_t arguments. Gives ownership of
* #wasmtime_val_t results.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_func_call(
wasmtime_context_t *store,
const wasmtime_func_t *func,
const wasmtime_val_t *args,
size_t nargs,
wasmtime_val_t *results,
size_t nresults,
wasm_trap_t **trap
);
/**
* \brief Loads a #wasmtime_extern_t from the caller's context
*
* This function will attempt to look up the export named `name` on the caller
* instance provided. If it is found then the #wasmtime_extern_t for that is
* returned, otherwise `NULL` is returned.
*
* Note that this only works for exported memories right now for WASI
* compatibility.
*
* \param caller the caller object to look up the export from
* \param name the name that's being looked up
* \param name_len the byte length of `name`
* \param item where to store the return value
*
* Returns a nonzero value if the export was found, or 0 if the export wasn't
* found. If the export wasn't found then `item` isn't written to.
*/
WASM_API_EXTERN bool wasmtime_caller_export_get(
wasmtime_caller_t *caller,
const char *name,
size_t name_len,
wasmtime_extern_t *item
);
/**
* \brief Returns the store context of the caller object.
*/
WASM_API_EXTERN wasmtime_context_t* wasmtime_caller_context(wasmtime_caller_t* caller);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_FUNC_H

View File

@@ -0,0 +1,91 @@
/**
* \file wasmtime/global.h
*
* Wasmtime APIs for interacting with WebAssembly globals.
*/
#ifndef WASMTIME_GLOBAL_H
#define WASMTIME_GLOBAL_H
#include <wasm.h>
#include <wasmtime/extern.h>
#include <wasmtime/store.h>
#include <wasmtime/val.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Creates a new global value.
*
* Creates a new host-defined global value within the provided `store`
*
* \param store the store in which to create the global
* \param type the wasm type of the global being created
* \param val the initial value of the global
* \param ret a return pointer for the created global.
*
* This function may return an error if the `val` argument does not match the
* specified type of the global, or if `val` comes from a different store than
* the one provided.
*
* This function does not take ownership of any of its arguments but error is
* owned by the caller.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_global_new(
wasmtime_context_t *store,
const wasm_globaltype_t *type,
const wasmtime_val_t *val,
wasmtime_global_t *ret
);
/**
* \brief Returns the wasm type of the specified global.
*
* The returned #wasm_globaltype_t is owned by the caller.
*/
WASM_API_EXTERN wasm_globaltype_t* wasmtime_global_type(
const wasmtime_context_t *store,
const wasmtime_global_t *global
);
/**
* \brief Get the value of the specified global.
*
* \param store the store that owns `global`
* \param global the global to get
* \param out where to store the value in this global.
*
* This function returns ownership of the contents of `out`, so
* #wasmtime_val_delete may need to be called on the value.
*/
WASM_API_EXTERN void wasmtime_global_get(
wasmtime_context_t *store,
const wasmtime_global_t *global,
wasmtime_val_t *out
);
/**
* \brief Sets a global to a new value.
*
* \param store the store that owns `global`
* \param global the global to set
* \param val the value to store in the global
*
* This function may return an error if `global` is not mutable or if `val` has
* the wrong type for `global`.
*
* THis does not take ownership of any argument but returns ownership of the error.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_global_set(
wasmtime_context_t *store,
const wasmtime_global_t *global,
const wasmtime_val_t *val
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_GLOBAL_H

View File

@@ -0,0 +1,158 @@
/**
* \file wasmtime/instance.h
*
* Wasmtime APIs for interacting with wasm instances.
*/
#ifndef WASMTIME_INSTANCE_H
#define WASMTIME_INSTANCE_H
#include <wasm.h>
#include <wasmtime/extern.h>
#include <wasmtime/module.h>
#include <wasmtime/store.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief An opaque object representing the type of an instance.
*/
typedef struct wasmtime_instancetype wasmtime_instancetype_t;
/// \brief Deletes an instance type
WASM_API_EXTERN void wasmtime_instancetype_delete(wasmtime_instancetype_t *ty);
/**
* \brief Returns the list of exports that this instance type provides.
*
* This function does not take ownership of the provided instance type but
* ownership of `out` is passed to the caller. Note that `out` is treated as
* uninitialized when passed to this function.
*/
WASM_API_EXTERN void wasmtime_instancetype_exports(const wasmtime_instancetype_t*, wasm_exporttype_vec_t* out);
/**
* \brief Converts a #wasmtime_instancetype_t to a #wasm_externtype_t
*
* The returned value is owned by the #wasmtime_instancetype_t argument and should not
* be deleted.
*/
WASM_API_EXTERN wasm_externtype_t* wasmtime_instancetype_as_externtype(wasmtime_instancetype_t*);
/**
* \brief Attempts to convert a #wasm_externtype_t to a #wasmtime_instancetype_t
*
* The returned value is owned by the #wasmtime_instancetype_t argument and should not
* be deleted. Returns `NULL` if the provided argument is not a
* #wasmtime_instancetype_t.
*/
WASM_API_EXTERN wasmtime_instancetype_t* wasmtime_externtype_as_instancetype(wasm_externtype_t*);
/**
* \brief Instantiate a wasm module.
*
* This function will instantiate a WebAssembly module with the provided
* imports, creating a WebAssembly instance. The returned instance can then
* afterwards be inspected for exports.
*
* \param store the store in which to create the instance
* \param module the module that's being instantiated
* \param imports the imports provided to the module
* \param nimports the size of `imports`
* \param instance where to store the returned instance
* \param trap where to store the returned trap
*
* This function requires that `imports` is the same size as the imports that
* `module` has. Additionally the `imports` array must be 1:1 lined up with the
* imports of the `module` specified. This is intended to be relatively low
* level, and #wasmtime_linker_instantiate is provided for a more ergonomic
* name-based resolution API.
*
* The states of return values from this function are similar to
* #wasmtime_func_call where an error can be returned meaning something like a
* link error in this context. A trap can be returned (meaning no error or
* instance is returned), or an instance can be returned (meaning no error or
* trap is returned).
*
* Note that this function requires that all `imports` specified must be owned
* by the `store` provided as well.
*
* This function does not take ownership of any of its arguments, but all return
* values are owned by the caller.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_instance_new(
wasmtime_context_t *store,
const wasmtime_module_t *module,
const wasmtime_extern_t* imports,
size_t nimports,
wasmtime_instance_t *instance,
wasm_trap_t **trap
);
/**
* \brief Returns the type of the specified instance.
*
* The returned type is owned by the caller.
*/
WASM_API_EXTERN wasmtime_instancetype_t *wasmtime_instance_type(
const wasmtime_context_t *store,
const wasmtime_instance_t *instance
);
/**
* \brief Get an export by name from an instance.
*
* \param store the store that owns `instance`
* \param instance the instance to lookup within
* \param name the export name to lookup
* \param name_len the byte length of `name`
* \param item where to store the returned value
*
* Returns nonzero if the export was found, and `item` is filled in. Otherwise
* returns 0.
*
* Doesn't take ownership of any arguments but does return ownership of the
* #wasmtime_extern_t.
*/
WASM_API_EXTERN bool wasmtime_instance_export_get(
wasmtime_context_t *store,
const wasmtime_instance_t *instance,
const char *name,
size_t name_len,
wasmtime_extern_t *item
);
/**
* \brief Get an export by index from an instance.
*
* \param store the store that owns `instance`
* \param instance the instance to lookup within
* \param index the index to lookup
* \param name where to store the name of the export
* \param name_len where to store the byte length of the name
* \param item where to store the export itself
*
* Returns nonzero if the export was found, and `name`, `name_len`, and `item`
* are filled in. Otherwise returns 0.
*
* Doesn't take ownership of any arguments but does return ownership of the
* #wasmtime_extern_t. The `name` pointer return value is owned by the `store`
* and must be immediately used before calling any other APIs on
* #wasmtime_context_t.
*/
WASM_API_EXTERN bool wasmtime_instance_export_nth(
wasmtime_context_t *store,
const wasmtime_instance_t *instance,
size_t index,
char **name,
size_t *name_len,
wasmtime_extern_t *item
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_INSTANCE_H

View File

@@ -0,0 +1,239 @@
/**
* \file wasmtime/linker.h
*
* Wasmtime API for a name-based linker used to instantiate modules.
*/
#ifndef WASMTIME_LINKER_H
#define WASMTIME_LINKER_H
#include <wasm.h>
#include <wasmtime/error.h>
#include <wasmtime/store.h>
#include <wasmtime/extern.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_linker_t
* \brief Alias to #wasmtime_linker
*
* \struct #wasmtime_linker
* \brief Object used to conveniently link together and instantiate wasm
* modules.
*
* This type corresponds to the `wasmtime::Linker` type in Rust. This
* type is intended to make it easier to manage a set of modules that link
* together, or to make it easier to link WebAssembly modules to WASI.
*
* A #wasmtime_linker_t is a higher level way to instantiate a module than
* #wasm_instance_new since it works at the "string" level of imports rather
* than requiring 1:1 mappings.
*/
typedef struct wasmtime_linker wasmtime_linker_t;
/**
* \brief Creates a new linker for the specified engine.
*
* This function does not take ownership of the engine argument, and the caller
* is expected to delete the returned linker.
*/
WASM_API_EXTERN wasmtime_linker_t* wasmtime_linker_new(wasm_engine_t* engine);
/**
* \brief Deletes a linker
*/
WASM_API_EXTERN void wasmtime_linker_delete(wasmtime_linker_t* linker);
/**
* \brief Configures whether this linker allows later definitions to shadow
* previous definitions.
*
* By default this setting is `false`.
*/
WASM_API_EXTERN void wasmtime_linker_allow_shadowing(wasmtime_linker_t* linker, bool allow_shadowing);
/**
* \brief Defines a new item in this linker.
*
* \param linker the linker the name is being defined in.
* \param module the module name the item is defined under.
* \param module_len the byte length of `module`
* \param name the field name the item is defined under
* \param name_len the byte length of `name`
* \param item the item that is being defined in this linker.
*
* \return On success `NULL` is returned, otherwise an error is returned which
* describes why the definition failed.
*
* For more information about name resolution consult the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define(
wasmtime_linker_t *linker,
const char *module,
size_t module_len,
const char *name,
size_t name_len,
const wasmtime_extern_t *item
);
/**
* \brief Defines WASI functions in this linker.
*
* \param linker the linker the name is being defined in.
*
* \return On success `NULL` is returned, otherwise an error is returned which
* describes why the definition failed.
*
* This function will provide WASI function names in the specified linker. Note
* that when an instance is created within a store then the store also needs to
* have its WASI settings configured with #wasmtime_context_set_wasi for WASI
* functions to work, otherwise an assert will be tripped that will abort the
* process.
*
* For more information about name resolution consult the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_wasi(
wasmtime_linker_t *linker
);
/**
* \brief Defines an instance under the specified name in this linker.
*
* \param linker the linker the name is being defined in.
* \param store the store that owns `instance`
* \param name the module name to define `instance` under.
* \param name_len the byte length of `name`
* \param instance a previously-created instance.
*
* \return On success `NULL` is returned, otherwise an error is returned which
* describes why the definition failed.
*
* This function will take all of the exports of the `instance` provided and
* defined them under a module called `name` with a field name as the export's
* own name.
*
* For more information about name resolution consult the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#name-resolution).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_define_instance(
wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *name,
size_t name_len,
const wasmtime_instance_t *instance
);
/**
* \brief Instantiates a #wasm_module_t with the items defined in this linker.
*
* \param linker the linker used to instantiate the provided module.
* \param store the store that is used to instantiate within
* \param module the module that is being instantiated.
* \param instance the returned instance, if successful.
* \param trap a trap returned, if the start function traps.
*
* \return One of three things can happen as a result of this function. First
* the module could be successfully instantiated and returned through
* `instance`, meaning the return value and `trap` are both set to `NULL`.
* Second the start function may trap, meaning the return value and `instance`
* are set to `NULL` and `trap` describes the trap that happens. Finally
* instantiation may fail for another reason, in which case an error is returned
* and `trap` and `instance` are set to `NULL`.
*
* This function will attempt to satisfy all of the imports of the `module`
* provided with items previously defined in this linker. If any name isn't
* defined in the linker than an error is returned. (or if the previously
* defined item is of the wrong type).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_instantiate(
const wasmtime_linker_t *linker,
wasmtime_context_t *store,
const wasmtime_module_t *module,
wasmtime_instance_t *instance,
wasm_trap_t **trap
);
/**
* \brief Defines automatic instantiations of a #wasm_module_t in this linker.
*
* \param linker the linker the module is being added to
* \param store the store that is used to instantiate `module`
* \param name the name of the module within the linker
* \param name_len the byte length of `name`
* \param module the module that's being instantiated
*
* \return An error if the module could not be instantiated or added or `NULL`
* on success.
*
* This function automatically handles [Commands and
* Reactors](https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi)
* instantiation and initialization.
*
* For more information see the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.module).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_module(
wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *name,
size_t name_len,
const wasmtime_module_t *module
);
/**
* \brief Acquires the "default export" of the named module in this linker.
*
* \param linker the linker to load from
* \param store the store to load a function into
* \param name the name of the module to get the default export for
* \param name_len the byte length of `name`
* \param func where to store the extracted default function.
*
* \return An error is returned if the default export could not be found, or
* `NULL` is returned and `func` is filled in otherwise.
*
* For more information see the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Linker.html#method.get_default).
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_linker_get_default(
const wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *name,
size_t name_len,
wasmtime_func_t *func
);
/**
* \brief Loads an item by name from this linker.
*
* \param linker the linker to load from
* \param store the store to load the item into
* \param module the name of the module to get
* \param module_len the byte length of `module`
* \param name the name of the field to get
* \param name_len the byte length of `name`
* \param item where to store the extracted item
*
* \return A nonzero value if the item is defined, in which case `item` is also
* filled in. Otherwise zero is returned.
*/
WASM_API_EXTERN bool wasmtime_linker_get(
const wasmtime_linker_t *linker,
wasmtime_context_t *store,
const char *module,
size_t module_len,
const char *name,
size_t name_len,
wasmtime_extern_t *item
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_LINKER_H

View File

@@ -0,0 +1,90 @@
/**
* \file wasmtime/memory.h
*
* Wasmtime API for interacting with wasm memories.
*/
#ifndef WASMTIME_MEMORY_H
#define WASMTIME_MEMORY_H
#include <wasm.h>
#include <wasmtime/extern.h>
#include <wasmtime/store.h>
#include <wasmtime/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Creates a new WebAssembly linear memory
*
* \param store the store to create the memory within
* \param ty the type of the memory to create
* \param ret where to store the returned memory
*
* If an error happens when creating the memory it's returned and owned by the
* caller. If an error happens then `ret` is not filled in.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_new(
wasmtime_context_t *store,
const wasm_memorytype_t* ty,
wasmtime_memory_t *ret
);
/**
* \brief Returns the tyep of the memory specified
*/
WASM_API_EXTERN wasm_memorytype_t* wasmtime_memory_type(
const wasmtime_context_t *store,
const wasmtime_memory_t *memory
);
/**
* \brief Returns the base pointer in memory where the linear memory starts.
*/
WASM_API_EXTERN uint8_t *wasmtime_memory_data(
const wasmtime_context_t *store,
const wasmtime_memory_t *memory
);
/**
* \brief Returns the byte length of this linear memory.
*/
WASM_API_EXTERN size_t wasmtime_memory_data_size(
const wasmtime_context_t *store,
const wasmtime_memory_t *memory
);
/**
* \brief Returns the length, in WebAssembly pages, of this linear memory
*/
WASM_API_EXTERN uint32_t wasmtime_memory_size(
const wasmtime_context_t *store,
const wasmtime_memory_t *memory
);
/**
* \brief Attempts to grow the specified memory by `delta` pages.
*
* \param store the store that owns `memory`
* \param memory the memory to grow
* \param delta the number of pages to grow by
* \param prev_size where to store the previous size of memory
*
* If memory cannot be grown then `prev_size` is left unchanged and an error is
* returned. Otherwise `prev_size` is set to the previous size of the memory, in
* WebAssembly pages, and `NULL` is returned.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_memory_grow(
wasmtime_context_t *store,
const wasmtime_memory_t *memory,
uint32_t delta,
uint32_t *prev_size
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_MEMORY_H

View File

@@ -0,0 +1,172 @@
/**
* \file wasmtime/module.h
*
* APIs for interacting with modules in Wasmtime
*/
#ifndef WASMTIME_MODULE_H
#define WASMTIME_MODULE_H
#include <wasm.h>
#include <wasmtime/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief An opaque object representing the type of a module.
*/
typedef struct wasmtime_moduletype wasmtime_moduletype_t;
/**
* \brief Deletes a module type.
*/
WASM_API_EXTERN void wasmtime_moduletype_delete(wasmtime_moduletype_t *ty);
/**
* \brief Returns the list of imports that this module type requires.
*
* This function does not take ownership of the provided module type but
* ownership of `out` is passed to the caller. Note that `out` is treated as
* uninitialized when passed to this function.
*/
WASM_API_EXTERN void wasmtime_moduletype_imports(const wasmtime_moduletype_t*, wasm_importtype_vec_t* out);
/**
* \brief Returns the list of exports that this module type provides.
*
* This function does not take ownership of the provided module type but
* ownership of `out` is passed to the caller. Note that `out` is treated as
* uninitialized when passed to this function.
*/
WASM_API_EXTERN void wasmtime_moduletype_exports(const wasmtime_moduletype_t*, wasm_exporttype_vec_t* out);
/**
* \brief Converts a #wasmtime_moduletype_t to a #wasm_externtype_t
*
* The returned value is owned by the #wasmtime_moduletype_t argument and should not
* be deleted.
*/
WASM_API_EXTERN wasm_externtype_t* wasmtime_moduletype_as_externtype(wasmtime_moduletype_t*);
/**
* \brief Attempts to convert a #wasm_externtype_t to a #wasmtime_moduletype_t
*
* The returned value is owned by the #wasmtime_moduletype_t argument and
* should not be deleted. Returns `NULL` if the provided argument is not a
* #wasmtime_moduletype_t.
*/
WASM_API_EXTERN wasmtime_moduletype_t* wasmtime_externtype_as_moduletype(wasm_externtype_t*);
/**
* \typedef wasmtime_module_t
* \brief Convenience alias for #wasmtime_module
*
* \struct wasmtime_module
* \brief A compiled Wasmtime module.
*
* This type represents a compiled WebAssembly module. The compiled module is
* ready to be instantiated and can be inspected for imports/exports. It is safe
* to use a module across multiple threads simultaneously.
*/
typedef struct wasmtime_module wasmtime_module_t;
/**
* \brief Compiles a WebAssembly binary into a #wasmtime_module_t
*
* This function will compile a WebAssembly binary into an owned #wasm_module_t.
* This performs the same as #wasm_module_new except that it returns a
* #wasmtime_error_t type to get richer error information.
*
* On success the returned #wasmtime_error_t is `NULL` and the `ret` pointer is
* filled in with a #wasm_module_t. On failure the #wasmtime_error_t is
* non-`NULL` and the `ret` pointer is unmodified.
*
* This function does not take ownership of any of its arguments, but the
* returned error and module are owned by the caller.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_module_new(
wasm_engine_t *engine,
const uint8_t *wasm,
size_t wasm_len,
wasmtime_module_t **ret
);
/**
* \brief Deletes a module.
*/
WASM_API_EXTERN void wasmtime_module_delete(wasmtime_module_t *m);
/**
* \brief Creates a shallow clone of the specified module, increasing the
* internal reference count.
*/
WASM_API_EXTERN wasmtime_module_t *wasmtime_module_clone(wasmtime_module_t *m);
/**
* \brief Validate a WebAssembly binary.
*
* This function will validate the provided byte sequence to determine if it is
* a valid WebAssembly binary within the context of the engine provided.
*
* This function does not take ownership of its arguments but the caller is
* expected to deallocate the returned error if it is non-`NULL`.
*
* If the binary validates then `NULL` is returned, otherwise the error returned
* describes why the binary did not validate.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_module_validate(
wasm_engine_t *engine,
const uint8_t *wasm,
size_t wasm_len
);
/**
* \brief Returns the type of this module.
*
* The returned #wasmtime_moduletype_t is expected to be deallocated by the
* caller.
*/
WASM_API_EXTERN wasmtime_moduletype_t* wasmtime_module_type(const wasmtime_module_t*);
/**
* \brief This function serializes compiled module artifacts as blob data.
*
* \param module the module
* \param ret if the conversion is successful, this byte vector is filled in with
* the serialized compiled module.
*
* \return a non-null error if parsing fails, or returns `NULL`. If parsing
* fails then `ret` isn't touched.
*
* This function does not take ownership of `module`, and the caller is
* expected to deallocate the returned #wasmtime_error_t and #wasm_byte_vec_t.
*/
WASM_API_EXTERN wasmtime_error_t* wasmtime_module_serialize(
wasmtime_module_t* module,
wasm_byte_vec_t *ret
);
/**
* \brief Build a module from serialized data.
*
* This function does not take ownership of any of its arguments, but the
* returned error and module are owned by the caller.
*
* This function is not safe to receive arbitrary user input. See the Rust
* documentation for more information on what inputs are safe to pass in here
* (e.g. only that of #wasmtime_module_serialize)
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_module_deserialize(
wasm_engine_t *engine,
const uint8_t *bytes,
size_t bytes_len,
wasmtime_module_t **ret
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_MODULE_H

View File

@@ -0,0 +1,203 @@
/**
* \file wasmtime/store.h
*
* Wasmtime definition of a "store".
*/
#ifndef WASMTIME_STORE_H
#define WASMTIME_STORE_H
#include <wasm.h>
#include <wasi.h>
#include <wasmtime/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_store_t
* \brief Convenience alias for #wasmtime_store_t
*
* \struct wasmtime_store
* \brief Storage of WebAssembly objects
*
* A store is the unit of isolation between WebAssembly instances in an
* embedding of Wasmtime. Values in one #wasmtime_store_t cannot flow into
* another #wasmtime_store_t. Stores are cheap to create and cheap to dispose.
* It's expected that one-off stores are common in embeddings.
*
* Objects stored within a #wasmtime_store_t are referenced with integer handles
* rather than interior pointers. This means that most APIs require that the
* store be explicitly passed in, which is done via #wasmtime_context_t. It is
* safe to move a #wasmtime_store_t to any thread at any time. A store generally
* cannot be concurrently used, however.
*/
typedef struct wasmtime_store wasmtime_store_t;
/**
* \typedef wasmtime_context_t
* \brief Convenience alias for #wasmtime_context
*
* \struct wasmtime_context
* \brief An interior pointer into a #wasmtime_store_t which is used as
* "context" for many functions.
*
* This context pointer is used pervasively throught Wasmtime's API. This can be
* acquired from #wasmtime_store_context or #wasmtime_caller_context. The
* context pointer for a store is the same for the entire lifetime of a store,
* so it can safely be stored adjacent to a #wasmtime_store_t itself.
*
* Usage of a #wasmtime_context_t must not outlive the original
* #wasmtime_store_t. Additionally #wasmtime_context_t can only be used in
* situations where it has explicitly been granted access to doing so. For
* example finalizers cannot use #wasmtime_context_t because they are not given
* access to it.
*/
typedef struct wasmtime_context wasmtime_context_t;
/**
* \brief Creates a new store within the specified engine.
*
* \param engine the compilation environment with configuration this store is
* connected to
* \param data user-provided data to store, can later be acquired with
* #wasmtime_context_get_data.
* \param finalizer an optional finalizer for `data`
*
* This function creates a fresh store with the provided configuration settings.
* The returned store must be deleted with #wasmtime_store_delete.
*/
WASM_API_EXTERN wasmtime_store_t *wasmtime_store_new(
wasm_engine_t *engine,
void *data,
void (*finalizer)(void*)
);
/**
* \brief Returns the interior #wasmtime_context_t pointer to this store
*/
WASM_API_EXTERN wasmtime_context_t *wasmtime_store_context(wasmtime_store_t *store);
/**
* \brief Deletes a store.
*/
WASM_API_EXTERN void wasmtime_store_delete(wasmtime_store_t *store);
/**
* \brief Returns the user-specified data associated with the specified store
*/
WASM_API_EXTERN void *wasmtime_context_get_data(const wasmtime_context_t* context);
/**
* \brief Overwrites the user-specified data associated with this store.
*
* Note that this does not execute the original finalizer for the provided data,
* and the original finalizer will be executed for the provided data when the
* store is deleted.
*/
WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void *data);
/**
* \brief Perform garbage collection within the given context.
*
* Garbage collects `externref`s that are used within this store. Any
* `externref`s that are discovered to be unreachable by other code or objects
* will have their finalizers run.
*
* The `context` argument must not be NULL.
*/
WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t* context);
/**
* \brief Adds fuel to this context's store for wasm to consume while executing.
*
* For this method to work fuel consumption must be enabled via
* #wasmtime_config_consume_fuel_set. By default a store starts with 0 fuel
* for wasm to execute with (meaning it will immediately trap).
* This function must be called for the store to have
* some fuel to allow WebAssembly to execute.
*
* Note that at this time when fuel is entirely consumed it will cause
* wasm to trap. More usages of fuel are planned for the future.
*
* If fuel is not enabled within this store then an error is returned. If fuel
* is successfully added then NULL is returned.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t *store, uint64_t fuel);
/**
* \brief Returns the amount of fuel consumed by this context's store execution
* so far.
*
* If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set
* then this function will return false. Otherwise true is returned and the
* fuel parameter is filled in with fuel consuemd so far.
*
* Also note that fuel, if enabled, must be originally configured via
* #wasmtime_context_add_fuel.
*/
WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel);
/**
* \brief Configres WASI state within the specified store.
*
* This function is required if #wasmtime_linker_define_wasi is called. This
* will configure the WASI state for instances defined within this store to the
* configuration specified.
*
* This function does not take ownership of `context` but it does take ownership
* of `wasi`. The caller should no longer use `wasi` after calling this function
* (even if an error is returned).
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_wasi(wasmtime_context_t *context, wasi_config_t *wasi);
/**
* \typedef wasmtime_interrupt_handle_t
* \brief Convenience alias for #wasmtime_interrupt_handle_t
*
* \struct wasmtime_interrupt_handle_t
* \brief A handle used to interrupt executing WebAssembly code.
*
* This structure is an opaque handle that represents a handle to a store. This
* handle can be used to remotely (from another thread) interrupt currently
* executing WebAssembly code.
*
* This structure is safe to share from multiple threads.
*/
typedef struct wasmtime_interrupt_handle wasmtime_interrupt_handle_t;
/**
* \brief Creates a new interrupt handle to interrupt executing WebAssembly from
* the provided store.
*
* There are a number of caveats about how interrupt is handled in Wasmtime. For
* more information see the [Rust
* documentation](https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Store.html#method.interrupt_handle).
*
* This function returns `NULL` if the store's configuration does not have
* interrupts enabled. See #wasmtime_config_interruptable_set.
*/
WASM_API_EXTERN wasmtime_interrupt_handle_t *wasmtime_interrupt_handle_new(wasmtime_context_t *context);
/**
* \brief Requests that WebAssembly code running in the store attached to this
* interrupt handle is interrupted.
*
* For more information about interrupts see #wasmtime_interrupt_handle_new.
*
* Note that this is safe to call from any thread.
*/
WASM_API_EXTERN void wasmtime_interrupt_handle_interrupt(wasmtime_interrupt_handle_t *handle);
/**
* \brief Deletes an interrupt handle.
*/
WASM_API_EXTERN void wasmtime_interrupt_handle_delete(wasmtime_interrupt_handle_t *handle);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_STORE_H

View File

@@ -0,0 +1,126 @@
/**
* \file wasmtime/table.h
*
* Wasmtime APIs for interacting with WebAssembly tables.
*/
#ifndef WASMTIME_TABLE_H
#define WASMTIME_TABLE_H
#include <wasm.h>
#include <wasmtime/extern.h>
#include <wasmtime/store.h>
#include <wasmtime/error.h>
#include <wasmtime/val.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Creates a new host-defined wasm table.
*
* \param store the store to create the table within
* \param ty the type of the table to create
* \param init the initial value for this table's elements
* \param table where to store the returned table
*
* This function does not take ownership of any of its parameters, but yields
* ownership of returned error. This function may return an error if the `init`
* value does not match `ty`, for example.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_table_new(
wasmtime_context_t *store,
const wasm_tabletype_t *ty,
const wasmtime_val_t *init,
wasmtime_table_t *table
);
/**
* \brief Returns the type of this table.
*
* The caller has ownership of the returned #wasm_tabletype_t
*/
WASM_API_EXTERN wasm_tabletype_t* wasmtime_table_type(
const wasmtime_context_t *store,
const wasmtime_table_t *table
);
/**
* \brief Gets a value in a table.
*
* \param store the store that owns `table`
* \param table the table to access
* \param index the table index to access
* \param val where to store the table's value
*
* This function will attempt to access a table element. If a nonzero value is
* returned then `val` is filled in and is owned by the caller. Otherwise zero
* is returned because the `index` is out-of-bounds.
*/
WASM_API_EXTERN bool wasmtime_table_get(
wasmtime_context_t *store,
const wasmtime_table_t *table,
uint32_t index,
wasmtime_val_t *val
);
/**
* \brief Sets a value in a table.
*
* \param store the store that owns `table`
* \param table the table to write to
* \param index the table index to write
* \param value the value to store.
*
* This function will store `value` into the specified index in the table. This
* does not take ownership of any argument but yields ownership of the error.
* This function can fail if `value` has the wrong type for the table, or if
* `index` is out of bounds.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_table_set(
wasmtime_context_t *store,
const wasmtime_table_t *table,
uint32_t index,
const wasmtime_val_t *value
);
/**
* \brief Returns the size, in elements, of the specified table
*/
WASM_API_EXTERN uint32_t wasmtime_table_size(
const wasmtime_context_t *store,
const wasmtime_table_t *table
);
/**
* \brief Grows a table.
*
* \param store the store that owns `table`
* \param table the table to grow
* \param delta the number of elements to grow the table by
* \param init the initial value for new table element slots
* \param prev_size where to store the previous size of the table before growth
*
* This function will attempt to grow the table by `delta` table elements. This
* can fail if `delta` would exceed the maximum size of the table or if `init`
* is the wrong type for this table. If growth is successful then `NULL` is
* returned and `prev_size` is filled in with the previous size of the table, in
* elements, before the growth happened.
*
* This function does not take ownership of any of its arguments.
*/
WASM_API_EXTERN wasmtime_error_t *wasmtime_table_grow(
wasmtime_context_t *store,
const wasmtime_table_t *table,
uint32_t delta,
const wasmtime_val_t *init,
uint32_t *prev_size
);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_TABLE_H

View File

@@ -0,0 +1,60 @@
/**
* \file wasmtime/trap.h
*
* Wasmtime APIs for interacting with traps and extensions to #wasm_trap_t.
*/
#ifndef WASMTIME_TRAP_H
#define WASMTIME_TRAP_H
#include <wasm.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Creates a new trap.
*
* \param msg the message to associate with this trap
* \param msg_len the byte length of `msg`
*
* The #wasm_trap_t returned is owned by the caller.
*/
WASM_API_EXTERN wasm_trap_t *wasmtime_trap_new(const char *msg, size_t msg_len);
/**
* \brief Attempts to extract a WASI-specific exit status from this trap.
*
* Returns `true` if the trap is a WASI "exit" trap and has a return status. If
* `true` is returned then the exit status is returned through the `status`
* pointer. If `false` is returned then this is not a wasi exit trap.
*/
WASM_API_EXTERN bool wasmtime_trap_exit_status(const wasm_trap_t*, int *status);
/**
* \brief Returns a human-readable name for this frame's function.
*
* This function will attempt to load a human-readable name for function this
* frame points to. This function may return `NULL`.
*
* The lifetime of the returned name is the same as the #wasm_frame_t itself.
*/
WASM_API_EXTERN const wasm_name_t *wasmtime_frame_func_name(const wasm_frame_t*);
/**
* \brief Returns a human-readable name for this frame's module.
*
* This function will attempt to load a human-readable name for module this
* frame points to. This function may return `NULL`.
*
* The lifetime of the returned name is the same as the #wasm_frame_t itself.
*/
WASM_API_EXTERN const wasm_name_t *wasmtime_frame_module_name(const wasm_frame_t*);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_TRAP_H

View File

@@ -0,0 +1,158 @@
/**
* \file wasmtime/val.h
*
* APIs for interacting with WebAssembly values in Wasmtime.
*/
#ifndef WASMTIME_VAL_H
#define WASMTIME_VAL_H
#include <wasm.h>
#include <wasmtime/extern.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \typedef wasmtime_externref_t
* \brief Convenience alias for #wasmtime_externref
*
* \struct wasmtime_externref
* \brief A host-defined un-forgeable reference to pass into WebAssembly.
*
* This structure represents an `externref` that can be passed to WebAssembly.
* It cannot be forged by WebAssembly itself and is guaranteed to have been
* created by the host.
*/
typedef struct wasmtime_externref wasmtime_externref_t;
/**
* \brief Create a new `externref` value.
*
* Creates a new `externref` value wrapping the provided data, returning the
* pointer to the externref.
*
* \param data the host-specific data to wrap
* \param finalizer an optional finalizer for `data`
*
* When the reference is reclaimed, the wrapped data is cleaned up with the
* provided `finalizer`.
*
* The returned value must be deleted with #wasmtime_externref_delete
*/
WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_new(void *data, void (*finalizer)(void*));
/**
* \brief Get an `externref`'s wrapped data
*
* Returns the original `data` passed to #wasmtime_externref_new. It is required
* that `data` is not `NULL`.
*/
WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_externref_t *data);
/**
* \brief Creates a shallow copy of the `externref` argument, returning a
* separately owned pointer (increases the reference count).
*/
WASM_API_EXTERN wasmtime_externref_t *wasmtime_externref_clone(wasmtime_externref_t *ref);
/**
* \brief Decrements the reference count of the `ref`, deleting it if it's the
* last reference.
*/
WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_externref_t *ref);
/// \brief Discriminant stored in #wasmtime_val::kind
typedef uint8_t wasmtime_valkind_t;
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i32
#define WASMTIME_I32 0
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an i64
#define WASMTIME_I64 1
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a f32
#define WASMTIME_F32 2
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a f64
#define WASMTIME_F64 3
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a v128
#define WASMTIME_V128 4
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is a funcref
#define WASMTIME_FUNCREF 5
/// \brief Value of #wasmtime_valkind_t meaning that #wasmtime_val_t is an externref
#define WASMTIME_EXTERNREF 6
/// \brief A 128-bit value representing the WebAssembly `v128` type. Bytes are
/// stored in little-endian order.
typedef uint8_t wasmtime_v128[16];
/**
* \typedef wasmtime_valunion_t
* \brief Convenience alias for #wasmtime_valunion
*
* \union wasmtime_valunion
* \brief Container for different kinds of wasm values.
*
* This type is contained in #wasmtime_val_t and contains the payload for the
* various kinds of items a value can be.
*/
typedef union wasmtime_valunion {
/// Field used if #wasmtime_val_t::kind is #WASMTIME_I32
int32_t i32;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_I64
int64_t i64;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_F32
float32_t f32;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_F64
float64_t f64;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_FUNCREF
///
/// If this value represents a `ref.null func` value then the `store_id` field
/// is set to zero.
wasmtime_func_t funcref;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF
///
/// If this value represents a `ref.null extern` value then this pointer will
/// be `NULL`.
wasmtime_externref_t *externref;
/// Field used if #wasmtime_val_t::kind is #WASMTIME_V128
wasmtime_v128 v128;
} wasmtime_valunion_t;
/**
* \typedef wasmtime_val_t
* \brief Convenience alias for #wasmtime_val_t
*
* \union wasmtime_val
* \brief Container for different kinds of wasm values.
*
* Note that this structure may contain an owned value, namely
* #wasmtime_externref_t, depending on the context in which this is used. APIs
* which consume a #wasmtime_val_t do not take ownership, but APIs that return
* #wasmtime_val_t require that #wasmtime_val_delete is called to deallocate
* the value.
*/
typedef struct wasmtime_val {
/// Discriminant of which field of #of is valid.
wasmtime_valkind_t kind;
/// Container for the extern item's value.
wasmtime_valunion_t of;
} wasmtime_val_t;
/**
* \brief Delets an owned #wasmtime_val_t.
*
* Note that this only deletes the contents, not the memory that `val` points to
* itself (which is owned by the caller).
*/
WASM_API_EXTERN void wasmtime_val_delete(wasmtime_val_t *val);
/**
* \brief Copies `src` into `dst`.
*/
WASM_API_EXTERN void wasmtime_val_copy(wasmtime_val_t *dst, const wasmtime_val_t *src);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WASMTIME_VAL_H

View File

@@ -1,6 +1,5 @@
use crate::{wasm_name_t, wasm_trap_t};
use crate::wasm_name_t;
use anyhow::{anyhow, Error, Result};
use wasmtime::Trap;
#[repr(C)]
pub struct wasmtime_error_t {
@@ -9,12 +8,6 @@ pub struct wasmtime_error_t {
wasmtime_c_api_macros::declare_own!(wasmtime_error_t);
impl wasmtime_error_t {
pub(crate) fn to_trap(self) -> Box<wasm_trap_t> {
Box::new(wasm_trap_t::new(Trap::from(self.error)))
}
}
impl From<Error> for wasmtime_error_t {
fn from(error: Error) -> wasmtime_error_t {
wasmtime_error_t { error }

View File

@@ -1,11 +1,13 @@
use crate::{
wasm_externkind_t, wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_instance_t,
wasm_memory_t, wasm_module_t, wasm_table_t,
wasm_memory_t, wasm_module_t, wasm_table_t, wasmtime_module_t, CStoreContext, StoreRef,
};
use wasmtime::Extern;
use std::mem::ManuallyDrop;
use wasmtime::{Extern, Func, Global, Instance, Memory, Table};
#[derive(Clone)]
pub struct wasm_extern_t {
pub(crate) store: StoreRef,
pub(crate) which: Extern,
}
@@ -24,8 +26,8 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t {
}
#[no_mangle]
pub extern "C" fn wasm_extern_type(e: &wasm_extern_t) -> Box<wasm_externtype_t> {
Box::new(wasm_externtype_t::new(e.which.ty()))
pub unsafe extern "C" fn wasm_extern_type(e: &wasm_extern_t) -> Box<wasm_externtype_t> {
Box::new(wasm_externtype_t::new(e.which.ty(&e.store.context())))
}
#[no_mangle]
@@ -87,3 +89,97 @@ pub extern "C" fn wasm_extern_as_instance(e: &wasm_extern_t) -> Option<&wasm_ins
pub extern "C" fn wasm_extern_as_instance_const(e: &wasm_extern_t) -> Option<&wasm_instance_t> {
wasm_extern_as_instance(e)
}
#[repr(C)]
pub struct wasmtime_extern_t {
pub kind: wasmtime_extern_kind_t,
pub of: wasmtime_extern_union,
}
pub type wasmtime_extern_kind_t = u8;
pub const WASMTIME_EXTERN_FUNC: wasmtime_extern_kind_t = 0;
pub const WASMTIME_EXTERN_GLOBAL: wasmtime_extern_kind_t = 1;
pub const WASMTIME_EXTERN_TABLE: wasmtime_extern_kind_t = 2;
pub const WASMTIME_EXTERN_MEMORY: wasmtime_extern_kind_t = 3;
pub const WASMTIME_EXTERN_INSTANCE: wasmtime_extern_kind_t = 4;
pub const WASMTIME_EXTERN_MODULE: wasmtime_extern_kind_t = 5;
#[repr(C)]
pub union wasmtime_extern_union {
pub func: Func,
pub table: Table,
pub global: Global,
pub instance: Instance,
pub memory: Memory,
pub module: ManuallyDrop<Box<wasmtime_module_t>>,
}
impl wasmtime_extern_t {
pub unsafe fn to_extern(&self) -> Extern {
match self.kind {
WASMTIME_EXTERN_FUNC => Extern::Func(self.of.func),
WASMTIME_EXTERN_GLOBAL => Extern::Global(self.of.global),
WASMTIME_EXTERN_TABLE => Extern::Table(self.of.table),
WASMTIME_EXTERN_MEMORY => Extern::Memory(self.of.memory),
WASMTIME_EXTERN_INSTANCE => Extern::Instance(self.of.instance),
WASMTIME_EXTERN_MODULE => Extern::Module(self.of.module.module.clone()),
other => panic!("unknown wasm_extern_kind_t: {}", other),
}
}
}
impl From<Extern> for wasmtime_extern_t {
fn from(item: Extern) -> wasmtime_extern_t {
match item {
Extern::Func(func) => wasmtime_extern_t {
kind: WASMTIME_EXTERN_FUNC,
of: wasmtime_extern_union { func },
},
Extern::Global(global) => wasmtime_extern_t {
kind: WASMTIME_EXTERN_GLOBAL,
of: wasmtime_extern_union { global },
},
Extern::Table(table) => wasmtime_extern_t {
kind: WASMTIME_EXTERN_TABLE,
of: wasmtime_extern_union { table },
},
Extern::Memory(memory) => wasmtime_extern_t {
kind: WASMTIME_EXTERN_MEMORY,
of: wasmtime_extern_union { memory },
},
Extern::Instance(instance) => wasmtime_extern_t {
kind: WASMTIME_EXTERN_INSTANCE,
of: wasmtime_extern_union { instance },
},
Extern::Module(module) => wasmtime_extern_t {
kind: WASMTIME_EXTERN_MODULE,
of: wasmtime_extern_union {
module: ManuallyDrop::new(Box::new(wasmtime_module_t { module })),
},
},
}
}
}
impl Drop for wasmtime_extern_t {
fn drop(&mut self) {
if self.kind == WASMTIME_EXTERN_MODULE {
unsafe {
ManuallyDrop::drop(&mut self.of.module);
}
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_extern_delete(e: &mut ManuallyDrop<wasmtime_extern_t>) {
ManuallyDrop::drop(e);
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_extern_type(
store: CStoreContext<'_>,
e: &wasmtime_extern_t,
) -> Box<wasm_externtype_t> {
Box::new(wasm_externtype_t::new(e.to_extern().ty(store)))
}

View File

@@ -1,12 +1,15 @@
use crate::{wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t};
use crate::{wasm_name_t, wasm_trap_t, wasmtime_error_t};
use crate::wasm_trap_t;
use crate::{
wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t, wasmtime_error_t,
wasmtime_extern_t, wasmtime_val_t, wasmtime_val_union, CStoreContext, CStoreContextMut,
};
use anyhow::anyhow;
use std::ffi::c_void;
use std::mem::MaybeUninit;
use std::panic::{self, AssertUnwindSafe};
use std::ptr;
use std::str;
use wasmtime::{Caller, Extern, Func, Trap, Val};
use wasmtime::{AsContextMut, Caller, Extern, Func, Trap};
#[derive(Clone)]
#[repr(transparent)]
@@ -16,11 +19,6 @@ pub struct wasm_func_t {
wasmtime_c_api_macros::declare_ref!(wasm_func_t);
#[repr(C)]
pub struct wasmtime_caller_t<'a> {
caller: Caller<'a>,
}
pub type wasm_func_callback_t = extern "C" fn(
args: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
@@ -32,32 +30,6 @@ pub type wasm_func_callback_with_env_t = extern "C" fn(
results: *mut wasm_val_vec_t,
) -> Option<Box<wasm_trap_t>>;
pub type wasmtime_func_callback_t = extern "C" fn(
caller: *const wasmtime_caller_t,
args: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
) -> Option<Box<wasm_trap_t>>;
pub type wasmtime_func_callback_with_env_t = extern "C" fn(
caller: *const wasmtime_caller_t,
env: *mut std::ffi::c_void,
args: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
) -> Option<Box<wasm_trap_t>>;
struct Finalizer {
env: *mut c_void,
finalizer: Option<extern "C" fn(*mut c_void)>,
}
impl Drop for Finalizer {
fn drop(&mut self) {
if let Some(f) = self.finalizer {
f(self.env);
}
}
}
impl wasm_func_t {
pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> {
match &e.which {
@@ -66,150 +38,90 @@ impl wasm_func_t {
}
}
pub(crate) fn func(&self) -> &Func {
match &self.ext.which {
pub(crate) fn func(&self) -> Func {
match self.ext.which {
Extern::Func(f) => f,
_ => unsafe { std::hint::unreachable_unchecked() },
}
}
}
impl From<Func> for wasm_func_t {
fn from(func: Func) -> wasm_func_t {
wasm_func_t {
ext: wasm_extern_t { which: func.into() },
}
}
}
fn create_function(
store: &wasm_store_t,
unsafe fn create_function(
store: &mut wasm_store_t,
ty: &wasm_functype_t,
func: impl Fn(Caller<'_>, *const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>>
func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>>
+ Send
+ Sync
+ 'static,
) -> Box<wasm_func_t> {
let store = &store.store;
let ty = ty.ty().ty.clone();
let func = Func::new(store, ty, move |caller, params, results| {
let params: wasm_val_vec_t = params
.iter()
.cloned()
.map(|p| wasm_val_t::from_val(p))
.collect::<Vec<_>>()
.into();
let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into();
let out = func(caller, &params, &mut out_results);
if let Some(trap) = out {
return Err(trap.trap.clone());
}
let func = Func::new(
store.store.context_mut(),
ty,
move |_caller, params, results| {
let params: wasm_val_vec_t = params
.iter()
.cloned()
.map(|p| wasm_val_t::from_val(p))
.collect::<Vec<_>>()
.into();
let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into();
let out = func(&params, &mut out_results);
if let Some(trap) = out {
return Err(trap.trap.clone());
}
let out_results = out_results.as_slice();
for i in 0..results.len() {
results[i] = out_results[i].val();
}
Ok(())
});
Box::new(func.into())
let out_results = out_results.as_slice();
for i in 0..results.len() {
results[i] = out_results[i].val();
}
Ok(())
},
);
Box::new(wasm_func_t {
ext: wasm_extern_t {
store: store.store.clone(),
which: func.into(),
},
})
}
#[no_mangle]
pub extern "C" fn wasm_func_new(
store: &wasm_store_t,
pub unsafe extern "C" fn wasm_func_new(
store: &mut wasm_store_t,
ty: &wasm_functype_t,
callback: wasm_func_callback_t,
) -> Box<wasm_func_t> {
create_function(store, ty, move |_caller, params, results| {
callback(params, results)
})
create_function(store, ty, move |params, results| callback(params, results))
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_func_new(
store: &wasm_store_t,
ty: &wasm_functype_t,
callback: wasmtime_func_callback_t,
) -> Box<wasm_func_t> {
create_function(store, ty, move |caller, params, results| {
callback(&wasmtime_caller_t { caller }, params, results)
})
}
#[no_mangle]
pub extern "C" fn wasm_func_new_with_env(
store: &wasm_store_t,
pub unsafe extern "C" fn wasm_func_new_with_env(
store: &mut wasm_store_t,
ty: &wasm_functype_t,
callback: wasm_func_callback_with_env_t,
env: *mut c_void,
data: *mut c_void,
finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
) -> Box<wasm_func_t> {
let finalizer = Finalizer { env, finalizer };
create_function(store, ty, move |_caller, params, results| {
callback(finalizer.env, params, results)
})
}
#[no_mangle]
pub extern "C" fn wasmtime_func_new_with_env(
store: &wasm_store_t,
ty: &wasm_functype_t,
callback: wasmtime_func_callback_with_env_t,
env: *mut c_void,
finalizer: Option<extern "C" fn(*mut c_void)>,
) -> Box<wasm_func_t> {
let finalizer = Finalizer { env, finalizer };
create_function(store, ty, move |caller, params, results| {
callback(
&wasmtime_caller_t { caller },
finalizer.env,
params,
results,
)
let finalizer = crate::ForeignData { data, finalizer };
create_function(store, ty, move |params, results| {
callback(finalizer.data, params, results)
})
}
#[no_mangle]
pub unsafe extern "C" fn wasm_func_call(
wasm_func: &wasm_func_t,
func: &mut wasm_func_t,
args: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
) -> *mut wasm_trap_t {
let mut trap = ptr::null_mut();
let error = _wasmtime_func_call(
wasm_func,
(*args).as_slice(),
(*results).as_uninit_slice(),
&mut trap,
);
match error {
Some(err) => Box::into_raw(err.to_trap()),
None => trap,
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_func_call(
func: &wasm_func_t,
args: *const wasm_val_vec_t,
results: *mut wasm_val_vec_t,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
_wasmtime_func_call(
func,
(*args).as_slice(),
(*results).as_uninit_slice(),
trap_ptr,
)
}
fn _wasmtime_func_call(
func: &wasm_func_t,
args: &[wasm_val_t],
results: &mut [MaybeUninit<wasm_val_t>],
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
let func = func.func();
if results.len() != func.result_arity() {
return Some(Box::new(anyhow!("wrong number of results provided").into()));
let f = func.func();
let results = (*results).as_uninit_slice();
let args = (*args).as_slice();
if results.len() != f.ty(func.ext.store.context()).results().len() {
return Box::into_raw(Box::new(wasm_trap_t::new(
anyhow!("wrong number of results provided").into(),
)));
}
let params = args.iter().map(|i| i.val()).collect::<Vec<_>>();
@@ -217,20 +129,19 @@ fn _wasmtime_func_call(
// want to try to insulate callers against bugs in wasmtime/wasi/etc if we
// can. As a result we catch panics here and transform them to traps to
// allow the caller to have any insulation possible against Rust panics.
let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(&params)));
let result = panic::catch_unwind(AssertUnwindSafe(|| {
f.call(func.ext.store.context_mut(), &params)
}));
match result {
Ok(Ok(out)) => {
for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) {
crate::initialize(slot, wasm_val_t::from_val(val));
}
None
ptr::null_mut()
}
Ok(Err(trap)) => match trap.downcast::<Trap>() {
Ok(trap) => {
*trap_ptr = Box::into_raw(Box::new(wasm_trap_t::new(trap)));
None
}
Err(err) => Some(Box::new(err.into())),
Ok(trap) => Box::into_raw(Box::new(wasm_trap_t::new(trap))),
Err(err) => Box::into_raw(Box::new(wasm_trap_t::new(err.into()))),
},
Err(panic) => {
let trap = if let Some(msg) = panic.downcast_ref::<String>() {
@@ -241,25 +152,24 @@ fn _wasmtime_func_call(
Trap::new("rust panic happened")
};
let trap = Box::new(wasm_trap_t::new(trap));
*trap_ptr = Box::into_raw(trap);
None
Box::into_raw(trap)
}
}
}
#[no_mangle]
pub extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
Box::new(wasm_functype_t::new(f.func().ty()))
pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> {
Box::new(wasm_functype_t::new(f.func().ty(f.ext.store.context())))
}
#[no_mangle]
pub extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize {
f.func().param_arity()
pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize {
f.func().ty(f.ext.store.context()).params().len()
}
#[no_mangle]
pub extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize {
f.func().result_arity()
pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize {
f.func().ty(f.ext.store.context()).results().len()
}
#[no_mangle]
@@ -268,29 +178,150 @@ pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t
}
#[no_mangle]
pub extern "C" fn wasmtime_caller_export_get(
caller: &wasmtime_caller_t,
name: &wasm_name_t,
) -> Option<Box<wasm_extern_t>> {
let name = str::from_utf8(name.as_slice()).ok()?;
let which = caller.caller.get_export(name)?;
Some(Box::new(wasm_extern_t { which }))
pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t {
&(*f).ext
}
#[repr(C)]
pub struct wasmtime_caller_t<'a> {
caller: Caller<'a, crate::StoreData>,
}
#[no_mangle]
pub extern "C" fn wasmtime_func_as_funcref(
func: &wasm_func_t,
funcrefp: &mut MaybeUninit<wasm_val_t>,
pub unsafe extern "C" fn wasmtime_func_new(
store: CStoreContextMut<'_>,
ty: &wasm_functype_t,
callback: extern "C" fn(
*mut c_void,
*mut wasmtime_caller_t,
*const wasmtime_val_t,
usize,
*mut wasmtime_val_t,
usize,
) -> Option<Box<wasm_trap_t>>,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
func: &mut Func,
) {
let funcref = wasm_val_t::from_val(Val::FuncRef(Some(func.func().clone())));
crate::initialize(funcrefp, funcref);
let foreign = crate::ForeignData { data, finalizer };
let ty = ty.ty().ty.clone();
let f = Func::new(store, ty, move |caller, params, results| {
let params = params
.iter()
.cloned()
.map(|p| wasmtime_val_t::from_val(p))
.collect::<Vec<_>>();
let mut out_results = (0..results.len())
.map(|_| wasmtime_val_t {
kind: crate::WASMTIME_I32,
of: wasmtime_val_union { i32: 0 },
})
.collect::<Vec<_>>();
let mut caller = wasmtime_caller_t { caller };
let out = callback(
foreign.data,
&mut caller,
params.as_ptr(),
params.len(),
out_results.as_mut_ptr(),
out_results.len(),
);
if let Some(trap) = out {
return Err(trap.trap);
}
for (i, result) in out_results.iter().enumerate() {
results[i] = unsafe { result.to_val() };
}
Ok(())
});
*func = f;
}
#[no_mangle]
pub extern "C" fn wasmtime_funcref_as_func(val: &wasm_val_t) -> Option<Box<wasm_func_t>> {
if let Val::FuncRef(Some(f)) = val.val() {
Some(Box::new(f.into()))
} else {
None
pub unsafe extern "C" fn wasmtime_func_call(
store: CStoreContextMut<'_>,
func: &Func,
args: *const wasmtime_val_t,
nargs: usize,
results: *mut MaybeUninit<wasmtime_val_t>,
nresults: usize,
trap_ret: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
if nresults != func.ty(&store).results().len() {
return Some(Box::new(wasmtime_error_t::from(anyhow!(
"wrong number of results provided"
))));
}
let params = crate::slice_from_raw_parts(args, nargs)
.iter()
.map(|i| i.to_val())
.collect::<Vec<_>>();
// We're calling arbitrary code here most of the time, and we in general
// want to try to insulate callers against bugs in wasmtime/wasi/etc if we
// can. As a result we catch panics here and transform them to traps to
// allow the caller to have any insulation possible against Rust panics.
let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(store, &params)));
match result {
Ok(Ok(out)) => {
let results = crate::slice_from_raw_parts_mut(results, nresults);
for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) {
crate::initialize(slot, wasmtime_val_t::from_val(val));
}
None
}
Ok(Err(trap)) => match trap.downcast::<Trap>() {
Ok(trap) => {
*trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(trap)));
None
}
Err(err) => Some(Box::new(wasmtime_error_t::from(err))),
},
Err(panic) => {
let trap = if let Some(msg) = panic.downcast_ref::<String>() {
Trap::new(msg)
} else if let Some(msg) = panic.downcast_ref::<&'static str>() {
Trap::new(*msg)
} else {
Trap::new("rust panic happened")
};
*trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(trap)));
None
}
}
}
#[no_mangle]
pub extern "C" fn wasmtime_func_type(
store: CStoreContext<'_>,
func: &Func,
) -> Box<wasm_functype_t> {
Box::new(wasm_functype_t::new(func.ty(store)))
}
#[no_mangle]
pub extern "C" fn wasmtime_caller_context<'a>(
caller: &'a mut wasmtime_caller_t,
) -> CStoreContextMut<'a> {
caller.caller.as_context_mut()
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_caller_export_get(
caller: &mut wasmtime_caller_t,
name: *const u8,
name_len: usize,
item: &mut MaybeUninit<wasmtime_extern_t>,
) -> bool {
let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) {
Ok(name) => name,
Err(_) => return false,
};
let which = match caller.caller.get_export(name) {
Some(item) => item,
None => return false,
};
crate::initialize(item, which.into());
true
}

View File

@@ -1,7 +1,8 @@
use crate::{handle_result, wasmtime_error_t};
use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t};
use crate::{
handle_result, wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t, wasmtime_error_t,
wasmtime_val_t, CStoreContext, CStoreContextMut,
};
use std::mem::MaybeUninit;
use std::ptr;
use wasmtime::{Extern, Global};
#[derive(Clone)]
@@ -20,8 +21,8 @@ impl wasm_global_t {
}
}
fn global(&self) -> &Global {
match &self.ext.which {
fn global(&self) -> Global {
match self.ext.which {
Extern::Global(g) => g,
_ => unsafe { std::hint::unreachable_unchecked() },
}
@@ -29,65 +30,88 @@ impl wasm_global_t {
}
#[no_mangle]
pub extern "C" fn wasm_global_new(
store: &wasm_store_t,
pub unsafe extern "C" fn wasm_global_new(
store: &mut wasm_store_t,
gt: &wasm_globaltype_t,
val: &wasm_val_t,
) -> Option<Box<wasm_global_t>> {
let mut global = ptr::null_mut();
match wasmtime_global_new(store, gt, val, &mut global) {
Some(_err) => None,
None => {
assert!(!global.is_null());
Some(unsafe { Box::from_raw(global) })
}
match Global::new(store.store.context_mut(), gt.ty().ty.clone(), val.val()) {
Ok(global) => Some(Box::new(wasm_global_t {
ext: wasm_extern_t {
store: store.store.clone(),
which: global.into(),
},
})),
Err(_) => None,
}
}
#[no_mangle]
pub extern "C" fn wasmtime_global_new(
store: &wasm_store_t,
gt: &wasm_globaltype_t,
val: &wasm_val_t,
ret: &mut *mut wasm_global_t,
) -> Option<Box<wasmtime_error_t>> {
let global = Global::new(&store.store, gt.ty().ty.clone(), val.val());
handle_result(global, |global| {
*ret = Box::into_raw(Box::new(wasm_global_t {
ext: wasm_extern_t {
which: global.into(),
},
}));
})
pub extern "C" fn wasm_global_as_extern(g: &mut wasm_global_t) -> &mut wasm_extern_t {
&mut g.ext
}
#[no_mangle]
pub extern "C" fn wasm_global_as_extern(g: &wasm_global_t) -> &wasm_extern_t {
pub extern "C" fn wasm_global_as_extern_const(g: &wasm_global_t) -> &wasm_extern_t {
&g.ext
}
#[no_mangle]
pub extern "C" fn wasm_global_type(g: &wasm_global_t) -> Box<wasm_globaltype_t> {
let globaltype = g.global().ty();
pub unsafe extern "C" fn wasm_global_type(g: &wasm_global_t) -> Box<wasm_globaltype_t> {
let globaltype = g.global().ty(&g.ext.store.context());
Box::new(wasm_globaltype_t::new(globaltype))
}
#[no_mangle]
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut MaybeUninit<wasm_val_t>) {
crate::initialize(out, wasm_val_t::from_val(g.global().get()));
pub unsafe extern "C" fn wasm_global_get(g: &mut wasm_global_t, out: &mut MaybeUninit<wasm_val_t>) {
let global = g.global();
crate::initialize(
out,
wasm_val_t::from_val(global.get(g.ext.store.context_mut())),
);
}
#[no_mangle]
pub extern "C" fn wasm_global_set(g: &wasm_global_t, val: &wasm_val_t) {
let result = g.global().set(val.val());
// FIXME(WebAssembly/wasm-c-api#131) should communicate the error here
drop(result);
pub unsafe extern "C" fn wasm_global_set(g: &mut wasm_global_t, val: &wasm_val_t) {
let global = g.global();
drop(global.set(g.ext.store.context_mut(), val.val()));
}
#[no_mangle]
pub extern "C" fn wasmtime_global_set(
g: &wasm_global_t,
val: &wasm_val_t,
pub unsafe extern "C" fn wasmtime_global_new(
store: CStoreContextMut<'_>,
gt: &wasm_globaltype_t,
val: &wasmtime_val_t,
ret: &mut Global,
) -> Option<Box<wasmtime_error_t>> {
handle_result(g.global().set(val.val()), |()| {})
let global = Global::new(store, gt.ty().ty.clone(), val.to_val());
handle_result(global, |global| {
*ret = global;
})
}
#[no_mangle]
pub extern "C" fn wasmtime_global_type(
store: CStoreContext<'_>,
global: &Global,
) -> Box<wasm_globaltype_t> {
Box::new(wasm_globaltype_t::new(global.ty(store)))
}
#[no_mangle]
pub extern "C" fn wasmtime_global_get(
store: CStoreContextMut<'_>,
global: &Global,
val: &mut MaybeUninit<wasmtime_val_t>,
) {
crate::initialize(val, wasmtime_val_t::from_val(global.get(store)))
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_global_set(
store: CStoreContextMut<'_>,
global: &Global,
val: &wasmtime_val_t,
) -> Option<Box<wasmtime_error_t>> {
handle_result(global.set(store, val.to_val()), |()| {})
}

View File

@@ -1,7 +1,9 @@
use crate::{wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_trap_t};
use crate::{wasm_instancetype_t, wasm_store_t, wasmtime_error_t};
use anyhow::Result;
use std::ptr;
use crate::{
wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_store_t, wasm_trap_t, wasmtime_error_t,
wasmtime_extern_t, wasmtime_instancetype_t, wasmtime_module_t, CStoreContext, CStoreContextMut,
StoreRef,
};
use std::mem::MaybeUninit;
use wasmtime::{Extern, Instance, Trap};
#[derive(Clone)]
@@ -13,9 +15,10 @@ pub struct wasm_instance_t {
wasmtime_c_api_macros::declare_ref!(wasm_instance_t);
impl wasm_instance_t {
pub(crate) fn new(instance: Instance) -> wasm_instance_t {
pub(crate) fn new(store: StoreRef, instance: Instance) -> wasm_instance_t {
wasm_instance_t {
ext: wasm_extern_t {
store: store,
which: instance.into(),
},
}
@@ -28,8 +31,8 @@ impl wasm_instance_t {
}
}
pub(crate) fn instance(&self) -> &Instance {
match &self.ext.which {
pub(crate) fn instance(&self) -> Instance {
match self.ext.which {
Extern::Instance(i) => i,
_ => unreachable!(),
}
@@ -38,100 +41,30 @@ impl wasm_instance_t {
#[no_mangle]
pub unsafe extern "C" fn wasm_instance_new(
store: &wasm_store_t,
store: &mut wasm_store_t,
wasm_module: &wasm_module_t,
imports: *const wasm_extern_vec_t,
result: Option<&mut *mut wasm_trap_t>,
) -> Option<Box<wasm_instance_t>> {
let mut instance = ptr::null_mut();
let mut trap = ptr::null_mut();
let err = _wasmtime_instance_new(
store,
wasm_module,
(*imports).as_slice(),
&mut instance,
&mut trap,
);
match err {
Some(err) => {
assert!(trap.is_null());
assert!(instance.is_null());
if let Some(result) = result {
*result = Box::into_raw(err.to_trap());
}
None
}
None => {
if instance.is_null() {
assert!(!trap.is_null());
if let Some(result) = result {
*result = trap;
} else {
drop(Box::from_raw(trap))
}
None
} else {
assert!(trap.is_null());
Some(Box::from_raw(instance))
}
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_instance_new(
store: &wasm_store_t,
module: &wasm_module_t,
imports: *const wasm_extern_vec_t,
instance_ptr: &mut *mut wasm_instance_t,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
_wasmtime_instance_new(store, module, (*imports).as_slice(), instance_ptr, trap_ptr)
}
fn _wasmtime_instance_new(
store: &wasm_store_t,
module: &wasm_module_t,
imports: &[Option<Box<wasm_extern_t>>],
instance_ptr: &mut *mut wasm_instance_t,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
let store = &store.store;
let imports = imports
let imports = (*imports)
.as_slice()
.iter()
.filter_map(|import| match import {
Some(i) => Some(i.which.clone()),
None => None,
})
.collect::<Vec<_>>();
handle_instantiate(
Instance::new(store, module.module(), &imports),
instance_ptr,
trap_ptr,
)
}
pub fn handle_instantiate(
instance: Result<Instance>,
instance_ptr: &mut *mut wasm_instance_t,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
fn write<T>(ptr: &mut *mut T, val: T) {
*ptr = Box::into_raw(Box::new(val))
}
match instance {
Ok(instance) => {
write(instance_ptr, wasm_instance_t::new(instance));
match Instance::new(store.store.context_mut(), wasm_module.module(), &imports) {
Ok(instance) => Some(Box::new(wasm_instance_t::new(
store.store.clone(),
instance,
))),
Err(e) => {
if let Some(ptr) = result {
*ptr = Box::into_raw(Box::new(wasm_trap_t::new(e.into())));
}
None
}
Err(e) => match e.downcast::<Trap>() {
Ok(trap) => {
write(trap_ptr, wasm_trap_t::new(trap));
None
}
Err(e) => Some(Box::new(e.into())),
},
}
}
@@ -141,14 +74,19 @@ pub extern "C" fn wasm_instance_as_extern(m: &wasm_instance_t) -> &wasm_extern_t
}
#[no_mangle]
pub extern "C" fn wasm_instance_exports(instance: &wasm_instance_t, out: &mut wasm_extern_vec_t) {
pub unsafe extern "C" fn wasm_instance_exports(
instance: &mut wasm_instance_t,
out: &mut wasm_extern_vec_t,
) {
let store = instance.ext.store.clone();
out.set_buffer(
instance
.instance()
.exports()
.exports(instance.ext.store.context_mut())
.map(|e| {
Some(Box::new(wasm_extern_t {
which: e.into_extern(),
store: store.clone(),
}))
})
.collect(),
@@ -156,6 +94,91 @@ pub extern "C" fn wasm_instance_exports(instance: &wasm_instance_t, out: &mut wa
}
#[no_mangle]
pub extern "C" fn wasm_instance_type(f: &wasm_instance_t) -> Box<wasm_instancetype_t> {
Box::new(wasm_instancetype_t::new(f.instance().ty()))
pub unsafe extern "C" fn wasmtime_instance_new(
store: CStoreContextMut<'_>,
module: &wasmtime_module_t,
imports: *const wasmtime_extern_t,
nimports: usize,
instance: &mut Instance,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
let imports = crate::slice_from_raw_parts(imports, nimports)
.iter()
.map(|i| i.to_extern())
.collect::<Vec<_>>();
handle_instantiate(
Instance::new(store, &module.module, &imports),
instance,
trap_ptr,
)
}
pub(crate) fn handle_instantiate(
instance: anyhow::Result<Instance>,
instance_ptr: &mut Instance,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
match instance {
Ok(i) => {
*instance_ptr = i;
None
}
Err(e) => match e.downcast::<Trap>() {
Ok(trap) => {
*trap_ptr = Box::into_raw(Box::new(wasm_trap_t::new(trap)));
None
}
Err(e) => Some(Box::new(e.into())),
},
}
}
#[no_mangle]
pub extern "C" fn wasmtime_instance_type(
store: CStoreContext<'_>,
instance: &Instance,
) -> Box<wasmtime_instancetype_t> {
Box::new(wasmtime_instancetype_t::new(instance.ty(store)))
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_instance_export_get(
store: CStoreContextMut<'_>,
instance: &Instance,
name: *const u8,
name_len: usize,
item: &mut MaybeUninit<wasmtime_extern_t>,
) -> bool {
let name = crate::slice_from_raw_parts(name, name_len);
let name = match std::str::from_utf8(name) {
Ok(name) => name,
Err(_) => return false,
};
match instance.get_export(store, name) {
Some(e) => {
crate::initialize(item, e.into());
true
}
None => false,
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_instance_export_nth(
store: CStoreContextMut<'_>,
instance: &Instance,
index: usize,
name_ptr: &mut *const u8,
name_len: &mut usize,
item: &mut MaybeUninit<wasmtime_extern_t>,
) -> bool {
match instance.exports(store).nth(index) {
Some(e) => {
*name_ptr = e.name().as_ptr();
*name_len = e.name().len();
crate::initialize(item, e.into_extern().into());
true
}
None => false,
}
}

View File

@@ -1,11 +1,15 @@
//! This file defines the extern "C" API, which is compatible with the
//! [Wasm C API](https://github.com/WebAssembly/wasm-c-api).
//! This crate is the implementation of Wasmtime's C API.
//!
//! This crate is not intended to be used from Rust itself, for that see the
//! `wasmtime` crate. Otherwise this is typically compiled as a
//! cdylib/staticlib. Documentation for this crate largely lives in the header
//! files of the `include` directory for this crate.
//!
//! At a high level this crate implements the `wasm.h` API with some gymnastics,
//! but otherwise an accompanying `wasmtime.h` API is provided which is more
//! specific to Wasmtime and has fewer gymnastics to implement.
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
#![allow(unknown_lints)]
#![allow(improper_ctypes_definitions)]
// TODO complete the C API
mod config;
mod engine;
@@ -53,18 +57,6 @@ mod wat2wasm;
#[cfg(feature = "wat")]
pub use crate::wat2wasm::*;
#[repr(C)]
#[derive(Clone)]
pub struct wasm_foreign_t {
_unused: [u8; 0],
}
#[repr(C)]
#[derive(Clone)]
pub struct wasm_shared_module_t {
_unused: [u8; 0],
}
/// Initialize a `MaybeUninit<T>`
///
/// TODO: Replace calls to this function with
@@ -75,3 +67,43 @@ pub(crate) fn initialize<T>(dst: &mut std::mem::MaybeUninit<T>, val: T) {
std::ptr::write(dst.as_mut_ptr(), val);
}
}
/// Helper for running a C-defined finalizer over some data when the Rust
/// structure is dropped.
pub struct ForeignData {
data: *mut std::ffi::c_void,
finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
}
unsafe impl Send for ForeignData {}
unsafe impl Sync for ForeignData {}
impl Drop for ForeignData {
fn drop(&mut self) {
if let Some(f) = self.finalizer {
f(self.data);
}
}
}
/// Helper for creating Rust slices from C inputs.
///
/// This specifically disregards the `ptr` argument if the length is zero. The
/// `ptr` in that case maybe `NULL` or invalid, and it's not valid to have a
/// zero-length Rust slice with a `NULL` pointer.
unsafe fn slice_from_raw_parts<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
if len == 0 {
&[]
} else {
std::slice::from_raw_parts(ptr, len)
}
}
/// Same as above, but for `*_mut`
unsafe fn slice_from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T] {
if len == 0 {
&mut []
} else {
std::slice::from_raw_parts_mut(ptr, len)
}
}

View File

@@ -1,18 +1,20 @@
use crate::{bad_utf8, handle_result, wasmtime_error_t};
use crate::{wasm_extern_t, wasm_store_t};
use crate::{wasm_func_t, wasm_instance_t, wasm_module_t, wasm_name_t, wasm_trap_t};
use crate::{
bad_utf8, handle_result, wasm_engine_t, wasm_trap_t, wasmtime_error_t, wasmtime_extern_t,
wasmtime_module_t, CStoreContextMut,
};
use std::mem::MaybeUninit;
use std::str;
use wasmtime::Linker;
use wasmtime::{Func, Instance, Linker};
#[repr(C)]
pub struct wasmtime_linker_t {
linker: Linker,
linker: Linker<crate::StoreData>,
}
#[no_mangle]
pub extern "C" fn wasmtime_linker_new(store: &wasm_store_t) -> Box<wasmtime_linker_t> {
pub extern "C" fn wasmtime_linker_new(engine: &wasm_engine_t) -> Box<wasmtime_linker_t> {
Box::new(wasmtime_linker_t {
linker: Linker::new(&store.store),
linker: Linker::new(&engine.engine),
})
}
@@ -27,23 +29,28 @@ pub extern "C" fn wasmtime_linker_allow_shadowing(
#[no_mangle]
pub extern "C" fn wasmtime_linker_delete(_linker: Box<wasmtime_linker_t>) {}
macro_rules! to_str {
($ptr:expr, $len:expr) => {
match str::from_utf8(crate::slice_from_raw_parts($ptr, $len)) {
Ok(s) => s,
Err(_) => return bad_utf8(),
}
};
}
#[no_mangle]
pub extern "C" fn wasmtime_linker_define(
pub unsafe extern "C" fn wasmtime_linker_define(
linker: &mut wasmtime_linker_t,
module: &wasm_name_t,
name: &wasm_name_t,
item: &wasm_extern_t,
module: *const u8,
module_len: usize,
name: *const u8,
name_len: usize,
item: &wasmtime_extern_t,
) -> Option<Box<wasmtime_error_t>> {
let linker = &mut linker.linker;
let module = match str::from_utf8(module.as_slice()) {
Ok(s) => s,
Err(_) => return bad_utf8(),
};
let name = match str::from_utf8(name.as_slice()) {
Ok(s) => s,
Err(_) => return bad_utf8(),
};
let item = item.which.clone();
let module = to_str!(module, module_len);
let name = to_str!(name, name_len);
let item = item.to_extern();
handle_result(linker.define(module, name, item), |_linker| ())
}
@@ -51,87 +58,92 @@ pub extern "C" fn wasmtime_linker_define(
#[no_mangle]
pub extern "C" fn wasmtime_linker_define_wasi(
linker: &mut wasmtime_linker_t,
instance: &crate::wasi_instance_t,
) -> Option<Box<wasmtime_error_t>> {
let linker = &mut linker.linker;
handle_result(instance.add_to_linker(linker), |_linker| ())
handle_result(
wasmtime_wasi::add_to_linker(&mut linker.linker, |cx| cx.wasi.as_mut().unwrap()),
|_linker| (),
)
}
#[no_mangle]
pub extern "C" fn wasmtime_linker_define_instance(
pub unsafe extern "C" fn wasmtime_linker_define_instance(
linker: &mut wasmtime_linker_t,
name: &wasm_name_t,
instance: &wasm_instance_t,
store: CStoreContextMut<'_>,
name: *const u8,
name_len: usize,
instance: &Instance,
) -> Option<Box<wasmtime_error_t>> {
let linker = &mut linker.linker;
let name = match str::from_utf8(name.as_slice()) {
Ok(s) => s,
Err(_) => return bad_utf8(),
};
handle_result(linker.instance(name, instance.instance()), |_linker| ())
let name = to_str!(name, name_len);
handle_result(linker.instance(store, name, *instance), |_linker| ())
}
#[no_mangle]
pub extern "C" fn wasmtime_linker_instantiate(
linker: &wasmtime_linker_t,
module: &wasm_module_t,
instance_ptr: &mut *mut wasm_instance_t,
store: CStoreContextMut<'_>,
module: &wasmtime_module_t,
instance_ptr: &mut Instance,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
let result = linker.linker.instantiate(module.module());
let result = linker.linker.instantiate(store, &module.module);
super::instance::handle_instantiate(result, instance_ptr, trap_ptr)
}
#[no_mangle]
pub extern "C" fn wasmtime_linker_module(
pub unsafe extern "C" fn wasmtime_linker_module(
linker: &mut wasmtime_linker_t,
name: &wasm_name_t,
module: &wasm_module_t,
store: CStoreContextMut<'_>,
name: *const u8,
name_len: usize,
module: &wasmtime_module_t,
) -> Option<Box<wasmtime_error_t>> {
let linker = &mut linker.linker;
let name = match str::from_utf8(name.as_slice()) {
Ok(s) => s,
Err(_) => return bad_utf8(),
};
handle_result(linker.module(name, module.module()), |_linker| ())
let name = to_str!(name, name_len);
handle_result(linker.module(store, name, &module.module), |_linker| ())
}
#[no_mangle]
pub extern "C" fn wasmtime_linker_get_default(
pub unsafe extern "C" fn wasmtime_linker_get_default(
linker: &wasmtime_linker_t,
name: &wasm_name_t,
func: &mut *mut wasm_func_t,
store: CStoreContextMut<'_>,
name: *const u8,
name_len: usize,
func: &mut Func,
) -> Option<Box<wasmtime_error_t>> {
let linker = &linker.linker;
let name = match str::from_utf8(name.as_slice()) {
Ok(s) => s,
Err(_) => return bad_utf8(),
};
handle_result(linker.get_default(name), |f| {
*func = Box::into_raw(Box::new(f.into()))
})
let name = to_str!(name, name_len);
handle_result(linker.get_default(store, name), |f| *func = f)
}
#[no_mangle]
pub extern "C" fn wasmtime_linker_get_one_by_name(
pub unsafe extern "C" fn wasmtime_linker_get(
linker: &wasmtime_linker_t,
module: &wasm_name_t,
name: Option<&wasm_name_t>,
item_ptr: &mut *mut wasm_extern_t,
) -> Option<Box<wasmtime_error_t>> {
store: CStoreContextMut<'_>,
module: *const u8,
module_len: usize,
name: *const u8,
name_len: usize,
item_ptr: &mut MaybeUninit<wasmtime_extern_t>,
) -> bool {
let linker = &linker.linker;
let module = match str::from_utf8(module.as_slice()) {
let module = match str::from_utf8(crate::slice_from_raw_parts(module, module_len)) {
Ok(s) => s,
Err(_) => return bad_utf8(),
Err(_) => return false,
};
let name = match name {
Some(name) => match str::from_utf8(name.as_slice()) {
let name = if name.is_null() {
None
} else {
match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) {
Ok(s) => Some(s),
Err(_) => return bad_utf8(),
},
None => None,
Err(_) => return false,
}
};
handle_result(linker.get_one_by_name(module, name), |which| {
*item_ptr = Box::into_raw(Box::new(wasm_extern_t { which }))
})
match linker.get(store, module, name) {
Some(which) => {
crate::initialize(item_ptr, which.into());
true
}
None => false,
}
}

View File

@@ -1,4 +1,7 @@
use crate::{wasm_extern_t, wasm_memorytype_t, wasm_store_t};
use crate::{
handle_result, wasm_extern_t, wasm_memorytype_t, wasm_store_t, wasmtime_error_t, CStoreContext,
CStoreContextMut,
};
use wasmtime::{Extern, Memory};
#[derive(Clone)]
@@ -19,8 +22,8 @@ impl wasm_memory_t {
}
}
fn memory(&self) -> &Memory {
match &self.ext.which {
fn memory(&self) -> Memory {
match self.ext.which {
Extern::Memory(m) => m,
_ => unsafe { std::hint::unreachable_unchecked() },
}
@@ -28,45 +31,98 @@ impl wasm_memory_t {
}
#[no_mangle]
pub extern "C" fn wasm_memory_new(
store: &wasm_store_t,
pub unsafe extern "C" fn wasm_memory_new(
store: &mut wasm_store_t,
mt: &wasm_memorytype_t,
) -> Option<Box<wasm_memory_t>> {
let memory = Memory::new(&store.store, mt.ty().ty.clone()).ok()?;
let memory = Memory::new(store.store.context_mut(), mt.ty().ty.clone()).ok()?;
Some(Box::new(wasm_memory_t {
ext: wasm_extern_t {
store: store.store.clone(),
which: memory.into(),
},
}))
}
#[no_mangle]
pub extern "C" fn wasm_memory_as_extern(m: &wasm_memory_t) -> &wasm_extern_t {
pub extern "C" fn wasm_memory_as_extern(m: &mut wasm_memory_t) -> &mut wasm_extern_t {
&mut m.ext
}
#[no_mangle]
pub extern "C" fn wasm_memory_as_extern_const(m: &wasm_memory_t) -> &wasm_extern_t {
&m.ext
}
#[no_mangle]
pub extern "C" fn wasm_memory_type(m: &wasm_memory_t) -> Box<wasm_memorytype_t> {
let ty = m.memory().ty();
pub unsafe extern "C" fn wasm_memory_type(m: &wasm_memory_t) -> Box<wasm_memorytype_t> {
let ty = m.memory().ty(m.ext.store.context());
Box::new(wasm_memorytype_t::new(ty))
}
#[no_mangle]
pub extern "C" fn wasm_memory_data(m: &wasm_memory_t) -> *mut u8 {
m.memory().data_ptr()
pub unsafe extern "C" fn wasm_memory_data(m: &wasm_memory_t) -> *mut u8 {
m.memory().data_ptr(m.ext.store.context())
}
#[no_mangle]
pub extern "C" fn wasm_memory_data_size(m: &wasm_memory_t) -> usize {
m.memory().data_size()
pub unsafe extern "C" fn wasm_memory_data_size(m: &wasm_memory_t) -> usize {
m.memory().data_size(m.ext.store.context())
}
#[no_mangle]
pub extern "C" fn wasm_memory_size(m: &wasm_memory_t) -> wasm_memory_pages_t {
m.memory().size()
pub unsafe extern "C" fn wasm_memory_size(m: &wasm_memory_t) -> wasm_memory_pages_t {
m.memory().size(m.ext.store.context())
}
#[no_mangle]
pub extern "C" fn wasm_memory_grow(m: &wasm_memory_t, delta: wasm_memory_pages_t) -> bool {
m.memory().grow(delta).is_ok()
pub unsafe extern "C" fn wasm_memory_grow(
m: &mut wasm_memory_t,
delta: wasm_memory_pages_t,
) -> bool {
let memory = m.memory();
let mut store = m.ext.store.context_mut();
memory.grow(&mut store, delta).is_ok()
}
#[no_mangle]
pub extern "C" fn wasmtime_memory_new(
store: CStoreContextMut<'_>,
ty: &wasm_memorytype_t,
ret: &mut Memory,
) -> Option<Box<wasmtime_error_t>> {
handle_result(Memory::new(store, ty.ty().ty.clone()), |mem| *ret = mem)
}
#[no_mangle]
pub extern "C" fn wasmtime_memory_type(
store: CStoreContext<'_>,
mem: &Memory,
) -> Box<wasm_memorytype_t> {
Box::new(wasm_memorytype_t::new(mem.ty(store)))
}
#[no_mangle]
pub extern "C" fn wasmtime_memory_data(store: CStoreContext<'_>, mem: &Memory) -> *const u8 {
mem.data(store).as_ptr()
}
#[no_mangle]
pub extern "C" fn wasmtime_memory_data_size(store: CStoreContext<'_>, mem: &Memory) -> usize {
mem.data(store).len()
}
#[no_mangle]
pub extern "C" fn wasmtime_memory_size(store: CStoreContext<'_>, mem: &Memory) -> u32 {
mem.size(store)
}
#[no_mangle]
pub extern "C" fn wasmtime_memory_grow(
store: CStoreContextMut<'_>,
mem: &Memory,
delta: u32,
prev_size: &mut u32,
) -> Option<Box<wasmtime_error_t>> {
handle_result(mem.grow(store, delta), |prev| *prev_size = prev)
}

View File

@@ -1,9 +1,8 @@
use crate::{
handle_result, wasm_byte_vec_t, wasm_engine_t, wasm_exporttype_t, wasm_exporttype_vec_t,
wasm_extern_t, wasm_importtype_t, wasm_importtype_vec_t, wasm_moduletype_t, wasm_store_t,
wasmtime_error_t,
wasm_extern_t, wasm_importtype_t, wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t,
wasmtime_moduletype_t, StoreRef,
};
use std::ptr;
use wasmtime::{Engine, Extern, Module};
#[derive(Clone)]
@@ -15,9 +14,10 @@ pub struct wasm_module_t {
wasmtime_c_api_macros::declare_ref!(wasm_module_t);
impl wasm_module_t {
pub(crate) fn new(module: Module) -> wasm_module_t {
pub(crate) fn new(store: StoreRef, module: Module) -> wasm_module_t {
wasm_module_t {
ext: wasm_extern_t {
store: store,
which: module.into(),
},
}
@@ -47,48 +47,22 @@ pub struct wasm_shared_module_t {
wasmtime_c_api_macros::declare_own!(wasm_shared_module_t);
#[no_mangle]
pub extern "C" fn wasm_module_new(
store: &wasm_store_t,
pub unsafe extern "C" fn wasm_module_new(
store: &mut wasm_store_t,
binary: &wasm_byte_vec_t,
) -> Option<Box<wasm_module_t>> {
let mut ret = ptr::null_mut();
let engine = wasm_engine_t {
engine: store.store.engine().clone(),
};
match wasmtime_module_new(&engine, binary, &mut ret) {
Some(_err) => None,
None => {
assert!(!ret.is_null());
Some(unsafe { Box::from_raw(ret) })
}
match Module::from_binary(store.store.context().engine(), binary.as_slice()) {
Ok(module) => Some(Box::new(wasm_module_t::new(store.store.clone(), module))),
Err(_) => None,
}
}
#[no_mangle]
pub extern "C" fn wasmtime_module_new(
engine: &wasm_engine_t,
pub unsafe extern "C" fn wasm_module_validate(
store: &mut wasm_store_t,
binary: &wasm_byte_vec_t,
ret: &mut *mut wasm_module_t,
) -> Option<Box<wasmtime_error_t>> {
let binary = binary.as_slice();
handle_result(Module::from_binary(&engine.engine, binary), |module| {
let module = Box::new(wasm_module_t::new(module));
*ret = Box::into_raw(module);
})
}
#[no_mangle]
pub extern "C" fn wasm_module_validate(store: &wasm_store_t, binary: &wasm_byte_vec_t) -> bool {
wasmtime_module_validate(store, binary).is_none()
}
#[no_mangle]
pub extern "C" fn wasmtime_module_validate(
store: &wasm_store_t,
binary: &wasm_byte_vec_t,
) -> Option<Box<wasmtime_error_t>> {
let binary = binary.as_slice();
handle_result(Module::validate(store.store.engine(), binary), |()| {})
) -> bool {
Module::validate(store.store.context().engine(), binary.as_slice()).is_ok()
}
#[no_mangle]
@@ -135,66 +109,96 @@ pub extern "C" fn wasm_module_share(module: &wasm_module_t) -> Box<wasm_shared_m
}
#[no_mangle]
pub extern "C" fn wasm_module_obtain(
store: &wasm_store_t,
pub unsafe extern "C" fn wasm_module_obtain(
store: &mut wasm_store_t,
shared_module: &wasm_shared_module_t,
) -> Option<Box<wasm_module_t>> {
let module = shared_module.module.clone();
if !Engine::same(store.store.engine(), module.engine()) {
return None;
if Engine::same(store.store.context().engine(), module.engine()) {
Some(Box::new(wasm_module_t::new(store.store.clone(), module)))
} else {
None
}
Some(Box::new(wasm_module_t::new(module)))
}
#[no_mangle]
pub extern "C" fn wasm_module_serialize(module: &wasm_module_t, ret: &mut wasm_byte_vec_t) {
drop(wasmtime_module_serialize(module, ret));
}
#[no_mangle]
pub extern "C" fn wasm_module_deserialize(
store: &wasm_store_t,
binary: &wasm_byte_vec_t,
) -> Option<Box<wasm_module_t>> {
let mut ret = ptr::null_mut();
let engine = wasm_engine_t {
engine: store.store.engine().clone(),
};
match wasmtime_module_deserialize(&engine, binary, &mut ret) {
Some(_err) => None,
None => {
assert!(!ret.is_null());
Some(unsafe { Box::from_raw(ret) })
}
if let Ok(buf) = module.module().serialize() {
ret.set_buffer(buf);
}
}
#[no_mangle]
pub extern "C" fn wasmtime_module_serialize(
module: &wasm_module_t,
ret: &mut wasm_byte_vec_t,
) -> Option<Box<wasmtime_error_t>> {
handle_result(module.module().serialize(), |buf| {
ret.set_buffer(buf);
})
pub unsafe extern "C" fn wasm_module_deserialize(
store: &mut wasm_store_t,
binary: &wasm_byte_vec_t,
) -> Option<Box<wasm_module_t>> {
match Module::deserialize(store.store.context().engine(), binary.as_slice()) {
Ok(module) => Some(Box::new(wasm_module_t::new(store.store.clone(), module))),
Err(_) => None,
}
}
#[derive(Clone)]
pub struct wasmtime_module_t {
pub(crate) module: Module,
}
#[no_mangle]
pub extern "C" fn wasmtime_module_deserialize(
pub unsafe extern "C" fn wasmtime_module_new(
engine: &wasm_engine_t,
binary: &wasm_byte_vec_t,
ret: &mut *mut wasm_module_t,
wasm: *const u8,
len: usize,
out: &mut *mut wasmtime_module_t,
) -> Option<Box<wasmtime_error_t>> {
handle_result(
unsafe { Module::deserialize(&engine.engine, binary.as_slice()) },
Module::from_binary(&engine.engine, crate::slice_from_raw_parts(wasm, len)),
|module| {
let module = Box::new(wasm_module_t::new(module));
*ret = Box::into_raw(module);
*out = Box::into_raw(Box::new(wasmtime_module_t { module }));
},
)
}
#[no_mangle]
pub extern "C" fn wasm_module_type(f: &wasm_module_t) -> Box<wasm_moduletype_t> {
Box::new(wasm_moduletype_t::new(f.module().ty()))
pub extern "C" fn wasmtime_module_delete(_module: Box<wasmtime_module_t>) {}
#[no_mangle]
pub extern "C" fn wasmtime_module_clone(module: &wasmtime_module_t) -> Box<wasmtime_module_t> {
Box::new(module.clone())
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_module_validate(
engine: &wasm_engine_t,
wasm: *const u8,
len: usize,
) -> Option<Box<wasmtime_error_t>> {
let binary = crate::slice_from_raw_parts(wasm, len);
handle_result(Module::validate(&engine.engine, binary), |()| {})
}
#[no_mangle]
pub extern "C" fn wasmtime_module_type(m: &wasmtime_module_t) -> Box<wasmtime_moduletype_t> {
Box::new(wasmtime_moduletype_t::new(m.module.ty()))
}
#[no_mangle]
pub extern "C" fn wasmtime_module_serialize(
module: &wasmtime_module_t,
ret: &mut wasm_byte_vec_t,
) -> Option<Box<wasmtime_error_t>> {
handle_result(module.module.serialize(), |buf| ret.set_buffer(buf))
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_module_deserialize(
engine: &wasm_engine_t,
bytes: *const u8,
len: usize,
out: &mut *mut wasmtime_module_t,
) -> Option<Box<wasmtime_error_t>> {
let bytes = crate::slice_from_raw_parts(bytes, len);
handle_result(Module::deserialize(&engine.engine, bytes), |module| {
*out = Box::into_raw(Box::new(wasmtime_module_t { module }));
})
}

View File

@@ -1,8 +1,4 @@
use crate::wasm_val_t;
use std::any::Any;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::ptr;
use wasmtime::{ExternRef, Func, Val};
/// `*mut wasm_ref_t` is a reference type (`externref` or `funcref`), as seen by
@@ -54,6 +50,11 @@ pub extern "C" fn wasm_ref_copy(r: Option<&wasm_ref_t>) -> Option<Box<wasm_ref_t
r.map(|r| Box::new(r.clone()))
}
fn abort(name: &str) -> ! {
eprintln!("`{}` is not implemented", name);
std::process::abort();
}
#[no_mangle]
pub extern "C" fn wasm_ref_same(a: Option<&wasm_ref_t>, b: Option<&wasm_ref_t>) -> bool {
match (a.map(|a| &a.r), b.map(|b| &b.r)) {
@@ -72,8 +73,7 @@ pub extern "C" fn wasm_ref_get_host_info(_ref: Option<&wasm_ref_t>) -> *mut c_vo
#[no_mangle]
pub extern "C" fn wasm_ref_set_host_info(_ref: Option<&wasm_ref_t>, _info: *mut c_void) {
eprintln!("`wasm_ref_set_host_info` is not implemented");
std::process::abort();
abort("wasm_ref_set_host_info")
}
#[no_mangle]
@@ -82,63 +82,162 @@ pub extern "C" fn wasm_ref_set_host_info_with_finalizer(
_info: *mut c_void,
_finalizer: Option<extern "C" fn(*mut c_void)>,
) {
eprintln!("`wasm_ref_set_host_info_with_finalizer` is not implemented");
std::process::abort();
}
type wasmtime_externref_finalizer_t = extern "C" fn(*mut c_void);
struct CExternRef {
data: *mut c_void,
finalizer: Option<wasmtime_externref_finalizer_t>,
}
impl Drop for CExternRef {
fn drop(&mut self) {
if let Some(f) = self.finalizer {
f(self.data);
}
}
abort("wasm_ref_set_host_info_with_finalizer")
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_new(data: *mut c_void, valp: &mut MaybeUninit<wasm_val_t>) {
wasmtime_externref_new_with_finalizer(data, None, valp)
pub extern "C" fn wasm_ref_as_extern(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_extern_t> {
abort("wasm_ref_as_extern")
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_new_with_finalizer(
data: *mut c_void,
finalizer: Option<wasmtime_externref_finalizer_t>,
valp: &mut MaybeUninit<wasm_val_t>,
pub extern "C" fn wasm_ref_as_extern_const(
_ref: Option<&wasm_ref_t>,
) -> Option<&crate::wasm_extern_t> {
abort("wasm_ref_as_extern_const")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_foreign(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_foreign_t> {
abort("wasm_ref_as_foreign")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_foreign_const(
_ref: Option<&wasm_ref_t>,
) -> Option<&crate::wasm_foreign_t> {
abort("wasm_ref_as_foreign_const")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_func(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_func_t> {
abort("wasm_ref_as_func")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_func_const(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_func_t> {
abort("wasm_ref_as_func_const")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_global(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_global_t> {
abort("wasm_ref_as_global")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_global_const(
_ref: Option<&wasm_ref_t>,
) -> Option<&crate::wasm_global_t> {
abort("wasm_ref_as_global_const")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_instance(
_ref: Option<&wasm_ref_t>,
) -> Option<&crate::wasm_instance_t> {
abort("wasm_ref_as_instance")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_instance_const(
_ref: Option<&wasm_ref_t>,
) -> Option<&crate::wasm_instance_t> {
abort("wasm_ref_as_instance_const")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_memory(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_memory_t> {
abort("wasm_ref_as_memory")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_memory_const(
_ref: Option<&wasm_ref_t>,
) -> Option<&crate::wasm_memory_t> {
abort("wasm_ref_as_memory_const")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_module(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_module_t> {
abort("wasm_ref_as_module")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_module_const(
_ref: Option<&wasm_ref_t>,
) -> Option<&crate::wasm_module_t> {
abort("wasm_ref_as_module_const")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_table(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_table_t> {
abort("wasm_ref_as_table")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_table_const(
_ref: Option<&wasm_ref_t>,
) -> Option<&crate::wasm_table_t> {
abort("wasm_ref_as_table_const")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_trap(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_trap_t> {
abort("wasm_ref_as_trap")
}
#[no_mangle]
pub extern "C" fn wasm_ref_as_trap_const(_ref: Option<&wasm_ref_t>) -> Option<&crate::wasm_trap_t> {
abort("wasm_ref_as_trap_const")
}
#[derive(Clone)]
#[repr(C)]
pub struct wasm_foreign_t {}
#[no_mangle]
pub extern "C" fn wasm_foreign_new(_store: &crate::wasm_store_t) -> Box<wasm_foreign_t> {
abort("wasm_foreign_new")
}
#[no_mangle]
pub extern "C" fn wasm_foreign_delete(_foreign: Box<wasm_foreign_t>) {}
#[no_mangle]
pub extern "C" fn wasm_foreign_copy(r: &wasm_foreign_t) -> Box<wasm_foreign_t> {
Box::new(r.clone())
}
#[no_mangle]
pub extern "C" fn wasm_foreign_same(_a: &wasm_foreign_t, _b: &wasm_foreign_t) -> bool {
abort("wasm_foreign_same")
}
#[no_mangle]
pub extern "C" fn wasm_foreign_get_host_info(_foreign: &wasm_foreign_t) -> *mut c_void {
std::ptr::null_mut()
}
#[no_mangle]
pub extern "C" fn wasm_foreign_set_host_info(_foreign: &wasm_foreign_t, _info: *mut c_void) {
abort("wasm_foreign_set_host_info")
}
#[no_mangle]
pub extern "C" fn wasm_foreign_set_host_info_with_finalizer(
_foreign: &wasm_foreign_t,
_info: *mut c_void,
_finalizer: Option<extern "C" fn(*mut c_void)>,
) {
crate::initialize(
valp,
wasm_val_t::from_val(Val::ExternRef(Some(ExternRef::new(CExternRef {
data,
finalizer,
})))),
);
abort("wasm_foreign_set_host_info_with_finalizer")
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_data(
val: &wasm_val_t,
datap: &mut MaybeUninit<*mut c_void>,
) -> bool {
match val.val() {
Val::ExternRef(None) => {
crate::initialize(datap, ptr::null_mut());
true
}
Val::ExternRef(Some(x)) => {
let data = match x.data().downcast_ref::<CExternRef>() {
Some(r) => r.data,
None => x.data() as *const dyn Any as *mut c_void,
};
crate::initialize(datap, data);
true
}
_ => false,
}
pub extern "C" fn wasm_foreign_as_ref(_: &wasm_foreign_t) -> &wasm_ref_t {
abort("wasm_foreign_as_ref")
}
#[no_mangle]
pub extern "C" fn wasm_foreign_as_ref_const(_: &wasm_foreign_t) -> Option<&wasm_ref_t> {
abort("wasm_foreign_as_ref_const")
}

View File

@@ -1,10 +1,38 @@
use crate::{wasm_engine_t, wasmtime_error_t};
use wasmtime::{InterruptHandle, Store};
use crate::{wasm_engine_t, wasmtime_error_t, ForeignData};
use std::cell::UnsafeCell;
use std::ffi::c_void;
use std::sync::Arc;
use wasmtime::{AsContext, AsContextMut, InterruptHandle, Store, StoreContext, StoreContextMut};
/// This representation of a `Store` is used to implement the `wasm.h` API.
///
/// This is stored alongside `Func` and such for `wasm.h` so each object is
/// independently owned. The usage of `Arc` here is mostly to just get it to be
/// safe to drop across multiple threads, but otherwise acquiring the `context`
/// values from this struct is considered unsafe due to it being unknown how the
/// aliasing is working on the C side of things.
///
/// The aliasing requirements are documented in the C API `wasm.h` itself (at
/// least Wasmtime's implementation).
#[derive(Clone)]
pub struct StoreRef {
store: Arc<UnsafeCell<Store<()>>>,
}
impl StoreRef {
pub unsafe fn context(&self) -> StoreContext<'_, ()> {
(*self.store.get()).as_context()
}
pub unsafe fn context_mut(&mut self) -> StoreContextMut<'_, ()> {
(*self.store.get()).as_context_mut()
}
}
#[repr(C)]
#[derive(Clone)]
pub struct wasm_store_t {
pub(crate) store: Store,
pub(crate) store: StoreRef,
}
wasmtime_c_api_macros::declare_own!(wasm_store_t);
@@ -12,14 +40,104 @@ wasmtime_c_api_macros::declare_own!(wasm_store_t);
#[no_mangle]
pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box<wasm_store_t> {
let engine = &engine.engine;
let store = Store::new(engine, ());
Box::new(wasm_store_t {
store: Store::new(&engine),
store: StoreRef {
store: Arc::new(UnsafeCell::new(store)),
},
})
}
/// Representation of a `Store` for `wasmtime.h` This notably tries to move more
/// burden of aliasing on the caller rather than internally, allowing for a more
/// raw representation of contexts and such that requires less `unsafe` in the
/// implementation.
///
/// Note that this notably carries `StoreData` as a payload which allows storing
/// foreign data and configuring WASI as well.
#[repr(C)]
pub struct wasmtime_store_t {
pub(crate) store: Store<StoreData>,
}
pub type CStoreContext<'a> = StoreContext<'a, StoreData>;
pub type CStoreContextMut<'a> = StoreContextMut<'a, StoreData>;
pub struct StoreData {
foreign: crate::ForeignData,
#[cfg(feature = "wasi")]
pub(crate) wasi: Option<wasmtime_wasi::WasiCtx>,
}
#[no_mangle]
pub extern "C" fn wasmtime_store_delete(_: Box<wasmtime_store_t>) {}
#[no_mangle]
pub extern "C" fn wasmtime_store_new(
engine: &wasm_engine_t,
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut c_void)>,
) -> Box<wasmtime_store_t> {
Box::new(wasmtime_store_t {
store: Store::new(
&engine.engine,
StoreData {
foreign: ForeignData { data, finalizer },
#[cfg(feature = "wasi")]
wasi: None,
},
),
})
}
#[no_mangle]
pub extern "C" fn wasmtime_store_gc(store: &wasm_store_t) {
store.store.gc();
pub extern "C" fn wasmtime_store_context(store: &mut wasmtime_store_t) -> CStoreContextMut<'_> {
store.store.as_context_mut()
}
#[no_mangle]
pub extern "C" fn wasmtime_context_get_data(store: CStoreContext<'_>) -> *mut c_void {
store.data().foreign.data
}
#[no_mangle]
pub extern "C" fn wasmtime_context_set_data(mut store: CStoreContextMut<'_>, data: *mut c_void) {
store.data_mut().foreign.data = data;
}
#[cfg(feature = "wasi")]
#[no_mangle]
pub extern "C" fn wasmtime_context_set_wasi(
mut context: CStoreContextMut<'_>,
wasi: Box<crate::wasi_config_t>,
) -> Option<Box<wasmtime_error_t>> {
crate::handle_result(wasi.into_wasi_ctx(), |wasi| {
context.data_mut().wasi = Some(wasi);
})
}
#[no_mangle]
pub extern "C" fn wasmtime_context_gc(mut context: CStoreContextMut<'_>) {
context.gc();
}
#[no_mangle]
pub extern "C" fn wasmtime_context_add_fuel(
mut store: CStoreContextMut<'_>,
fuel: u64,
) -> Option<Box<wasmtime_error_t>> {
crate::handle_result(store.add_fuel(fuel), |()| {})
}
#[no_mangle]
pub extern "C" fn wasmtime_context_fuel_consumed(store: CStoreContext<'_>, fuel: &mut u64) -> bool {
match store.fuel_consumed() {
Some(amt) => {
*fuel = amt;
true
}
None => false,
}
}
#[repr(C)]
@@ -27,14 +145,12 @@ pub struct wasmtime_interrupt_handle_t {
handle: InterruptHandle,
}
wasmtime_c_api_macros::declare_own!(wasmtime_interrupt_handle_t);
#[no_mangle]
pub extern "C" fn wasmtime_interrupt_handle_new(
store: &wasm_store_t,
store: CStoreContext<'_>,
) -> Option<Box<wasmtime_interrupt_handle_t>> {
Some(Box::new(wasmtime_interrupt_handle_t {
handle: store.store.interrupt_handle().ok()?,
handle: store.interrupt_handle().ok()?,
}))
}
@@ -44,20 +160,4 @@ pub extern "C" fn wasmtime_interrupt_handle_interrupt(handle: &wasmtime_interrup
}
#[no_mangle]
pub extern "C" fn wasmtime_store_add_fuel(
store: &wasm_store_t,
fuel: u64,
) -> Option<Box<wasmtime_error_t>> {
crate::handle_result(store.store.add_fuel(fuel), |()| {})
}
#[no_mangle]
pub extern "C" fn wasmtime_store_fuel_consumed(store: &wasm_store_t, fuel: &mut u64) -> bool {
match store.store.fuel_consumed() {
Some(amt) => {
*fuel = amt;
true
}
None => false,
}
}
pub extern "C" fn wasmtime_interrupt_handle_delete(_: Box<wasmtime_interrupt_handle_t>) {}

View File

@@ -1,7 +1,9 @@
use crate::r#ref::{ref_to_val, val_into_ref};
use crate::{handle_result, wasm_func_t, wasm_ref_t, wasmtime_error_t};
use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t};
use std::ptr;
use crate::{
handle_result, wasm_extern_t, wasm_ref_t, wasm_store_t, wasm_tabletype_t, wasmtime_error_t,
wasmtime_val_t, CStoreContext, CStoreContextMut,
};
use std::mem::MaybeUninit;
use wasmtime::{Extern, Table, TableType, Val, ValType};
#[derive(Clone)]
@@ -22,8 +24,8 @@ impl wasm_table_t {
}
}
fn table(&self) -> &Table {
match &self.ext.which {
fn table(&self) -> Table {
match self.ext.which {
Extern::Table(t) => t,
_ => unsafe { std::hint::unreachable_unchecked() },
}
@@ -42,135 +44,138 @@ fn ref_to_val_for_table(r: Option<&wasm_ref_t>, table_ty: &TableType) -> Val {
}
#[no_mangle]
pub extern "C" fn wasm_table_new(
store: &wasm_store_t,
pub unsafe extern "C" fn wasm_table_new(
store: &mut wasm_store_t,
tt: &wasm_tabletype_t,
init: Option<&wasm_ref_t>,
) -> Option<Box<wasm_table_t>> {
let init = ref_to_val_for_table(init, &tt.ty().ty);
let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?;
let table = Table::new(store.store.context_mut(), tt.ty().ty.clone(), init).ok()?;
Some(Box::new(wasm_table_t {
ext: wasm_extern_t {
store: store.store.clone(),
which: table.into(),
},
}))
}
#[no_mangle]
pub extern "C" fn wasmtime_funcref_table_new(
store: &wasm_store_t,
tt: &wasm_tabletype_t,
init: Option<&wasm_func_t>,
out: &mut *mut wasm_table_t,
) -> Option<Box<wasmtime_error_t>> {
let init: Val = match init {
Some(val) => Val::FuncRef(Some(val.func().clone())),
None => Val::FuncRef(None),
};
handle_result(
Table::new(&store.store, tt.ty().ty.clone(), init),
|table| {
*out = Box::into_raw(Box::new(wasm_table_t {
ext: wasm_extern_t {
which: table.into(),
},
}));
},
)
pub unsafe extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box<wasm_tabletype_t> {
let table = t.table();
let store = t.ext.store.context();
Box::new(wasm_tabletype_t::new(table.ty(&store)))
}
#[no_mangle]
pub extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box<wasm_tabletype_t> {
let ty = t.table().ty();
Box::new(wasm_tabletype_t::new(ty))
}
#[no_mangle]
pub extern "C" fn wasm_table_get(
t: &wasm_table_t,
pub unsafe extern "C" fn wasm_table_get(
t: &mut wasm_table_t,
index: wasm_table_size_t,
) -> Option<Box<wasm_ref_t>> {
let val = t.table().get(index)?;
let table = t.table();
let val = table.get(t.ext.store.context_mut(), index)?;
val_into_ref(val)
}
#[no_mangle]
pub extern "C" fn wasmtime_funcref_table_get(
t: &wasm_table_t,
index: wasm_table_size_t,
ptr: &mut *mut wasm_func_t,
) -> bool {
match t.table().get(index) {
Some(val) => {
*ptr = match val {
Val::FuncRef(None) => ptr::null_mut(),
Val::FuncRef(Some(f)) => Box::into_raw(Box::new(f.into())),
_ => return false,
};
}
_ => return false,
}
true
}
#[no_mangle]
pub unsafe extern "C" fn wasm_table_set(
t: &wasm_table_t,
t: &mut wasm_table_t,
index: wasm_table_size_t,
r: Option<&wasm_ref_t>,
) -> bool {
let val = ref_to_val_for_table(r, &t.table().ty());
t.table().set(index, val).is_ok()
let table = t.table();
let val = ref_to_val_for_table(r, &table.ty(t.ext.store.context()));
table.set(t.ext.store.context_mut(), index, val).is_ok()
}
#[no_mangle]
pub extern "C" fn wasmtime_funcref_table_set(
t: &wasm_table_t,
index: wasm_table_size_t,
val: Option<&wasm_func_t>,
) -> Option<Box<wasmtime_error_t>> {
let val = match val {
Some(val) => Val::FuncRef(Some(val.func().clone())),
None => Val::FuncRef(None),
};
handle_result(t.table().set(index, val), |()| {})
}
#[no_mangle]
pub extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t {
t.table().size()
pub unsafe extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t {
let table = t.table();
let store = t.ext.store.context();
table.size(&store)
}
#[no_mangle]
pub unsafe extern "C" fn wasm_table_grow(
t: &wasm_table_t,
t: &mut wasm_table_t,
delta: wasm_table_size_t,
init: Option<&wasm_ref_t>,
) -> bool {
let init = ref_to_val_for_table(init, &t.table().ty());
t.table().grow(delta, init).is_ok()
let table = t.table();
let init = ref_to_val_for_table(init, &table.ty(t.ext.store.context()));
table.grow(t.ext.store.context_mut(), delta, init).is_ok()
}
#[no_mangle]
pub extern "C" fn wasmtime_funcref_table_grow(
t: &wasm_table_t,
delta: wasm_table_size_t,
init: Option<&wasm_func_t>,
prev_size: Option<&mut wasm_table_size_t>,
) -> Option<Box<wasmtime_error_t>> {
let val = match init {
Some(val) => Val::FuncRef(Some(val.func().clone())),
None => Val::FuncRef(None),
};
handle_result(t.table().grow(delta, val), |prev| {
if let Some(ptr) = prev_size {
*ptr = prev;
}
})
pub extern "C" fn wasm_table_as_extern(t: &mut wasm_table_t) -> &mut wasm_extern_t {
&mut t.ext
}
#[no_mangle]
pub extern "C" fn wasm_table_as_extern(t: &wasm_table_t) -> &wasm_extern_t {
pub extern "C" fn wasm_table_as_extern_const(t: &wasm_table_t) -> &wasm_extern_t {
&t.ext
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_table_new(
store: CStoreContextMut<'_>,
tt: &wasm_tabletype_t,
init: &wasmtime_val_t,
out: &mut Table,
) -> Option<Box<wasmtime_error_t>> {
handle_result(
Table::new(store, tt.ty().ty.clone(), init.to_val()),
|table| *out = table,
)
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_table_type(
store: CStoreContext<'_>,
table: &Table,
) -> Box<wasm_tabletype_t> {
Box::new(wasm_tabletype_t::new(table.ty(store)))
}
#[no_mangle]
pub extern "C" fn wasmtime_table_get(
store: CStoreContextMut<'_>,
table: &Table,
index: u32,
ret: &mut MaybeUninit<wasmtime_val_t>,
) -> bool {
match table.get(store, index) {
Some(val) => {
crate::initialize(ret, wasmtime_val_t::from_val(val));
true
}
None => false,
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_table_set(
store: CStoreContextMut<'_>,
table: &Table,
index: u32,
val: &wasmtime_val_t,
) -> Option<Box<wasmtime_error_t>> {
handle_result(table.set(store, index, val.to_val()), |()| {})
}
#[no_mangle]
pub extern "C" fn wasmtime_table_size(store: CStoreContext<'_>, table: &Table) -> u32 {
table.size(store)
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_table_grow(
store: CStoreContextMut<'_>,
table: &Table,
delta: u32,
val: &wasmtime_val_t,
prev_size: &mut u32,
) -> Option<Box<wasmtime_error_t>> {
handle_result(table.grow(store, delta, val.to_val()), |prev| {
*prev_size = prev
})
}

View File

@@ -44,6 +44,15 @@ pub extern "C" fn wasm_trap_new(
})
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_trap_new(message: *const u8, len: usize) -> Box<wasm_trap_t> {
let bytes = crate::slice_from_raw_parts(message, len);
let message = String::from_utf8_lossy(&bytes);
Box::new(wasm_trap_t {
trap: Trap::new(message),
})
}
#[no_mangle]
pub extern "C" fn wasm_trap_message(trap: &wasm_trap_t, out: &mut wasm_message_t) {
let mut buffer = Vec::new();
@@ -136,3 +145,8 @@ pub extern "C" fn wasm_frame_instance(_arg1: *const wasm_frame_t) -> *mut wasm_i
pub extern "C" fn wasm_frame_module_offset(frame: &wasm_frame_t) -> usize {
frame.trap.trace()[frame.idx].module_offset()
}
#[no_mangle]
pub extern "C" fn wasm_frame_copy(frame: &wasm_frame_t) -> Box<wasm_frame_t> {
Box::new(frame.clone())
}

View File

@@ -1,5 +1,5 @@
use crate::{wasm_functype_t, wasm_globaltype_t, wasm_memorytype_t, wasm_tabletype_t};
use crate::{wasm_instancetype_t, wasm_moduletype_t};
use crate::{wasmtime_instancetype_t, wasmtime_moduletype_t};
use crate::{CFuncType, CGlobalType, CInstanceType, CMemoryType, CModuleType, CTableType};
use wasmtime::ExternType;
@@ -123,29 +123,15 @@ pub extern "C" fn wasm_externtype_as_memorytype_const(
}
#[no_mangle]
pub extern "C" fn wasm_externtype_as_moduletype(
pub extern "C" fn wasmtime_externtype_as_moduletype(
et: &wasm_externtype_t,
) -> Option<&wasm_moduletype_t> {
wasm_externtype_as_moduletype_const(et)
) -> Option<&wasmtime_moduletype_t> {
wasmtime_moduletype_t::try_from(et)
}
#[no_mangle]
pub extern "C" fn wasm_externtype_as_moduletype_const(
pub extern "C" fn wasmtime_externtype_as_instancetype(
et: &wasm_externtype_t,
) -> Option<&wasm_moduletype_t> {
wasm_moduletype_t::try_from(et)
}
#[no_mangle]
pub extern "C" fn wasm_externtype_as_instancetype(
et: &wasm_externtype_t,
) -> Option<&wasm_instancetype_t> {
wasm_externtype_as_instancetype_const(et)
}
#[no_mangle]
pub extern "C" fn wasm_externtype_as_instancetype_const(
et: &wasm_externtype_t,
) -> Option<&wasm_instancetype_t> {
wasm_instancetype_t::try_from(et)
) -> Option<&wasmtime_instancetype_t> {
wasmtime_instancetype_t::try_from(et)
}

View File

@@ -3,25 +3,25 @@ use wasmtime::InstanceType;
#[repr(transparent)]
#[derive(Clone)]
pub struct wasm_instancetype_t {
pub struct wasmtime_instancetype_t {
ext: wasm_externtype_t,
}
wasmtime_c_api_macros::declare_ty!(wasm_instancetype_t);
wasmtime_c_api_macros::declare_ty!(wasmtime_instancetype_t);
#[derive(Clone)]
pub(crate) struct CInstanceType {
pub(crate) ty: InstanceType,
}
impl wasm_instancetype_t {
pub(crate) fn new(ty: InstanceType) -> wasm_instancetype_t {
wasm_instancetype_t {
impl wasmtime_instancetype_t {
pub(crate) fn new(ty: InstanceType) -> wasmtime_instancetype_t {
wasmtime_instancetype_t {
ext: wasm_externtype_t::new(ty.into()),
}
}
pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasm_instancetype_t> {
pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasmtime_instancetype_t> {
match &e.which {
CExternType::Instance(_) => Some(unsafe { &*(e as *const _ as *const _) }),
_ => None,
@@ -42,20 +42,15 @@ impl CInstanceType {
}
}
#[no_mangle]
pub extern "C" fn wasm_instancetype_as_externtype(ty: &wasm_instancetype_t) -> &wasm_externtype_t {
&ty.ext
}
#[no_mangle]
pub extern "C" fn wasm_instancetype_as_externtype_const(
ty: &wasm_instancetype_t,
pub extern "C" fn wasmtime_instancetype_as_externtype(
ty: &wasmtime_instancetype_t,
) -> &wasm_externtype_t {
&ty.ext
}
#[no_mangle]
pub extern "C" fn wasm_instancetype_exports(
instance: &wasm_instancetype_t,
pub extern "C" fn wasmtime_instancetype_exports(
instance: &wasmtime_instancetype_t,
out: &mut wasm_exporttype_vec_t,
) {
let exports = instance

View File

@@ -6,25 +6,25 @@ use wasmtime::ModuleType;
#[repr(transparent)]
#[derive(Clone)]
pub struct wasm_moduletype_t {
pub struct wasmtime_moduletype_t {
ext: wasm_externtype_t,
}
wasmtime_c_api_macros::declare_ty!(wasm_moduletype_t);
wasmtime_c_api_macros::declare_ty!(wasmtime_moduletype_t);
#[derive(Clone)]
pub(crate) struct CModuleType {
pub(crate) ty: ModuleType,
}
impl wasm_moduletype_t {
pub(crate) fn new(ty: ModuleType) -> wasm_moduletype_t {
wasm_moduletype_t {
impl wasmtime_moduletype_t {
pub(crate) fn new(ty: ModuleType) -> wasmtime_moduletype_t {
wasmtime_moduletype_t {
ext: wasm_externtype_t::new(ty.into()),
}
}
pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasm_moduletype_t> {
pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasmtime_moduletype_t> {
match &e.which {
CExternType::Module(_) => Some(unsafe { &*(e as *const _ as *const _) }),
_ => None,
@@ -46,20 +46,15 @@ impl CModuleType {
}
#[no_mangle]
pub extern "C" fn wasm_moduletype_as_externtype(ty: &wasm_moduletype_t) -> &wasm_externtype_t {
&ty.ext
}
#[no_mangle]
pub extern "C" fn wasm_moduletype_as_externtype_const(
ty: &wasm_moduletype_t,
pub extern "C" fn wasmtime_moduletype_as_externtype(
ty: &wasmtime_moduletype_t,
) -> &wasm_externtype_t {
&ty.ext
}
#[no_mangle]
pub extern "C" fn wasm_moduletype_exports(
module: &wasm_moduletype_t,
pub extern "C" fn wasmtime_moduletype_exports(
module: &wasmtime_moduletype_t,
out: &mut wasm_exporttype_vec_t,
) {
let exports = module
@@ -77,8 +72,8 @@ pub extern "C" fn wasm_moduletype_exports(
}
#[no_mangle]
pub extern "C" fn wasm_moduletype_imports(
module: &wasm_moduletype_t,
pub extern "C" fn wasmtime_moduletype_imports(
module: &wasmtime_moduletype_t,
out: &mut wasm_importtype_vec_t,
) {
let imports = module

View File

@@ -36,6 +36,7 @@ pub(crate) fn into_valtype(kind: wasm_valkind_t) -> ValType {
WASM_F64 => ValType::F64,
WASM_EXTERNREF => ValType::ExternRef,
WASM_FUNCREF => ValType::FuncRef,
WASMTIME_V128 => ValType::V128,
_ => panic!("unexpected kind: {}", kind),
}
}
@@ -48,6 +49,15 @@ pub(crate) fn from_valtype(ty: &ValType) -> wasm_valkind_t {
ValType::F64 => WASM_F64,
ValType::ExternRef => WASM_EXTERNREF,
ValType::FuncRef => WASM_FUNCREF,
_ => panic!("wasm_valkind_t has no known conversion for {:?}", ty),
ValType::V128 => WASMTIME_V128,
}
}
pub type wasmtime_valkind_t = u8;
pub const WASMTIME_I32: wasmtime_valkind_t = 0;
pub const WASMTIME_I64: wasmtime_valkind_t = 1;
pub const WASMTIME_F32: wasmtime_valkind_t = 2;
pub const WASMTIME_F64: wasmtime_valkind_t = 3;
pub const WASMTIME_V128: wasmtime_valkind_t = 4;
pub const WASMTIME_FUNCREF: wasmtime_valkind_t = 5;
pub const WASMTIME_EXTERNREF: wasmtime_valkind_t = 6;

View File

@@ -1,8 +1,9 @@
use crate::r#ref::{ref_to_val, WasmRefInner};
use crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, WASM_I32};
use std::mem::MaybeUninit;
use crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, wasmtime_valkind_t, WASM_I32};
use std::ffi::c_void;
use std::mem::{self, ManuallyDrop, MaybeUninit};
use std::ptr;
use wasmtime::{Val, ValType};
use wasmtime::{ExternRef, Func, Val, ValType};
#[repr(C)]
pub struct wasm_val_t {
@@ -147,3 +148,143 @@ pub unsafe extern "C" fn wasm_val_copy(out: &mut MaybeUninit<wasm_val_t>, source
pub unsafe extern "C" fn wasm_val_delete(val: *mut wasm_val_t) {
ptr::drop_in_place(val);
}
#[repr(C)]
pub struct wasmtime_val_t {
pub kind: wasmtime_valkind_t,
pub of: wasmtime_val_union,
}
#[repr(C)]
pub union wasmtime_val_union {
pub i32: i32,
pub i64: i64,
pub f32: u32,
pub f64: u64,
pub funcref: wasmtime_func_t,
pub externref: ManuallyDrop<Option<ExternRef>>,
pub v128: [u8; 16],
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wasmtime_func_t {
pub store_id: u64,
pub index: usize,
}
impl wasmtime_val_t {
pub fn from_val(val: Val) -> wasmtime_val_t {
match val {
Val::I32(i) => wasmtime_val_t {
kind: crate::WASMTIME_I32,
of: wasmtime_val_union { i32: i },
},
Val::I64(i) => wasmtime_val_t {
kind: crate::WASMTIME_I64,
of: wasmtime_val_union { i64: i },
},
Val::F32(i) => wasmtime_val_t {
kind: crate::WASMTIME_F32,
of: wasmtime_val_union { f32: i },
},
Val::F64(i) => wasmtime_val_t {
kind: crate::WASMTIME_F64,
of: wasmtime_val_union { f64: i },
},
Val::ExternRef(i) => wasmtime_val_t {
kind: crate::WASMTIME_EXTERNREF,
of: wasmtime_val_union {
externref: ManuallyDrop::new(i),
},
},
Val::FuncRef(i) => wasmtime_val_t {
kind: crate::WASMTIME_FUNCREF,
of: wasmtime_val_union {
funcref: match i {
Some(func) => unsafe { mem::transmute::<Func, wasmtime_func_t>(func) },
None => wasmtime_func_t {
store_id: 0,
index: 0,
},
},
},
},
Val::V128(val) => wasmtime_val_t {
kind: crate::WASMTIME_V128,
of: wasmtime_val_union {
v128: val.to_le_bytes(),
},
},
}
}
pub unsafe fn to_val(&self) -> Val {
match self.kind {
crate::WASMTIME_I32 => Val::I32(self.of.i32),
crate::WASMTIME_I64 => Val::I64(self.of.i64),
crate::WASMTIME_F32 => Val::F32(self.of.f32),
crate::WASMTIME_F64 => Val::F64(self.of.f64),
crate::WASMTIME_V128 => Val::V128(u128::from_le_bytes(self.of.v128)),
crate::WASMTIME_FUNCREF => {
let store = self.of.funcref.store_id;
let index = self.of.funcref.index;
Val::FuncRef(if store == 0 && index == 0 {
None
} else {
Some(mem::transmute::<wasmtime_func_t, Func>(self.of.funcref))
})
}
crate::WASMTIME_EXTERNREF => Val::ExternRef((*self.of.externref).clone()),
other => panic!("unknown wasmtime_valkind_t: {}", other),
}
}
}
impl Drop for wasmtime_val_t {
fn drop(&mut self) {
if self.kind == crate::WASMTIME_EXTERNREF {
unsafe {
ManuallyDrop::drop(&mut self.of.externref);
}
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_val_delete(val: &mut ManuallyDrop<wasmtime_val_t>) {
ManuallyDrop::drop(val)
}
#[no_mangle]
pub unsafe extern "C" fn wasmtime_val_copy(
dst: &mut MaybeUninit<wasmtime_val_t>,
src: &wasmtime_val_t,
) {
crate::initialize(dst, wasmtime_val_t::from_val(src.to_val()))
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_new(
data: *mut c_void,
finalizer: Option<extern "C" fn(*mut c_void)>,
) -> ExternRef {
ExternRef::new(crate::ForeignData { data, finalizer })
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_data(externref: ManuallyDrop<ExternRef>) -> *mut c_void {
externref
.data()
.downcast_ref::<crate::ForeignData>()
.unwrap()
.data
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_clone(externref: ManuallyDrop<ExternRef>) -> ExternRef {
(*externref).clone()
}
#[no_mangle]
pub extern "C" fn wasmtime_externref_delete(_val: Option<ExternRef>) {}

View File

@@ -1,7 +1,7 @@
use crate::{
wasm_exporttype_t, wasm_extern_t, wasm_externtype_t, wasm_frame_t, wasm_functype_t,
wasm_globaltype_t, wasm_importtype_t, wasm_instancetype_t, wasm_memorytype_t,
wasm_moduletype_t, wasm_tabletype_t, wasm_val_t, wasm_valtype_t,
wasm_globaltype_t, wasm_importtype_t, wasm_memorytype_t, wasm_tabletype_t, wasm_val_t,
wasm_valtype_t,
};
use std::mem;
use std::mem::MaybeUninit;
@@ -186,24 +186,6 @@ declare_vecs! {
copy: wasm_memorytype_vec_copy,
delete: wasm_memorytype_vec_delete,
)
(
name: wasm_instancetype_vec_t,
ty: Option<Box<wasm_instancetype_t>>,
new: wasm_instancetype_vec_new,
empty: wasm_instancetype_vec_new_empty,
uninit: wasm_instancetype_vec_new_uninitialized,
copy: wasm_instancetype_vec_copy,
delete: wasm_instancetype_vec_delete,
)
(
name: wasm_moduletype_vec_t,
ty: Option<Box<wasm_moduletype_t>>,
new: wasm_moduletype_vec_new,
empty: wasm_moduletype_vec_new_empty,
uninit: wasm_moduletype_vec_new_uninitialized,
copy: wasm_moduletype_vec_copy,
delete: wasm_moduletype_vec_delete,
)
(
name: wasm_externtype_vec_t,
ty: Option<Box<wasm_externtype_t>>,

View File

@@ -1,21 +1,13 @@
//! The WASI embedding API definitions for Wasmtime.
use crate::{wasm_extern_t, wasm_importtype_t, wasm_store_t, wasm_trap_t};
use anyhow::Result;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fs::File;
use std::os::raw::{c_char, c_int};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::slice;
use std::str;
use wasmtime::{Extern, Linker, Trap};
use wasmtime_wasi::{
sync::{
snapshots::preview_0::Wasi as WasiSnapshot0, snapshots::preview_1::Wasi as WasiPreview1,
Dir, WasiCtxBuilder,
},
sync::{Dir, WasiCtxBuilder},
WasiCtx,
};
@@ -31,13 +23,6 @@ unsafe fn create_file(path: *const c_char) -> Option<File> {
File::create(cstr_to_path(path)?).ok()
}
pub enum WasiModule {
Snapshot0(WasiSnapshot0),
Preview1(WasiPreview1),
}
impl WasiModule {}
#[repr(C)]
#[derive(Default)]
pub struct wasi_config_t {
@@ -54,6 +39,61 @@ pub struct wasi_config_t {
inherit_stderr: bool,
}
impl wasi_config_t {
pub fn into_wasi_ctx(self) -> Result<WasiCtx> {
let mut builder = WasiCtxBuilder::new();
if self.inherit_args {
builder = builder.inherit_args()?;
} else if !self.args.is_empty() {
let args = self
.args
.into_iter()
.map(|bytes| Ok(String::from_utf8(bytes)?))
.collect::<Result<Vec<String>>>()?;
builder = builder.args(&args)?;
}
if self.inherit_env {
builder = builder.inherit_env()?;
} else if !self.env.is_empty() {
let env = self
.env
.into_iter()
.map(|(kbytes, vbytes)| {
let k = String::from_utf8(kbytes)?;
let v = String::from_utf8(vbytes)?;
Ok((k, v))
})
.collect::<Result<Vec<(String, String)>>>()?;
builder = builder.envs(&env)?;
}
if self.inherit_stdin {
builder = builder.inherit_stdin();
} else if let Some(file) = self.stdin {
let file = unsafe { cap_std::fs::File::from_std(file) };
let file = wasi_cap_std_sync::file::File::from_cap_std(file);
builder = builder.stdin(Box::new(file));
}
if self.inherit_stdout {
builder = builder.inherit_stdout();
} else if let Some(file) = self.stdout {
let file = unsafe { cap_std::fs::File::from_std(file) };
let file = wasi_cap_std_sync::file::File::from_cap_std(file);
builder = builder.stdout(Box::new(file));
}
if self.inherit_stderr {
builder = builder.inherit_stderr();
} else if let Some(file) = self.stderr {
let file = unsafe { cap_std::fs::File::from_std(file) };
let file = wasi_cap_std_sync::file::File::from_cap_std(file);
builder = builder.stderr(Box::new(file));
}
for (dir, path) in self.preopens {
builder = builder.preopened_dir(dir, path)?;
}
Ok(builder.build())
}
}
#[no_mangle]
pub extern "C" fn wasi_config_new() -> Box<wasi_config_t> {
Box::new(wasi_config_t::default())
@@ -198,151 +238,3 @@ pub unsafe extern "C" fn wasi_config_preopen_dir(
true
}
enum WasiInstance {
Preview1(WasiPreview1),
Snapshot0(WasiSnapshot0),
}
fn create_wasi_ctx(config: wasi_config_t) -> Result<Rc<RefCell<WasiCtx>>> {
let mut builder = WasiCtxBuilder::new();
if config.inherit_args {
builder = builder.inherit_args()?;
} else if !config.args.is_empty() {
let args = config
.args
.into_iter()
.map(|bytes| Ok(String::from_utf8(bytes)?))
.collect::<Result<Vec<String>>>()?;
builder = builder.args(&args)?;
}
if config.inherit_env {
builder = builder.inherit_env()?;
} else if !config.env.is_empty() {
let env = config
.env
.into_iter()
.map(|(kbytes, vbytes)| {
let k = String::from_utf8(kbytes)?;
let v = String::from_utf8(vbytes)?;
Ok((k, v))
})
.collect::<Result<Vec<(String, String)>>>()?;
builder = builder.envs(&env)?;
}
if config.inherit_stdin {
builder = builder.inherit_stdin();
} else if let Some(file) = config.stdin {
let file = unsafe { cap_std::fs::File::from_std(file) };
let file = wasi_cap_std_sync::file::File::from_cap_std(file);
builder = builder.stdin(Box::new(file));
}
if config.inherit_stdout {
builder = builder.inherit_stdout();
} else if let Some(file) = config.stdout {
let file = unsafe { cap_std::fs::File::from_std(file) };
let file = wasi_cap_std_sync::file::File::from_cap_std(file);
builder = builder.stdout(Box::new(file));
}
if config.inherit_stderr {
builder = builder.inherit_stderr();
} else if let Some(file) = config.stderr {
let file = unsafe { cap_std::fs::File::from_std(file) };
let file = wasi_cap_std_sync::file::File::from_cap_std(file);
builder = builder.stderr(Box::new(file));
}
for (dir, path) in config.preopens {
builder = builder.preopened_dir(dir, path)?;
}
Ok(Rc::new(RefCell::new(builder.build())))
}
#[repr(C)]
pub struct wasi_instance_t {
wasi: WasiInstance,
export_cache: HashMap<String, Box<wasm_extern_t>>,
}
impl wasi_instance_t {
pub fn add_to_linker(&self, linker: &mut Linker) -> Result<()> {
match &self.wasi {
WasiInstance::Snapshot0(w) => w.add_to_linker(linker),
WasiInstance::Preview1(w) => w.add_to_linker(linker),
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wasi_instance_new(
store: &wasm_store_t,
name: *const c_char,
config: Box<wasi_config_t>,
trap: &mut *mut wasm_trap_t,
) -> Option<Box<wasi_instance_t>> {
let store = &store.store;
let result = match CStr::from_ptr(name).to_str().unwrap_or("") {
"wasi_snapshot_preview1" => {
create_wasi_ctx(*config).map(|cx| WasiInstance::Preview1(WasiPreview1::new(store, cx)))
}
"wasi_unstable" => create_wasi_ctx(*config)
.map(|cx| WasiInstance::Snapshot0(WasiSnapshot0::new(store, cx))),
_ => Err(anyhow::anyhow!("unsupported WASI version")),
};
match result {
Ok(wasi) => Some(Box::new(wasi_instance_t {
wasi,
export_cache: HashMap::new(),
})),
Err(e) => {
*trap = Box::into_raw(Box::new(wasm_trap_t {
trap: Trap::from(e),
}));
None
}
}
}
#[no_mangle]
pub extern "C" fn wasi_instance_delete(_instance: Box<wasi_instance_t>) {}
#[no_mangle]
pub extern "C" fn wasi_instance_bind_import<'a>(
instance: &'a mut wasi_instance_t,
import: &wasm_importtype_t,
) -> Option<&'a wasm_extern_t> {
let module = &import.module;
let name = str::from_utf8(import.name.as_ref()?.as_bytes()).ok()?;
let export = match &instance.wasi {
WasiInstance::Preview1(wasi) => {
if module != "wasi_snapshot_preview1" {
return None;
}
wasi.get_export(&name)?
}
WasiInstance::Snapshot0(wasi) => {
if module != "wasi_unstable" {
return None;
}
wasi.get_export(&name)?
}
};
if &export.ty() != import.ty.func()? {
return None;
}
let entry = instance
.export_cache
.entry(name.to_string())
.or_insert_with(|| {
Box::new(wasm_extern_t {
which: Extern::Func(export.clone()),
})
});
Some(entry)
}

View File

@@ -1,11 +1,13 @@
use crate::{bad_utf8, handle_result, wasm_byte_vec_t, wasmtime_error_t};
#[no_mangle]
pub extern "C" fn wasmtime_wat2wasm(
wat: &wasm_byte_vec_t,
pub unsafe extern "C" fn wasmtime_wat2wasm(
wat: *const u8,
wat_len: usize,
ret: &mut wasm_byte_vec_t,
) -> Option<Box<wasmtime_error_t>> {
let wat = match std::str::from_utf8(wat.as_slice()) {
let wat = crate::slice_from_raw_parts(wat, wat_len);
let wat = match std::str::from_utf8(wat) {
Ok(s) => s,
Err(_) => return bad_utf8(),
};

View File

@@ -394,25 +394,18 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
debug_assert!(delta == -1 || delta == 1);
let pointer_type = self.pointer_type();
let ref_count_offset = ir::immediates::Offset32::new(
i32::try_from(VMOffsets::vm_extern_data_ref_count()).unwrap(),
);
let old_ref_count = builder.ins().load(
// If this changes that's ok, the `atomic_rmw` below just needs to be
// preceded with an add instruction of `externref` and the offset.
assert_eq!(VMOffsets::vm_extern_data_ref_count(), 0);
let delta = builder.ins().iconst(pointer_type, delta);
builder.ins().atomic_rmw(
pointer_type,
ir::MemFlags::trusted(),
ir::AtomicRmwOp::Add,
externref,
ref_count_offset,
);
let new_ref_count = builder.ins().iadd_imm(old_ref_count, delta);
builder.ins().store(
ir::MemFlags::trusted(),
new_ref_count,
externref,
ref_count_offset,
);
new_ref_count
delta,
)
}
fn get_global_location(
@@ -1052,8 +1045,11 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
builder.ins().jump(continue_block, &[]);
builder.switch_to_block(dec_ref_count_block);
let ref_count = self.mutate_extenref_ref_count(builder, current_elem, -1);
builder.ins().brz(ref_count, drop_block, &[]);
let prev_ref_count = self.mutate_extenref_ref_count(builder, current_elem, -1);
let one = builder.ins().iconst(pointer_type, 1);
builder
.ins()
.br_icmp(IntCC::Equal, one, prev_ref_count, drop_block, &[]);
builder.ins().jump(continue_block, &[]);
// Call the `drop_externref` builtin to (you guessed it) drop

View File

@@ -6,7 +6,7 @@
// struct VMContext {
// interrupts: *const VMInterrupts,
// externref_activations_table: *mut VMExternRefActivationsTable,
// module_info_lookup: *const dyn ModuleInfoLookup,
// store: *mut dyn Store,
// signature_ids: [VMSharedSignatureIndex; module.num_signature_ids],
// imported_functions: [VMFunctionImport; module.num_imported_functions],
// imported_tables: [VMTableImport; module.num_imported_tables],
@@ -77,7 +77,7 @@ pub struct VMOffsets {
// precalculated offsets of various member fields
interrupts: u32,
externref_activations_table: u32,
module_info_lookup: u32,
store: u32,
signature_ids: u32,
imported_functions: u32,
imported_tables: u32,
@@ -149,7 +149,7 @@ impl From<VMOffsetsFields> for VMOffsets {
num_defined_globals: fields.num_defined_globals,
interrupts: 0,
externref_activations_table: 0,
module_info_lookup: 0,
store: 0,
signature_ids: 0,
imported_functions: 0,
imported_tables: 0,
@@ -168,12 +168,12 @@ impl From<VMOffsetsFields> for VMOffsets {
.interrupts
.checked_add(u32::from(fields.pointer_size))
.unwrap();
ret.module_info_lookup = ret
ret.store = ret
.externref_activations_table
.checked_add(u32::from(fields.pointer_size))
.unwrap();
ret.signature_ids = ret
.module_info_lookup
.store
.checked_add(u32::from(fields.pointer_size * 2))
.unwrap();
ret.imported_functions = ret
@@ -501,16 +501,16 @@ impl VMOffsets {
self.interrupts
}
/// The offset of the `VMExternRefActivationsTable` member.
/// The offset of the `*mut VMExternRefActivationsTable` member.
#[inline]
pub fn vmctx_externref_activations_table(&self) -> u32 {
self.externref_activations_table
}
/// The offset of the `*const dyn ModuleInfoLookup` member.
/// The offset of the `*const dyn Store` member.
#[inline]
pub fn vmctx_module_info_lookup(&self) -> u32 {
self.module_info_lookup
pub fn vmctx_store(&self) -> u32 {
self.store
}
/// The offset of the `signature_ids` array.

View File

@@ -15,8 +15,6 @@ pub mod dummy;
use arbitrary::Arbitrary;
use dummy::dummy_linker;
use log::debug;
use std::cell::Cell;
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::sync::{Arc, Condvar, Mutex};
use std::time::{Duration, Instant};
@@ -44,9 +42,9 @@ fn log_wasm(wasm: &[u8]) {
}
}
fn create_store(engine: &Engine) -> Store {
Store::new_with_limits(
&engine,
fn create_store(engine: &Engine) -> Store<()> {
let mut store = Store::new(&engine, ());
store.limiter(
StoreLimitsBuilder::new()
// The limits here are chosen based on the default "maximum type size"
// configured in wasm-smith, which is 1000. This means that instances
@@ -56,7 +54,8 @@ fn create_store(engine: &Engine) -> Store {
.tables(1100)
.memories(1100)
.build(),
)
);
store
}
/// Methods of timing out execution of a WebAssembly module
@@ -110,7 +109,7 @@ pub fn instantiate_with_config(
_ => false,
});
let engine = Engine::new(&config).unwrap();
let store = create_store(&engine);
let mut store = create_store(&engine);
let mut timeout_state = SignalOnDrop::default();
match timeout {
@@ -137,9 +136,9 @@ pub fn instantiate_with_config(
Err(_) if !known_valid => return,
Err(e) => panic!("failed to compile module: {:?}", e),
};
let linker = dummy_linker(&store, &module);
let linker = dummy_linker(&mut store, &module);
match linker.instantiate(&module) {
match linker.instantiate(&mut store, &module) {
Ok(_) => {}
Err(e) => {
let string = e.to_string();
@@ -218,7 +217,7 @@ pub fn differential_execution(
config.wasm_module_linking(false);
let engine = Engine::new(&config).unwrap();
let store = create_store(&engine);
let mut store = create_store(&engine);
let module = Module::new(&engine, &wasm).unwrap();
@@ -227,13 +226,13 @@ pub fn differential_execution(
// in and with what values. Like the results of exported functions,
// calls to imports should also yield the same values for each
// configuration, and we should assert that.
let linker = dummy_linker(&store, &module);
let linker = dummy_linker(&mut store, &module);
// Don't unwrap this: there can be instantiation-/link-time errors that
// aren't caught during validation or compilation. For example, an imported
// table might not have room for an element segment that we want to
// initialize into it.
let instance = match linker.instantiate(&module) {
let instance = match linker.instantiate(&mut store, &module) {
Ok(instance) => instance,
Err(e) => {
eprintln!(
@@ -244,30 +243,36 @@ pub fn differential_execution(
}
};
for (name, f) in instance.exports().filter_map(|e| {
let name = e.name();
e.into_func().map(|f| (name, f))
}) {
let exports = instance
.exports(&mut store)
.filter_map(|e| {
let name = e.name().to_string();
e.into_func().map(|f| (name, f))
})
.collect::<Vec<_>>();
for (name, f) in exports {
// Always call the hang limit initializer first, so that we don't
// infinite loop when calling another export.
init_hang_limit(&instance);
init_hang_limit(&mut store, instance);
let ty = f.ty();
let ty = f.ty(&store);
let params = dummy::dummy_values(ty.params());
let this_result = f.call(&params).map_err(|e| e.downcast::<Trap>().unwrap());
let this_result = f
.call(&mut store, &params)
.map_err(|e| e.downcast::<Trap>().unwrap());
let existing_result = export_func_results
.entry(name.to_string())
.or_insert_with(|| this_result.clone());
assert_same_export_func_result(&existing_result, &this_result, name);
assert_same_export_func_result(&existing_result, &this_result, &name);
}
}
fn init_hang_limit(instance: &Instance) {
match instance.get_export("hangLimitInitializer") {
fn init_hang_limit(store: &mut Store<()>, instance: Instance) {
match instance.get_export(&mut *store, "hangLimitInitializer") {
None => return,
Some(Extern::Func(f)) => {
f.call(&[])
f.call(store, &[])
.expect("initializing the hang limit should not fail");
}
Some(_) => panic!("unexpected hangLimitInitializer export"),
@@ -332,7 +337,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
let mut config: Option<Config> = None;
let mut engine: Option<Engine> = None;
let mut store: Option<Store> = None;
let mut store: Option<Store<()>> = None;
let mut modules: HashMap<usize, Module> = Default::default();
let mut instances: HashMap<usize, Instance> = Default::default();
@@ -390,14 +395,14 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
None => continue,
};
let store = store.as_ref().unwrap();
let store = store.as_mut().unwrap();
let linker = dummy_linker(store, module);
// Don't unwrap this: there can be instantiation-/link-time errors that
// aren't caught during validation or compilation. For example, an imported
// table might not have room for an element segment that we want to
// initialize into it.
if let Ok(instance) = linker.instantiate(&module) {
if let Ok(instance) = linker.instantiate(store, &module) {
instances.insert(id, instance);
}
}
@@ -421,9 +426,10 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
continue;
}
};
let store = store.as_mut().unwrap();
let funcs = instance
.exports()
.exports(&mut *store)
.filter_map(|e| match e.into_extern() {
Extern::Func(f) => Some(f.clone()),
_ => None,
@@ -436,9 +442,9 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
let nth = nth % funcs.len();
let f = &funcs[nth];
let ty = f.ty();
let ty = f.ty(&store);
let params = dummy::dummy_values(ty.params());
let _ = f.call(&params);
let _ = f.call(store, &params);
}
}
}
@@ -454,7 +460,7 @@ pub fn spectest(fuzz_config: crate::generators::Config, test: crate::generators:
config.wasm_reference_types(false);
config.wasm_bulk_memory(false);
config.wasm_module_linking(false);
let store = create_store(&Engine::new(&config).unwrap());
let mut store = create_store(&Engine::new(&config).unwrap());
if fuzz_config.consume_fuel {
store.add_fuel(u64::max_value()).unwrap();
}
@@ -472,13 +478,13 @@ pub fn table_ops(
) {
let _ = env_logger::try_init();
let num_dropped = Rc::new(Cell::new(0));
let num_dropped = Arc::new(AtomicUsize::new(0));
{
let mut config = fuzz_config.to_wasmtime();
config.wasm_reference_types(true);
let engine = Engine::new(&config).unwrap();
let store = create_store(&engine);
let mut store = create_store(&engine);
if fuzz_config.consume_fuel {
store.add_fuel(u64::max_value()).unwrap();
}
@@ -494,31 +500,30 @@ pub fn table_ops(
// test case.
const MAX_GCS: usize = 5;
let num_gcs = Cell::new(0);
let gc = Func::wrap(&store, move |caller: Caller| {
if num_gcs.get() < MAX_GCS {
caller.store().gc();
num_gcs.set(num_gcs.get() + 1);
let num_gcs = AtomicUsize::new(0);
let gc = Func::wrap(&mut store, move |mut caller: Caller<'_, ()>| {
if num_gcs.fetch_add(1, SeqCst) < MAX_GCS {
caller.gc();
}
});
let instance = Instance::new(&store, &module, &[gc.into()]).unwrap();
let run = instance.get_func("run").unwrap();
let instance = Instance::new(&mut store, &module, &[gc.into()]).unwrap();
let run = instance.get_func(&mut store, "run").unwrap();
let args: Vec<_> = (0..ops.num_params())
.map(|_| Val::ExternRef(Some(ExternRef::new(CountDrops(num_dropped.clone())))))
.collect();
let _ = run.call(&args);
let _ = run.call(&mut store, &args);
}
assert_eq!(num_dropped.get(), ops.num_params());
assert_eq!(num_dropped.load(SeqCst), ops.num_params() as usize);
return;
struct CountDrops(Rc<Cell<u8>>);
struct CountDrops(Arc<AtomicUsize>);
impl Drop for CountDrops {
fn drop(&mut self) {
self.0.set(self.0.get().checked_add(1).unwrap());
self.0.fetch_add(1, SeqCst);
}
}
}
@@ -593,13 +598,13 @@ pub fn differential_wasmi_execution(wasm: &[u8], config: &crate::generators::Con
let mut wasmtime_config = config.to_wasmtime();
wasmtime_config.cranelift_nan_canonicalization(true);
let wasmtime_engine = Engine::new(&wasmtime_config).unwrap();
let wasmtime_store = create_store(&wasmtime_engine);
let mut wasmtime_store = create_store(&wasmtime_engine);
if config.consume_fuel {
wasmtime_store.add_fuel(u64::max_value()).unwrap();
}
let wasmtime_module =
Module::new(&wasmtime_engine, &wasm).expect("Wasmtime can compile module");
let wasmtime_instance = Instance::new(&wasmtime_store, &wasmtime_module, &[])
let wasmtime_instance = Instance::new(&mut wasmtime_store, &wasmtime_module, &[])
.expect("Wasmtime can instantiate module");
// Introspect wasmtime module to find name of an exported function and of an
@@ -628,12 +633,12 @@ pub fn differential_wasmi_execution(wasm: &[u8], config: &crate::generators::Con
let wasmi_val = wasmi::FuncInstance::invoke(&wasmi_main, &[], &mut wasmi::NopExternals);
let wasmtime_mem = wasmtime_instance
.get_memory(&memory_name[..])
.get_memory(&mut wasmtime_store, &memory_name[..])
.expect("memory export is present");
let wasmtime_main = wasmtime_instance
.get_func(&func_name[..])
.get_func(&mut wasmtime_store, &func_name[..])
.expect("function export is present");
let wasmtime_vals = wasmtime_main.call(&[]);
let wasmtime_vals = wasmtime_main.call(&mut wasmtime_store, &[]);
let wasmtime_val = wasmtime_vals.map(|v| v.iter().next().cloned());
debug!(
@@ -665,18 +670,18 @@ pub fn differential_wasmi_execution(wasm: &[u8], config: &crate::generators::Con
}
}
if wasmi_mem.current_size().0 != wasmtime_mem.size() as usize {
if wasmi_mem.current_size().0 != wasmtime_mem.size(&wasmtime_store) as usize {
show_wat();
panic!("resulting memories are not the same size");
}
// Wasmi memory may be stored non-contiguously; copy it out to a contiguous chunk.
let mut wasmi_buf: Vec<u8> = vec![0; wasmtime_mem.data_size()];
let mut wasmi_buf: Vec<u8> = vec![0; wasmtime_mem.data_size(&wasmtime_store)];
wasmi_mem
.get_into(0, &mut wasmi_buf[..])
.expect("can access wasmi memory");
let wasmtime_slice = unsafe { wasmtime_mem.data_unchecked() };
let wasmtime_slice = wasmtime_mem.data(&wasmtime_store);
if wasmi_buf.len() >= 64 {
debug!("-> First 64 bytes of wasmi heap: {:?}", &wasmi_buf[0..64]);

View File

@@ -4,8 +4,8 @@ use std::fmt::Write;
use wasmtime::*;
/// Create a set of dummy functions/globals/etc for the given imports.
pub fn dummy_linker<'module>(store: &Store, module: &Module) -> Linker {
let mut linker = Linker::new(store);
pub fn dummy_linker<'module>(store: &mut Store<()>, module: &Module) -> Linker<()> {
let mut linker = Linker::new(store.engine());
linker.allow_shadowing(true);
for import in module.imports() {
match import.name() {
@@ -34,19 +34,19 @@ pub fn dummy_linker<'module>(store: &Store, module: &Module) -> Linker {
}
/// Construct a dummy `Extern` from its type signature
pub fn dummy_extern(store: &Store, ty: ExternType) -> Extern {
pub fn dummy_extern(store: &mut Store<()>, ty: ExternType) -> Extern {
match ty {
ExternType::Func(func_ty) => Extern::Func(dummy_func(store, func_ty)),
ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_ty)),
ExternType::Table(table_ty) => Extern::Table(dummy_table(store, table_ty)),
ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(store, mem_ty)),
ExternType::Instance(instance_ty) => Extern::Instance(dummy_instance(store, instance_ty)),
ExternType::Module(module_ty) => Extern::Module(dummy_module(store, module_ty)),
ExternType::Module(module_ty) => Extern::Module(dummy_module(store.engine(), module_ty)),
}
}
/// Construct a dummy function for the given function type
pub fn dummy_func(store: &Store, ty: FuncType) -> Func {
pub fn dummy_func(store: &mut Store<()>, ty: FuncType) -> Func {
Func::new(store, ty.clone(), move |_, _, results| {
for (ret_ty, result) in ty.results().zip(results) {
*result = dummy_value(ret_ty);
@@ -74,19 +74,19 @@ pub fn dummy_values(val_tys: impl IntoIterator<Item = ValType>) -> Vec<Val> {
}
/// Construct a dummy global for the given global type.
pub fn dummy_global(store: &Store, ty: GlobalType) -> Global {
pub fn dummy_global(store: &mut Store<()>, ty: GlobalType) -> Global {
let val = dummy_value(ty.content().clone());
Global::new(store, ty, val).unwrap()
}
/// Construct a dummy table for the given table type.
pub fn dummy_table(store: &Store, ty: TableType) -> Table {
pub fn dummy_table(store: &mut Store<()>, ty: TableType) -> Table {
let init_val = dummy_value(ty.element().clone());
Table::new(store, ty, init_val).unwrap()
}
/// Construct a dummy memory for the given memory type.
pub fn dummy_memory(store: &Store, ty: MemoryType) -> Memory {
pub fn dummy_memory(store: &mut Store<()>, ty: MemoryType) -> Memory {
Memory::new(store, ty).unwrap()
}
@@ -94,7 +94,7 @@ pub fn dummy_memory(store: &Store, ty: MemoryType) -> Memory {
///
/// This is done by using the expected type to generate a module on-the-fly
/// which we the instantiate.
pub fn dummy_instance(store: &Store, ty: InstanceType) -> Instance {
pub fn dummy_instance(store: &mut Store<()>, ty: InstanceType) -> Instance {
let mut wat = WatGenerator::new();
for ty in ty.exports() {
wat.export(&ty);
@@ -106,7 +106,7 @@ pub fn dummy_instance(store: &Store, ty: InstanceType) -> Instance {
/// Construct a dummy module for the given module type.
///
/// This is done by using the expected type to generate a module on-the-fly.
pub fn dummy_module(store: &Store, ty: ModuleType) -> Module {
pub fn dummy_module(engine: &Engine, ty: ModuleType) -> Module {
let mut wat = WatGenerator::new();
for ty in ty.imports() {
wat.import(&ty);
@@ -114,7 +114,7 @@ pub fn dummy_module(store: &Store, ty: ModuleType) -> Module {
for ty in ty.exports() {
wat.export(&ty);
}
Module::new(store.engine(), &wat.finish()).unwrap()
Module::new(engine, &wat.finish()).unwrap()
}
struct WatGenerator {
@@ -378,53 +378,57 @@ mod tests {
use super::*;
use std::collections::HashSet;
fn store() -> Store {
fn store() -> Store<()> {
let mut config = Config::default();
config.wasm_module_linking(true);
config.wasm_multi_memory(true);
let engine = wasmtime::Engine::new(&config).unwrap();
Store::new(&engine)
Store::new(&engine, ())
}
#[test]
fn dummy_table_import() {
let store = store();
let mut store = store();
let table = dummy_table(
&store,
&mut store,
TableType::new(ValType::ExternRef, Limits::at_least(10)),
);
assert_eq!(table.size(), 10);
assert_eq!(table.size(&store), 10);
for i in 0..10 {
assert!(table.get(i).unwrap().unwrap_externref().is_none());
assert!(table
.get(&mut store, i)
.unwrap()
.unwrap_externref()
.is_none());
}
}
#[test]
fn dummy_global_import() {
let store = store();
let global = dummy_global(&store, GlobalType::new(ValType::I32, Mutability::Const));
assert_eq!(global.val_type(), ValType::I32);
assert_eq!(global.mutability(), Mutability::Const);
let mut store = store();
let global = dummy_global(&mut store, GlobalType::new(ValType::I32, Mutability::Const));
assert_eq!(*global.ty(&store).content(), ValType::I32);
assert_eq!(global.ty(&store).mutability(), Mutability::Const);
}
#[test]
fn dummy_memory_import() {
let store = store();
let memory = dummy_memory(&store, MemoryType::new(Limits::at_least(1)));
assert_eq!(memory.size(), 1);
let mut store = store();
let memory = dummy_memory(&mut store, MemoryType::new(Limits::at_least(1)));
assert_eq!(memory.size(&store), 1);
}
#[test]
fn dummy_function_import() {
let store = store();
let mut store = store();
let func_ty = FuncType::new(vec![ValType::I32], vec![ValType::I64]);
let func = dummy_func(&store, func_ty.clone());
assert_eq!(func.ty(), func_ty);
let func = dummy_func(&mut store, func_ty.clone());
assert_eq!(func.ty(&store), func_ty);
}
#[test]
fn dummy_instance_import() {
let store = store();
let mut store = store();
let mut instance_ty = InstanceType::new();
@@ -464,7 +468,7 @@ mod tests {
instance_ty.add_named_export("instance0", InstanceType::new().into());
instance_ty.add_named_export("instance1", InstanceType::new().into());
let instance = dummy_instance(&store, instance_ty.clone());
let instance = dummy_instance(&mut store, instance_ty.clone());
let mut expected_exports = vec![
"func0",
@@ -482,7 +486,7 @@ mod tests {
]
.into_iter()
.collect::<HashSet<_>>();
for exp in instance.ty().exports() {
for exp in instance.ty(&store).exports() {
let was_expected = expected_exports.remove(exp.name());
assert!(was_expected);
}
@@ -564,7 +568,7 @@ mod tests {
module_ty.add_named_import("instance1", None, InstanceType::new().into());
// Create the module.
let module = dummy_module(&store, module_ty);
let module = dummy_module(store.engine(), module_ty);
// Check that we have the expected exports.
assert!(module.get_export("func0").is_some());

View File

@@ -1,8 +1,6 @@
use crate::vmcontext::{
VMCallerCheckedAnyfunc, VMContext, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
};
use crate::RuntimeInstance;
use std::any::Any;
use std::ptr::NonNull;
use wasmtime_environ::wasm::Global;
use wasmtime_environ::{MemoryPlan, TablePlan};
@@ -20,16 +18,10 @@ pub enum Export {
/// A global export value.
Global(ExportGlobal),
/// An instance
Instance(RuntimeInstance),
/// A module
Module(Box<dyn Any>),
}
/// A function export value.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct ExportFunction {
/// The `VMCallerCheckedAnyfunc` for this exported function.
///
@@ -38,6 +30,12 @@ pub struct ExportFunction {
pub anyfunc: NonNull<VMCallerCheckedAnyfunc>,
}
// It's part of the contract of using `ExportFunction` that synchronization
// properties are upheld, so declare that despite the raw pointers inside this
// is send/sync.
unsafe impl Send for ExportFunction {}
unsafe impl Sync for ExportFunction {}
impl From<ExportFunction> for Export {
fn from(func: ExportFunction) -> Export {
Export::Function(func)
@@ -55,6 +53,10 @@ pub struct ExportTable {
pub table: TablePlan,
}
// See docs on send/sync for `ExportFunction` above.
unsafe impl Send for ExportTable {}
unsafe impl Sync for ExportTable {}
impl From<ExportTable> for Export {
fn from(func: ExportTable) -> Export {
Export::Table(func)
@@ -72,6 +74,10 @@ pub struct ExportMemory {
pub memory: MemoryPlan,
}
// See docs on send/sync for `ExportFunction` above.
unsafe impl Send for ExportMemory {}
unsafe impl Sync for ExportMemory {}
impl From<ExportMemory> for Export {
fn from(func: ExportMemory) -> Export {
Export::Memory(func)
@@ -89,6 +95,10 @@ pub struct ExportGlobal {
pub global: Global,
}
// See docs on send/sync for `ExportFunction` above.
unsafe impl Send for ExportGlobal {}
unsafe impl Sync for ExportGlobal {}
impl From<ExportGlobal> for Export {
fn from(func: ExportGlobal) -> Export {
Export::Global(func)

View File

@@ -100,13 +100,14 @@
//! <https://openresearch-repository.anu.edu.au/bitstream/1885/42030/2/hon-thesis.pdf>
use std::any::Any;
use std::cell::{Cell, RefCell, UnsafeCell};
use std::cmp::Ordering;
use std::cell::UnsafeCell;
use std::cmp;
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::Deref;
use std::ptr::{self, NonNull};
use std::sync::atomic::{self, AtomicUsize, Ordering};
use std::{alloc::Layout, sync::Arc};
use wasmtime_environ::ir::StackMap;
@@ -145,17 +146,16 @@ use wasmtime_environ::ir::StackMap;
/// let file = std::fs::File::create("some/file/path")?;
///
/// // Wrap the file up as an `VMExternRef` that can be passed to Wasm.
/// let extern_ref_to_file = VMExternRef::new(RefCell::new(file));
/// let extern_ref_to_file = VMExternRef::new(file);
///
/// // `VMExternRef`s dereference to `dyn Any`, so you can use `Any` methods to
/// // perform runtime type checks and downcasts.
///
/// assert!(extern_ref_to_file.is::<RefCell<std::fs::File>>());
/// assert!(extern_ref_to_file.is::<std::fs::File>());
/// assert!(!extern_ref_to_file.is::<String>());
///
/// if let Some(file) = extern_ref_to_file.downcast_ref::<RefCell<std::fs::File>>() {
/// if let Some(mut file) = extern_ref_to_file.downcast_ref::<std::fs::File>() {
/// use std::io::Write;
/// let mut file = file.borrow_mut();
/// writeln!(&mut file, "Hello, `VMExternRef`!")?;
/// }
/// # Ok(())
@@ -165,6 +165,10 @@ use wasmtime_environ::ir::StackMap;
#[repr(transparent)]
pub struct VMExternRef(NonNull<VMExternData>);
// Data contained is always Send+Sync so these should be safe.
unsafe impl Send for VMExternRef {}
unsafe impl Sync for VMExternRef {}
#[repr(C)]
pub(crate) struct VMExternData {
// Implicit, dynamically-sized member that always preceded an
@@ -180,11 +184,11 @@ pub(crate) struct VMExternData {
/// Note: this field's offset must be kept in sync with
/// `wasmtime_environ::VMOffsets::vm_extern_data_ref_count()` which is
/// currently always zero.
ref_count: UnsafeCell<usize>,
ref_count: AtomicUsize,
/// Always points to the implicit, dynamically-sized `value` member that
/// precedes this `VMExternData`.
value_ptr: NonNull<dyn Any>,
value_ptr: NonNull<dyn Any + Send + Sync>,
}
impl Clone for VMExternRef {
@@ -199,13 +203,23 @@ impl Drop for VMExternRef {
#[inline]
fn drop(&mut self) {
let data = self.extern_data();
data.decrement_ref_count();
if data.get_ref_count() == 0 {
// Drop our live reference to `data` before we drop it itself.
drop(data);
unsafe {
VMExternData::drop_and_dealloc(self.0);
}
// Note that the memory orderings here also match the standard library
// itself. Documentation is more available in the implementation of
// `Arc`, but the general idea is that this is a special pattern allowed
// by the C standard with atomic orderings where we "release" for all
// the decrements and only the final decrementer performs an acquire
// fence. This properly ensures that the final thread, which actually
// destroys the data, sees all the updates from all other threads.
if data.ref_count.fetch_sub(1, Ordering::Release) != 1 {
return;
}
atomic::fence(Ordering::Acquire);
// Drop our live reference to `data` before we drop it itself.
drop(data);
unsafe {
VMExternData::drop_and_dealloc(self.0);
}
}
}
@@ -241,7 +255,7 @@ impl VMExternData {
// resides within after this block.
let (alloc_ptr, layout) = {
let data = data.as_mut();
debug_assert_eq!(data.get_ref_count(), 0);
debug_assert_eq!(data.ref_count.load(Ordering::SeqCst), 0);
// Same thing, but for the dropping the reference to `value` before
// we drop it itself.
@@ -260,25 +274,16 @@ impl VMExternData {
std::alloc::dealloc(alloc_ptr.as_ptr(), layout);
}
#[inline]
fn get_ref_count(&self) -> usize {
unsafe { *self.ref_count.get() }
}
#[inline]
fn increment_ref_count(&self) {
unsafe {
let count = self.ref_count.get();
*count += 1;
}
}
#[inline]
fn decrement_ref_count(&self) {
unsafe {
let count = self.ref_count.get();
*count -= 1;
}
// This is only using during cloning operations, and like the standard
// library we use `Relaxed` here. The rationale is better documented in
// libstd's implementation of `Arc`, but the general gist is that we're
// creating a new pointer for our own thread, so there's no need to have
// any synchronization with orderings. The synchronization with other
// threads with respect to orderings happens when the pointer is sent to
// another thread.
self.ref_count.fetch_add(1, Ordering::Relaxed);
}
}
@@ -293,7 +298,7 @@ impl VMExternRef {
/// Wrap the given value inside an `VMExternRef`.
pub fn new<T>(value: T) -> VMExternRef
where
T: 'static + Any,
T: 'static + Any + Send + Sync,
{
VMExternRef::new_with(|| value)
}
@@ -301,7 +306,7 @@ impl VMExternRef {
/// Construct a new `VMExternRef` in place by invoking `make_value`.
pub fn new_with<T>(make_value: impl FnOnce() -> T) -> VMExternRef
where
T: 'static + Any,
T: 'static + Any + Send + Sync,
{
unsafe {
let (layout, footer_offset) =
@@ -315,19 +320,14 @@ impl VMExternRef {
let value_ptr = alloc_ptr.cast::<T>();
ptr::write(value_ptr.as_ptr(), make_value());
let value_ref: &T = value_ptr.as_ref();
let value_ref: &dyn Any = value_ref as _;
let value_ptr: *const dyn Any = value_ref as _;
let value_ptr: *mut dyn Any = value_ptr as _;
let value_ptr = NonNull::new_unchecked(value_ptr);
let extern_data_ptr =
alloc_ptr.cast::<u8>().as_ptr().add(footer_offset) as *mut VMExternData;
ptr::write(
extern_data_ptr,
VMExternData {
ref_count: UnsafeCell::new(1),
value_ptr,
ref_count: AtomicUsize::new(1),
// Cast from `*mut T` to `*mut dyn Any` here.
value_ptr: NonNull::new_unchecked(value_ptr.as_ptr()),
},
);
@@ -395,8 +395,11 @@ impl VMExternRef {
}
/// Get the strong reference count for this `VMExternRef`.
///
/// Note that this loads with a `SeqCst` ordering to synchronize with other
/// threads.
pub fn strong_count(&self) -> usize {
self.extern_data().get_ref_count()
self.extern_data().ref_count.load(Ordering::SeqCst)
}
#[inline]
@@ -439,7 +442,7 @@ impl VMExternRef {
/// semantics, and so only pointers are compared, and doesn't use any `Cmp`
/// or `PartialCmp` implementation of the pointed-to values.
#[inline]
pub fn cmp(a: &Self, b: &Self) -> Ordering {
pub fn cmp(a: &Self, b: &Self) -> cmp::Ordering {
let a = a.0.as_ptr() as usize;
let b = b.0.as_ptr() as usize;
a.cmp(&b)
@@ -486,8 +489,42 @@ type TableElem = UnsafeCell<Option<VMExternRef>>;
///
/// Under the covers, this is a simple bump allocator that allows duplicate
/// entries. Deduplication happens at GC time.
#[repr(C)]
#[repr(C)] // `alloc` must be the first member, it's accessed from JIT code
pub struct VMExternRefActivationsTable {
/// Structures used to perform fast bump allocation of storage of externref
/// values.
///
/// This is the only member of this structure accessed from JIT code.
alloc: VMExternRefTableAlloc,
/// When unioned with `chunk`, this is an over-approximation of the GC roots
/// on the stack, inside Wasm frames.
///
/// This is used by slow-path insertion, and when a GC cycle finishes, is
/// re-initialized to the just-discovered precise set of stack roots (which
/// immediately becomes an over-approximation again as soon as Wasm runs and
/// potentially drops references).
over_approximated_stack_roots: HashSet<VMExternRefWithTraits>,
/// The precise set of on-stack, inside-Wasm GC roots that we discover via
/// walking the stack and interpreting stack maps.
///
/// This is *only* used inside the `gc` function, and is empty otherwise. It
/// is just part of this struct so that we can reuse the allocation, rather
/// than create a new hash set every GC.
precise_stack_roots: HashSet<VMExternRefWithTraits>,
/// A pointer to the youngest host stack frame before we called
/// into Wasm for the first time. When walking the stack in garbage
/// collection, if we don't find this frame, then we failed to walk every
/// Wasm stack frame, which means we failed to find all on-stack,
/// inside-a-Wasm-frame roots, and doing a GC could lead to freeing one of
/// those missed roots, and use after free.
stack_canary: Option<usize>,
}
#[repr(C)] // this is accessed from JTI code
struct VMExternRefTableAlloc {
/// Bump-allocation finger within the `chunk`.
///
/// NB: this is an `UnsafeCell` because it is written to by compiled Wasm
@@ -501,32 +538,21 @@ pub struct VMExternRefActivationsTable {
end: NonNull<TableElem>,
/// Bump allocation chunk that stores fast-path insertions.
///
/// This is not accessed from JIT code.
chunk: Box<[TableElem]>,
}
/// When unioned with `chunk`, this is an over-approximation of the GC roots
/// on the stack, inside Wasm frames.
///
/// This is used by slow-path insertion, and when a GC cycle finishes, is
/// re-initialized to the just-discovered precise set of stack roots (which
/// immediately becomes an over-approximation again as soon as Wasm runs and
/// potentially drops references).
over_approximated_stack_roots: RefCell<HashSet<VMExternRefWithTraits>>,
// This gets around the usage of `UnsafeCell` throughout the internals of this
// allocator, but the storage should all be Send/Sync and synchronization isn't
// necessary since operations require `&mut self`.
unsafe impl Send for VMExternRefTableAlloc {}
unsafe impl Sync for VMExternRefTableAlloc {}
/// The precise set of on-stack, inside-Wasm GC roots that we discover via
/// walking the stack and interpreting stack maps.
///
/// This is *only* used inside the `gc` function, and is empty otherwise. It
/// is just part of this struct so that we can reuse the allocation, rather
/// than create a new hash set every GC.
precise_stack_roots: RefCell<HashSet<VMExternRefWithTraits>>,
/// A pointer to the youngest host stack frame before we called
/// into Wasm for the first time. When walking the stack in garbage
/// collection, if we don't find this frame, then we failed to walk every
/// Wasm stack frame, which means we failed to find all on-stack,
/// inside-a-Wasm-frame roots, and doing a GC could lead to freeing one of
/// those missed roots, and use after free.
stack_canary: Cell<Option<usize>>,
fn _assert_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<VMExternRefActivationsTable>();
_assert::<VMExternRef>();
}
impl VMExternRefActivationsTable {
@@ -539,12 +565,14 @@ impl VMExternRefActivationsTable {
let end = unsafe { next.add(chunk.len()) };
VMExternRefActivationsTable {
next: UnsafeCell::new(NonNull::new(next).unwrap()),
end: NonNull::new(end).unwrap(),
chunk,
over_approximated_stack_roots: RefCell::new(HashSet::with_capacity(Self::CHUNK_SIZE)),
precise_stack_roots: RefCell::new(HashSet::with_capacity(Self::CHUNK_SIZE)),
stack_canary: Cell::new(None),
alloc: VMExternRefTableAlloc {
next: UnsafeCell::new(NonNull::new(next).unwrap()),
end: NonNull::new(end).unwrap(),
chunk,
},
over_approximated_stack_roots: HashSet::with_capacity(Self::CHUNK_SIZE),
precise_stack_roots: HashSet::with_capacity(Self::CHUNK_SIZE),
stack_canary: None,
}
}
@@ -563,10 +591,10 @@ impl VMExternRefActivationsTable {
/// `insert_slow_path` to infallibly insert the reference (potentially
/// allocating additional space in the table to hold it).
#[inline]
pub fn try_insert(&self, externref: VMExternRef) -> Result<(), VMExternRef> {
pub fn try_insert(&mut self, externref: VMExternRef) -> Result<(), VMExternRef> {
unsafe {
let next = *self.next.get();
if next == self.end {
let next = *self.alloc.next.get();
if next == self.alloc.end {
return Err(externref);
}
@@ -577,8 +605,8 @@ impl VMExternRefActivationsTable {
ptr::write(next.as_ptr(), UnsafeCell::new(Some(externref)));
let next = NonNull::new_unchecked(next.as_ptr().add(1));
debug_assert!(next <= self.end);
*self.next.get() = next;
debug_assert!(next <= self.alloc.end);
*self.alloc.next.get() = next;
Ok(())
}
@@ -592,7 +620,7 @@ impl VMExternRefActivationsTable {
/// The same as `gc`.
#[inline]
pub unsafe fn insert_with_gc(
&self,
&mut self,
externref: VMExternRef,
module_info_lookup: &dyn ModuleInfoLookup,
) {
@@ -603,7 +631,7 @@ impl VMExternRefActivationsTable {
#[inline(never)]
unsafe fn gc_and_insert_slow(
&self,
&mut self,
externref: VMExternRef,
module_info_lookup: &dyn ModuleInfoLookup,
) {
@@ -612,27 +640,26 @@ impl VMExternRefActivationsTable {
// Might as well insert right into the hash set, rather than the bump
// chunk, since we are already on a slow path and we get de-duplication
// this way.
let mut roots = self.over_approximated_stack_roots.borrow_mut();
roots.insert(VMExternRefWithTraits(externref));
self.over_approximated_stack_roots
.insert(VMExternRefWithTraits(externref));
}
fn num_filled_in_bump_chunk(&self) -> usize {
let next = unsafe { *self.next.get() };
let bytes_unused = (self.end.as_ptr() as usize) - (next.as_ptr() as usize);
let next = unsafe { *self.alloc.next.get() };
let bytes_unused = (self.alloc.end.as_ptr() as usize) - (next.as_ptr() as usize);
let slots_unused = bytes_unused / mem::size_of::<TableElem>();
self.chunk.len().saturating_sub(slots_unused)
self.alloc.chunk.len().saturating_sub(slots_unused)
}
fn elements(&self, mut f: impl FnMut(&VMExternRef)) {
let roots = self.over_approximated_stack_roots.borrow();
for elem in roots.iter() {
for elem in self.over_approximated_stack_roots.iter() {
f(&elem.0);
}
// The bump chunk is not all the way full, so we only iterate over its
// filled-in slots.
let num_filled = self.num_filled_in_bump_chunk();
for slot in self.chunk.iter().take(num_filled) {
for slot in self.alloc.chunk.iter().take(num_filled) {
if let Some(elem) = unsafe { &*slot.get() } {
f(elem);
}
@@ -649,36 +676,20 @@ impl VMExternRefActivationsTable {
/// Sweep the bump allocation table after we've discovered our precise stack
/// roots.
fn sweep(&self, precise_stack_roots: &mut HashSet<VMExternRefWithTraits>) {
// Swap out the over-approximated set so we can distinguish between the
// over-approximation before we started sweeping, and any new elements
// we might insert into the table because of re-entering Wasm via an
// `externref`'s destructor. The new elements must be kept alive for
// memory safety, but we keep this set around because we likely want to
// reuse its allocation/capacity for the new `precise_stack_roots` in
// the next GC cycle.
let mut old_over_approximated = mem::replace(
&mut *self.over_approximated_stack_roots.borrow_mut(),
Default::default(),
);
fn sweep(&mut self) {
// Sweep our bump chunk.
//
// Just in case an `externref` destructor calls back into Wasm, passing
// more `externref`s into that Wasm, which requires the `externref`s to
// be inserted into this `VMExternRefActivationsTable`, make sure `next
// == end` so that they go into the over-approximation hash set.
let num_filled = self.num_filled_in_bump_chunk();
unsafe {
*self.next.get() = self.end;
*self.alloc.next.get() = self.alloc.end;
}
for slot in self.chunk.iter().take(num_filled) {
for slot in self.alloc.chunk.iter().take(num_filled) {
unsafe {
*slot.get() = None;
}
}
debug_assert!(
self.chunk
self.alloc
.chunk
.iter()
.all(|slot| unsafe { (*slot.get()).as_ref().is_none() }),
"after sweeping the bump chunk, all slots should be `None`"
@@ -686,33 +697,25 @@ impl VMExternRefActivationsTable {
// Reset our `next` finger to the start of the bump allocation chunk.
unsafe {
let next = self.chunk.as_ptr() as *mut TableElem;
let next = self.alloc.chunk.as_ptr() as *mut TableElem;
debug_assert!(!next.is_null());
*self.next.get() = NonNull::new_unchecked(next);
*self.alloc.next.get() = NonNull::new_unchecked(next);
}
// The current `precise_stack_roots` becomes our new over-appoximated
// set for the next GC cycle.
let mut over_approximated = self.over_approximated_stack_roots.borrow_mut();
mem::swap(&mut *precise_stack_roots, &mut *over_approximated);
mem::swap(
&mut self.precise_stack_roots,
&mut self.over_approximated_stack_roots,
);
// And finally, the new `precise_stack_roots` should be cleared and
// remain empty until the next GC cycle.
//
// However, if an `externref` destructor called re-entered Wasm with
// more `externref`s, then the temp over-approximated set we were using
// during sweeping (now `precise_stack_roots`) is not empty, and we need
// to keep its references alive in our new over-approximated set.
over_approximated.extend(precise_stack_roots.drain());
// If we didn't re-enter Wasm during destructors (likely),
// `precise_stack_roots` has zero capacity, and the old
// over-approximated has a bunch of capacity. Reuse whichever set has
// most capacity.
if old_over_approximated.capacity() > precise_stack_roots.capacity() {
old_over_approximated.clear();
*precise_stack_roots = old_over_approximated;
}
// Note that this may run arbitrary code as we run externref
// destructors. Because of our `&mut` borrow above on this table,
// though, we're guaranteed that nothing will touch this table.
self.precise_stack_roots.clear();
}
/// Fetches the current value of this table's stack canary.
@@ -724,7 +727,7 @@ impl VMExternRefActivationsTable {
/// For more information on canaries see the gc functions below.
#[inline]
pub fn stack_canary(&self) -> Option<usize> {
self.stack_canary.get()
self.stack_canary
}
/// Sets the current value of the stack canary.
@@ -736,14 +739,14 @@ impl VMExternRefActivationsTable {
///
/// For more information on canaries see the gc functions below.
#[inline]
pub fn set_stack_canary(&self, canary: Option<usize>) {
self.stack_canary.set(canary);
pub fn set_stack_canary(&mut self, canary: Option<usize>) {
self.stack_canary = canary;
}
}
/// Used by the runtime to lookup information about a module given a
/// program counter value.
pub trait ModuleInfoLookup: 'static {
pub trait ModuleInfoLookup {
/// Lookup the module information from a program counter value.
fn lookup(&self, pc: usize) -> Option<Arc<dyn ModuleInfo>>;
}
@@ -754,16 +757,6 @@ pub trait ModuleInfo {
fn lookup_stack_map(&self, pc: usize) -> Option<&StackMap>;
}
pub(crate) struct EmptyModuleInfoLookup;
impl ModuleInfoLookup for EmptyModuleInfoLookup {
fn lookup(&self, _pc: usize) -> Option<Arc<dyn ModuleInfo>> {
None
}
}
pub(crate) const EMPTY_MODULE_LOOKUP: EmptyModuleInfoLookup = EmptyModuleInfoLookup;
#[derive(Debug, Default)]
struct DebugOnly<T> {
inner: T,
@@ -810,22 +803,8 @@ impl<T> std::ops::DerefMut for DebugOnly<T> {
/// that has frames on the stack with the given `stack_maps_registry`.
pub unsafe fn gc(
module_info_lookup: &dyn ModuleInfoLookup,
externref_activations_table: &VMExternRefActivationsTable,
externref_activations_table: &mut VMExternRefActivationsTable,
) {
// We borrow the precise stack roots `RefCell` for the whole duration of
// GC. Whether it is dynamically borrowed serves as a flag for detecting
// re-entrancy into GC. Re-entrancy can occur if we do a GC, drop an
// `externref`, and that `externref`'s destructor then triggers another
// GC. Whenever we detect re-entrancy, we return and give the first,
// outermost GC call priority.
let mut precise_stack_roots = match externref_activations_table
.precise_stack_roots
.try_borrow_mut()
{
Err(_) => return,
Ok(roots) => roots,
};
log::debug!("start GC");
debug_assert!({
@@ -834,7 +813,7 @@ pub unsafe fn gc(
// into the activations table's bump-allocated space at the
// end. Therefore, it should always be empty upon entering this
// function.
precise_stack_roots.is_empty()
externref_activations_table.precise_stack_roots.is_empty()
});
// Whenever we call into Wasm from host code for the first time, we set a
@@ -842,7 +821,7 @@ pub unsafe fn gc(
// canary. If there is *not* a stack canary, then there must be zero Wasm
// frames on the stack. Therefore, we can simply reset the table without
// walking the stack.
let stack_canary = match externref_activations_table.stack_canary.get() {
let stack_canary = match externref_activations_table.stack_canary {
None => {
if cfg!(debug_assertions) {
// Assert that there aren't any Wasm frames on the stack.
@@ -851,7 +830,7 @@ pub unsafe fn gc(
true
});
}
externref_activations_table.sweep(&mut precise_stack_roots);
externref_activations_table.sweep();
log::debug!("end GC");
return;
}
@@ -911,7 +890,7 @@ pub unsafe fn gc(
);
if let Some(r) = NonNull::new(r) {
VMExternRefActivationsTable::insert_precise_stack_root(
&mut precise_stack_roots,
&mut externref_activations_table.precise_stack_roots,
r,
);
}
@@ -941,10 +920,10 @@ pub unsafe fn gc(
// would free those missing roots while they are still in use, leading to
// use-after-free.
if found_canary {
externref_activations_table.sweep(&mut precise_stack_roots);
externref_activations_table.sweep();
} else {
log::warn!("did not find stack canary; skipping GC sweep");
precise_stack_roots.clear();
externref_activations_table.precise_stack_roots.clear();
}
log::debug!("end GC");
@@ -972,12 +951,12 @@ mod tests {
#[test]
fn ref_count_is_at_correct_offset() {
let s = "hi";
let s: &dyn Any = &s as _;
let s: *const dyn Any = s as _;
let s: *mut dyn Any = s as _;
let s: &(dyn Any + Send + Sync) = &s as _;
let s: *const (dyn Any + Send + Sync) = s as _;
let s: *mut (dyn Any + Send + Sync) = s as _;
let extern_data = VMExternData {
ref_count: UnsafeCell::new(0),
ref_count: AtomicUsize::new(0),
value_ptr: NonNull::new(s).unwrap(),
};
@@ -997,7 +976,7 @@ mod tests {
let table = VMExternRefActivationsTable::new();
let table_ptr = &table as *const _;
let next_ptr = &table.next as *const _;
let next_ptr = &table.alloc.next as *const _;
let actual_offset = (next_ptr as usize) - (table_ptr as usize);
@@ -1024,7 +1003,7 @@ mod tests {
let table = VMExternRefActivationsTable::new();
let table_ptr = &table as *const _;
let end_ptr = &table.end as *const _;
let end_ptr = &table.alloc.end as *const _;
let actual_offset = (end_ptr as usize) - (table_ptr as usize);

View File

@@ -16,14 +16,15 @@
int RegisterSetjmp(
void **buf_storage,
void (*body)(void*),
void *payload) {
void (*body)(void*, void*),
void *payload,
void *callee) {
platform_jmp_buf buf;
if (platform_setjmp(buf) != 0) {
return 0;
}
*buf_storage = &buf;
body(payload);
body(payload, callee);
return 1;
}

View File

@@ -3,7 +3,7 @@
//! `InstanceHandle` is a reference-counting handle for an `Instance`.
use crate::export::Export;
use crate::externref::{ModuleInfoLookup, VMExternRefActivationsTable};
use crate::externref::VMExternRefActivationsTable;
use crate::memory::{Memory, RuntimeMemoryCreator};
use crate::table::{Table, TableElement};
use crate::traphandlers::Trap;
@@ -12,24 +12,21 @@ use crate::vmcontext::{
VMGlobalDefinition, VMGlobalImport, VMInterrupts, VMMemoryDefinition, VMMemoryImport,
VMSharedSignatureIndex, VMTableDefinition, VMTableImport,
};
use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable};
use indexmap::IndexMap;
use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable, Store};
use memoffset::offset_of;
use more_asserts::assert_lt;
use std::alloc::Layout;
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::hash::Hash;
use std::ptr::NonNull;
use std::rc::Rc;
use std::sync::Arc;
use std::{mem, ptr, slice};
use wasmtime_environ::entity::{packed_option::ReservedValue, EntityRef, EntitySet, PrimaryMap};
use wasmtime_environ::wasm::{
DataIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, ElemIndex, EntityIndex,
FuncIndex, GlobalIndex, MemoryIndex, TableElementType, TableIndex,
FuncIndex, GlobalIndex, MemoryIndex, TableElementType, TableIndex, WasmType,
};
use wasmtime_environ::{ir, Module, VMOffsets};
@@ -41,7 +38,7 @@ pub use allocator::*;
///
/// An instance can be created with a resource limiter so that hosts can take into account
/// non-WebAssembly resource usage to determine if a linear memory or table should grow.
pub trait ResourceLimiter {
pub trait ResourceLimiter: Send + Sync + 'static {
/// Notifies the resource limiter that an instance's linear memory has been requested to grow.
///
/// * `current` is the current size of the linear memory in WebAssembly page units.
@@ -53,7 +50,7 @@ pub trait ResourceLimiter {
/// This function should return `true` to indicate that the growing operation is permitted or
/// `false` if not permitted. Returning `true` when a maximum has been exceeded will have no
/// effect as the linear memory will not grow.
fn memory_growing(&self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
fn memory_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
/// Notifies the resource limiter that an instance's table has been requested to grow.
///
@@ -65,7 +62,7 @@ pub trait ResourceLimiter {
/// This function should return `true` to indicate that the growing operation is permitted or
/// `false` if not permitted. Returning `true` when a maximum has been exceeded will have no
/// effect as the table will not grow.
fn table_growing(&self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool;
/// The maximum number of instances that can be created for a `Store`.
///
@@ -83,10 +80,6 @@ pub trait ResourceLimiter {
fn memories(&self) -> usize;
}
/// Runtime representation of an instance value, which erases all `Instance`
/// information since instances are just a collection of values.
pub type RuntimeInstance = Rc<IndexMap<String, Export>>;
/// A WebAssembly instance.
///
/// This is repr(C) to ensure that the vmctx field is last.
@@ -106,14 +99,14 @@ pub(crate) struct Instance {
/// Stores the dropped passive element segments in this instantiation by index.
/// If the index is present in the set, the segment has been dropped.
dropped_elements: RefCell<EntitySet<ElemIndex>>,
dropped_elements: EntitySet<ElemIndex>,
/// Stores the dropped passive data segments in this instantiation by index.
/// If the index is present in the set, the segment has been dropped.
dropped_data: RefCell<EntitySet<DataIndex>>,
dropped_data: EntitySet<DataIndex>,
/// Hosts can store arbitrary per-instance information here.
host_state: Box<dyn Any>,
host_state: Box<dyn Any + Send + Sync>,
/// Additional context used by compiled wasm code. This field is last, and
/// represents a dynamically-sized array that extends beyond the nominal
@@ -242,16 +235,8 @@ impl Instance {
}
/// Return the indexed `VMGlobalDefinition`.
fn global(&self, index: DefinedGlobalIndex) -> VMGlobalDefinition {
unsafe { *self.global_ptr(index) }
}
/// Set the indexed global to `VMGlobalDefinition`.
#[allow(dead_code)]
fn set_global(&self, index: DefinedGlobalIndex, global: VMGlobalDefinition) {
unsafe {
*self.global_ptr(index) = global;
}
fn global(&self, index: DefinedGlobalIndex) -> &VMGlobalDefinition {
unsafe { &*self.global_ptr(index) }
}
/// Return the indexed `VMGlobalDefinition`.
@@ -295,17 +280,35 @@ impl Instance {
unsafe { self.vmctx_plus_offset(self.offsets.vmctx_externref_activations_table()) }
}
/// Return a pointer to the `ModuleInfoLookup`.
pub fn module_info_lookup(&self) -> *mut *const dyn ModuleInfoLookup {
unsafe { self.vmctx_plus_offset(self.offsets.vmctx_module_info_lookup()) }
/// Gets a pointer to this instance's `Store` which was originally
/// configured on creation.
///
/// # Panics
///
/// This will panic if the originally configured store was `None`. That can
/// happen for host functions so host functions can't be queried what their
/// original `Store` was since it's just retained as null (since host
/// functions are shared amongst threads and don't all share the same
/// store).
#[inline]
pub fn store(&self) -> *mut dyn Store {
let ptr = unsafe { *self.vmctx_plus_offset::<*mut dyn Store>(self.offsets.vmctx_store()) };
assert!(!ptr.is_null());
ptr
}
pub unsafe fn set_store(&mut self, store: *mut dyn Store) {
*self.vmctx_plus_offset(self.offsets.vmctx_store()) = store;
}
/// Return a reference to the vmctx used by compiled wasm code.
#[inline]
pub fn vmctx(&self) -> &VMContext {
&self.vmctx
}
/// Return a raw pointer to the vmctx used by compiled wasm code.
#[inline]
pub fn vmctx_ptr(&self) -> *mut VMContext {
self.vmctx() as *const VMContext as *mut VMContext
}
@@ -423,13 +426,18 @@ impl Instance {
///
/// Returns `None` if memory can't be grown by the specified amount
/// of pages.
pub(crate) fn memory_grow(&self, memory_index: DefinedMemoryIndex, delta: u32) -> Option<u32> {
pub(crate) fn memory_grow(
&mut self,
memory_index: DefinedMemoryIndex,
delta: u32,
) -> Option<u32> {
let limiter = unsafe { (*self.store()).limiter() };
let memory = self
.memories
.get(memory_index)
.get_mut(memory_index)
.unwrap_or_else(|| panic!("no memory for index {}", memory_index.index()));
let result = unsafe { memory.grow(delta) };
let result = unsafe { memory.grow(delta, limiter) };
// Keep current the VMContext pointers used by compiled wasm code.
self.set_memory(memory_index, self.memories[memory_index].vmmemory());
@@ -446,12 +454,12 @@ impl Instance {
/// This and `imported_memory_size` are currently unsafe because they
/// dereference the memory import's pointers.
pub(crate) unsafe fn imported_memory_grow(
&self,
&mut self,
memory_index: MemoryIndex,
delta: u32,
) -> Option<u32> {
let import = self.imported_memory(memory_index);
let foreign_instance = (&*import.vmctx).instance();
let foreign_instance = (*import.vmctx).instance_mut();
let foreign_memory = &*import.from;
let foreign_index = foreign_instance.memory_index(foreign_memory);
@@ -480,9 +488,8 @@ impl Instance {
foreign_instance.memory_size(foreign_index)
}
pub(crate) fn table_element_type(&self, table_index: TableIndex) -> TableElementType {
let table = self.get_table(table_index);
table.element_type()
pub(crate) fn table_element_type(&mut self, table_index: TableIndex) -> TableElementType {
unsafe { (*self.get_table(table_index)).element_type() }
}
/// Grow table by the specified amount of elements, filling them with
@@ -491,7 +498,7 @@ impl Instance {
/// Returns `None` if table can't be grown by the specified amount of
/// elements, or if `init_value` is the wrong type of table element.
pub(crate) fn table_grow(
&self,
&mut self,
table_index: TableIndex,
delta: u32,
init_value: TableElement,
@@ -502,17 +509,18 @@ impl Instance {
}
fn defined_table_grow(
&self,
&mut self,
table_index: DefinedTableIndex,
delta: u32,
init_value: TableElement,
) -> Option<u32> {
let limiter = unsafe { (*self.store()).limiter() };
let table = self
.tables
.get(table_index)
.get_mut(table_index)
.unwrap_or_else(|| panic!("no table for index {}", table_index.index()));
let result = unsafe { table.grow(delta, init_value) };
let result = unsafe { table.grow(delta, init_value, limiter) };
// Keep the `VMContext` pointers used by compiled Wasm code up to
// date.
@@ -521,36 +529,6 @@ impl Instance {
result
}
pub(crate) fn defined_table_fill(
&self,
table_index: DefinedTableIndex,
dst: u32,
val: TableElement,
len: u32,
) -> Result<(), Trap> {
self.tables.get(table_index).unwrap().fill(dst, val, len)
}
// Get table element by index.
fn table_get(&self, table_index: DefinedTableIndex, index: u32) -> Option<TableElement> {
self.tables
.get(table_index)
.unwrap_or_else(|| panic!("no table for index {}", table_index.index()))
.get(index)
}
fn table_set(
&self,
table_index: DefinedTableIndex,
index: u32,
val: TableElement,
) -> Result<(), ()> {
self.tables
.get(table_index)
.unwrap_or_else(|| panic!("no table for index {}", table_index.index()))
.set(index, val)
}
fn alloc_layout(&self) -> Layout {
let size = mem::size_of_val(self)
.checked_add(usize::try_from(self.offsets.size_of_vmctx()).unwrap())
@@ -584,14 +562,14 @@ impl Instance {
index: I,
index_map: &HashMap<I, usize>,
data: &'a Vec<D>,
dropped: &RefCell<EntitySet<I>>,
dropped: &EntitySet<I>,
) -> &'a [T]
where
D: AsRef<[T]>,
I: EntityRef + Hash,
{
match index_map.get(&index) {
Some(index) if !dropped.borrow().contains(I::new(*index)) => data[*index].as_ref(),
Some(index) if !dropped.contains(I::new(*index)) => data[*index].as_ref(),
_ => &[],
}
}
@@ -604,24 +582,28 @@ impl Instance {
/// Returns a `Trap` error when the range within the table is out of bounds
/// or the range within the passive element is out of bounds.
pub(crate) fn table_init(
&self,
&mut self,
table_index: TableIndex,
elem_index: ElemIndex,
dst: u32,
src: u32,
len: u32,
) -> Result<(), Trap> {
// TODO: this `clone()` shouldn't be necessary but is used for now to
// inform `rustc` that the lifetime of the elements here are
// disconnected from the lifetime of `self`.
let module = self.module.clone();
let elements = Self::find_passive_segment(
elem_index,
&self.module.passive_elements_map,
&self.module.passive_elements,
&module.passive_elements_map,
&module.passive_elements,
&self.dropped_elements,
);
self.table_init_segment(table_index, elements, dst, src, len)
}
pub(crate) fn table_init_segment(
&self,
&mut self,
table_index: TableIndex,
elements: &[FuncIndex],
dst: u32,
@@ -630,7 +612,7 @@ impl Instance {
) -> Result<(), Trap> {
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init
let table = self.get_table(table_index);
let table = unsafe { &mut *self.get_table(table_index) };
let elements = match elements
.get(usize::try_from(src).unwrap()..)
@@ -665,19 +647,22 @@ impl Instance {
}
/// Drop an element.
pub(crate) fn elem_drop(&self, elem_index: ElemIndex) {
pub(crate) fn elem_drop(&mut self, elem_index: ElemIndex) {
// https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-elem-drop
if let Some(index) = self.module.passive_elements_map.get(&elem_index) {
self.dropped_elements
.borrow_mut()
.insert(ElemIndex::new(*index));
self.dropped_elements.insert(ElemIndex::new(*index));
}
// Note that we don't check that we actually removed a segment because
// dropping a non-passive segment is a no-op (not a trap).
}
/// Get a locally-defined memory.
pub(crate) fn get_defined_memory(&mut self, index: DefinedMemoryIndex) -> *mut Memory {
ptr::addr_of_mut!(self.memories[index])
}
/// Do a `memory.copy`
///
/// # Errors
@@ -685,7 +670,7 @@ impl Instance {
/// Returns a `Trap` error when the source or destination ranges are out of
/// bounds.
pub(crate) fn memory_copy(
&self,
&mut self,
dst_index: MemoryIndex,
dst: u32,
src_index: MemoryIndex,
@@ -784,24 +769,28 @@ impl Instance {
/// memory's bounds or if the source range is outside the data segment's
/// bounds.
pub(crate) fn memory_init(
&self,
&mut self,
memory_index: MemoryIndex,
data_index: DataIndex,
dst: u32,
src: u32,
len: u32,
) -> Result<(), Trap> {
// TODO: this `clone()` shouldn't be necessary but is used for now to
// inform `rustc` that the lifetime of the elements here are
// disconnected from the lifetime of `self`.
let module = self.module.clone();
let data = Self::find_passive_segment(
data_index,
&self.module.passive_data_map,
&self.module.passive_data,
&module.passive_data_map,
&module.passive_data,
&self.dropped_data,
);
self.memory_init_segment(memory_index, &data, dst, src, len)
}
pub(crate) fn memory_init_segment(
&self,
&mut self,
memory_index: MemoryIndex,
data: &[u8],
dst: u32,
@@ -834,11 +823,9 @@ impl Instance {
}
/// Drop the given data segment, truncating its length to zero.
pub(crate) fn data_drop(&self, data_index: DataIndex) {
pub(crate) fn data_drop(&mut self, data_index: DataIndex) {
if let Some(index) = self.module.passive_data_map.get(&data_index) {
self.dropped_data
.borrow_mut()
.insert(DataIndex::new(*index));
self.dropped_data.insert(DataIndex::new(*index));
}
// Note that we don't check that we actually removed a segment because
@@ -847,7 +834,7 @@ impl Instance {
/// Get a table by index regardless of whether it is locally-defined or an
/// imported, foreign table.
pub(crate) fn get_table(&self, table_index: TableIndex) -> &Table {
pub(crate) fn get_table(&mut self, table_index: TableIndex) -> *mut Table {
if let Some(defined_table_index) = self.module.defined_table_index(table_index) {
self.get_defined_table(defined_table_index)
} else {
@@ -856,33 +843,56 @@ impl Instance {
}
/// Get a locally-defined table.
pub(crate) fn get_defined_table(&self, index: DefinedTableIndex) -> &Table {
&self.tables[index]
pub(crate) fn get_defined_table(&mut self, index: DefinedTableIndex) -> *mut Table {
ptr::addr_of_mut!(self.tables[index])
}
/// Get an imported, foreign table.
pub(crate) fn get_foreign_table(&self, index: TableIndex) -> &Table {
pub(crate) fn get_foreign_table(&mut self, index: TableIndex) -> *mut Table {
let import = self.imported_table(index);
let foreign_instance = unsafe { (&mut *(import).vmctx).instance() };
let foreign_table = unsafe { &mut *(import).from };
let foreign_instance = unsafe { (*import.vmctx).instance_mut() };
let foreign_table = unsafe { &*import.from };
let foreign_index = foreign_instance.table_index(foreign_table);
&foreign_instance.tables[foreign_index]
ptr::addr_of_mut!(foreign_instance.tables[foreign_index])
}
pub(crate) fn get_defined_table_index_and_instance(
&self,
&mut self,
index: TableIndex,
) -> (DefinedTableIndex, &Instance) {
) -> (DefinedTableIndex, &mut Instance) {
if let Some(defined_table_index) = self.module.defined_table_index(index) {
(defined_table_index, self)
} else {
let import = self.imported_table(index);
let foreign_instance = unsafe { (&mut *(import).vmctx).instance() };
let foreign_table_def = unsafe { &mut *(import).from };
let foreign_instance = unsafe { (*import.vmctx).instance_mut() };
let foreign_table_def = unsafe { &*import.from };
let foreign_table_index = foreign_instance.table_index(foreign_table_def);
(foreign_table_index, foreign_instance)
}
}
fn drop_globals(&mut self) {
for (idx, global) in self.module.globals.iter() {
let idx = match self.module.defined_global_index(idx) {
Some(idx) => idx,
None => continue,
};
match global.wasm_ty {
// For now only externref gloabls need to get destroyed
WasmType::ExternRef => {}
_ => continue,
}
unsafe {
drop((*self.global_ptr(idx)).as_externref_mut().take());
}
}
}
}
impl Drop for Instance {
fn drop(&mut self) {
self.drop_globals();
}
}
/// A handle holding an `Instance` of a WebAssembly module.
@@ -891,6 +901,16 @@ pub struct InstanceHandle {
instance: *mut Instance,
}
// These are only valid if the `Instance` type is send/sync, hence the
// assertion below.
unsafe impl Send for InstanceHandle {}
unsafe impl Sync for InstanceHandle {}
fn _assert_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<Instance>();
}
impl InstanceHandle {
/// Create a new `InstanceHandle` pointing at the instance
/// pointed to by the given `VMContext` pointer.
@@ -898,6 +918,7 @@ impl InstanceHandle {
/// # Safety
/// This is unsafe because it doesn't work on just any `VMContext`, it must
/// be a `VMContext` allocated as part of an `Instance`.
#[inline]
pub unsafe fn from_vmctx(vmctx: *mut VMContext) -> Self {
let instance = (&mut *vmctx).instance();
Self {
@@ -911,6 +932,7 @@ impl InstanceHandle {
}
/// Return a raw pointer to the vmctx used by compiled wasm code.
#[inline]
pub fn vmctx_ptr(&self) -> *mut VMContext {
self.instance().vmctx_ptr()
}
@@ -944,12 +966,9 @@ impl InstanceHandle {
self.instance().memory_index(memory)
}
/// Grow memory in this instance by the specified amount of pages.
///
/// Returns `None` if memory can't be grown by the specified amount
/// of pages.
pub fn memory_grow(&self, memory_index: DefinedMemoryIndex, delta: u32) -> Option<u32> {
self.instance().memory_grow(memory_index, delta)
/// Get a memory defined locally within this module.
pub fn get_defined_memory(&mut self, index: DefinedMemoryIndex) -> *mut Memory {
self.instance_mut().get_defined_memory(index)
}
/// Return the table index for the given `VMTableDefinition` in this instance.
@@ -957,83 +976,35 @@ impl InstanceHandle {
self.instance().table_index(table)
}
/// Grow table in this instance by the specified amount of elements.
///
/// When the table is successfully grown, returns the original size of the
/// table.
///
/// Returns `None` if memory can't be grown by the specified amount of pages
/// or if the `init_value` is the incorrect table element type.
pub fn table_grow(
&self,
table_index: TableIndex,
delta: u32,
init_value: TableElement,
) -> Option<u32> {
self.instance().table_grow(table_index, delta, init_value)
}
/// Grow table in this instance by the specified amount of elements.
///
/// When the table is successfully grown, returns the original size of the
/// table.
///
/// Returns `None` if memory can't be grown by the specified amount of pages
/// or if the `init_value` is the incorrect table element type.
pub fn defined_table_grow(
&self,
table_index: DefinedTableIndex,
delta: u32,
init_value: TableElement,
) -> Option<u32> {
self.instance()
.defined_table_grow(table_index, delta, init_value)
}
/// Get table element reference.
///
/// Returns `None` if index is out of bounds.
pub fn table_get(&self, table_index: DefinedTableIndex, index: u32) -> Option<TableElement> {
self.instance().table_get(table_index, index)
}
/// Set table element reference.
///
/// Returns an error if the index is out of bounds
pub fn table_set(
&self,
table_index: DefinedTableIndex,
index: u32,
val: TableElement,
) -> Result<(), ()> {
self.instance().table_set(table_index, index, val)
}
/// Fill a region of the table.
///
/// Returns an error if the region is out of bounds or val is not of the
/// correct type.
pub fn defined_table_fill(
&self,
table_index: DefinedTableIndex,
dst: u32,
val: TableElement,
len: u32,
) -> Result<(), Trap> {
self.instance()
.defined_table_fill(table_index, dst, val, len)
}
/// Get a table defined locally within this module.
pub fn get_defined_table(&self, index: DefinedTableIndex) -> &Table {
self.instance().get_defined_table(index)
pub fn get_defined_table(&mut self, index: DefinedTableIndex) -> *mut Table {
self.instance_mut().get_defined_table(index)
}
/// Return a reference to the contained `Instance`.
#[inline]
pub(crate) fn instance(&self) -> &Instance {
unsafe { &*(self.instance as *const Instance) }
}
pub(crate) fn instance_mut(&mut self) -> &mut Instance {
unsafe { &mut *self.instance }
}
/// Returns the `Store` pointer that was stored on creation
#[inline]
pub fn store(&self) -> *mut dyn Store {
self.instance().store()
}
/// Configure the `*mut dyn Store` internal pointer after-the-fact.
///
/// This is provided for the original `Store` itself to configure the first
/// self-pointer after the original `Box` has been initialized.
pub unsafe fn set_store(&mut self, store: *mut dyn Store) {
self.instance_mut().set_store(store);
}
/// Returns a clone of this instance.
///
/// This is unsafe because the returned handle here is just a cheap clone

View File

@@ -1,4 +1,3 @@
use crate::externref::{ModuleInfoLookup, VMExternRefActivationsTable, EMPTY_MODULE_LOOKUP};
use crate::imports::Imports;
use crate::instance::{Instance, InstanceHandle, ResourceLimiter, RuntimeMemoryCreator};
use crate::memory::{DefaultMemoryCreator, Memory};
@@ -6,16 +5,15 @@ use crate::table::Table;
use crate::traphandlers::Trap;
use crate::vmcontext::{
VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport,
VMGlobalDefinition, VMGlobalImport, VMInterrupts, VMMemoryImport, VMSharedSignatureIndex,
VMTableImport,
VMGlobalDefinition, VMGlobalImport, VMMemoryImport, VMSharedSignatureIndex, VMTableImport,
};
use crate::Store;
use anyhow::Result;
use std::alloc;
use std::any::Any;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::marker;
use std::ptr::{self, NonNull};
use std::rc::Rc;
use std::slice;
use std::sync::Arc;
use thiserror::Error;
@@ -49,19 +47,23 @@ pub struct InstanceAllocationRequest<'a> {
pub shared_signatures: SharedSignatures<'a>,
/// The host state to associate with the instance.
pub host_state: Box<dyn Any>,
pub host_state: Box<dyn Any + Send + Sync>,
/// The pointer to the VM interrupts structure to use for the instance.
pub interrupts: *const VMInterrupts,
/// The pointer to the reference activations table to use for the instance.
pub externref_activations_table: *mut VMExternRefActivationsTable,
/// The pointer to the module info lookup to use for the instance.
pub module_info_lookup: Option<*const dyn ModuleInfoLookup>,
/// The resource limiter to use for the instance.
pub limiter: Option<&'a Rc<dyn ResourceLimiter>>,
/// A pointer to the "store" for this instance to be allocated. The store
/// correlates with the `Store` in wasmtime itself, and lots of contextual
/// information about the execution of wasm can be learned through the store.
///
/// Note that this is a raw pointer and has a static lifetime, both of which
/// are a bit of a lie. This is done purely so a store can learn about
/// itself when it gets called as a host function, and additionally so this
/// runtime can access internals as necessary (such as the
/// VMExternRefActivationsTable or the ResourceLimiter).
///
/// Note that this ends up being a self-pointer to the instance when stored.
/// The reason is that the instance itself is then stored within the store.
/// We use a number of `PhantomPinned` declarations to indicate this to the
/// compiler. More info on this in `wasmtime/src/store.rs`
pub store: Option<*mut dyn Store>,
}
/// An link error while instantiating a module.
@@ -141,7 +143,8 @@ pub unsafe trait InstanceAllocator: Send + Sync {
/// This method is only safe to call immediately after an instance has been allocated.
unsafe fn initialize(
&self,
handle: &InstanceHandle,
handle: &mut InstanceHandle,
module: &Module,
is_bulk_memory: bool,
) -> Result<(), InstantiationError>;
@@ -232,9 +235,12 @@ fn get_table_init_start(
}
}
fn check_table_init_bounds(instance: &Instance) -> Result<(), InstantiationError> {
for init in &instance.module.table_initializers {
let table = instance.get_table(init.table_index);
fn check_table_init_bounds(
instance: &mut Instance,
module: &Module,
) -> Result<(), InstantiationError> {
for init in &module.table_initializers {
let table = unsafe { &*instance.get_table(init.table_index) };
let start = get_table_init_start(init, instance)?;
let start = usize::try_from(start).unwrap();
let end = start.checked_add(init.elements.len());
@@ -254,8 +260,8 @@ fn check_table_init_bounds(instance: &Instance) -> Result<(), InstantiationError
Ok(())
}
fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> {
for init in &instance.module.table_initializers {
fn initialize_tables(instance: &mut Instance, module: &Module) -> Result<(), InstantiationError> {
for init in &module.table_initializers {
instance
.table_init_segment(
init.table_index,
@@ -318,7 +324,7 @@ fn check_memory_init_bounds(
}
fn initialize_memories(
instance: &Instance,
instance: &mut Instance,
initializers: &[MemoryInitializer],
) -> Result<(), InstantiationError> {
for init in initializers {
@@ -336,8 +342,8 @@ fn initialize_memories(
Ok(())
}
fn check_init_bounds(instance: &Instance) -> Result<(), InstantiationError> {
check_table_init_bounds(instance)?;
fn check_init_bounds(instance: &mut Instance, module: &Module) -> Result<(), InstantiationError> {
check_table_init_bounds(instance, module)?;
match &instance.module.memory_initialization {
MemoryInitialization::Paged { out_of_bounds, .. } => {
@@ -356,7 +362,8 @@ fn check_init_bounds(instance: &Instance) -> Result<(), InstantiationError> {
}
fn initialize_instance(
instance: &Instance,
instance: &mut Instance,
module: &Module,
is_bulk_memory: bool,
) -> Result<(), InstantiationError> {
// If bulk memory is not enabled, bounds check the data and element segments before
@@ -364,14 +371,14 @@ fn initialize_instance(
// in-order and side effects are observed up to the point of an out-of-bounds
// initializer, so the early checking is not desired.
if !is_bulk_memory {
check_init_bounds(instance)?;
check_init_bounds(instance, module)?;
}
// Initialize the tables
initialize_tables(instance)?;
initialize_tables(instance, module)?;
// Initialize the memories
match &instance.module.memory_initialization {
match &module.memory_initialization {
MemoryInitialization::Paged { map, out_of_bounds } => {
for (index, pages) in map {
let memory = instance.memory(index);
@@ -404,12 +411,14 @@ fn initialize_instance(
Ok(())
}
unsafe fn initialize_vmcontext(instance: &Instance, req: InstanceAllocationRequest) {
let module = &instance.module;
unsafe fn initialize_vmcontext(instance: &mut Instance, req: InstanceAllocationRequest) {
if let Some(store) = req.store {
*instance.interrupts() = (*store).vminterrupts();
*instance.externref_activations_table() = (*store).externref_activations_table().0;
instance.set_store(store);
}
*instance.interrupts() = req.interrupts;
*instance.externref_activations_table() = req.externref_activations_table;
*instance.module_info_lookup() = req.module_info_lookup.unwrap_or(&EMPTY_MODULE_LOOKUP);
let module = &instance.module;
// Initialize shared signatures
let mut ptr = instance.signature_ids_ptr();
@@ -520,17 +529,24 @@ unsafe fn initialize_vmcontext_globals(instance: &Instance) {
let from = if let Some(def_x) = module.defined_global_index(x) {
instance.global(def_x)
} else {
*instance.imported_global(x).from
&*instance.imported_global(x).from
};
*to = from;
// Globals of type `externref` need to manage the reference
// count as values move between globals, everything else is just
// copy-able bits.
match global.wasm_ty {
WasmType::ExternRef => *(*to).as_externref_mut() = from.as_externref().clone(),
_ => ptr::copy_nonoverlapping(from, to, 1),
}
}
GlobalInit::RefFunc(f) => {
*(*to).as_anyfunc_mut() = instance.get_caller_checked_anyfunc(f).unwrap()
as *const VMCallerCheckedAnyfunc;
}
GlobalInit::RefNullConst => match global.wasm_ty {
WasmType::FuncRef => *(*to).as_anyfunc_mut() = ptr::null(),
WasmType::ExternRef => *(*to).as_externref_mut() = None,
// `VMGlobalDefinition::new()` already zeroed out the bits
WasmType::FuncRef => {}
WasmType::ExternRef => {}
ty => panic!("unsupported reference type for global: {:?}", ty),
},
GlobalInit::Import => panic!("locally-defined global initialized as import"),
@@ -545,6 +561,17 @@ pub struct OnDemandInstanceAllocator {
stack_size: usize,
}
// rustc is quite strict with the lifetimes when dealing with mutable borrows,
// so this is a little helper to get a shorter lifetime on `Option<&mut T>`
fn borrow_limiter<'a>(
limiter: &'a mut Option<&mut dyn ResourceLimiter>,
) -> Option<&'a mut dyn ResourceLimiter> {
match limiter {
Some(limiter) => Some(&mut **limiter),
None => None,
}
}
impl OnDemandInstanceAllocator {
/// Creates a new on-demand instance allocator.
pub fn new(mem_creator: Option<Arc<dyn RuntimeMemoryCreator>>, stack_size: usize) -> Self {
@@ -556,13 +583,16 @@ impl OnDemandInstanceAllocator {
fn create_tables(
module: &Module,
limiter: Option<&Rc<dyn ResourceLimiter>>,
mut limiter: Option<&mut dyn ResourceLimiter>,
) -> Result<PrimaryMap<DefinedTableIndex, Table>, InstantiationError> {
let num_imports = module.num_imported_tables;
let mut tables: PrimaryMap<DefinedTableIndex, _> =
PrimaryMap::with_capacity(module.table_plans.len() - num_imports);
for table in &module.table_plans.values().as_slice()[num_imports..] {
tables.push(Table::new_dynamic(table, limiter).map_err(InstantiationError::Resource)?);
tables.push(
Table::new_dynamic(table, borrow_limiter(&mut limiter))
.map_err(InstantiationError::Resource)?,
);
}
Ok(tables)
}
@@ -570,7 +600,7 @@ impl OnDemandInstanceAllocator {
fn create_memories(
&self,
module: &Module,
limiter: Option<&Rc<dyn ResourceLimiter>>,
mut limiter: Option<&mut dyn ResourceLimiter>,
) -> Result<PrimaryMap<DefinedMemoryIndex, Memory>, InstantiationError> {
let creator = self
.mem_creator
@@ -581,7 +611,7 @@ impl OnDemandInstanceAllocator {
PrimaryMap::with_capacity(module.memory_plans.len() - num_imports);
for plan in &module.memory_plans.values().as_slice()[num_imports..] {
memories.push(
Memory::new_dynamic(plan, creator, limiter)
Memory::new_dynamic(plan, creator, borrow_limiter(&mut limiter))
.map_err(InstantiationError::Resource)?,
);
}
@@ -603,23 +633,24 @@ unsafe impl InstanceAllocator for OnDemandInstanceAllocator {
&self,
mut req: InstanceAllocationRequest,
) -> Result<InstanceHandle, InstantiationError> {
let memories = self.create_memories(&req.module, req.limiter)?;
let tables = Self::create_tables(&req.module, req.limiter)?;
let mut limiter = req.store.and_then(|s| (*s).limiter());
let memories = self.create_memories(&req.module, borrow_limiter(&mut limiter))?;
let tables = Self::create_tables(&req.module, borrow_limiter(&mut limiter))?;
let host_state = std::mem::replace(&mut req.host_state, Box::new(()));
let handle = {
let mut handle = {
let instance = Instance {
module: req.module.clone(),
offsets: VMOffsets::new(std::mem::size_of::<*const u8>() as u8, &req.module),
memories,
tables,
dropped_elements: RefCell::new(EntitySet::with_capacity(
req.module.passive_elements.len(),
)),
dropped_data: RefCell::new(EntitySet::with_capacity(req.module.passive_data.len())),
dropped_elements: EntitySet::with_capacity(req.module.passive_elements.len()),
dropped_data: EntitySet::with_capacity(req.module.passive_data.len()),
host_state,
vmctx: VMContext {},
vmctx: VMContext {
_marker: marker::PhantomPinned,
},
};
let layout = instance.alloc_layout();
let instance_ptr = alloc::alloc(layout) as *mut Instance;
@@ -632,17 +663,18 @@ unsafe impl InstanceAllocator for OnDemandInstanceAllocator {
}
};
initialize_vmcontext(handle.instance(), req);
initialize_vmcontext(handle.instance_mut(), req);
Ok(handle)
}
unsafe fn initialize(
&self,
handle: &InstanceHandle,
handle: &mut InstanceHandle,
module: &Module,
is_bulk_memory: bool,
) -> Result<(), InstantiationError> {
initialize_instance(handle.instance(), is_bulk_memory)
initialize_instance(handle.instance_mut(), module, is_bulk_memory)
}
unsafe fn deallocate(&self, handle: &InstanceHandle) {

View File

@@ -7,6 +7,7 @@
//! Using the pooling instance allocator can speed up module instantiation
//! when modules can be constrained based on configurable limits.
use super::borrow_limiter;
use super::{
initialize_instance, initialize_vmcontext, InstanceAllocationRequest, InstanceAllocator,
InstanceHandle, InstantiationError, ResourceLimiter,
@@ -14,11 +15,10 @@ use super::{
use crate::{instance::Instance, Memory, Mmap, Table, VMContext};
use anyhow::{anyhow, bail, Context, Result};
use rand::Rng;
use std::cell::RefCell;
use std::cmp::min;
use std::convert::TryFrom;
use std::marker;
use std::mem;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use wasmtime_environ::{
entity::{EntitySet, PrimaryMap},
@@ -290,12 +290,12 @@ impl Default for PoolingAllocationStrategy {
#[derive(Debug)]
struct InstancePool {
mapping: Mmap,
offsets: VMOffsets,
instance_size: usize,
max_instances: usize,
free_list: Mutex<Vec<usize>>,
memories: MemoryPool,
tables: TablePool,
empty_module: Arc<Module>,
}
impl InstancePool {
@@ -334,18 +334,17 @@ impl InstancePool {
let pool = Self {
mapping,
offsets,
instance_size,
max_instances,
free_list: Mutex::new((0..max_instances).collect()),
memories: MemoryPool::new(module_limits, instance_limits)?,
tables: TablePool::new(module_limits, instance_limits)?,
empty_module: Arc::new(Module::default()),
};
// Use a default module to initialize the instances to start
let module = Arc::new(Module::default());
for i in 0..instance_limits.count as usize {
pool.initialize(i, &module);
pool.initialize(module_limits, i);
}
Ok(pool)
@@ -356,7 +355,7 @@ impl InstancePool {
&mut *(self.mapping.as_mut_ptr().add(index * self.instance_size) as *mut Instance)
}
fn initialize(&self, index: usize, module: &Arc<Module>) {
fn initialize(&self, limits: &ModuleLimits, index: usize) {
unsafe {
let instance = self.instance(index);
@@ -364,14 +363,19 @@ impl InstancePool {
std::ptr::write(
instance as _,
Instance {
module: module.clone(),
offsets: self.offsets,
memories: PrimaryMap::with_capacity(self.offsets.num_defined_memories as usize),
tables: PrimaryMap::with_capacity(self.offsets.num_defined_tables as usize),
dropped_elements: RefCell::new(EntitySet::new()),
dropped_data: RefCell::new(EntitySet::new()),
module: self.empty_module.clone(),
offsets: VMOffsets::new(
std::mem::size_of::<*const u8>() as u8,
&self.empty_module,
),
memories: PrimaryMap::with_capacity(limits.memories as usize),
tables: PrimaryMap::with_capacity(limits.tables as usize),
dropped_elements: EntitySet::new(),
dropped_data: EntitySet::new(),
host_state: Box::new(()),
vmctx: VMContext {},
vmctx: VMContext {
_marker: marker::PhantomPinned,
},
},
);
}
@@ -391,18 +395,19 @@ impl InstancePool {
);
instance.host_state = std::mem::replace(&mut req.host_state, Box::new(()));
let mut limiter = req.store.and_then(|s| (*s).limiter());
Self::set_instance_memories(
instance,
self.memories.get(index),
self.memories.max_wasm_pages,
req.limiter,
borrow_limiter(&mut limiter),
)?;
Self::set_instance_tables(
instance,
self.tables.get(index),
self.tables.get(index).map(|x| x as *mut usize),
self.tables.max_elements,
req.limiter,
borrow_limiter(&mut limiter),
)?;
initialize_vmcontext(instance, req);
@@ -452,7 +457,7 @@ impl InstancePool {
// Decommit any linear memories that were used
for (memory, base) in instance.memories.values_mut().zip(self.memories.get(index)) {
let memory = mem::take(memory);
let mut memory = mem::take(memory);
debug_assert!(memory.is_static());
// Reset any faulted guard pages as the physical memory may be reused for another instance in the future
@@ -460,14 +465,15 @@ impl InstancePool {
memory
.reset_guard_pages()
.expect("failed to reset guard pages");
drop(&mut memory); // require mutable on all platforms, not just uffd
let size = (memory.size() * WASM_PAGE_SIZE) as usize;
let size = (memory.size() as usize) * (WASM_PAGE_SIZE as usize);
drop(memory);
decommit_memory_pages(base, size).expect("failed to decommit linear memory pages");
}
instance.memories.clear();
instance.dropped_data.borrow_mut().clear();
instance.dropped_data.clear();
// Decommit any tables that were used
for (table, base) in instance.tables.values_mut().zip(self.tables.get(index)) {
@@ -484,11 +490,22 @@ impl InstancePool {
}
instance.tables.clear();
instance.dropped_elements.borrow_mut().clear();
instance.dropped_elements.clear();
// Drop all `global` values which need a destructor, such as externref
// values which now need their reference count dropped.
instance.drop_globals();
// Drop any host state
instance.host_state = Box::new(());
// And finally reset the module/offsets back to their original. This
// should put everything back in a relatively pristine state for each
// fresh allocation later on.
instance.module = self.empty_module.clone();
instance.offsets =
VMOffsets::new(std::mem::size_of::<*const u8>() as u8, &self.empty_module);
self.free_list.lock().unwrap().push(index);
}
@@ -496,7 +513,7 @@ impl InstancePool {
instance: &mut Instance,
mut memories: impl Iterator<Item = *mut u8>,
max_pages: u32,
limiter: Option<&Rc<dyn ResourceLimiter>>,
mut limiter: Option<&mut dyn ResourceLimiter>,
) -> Result<(), InstantiationError> {
let module = instance.module.as_ref();
@@ -505,30 +522,34 @@ impl InstancePool {
for plan in
(&module.memory_plans.values().as_slice()[module.num_imported_memories..]).iter()
{
let memory = unsafe {
std::slice::from_raw_parts_mut(
memories.next().unwrap(),
(max_pages as usize) * (WASM_PAGE_SIZE as usize),
)
};
instance.memories.push(
Memory::new_static(
plan,
memories.next().unwrap(),
max_pages,
memory,
commit_memory_pages,
limiter,
borrow_limiter(&mut limiter),
)
.map_err(InstantiationError::Resource)?,
);
}
let mut dropped_data = instance.dropped_data.borrow_mut();
debug_assert!(dropped_data.is_empty());
dropped_data.resize(module.passive_data.len());
debug_assert!(instance.dropped_data.is_empty());
instance.dropped_data.resize(module.passive_data.len());
Ok(())
}
fn set_instance_tables(
instance: &mut Instance,
mut tables: impl Iterator<Item = *mut u8>,
mut tables: impl Iterator<Item = *mut usize>,
max_elements: u32,
limiter: Option<&Rc<dyn ResourceLimiter>>,
mut limiter: Option<&mut dyn ResourceLimiter>,
) -> Result<(), InstantiationError> {
let module = instance.module.as_ref();
@@ -537,18 +558,23 @@ impl InstancePool {
for plan in (&module.table_plans.values().as_slice()[module.num_imported_tables..]).iter() {
let base = tables.next().unwrap();
commit_table_pages(base, max_elements as usize * mem::size_of::<*mut u8>())
.map_err(InstantiationError::Resource)?;
commit_table_pages(
base as *mut u8,
max_elements as usize * mem::size_of::<*mut u8>(),
)
.map_err(InstantiationError::Resource)?;
let table = unsafe { std::slice::from_raw_parts_mut(base, max_elements as usize) };
instance.tables.push(
Table::new_static(plan, base as _, max_elements, limiter)
Table::new_static(plan, table, borrow_limiter(&mut limiter))
.map_err(InstantiationError::Resource)?,
);
}
let mut dropped_elements = instance.dropped_elements.borrow_mut();
debug_assert!(dropped_elements.is_empty());
dropped_elements.resize(module.passive_elements.len());
debug_assert!(instance.dropped_elements.is_empty());
instance
.dropped_elements
.resize(module.passive_elements.len());
Ok(())
}
@@ -595,7 +621,7 @@ impl MemoryPool {
}
// The maximum module memory page count cannot exceed the memory reservation size
if (module_limits.memory_pages * WASM_PAGE_SIZE) as u64
if u64::from(module_limits.memory_pages) * u64::from(WASM_PAGE_SIZE)
> instance_limits.memory_reservation_size
{
bail!(
@@ -957,21 +983,22 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator {
unsafe fn initialize(
&self,
handle: &InstanceHandle,
handle: &mut InstanceHandle,
module: &Module,
is_bulk_memory: bool,
) -> Result<(), InstantiationError> {
let instance = handle.instance();
let instance = handle.instance_mut();
cfg_if::cfg_if! {
if #[cfg(all(feature = "uffd", target_os = "linux"))] {
match &instance.module.memory_initialization {
match &module.memory_initialization {
wasmtime_environ::MemoryInitialization::Paged{ out_of_bounds, .. } => {
if !is_bulk_memory {
super::check_init_bounds(instance)?;
super::check_init_bounds(instance, module)?;
}
// Initialize the tables
super::initialize_tables(instance)?;
super::initialize_tables(instance, module)?;
// Don't initialize the memory; the fault handler will back the pages when accessed
@@ -984,10 +1011,10 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator {
Ok(())
},
_ => initialize_instance(instance, is_bulk_memory)
_ => initialize_instance(instance, module, is_bulk_memory)
}
} else {
initialize_instance(instance, is_bulk_memory)
initialize_instance(instance, module, is_bulk_memory)
}
}
}
@@ -1355,19 +1382,6 @@ mod test {
let instances = InstancePool::new(&module_limits, &instance_limits)?;
assert_eq!(
instances.offsets.pointer_size,
std::mem::size_of::<*const u8>() as u8
);
assert_eq!(instances.offsets.num_signature_ids, 0);
assert_eq!(instances.offsets.num_imported_functions, 0);
assert_eq!(instances.offsets.num_imported_tables, 0);
assert_eq!(instances.offsets.num_imported_memories, 0);
assert_eq!(instances.offsets.num_imported_globals, 0);
assert_eq!(instances.offsets.num_defined_functions, 0);
assert_eq!(instances.offsets.num_defined_tables, 1);
assert_eq!(instances.offsets.num_defined_memories, 1);
assert_eq!(instances.offsets.num_defined_globals, 0);
// As of April 2021, the instance struct's size is largely below the size of a single page,
// so it's safe to assume it's been rounded to the size of a single memory page here.
assert_eq!(instances.instance_size, region::page::size());
@@ -1395,10 +1409,7 @@ mod test {
},
shared_signatures: VMSharedSignatureIndex::default().into(),
host_state: Box::new(()),
interrupts: std::ptr::null(),
externref_activations_table: std::ptr::null_mut(),
module_info_lookup: None,
limiter: None,
store: None,
},
)
.expect("allocation should succeed"),
@@ -1420,10 +1431,7 @@ mod test {
},
shared_signatures: VMSharedSignatureIndex::default().into(),
host_state: Box::new(()),
interrupts: std::ptr::null(),
externref_activations_table: std::ptr::null_mut(),
module_info_lookup: None,
limiter: None,
store: None,
},
) {
Err(InstantiationError::Limit(3)) => {}

View File

@@ -130,7 +130,7 @@ fn reset_guard_page(addr: *mut u8, len: usize) -> Result<()> {
}
/// Represents a location of a page fault within monitored regions of memory.
enum FaultLocation<'a> {
enum FaultLocation {
/// The address location is in a WebAssembly linear memory page.
/// The fault handler will copy the pages from initialization data if necessary.
MemoryPage {
@@ -139,7 +139,7 @@ enum FaultLocation<'a> {
/// The length of the page being accessed.
len: usize,
/// The instance related to the memory page that was accessed.
instance: &'a Instance,
instance: *mut Instance,
/// The index of the memory that was accessed.
memory_index: DefinedMemoryIndex,
/// The Wasm page index to initialize if the access was not a guard page.
@@ -194,9 +194,9 @@ impl FaultLocator {
///
/// If the assumption holds true, accessing the instance data from the handler thread
/// should, in theory, be safe.
unsafe fn get_instance(&self, index: usize) -> &Instance {
unsafe fn get_instance(&self, index: usize) -> *mut Instance {
debug_assert!(index < self.max_instances);
&*((self.instances_start + (index * self.instance_size)) as *const Instance)
(self.instances_start + (index * self.instance_size)) as *mut Instance
}
unsafe fn locate(&self, addr: usize) -> Option<FaultLocation> {
@@ -208,7 +208,7 @@ impl FaultLocator {
let page_index = (addr - memory_start) / WASM_PAGE_SIZE;
let instance = self.get_instance(index / self.max_memories);
let init_page_index = instance.memories.get(memory_index).and_then(|m| {
let init_page_index = (*instance).memories.get(memory_index).and_then(|m| {
if page_index < m.size() as usize {
Some(page_index)
} else {
@@ -310,13 +310,13 @@ unsafe fn handle_page_fault(
match page_index {
Some(page_index) => {
initialize_wasm_page(&uffd, instance, page_addr, memory_index, page_index)?;
initialize_wasm_page(&uffd, &*instance, page_addr, memory_index, page_index)?;
}
None => {
log::trace!("out of bounds memory access at {:p}", addr);
// Record the guard page fault so the page protection level can be reset later
instance.memories[memory_index].record_guard_page_fault(
(*instance).memories[memory_index].record_guard_page_fault(
page_addr,
len,
reset_guard_page,
@@ -436,7 +436,6 @@ mod test {
Imports, InstanceAllocationRequest, InstanceLimits, ModuleLimits,
PoolingAllocationStrategy, VMSharedSignatureIndex,
};
use std::ptr;
use std::sync::Arc;
use wasmtime_environ::{entity::PrimaryMap, wasm::Memory, MemoryPlan, MemoryStyle, Module};
@@ -521,10 +520,7 @@ mod test {
},
shared_signatures: VMSharedSignatureIndex::default().into(),
host_state: Box::new(()),
interrupts: ptr::null(),
externref_activations_table: ptr::null_mut(),
module_info_lookup: None,
limiter: None,
store: None,
},
)
.expect("instance should allocate"),

View File

@@ -20,6 +20,8 @@
)
)]
use std::error::Error;
mod export;
mod externref;
mod imports;
@@ -40,15 +42,15 @@ pub use crate::imports::Imports;
pub use crate::instance::{
InstanceAllocationRequest, InstanceAllocator, InstanceHandle, InstanceLimits,
InstantiationError, LinkError, ModuleLimits, OnDemandInstanceAllocator,
PoolingAllocationStrategy, PoolingInstanceAllocator, ResourceLimiter, RuntimeInstance,
PoolingAllocationStrategy, PoolingInstanceAllocator, ResourceLimiter,
};
pub use crate::jit_int::GdbJitImageRegistration;
pub use crate::memory::{Memory, RuntimeLinearMemory, RuntimeMemoryCreator};
pub use crate::mmap::Mmap;
pub use crate::table::{Table, TableElement};
pub use crate::traphandlers::{
catch_traps, init_traps, raise_lib_trap, raise_user_trap, resume_panic, with_last_info,
SignalHandler, TlsRestore, Trap, TrapInfo,
catch_traps, init_traps, raise_lib_trap, raise_user_trap, resume_panic, SignalHandler,
TlsRestore, Trap,
};
pub use crate::vmcontext::{
VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, VMGlobalDefinition,
@@ -80,3 +82,41 @@ pub fn pointer_type() -> wasmtime_environ::ir::Type {
unreachable!()
}
}
/// Dynamic runtime functionality needed by this crate throughout the execution
/// of a wasm instance.
///
/// This trait is used to store a raw pointer trait object within each
/// `VMContext`. This raw pointer trait object points back to the
/// `wasmtime::Store` internally but is type-erased so this `wasmtime_runtime`
/// crate doesn't need the entire `wasmtime` crate to build.
///
/// Note that this is an extra-unsafe trait because no heed is paid to the
/// lifetime of this store or the Send/Sync-ness of this store. All of that must
/// be respected by embedders (e.g. the `wasmtime::Store` structure). The theory
/// is that `wasmtime::Store` handles all this correctly.
pub unsafe trait Store {
/// Returns the raw pointer in memory where this store's shared
/// `VMInterrupts` structure is located.
///
/// Used to configure `VMContext` initialization and store the right pointer
/// in the `VMContext`.
fn vminterrupts(&self) -> *mut VMInterrupts;
/// Returns the externref management structures necessary for this store.
///
/// The first element returned is the table in which externrefs are stored
/// throughout wasm execution, and the second element is how to look up
/// module information for gc requests.
fn externref_activations_table(
&mut self,
) -> (&mut VMExternRefActivationsTable, &dyn ModuleInfoLookup);
/// Returns a reference to the store's limiter for limiting resources, if any.
fn limiter(&mut self) -> Option<&mut dyn ResourceLimiter>;
/// Callback invoked whenever fuel runs out by a wasm instance. If an error
/// is returned that's raised as a trap. Otherwise wasm execution will
/// continue as normal.
fn out_of_gas(&mut self) -> Result<(), Box<dyn Error + Send + Sync>>;
}

View File

@@ -191,7 +191,7 @@ pub unsafe extern "C" fn wasmtime_memory32_grow(
delta: u32,
memory_index: u32,
) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
instance
@@ -205,7 +205,7 @@ pub unsafe extern "C" fn wasmtime_imported_memory32_grow(
delta: u32,
memory_index: u32,
) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
let memory_index = MemoryIndex::from_u32(memory_index);
instance
@@ -215,7 +215,7 @@ pub unsafe extern "C" fn wasmtime_imported_memory32_grow(
/// Implementation of memory.size for locally-defined 32-bit memories.
pub unsafe extern "C" fn wasmtime_memory32_size(vmctx: *mut VMContext, memory_index: u32) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance();
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
instance.memory_size(memory_index)
@@ -226,7 +226,7 @@ pub unsafe extern "C" fn wasmtime_imported_memory32_size(
vmctx: *mut VMContext,
memory_index: u32,
) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance();
let memory_index = MemoryIndex::from_u32(memory_index);
instance.imported_memory_size(memory_index)
@@ -241,7 +241,7 @@ pub unsafe extern "C" fn wasmtime_table_grow(
// or is a `VMExternRef` until we look at the table type.
init_value: *mut u8,
) -> u32 {
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
let table_index = TableIndex::from_u32(table_index);
match instance.table_element_type(table_index) {
TableElementType::Func => {
@@ -277,9 +277,9 @@ pub unsafe extern "C" fn wasmtime_table_fill(
len: u32,
) {
let result = {
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
let table_index = TableIndex::from_u32(table_index);
let table = instance.get_table(table_index);
let table = &mut *instance.get_table(table_index);
match table.element_type() {
TableElementType::Func => {
let val = val as *mut VMCallerCheckedAnyfunc;
@@ -313,7 +313,7 @@ pub unsafe extern "C" fn wasmtime_table_copy(
let result = {
let dst_table_index = TableIndex::from_u32(dst_table_index);
let src_table_index = TableIndex::from_u32(src_table_index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
let dst_table = instance.get_table(dst_table_index);
let src_table = instance.get_table(src_table_index);
Table::copy(dst_table, src_table, dst, src, len)
@@ -335,7 +335,7 @@ pub unsafe extern "C" fn wasmtime_table_init(
let result = {
let table_index = TableIndex::from_u32(table_index);
let elem_index = ElemIndex::from_u32(elem_index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
instance.table_init(table_index, elem_index, dst, src, len)
};
if let Err(trap) = result {
@@ -346,7 +346,7 @@ pub unsafe extern "C" fn wasmtime_table_init(
/// Implementation of `elem.drop`.
pub unsafe extern "C" fn wasmtime_elem_drop(vmctx: *mut VMContext, elem_index: u32) {
let elem_index = ElemIndex::from_u32(elem_index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
instance.elem_drop(elem_index);
}
@@ -362,7 +362,7 @@ pub unsafe extern "C" fn wasmtime_memory_copy(
let result = {
let src_index = MemoryIndex::from_u32(src_index);
let dst_index = MemoryIndex::from_u32(dst_index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
instance.memory_copy(dst_index, dst, src_index, src, len)
};
if let Err(trap) = result {
@@ -380,7 +380,7 @@ pub unsafe extern "C" fn wasmtime_memory_fill(
) {
let result = {
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance();
instance.defined_memory_fill(memory_index, dst, val, len)
};
if let Err(trap) = result {
@@ -398,7 +398,7 @@ pub unsafe extern "C" fn wasmtime_imported_memory_fill(
) {
let result = {
let memory_index = MemoryIndex::from_u32(memory_index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
instance.imported_memory_fill(memory_index, dst, val, len)
};
if let Err(trap) = result {
@@ -418,7 +418,7 @@ pub unsafe extern "C" fn wasmtime_memory_init(
let result = {
let memory_index = MemoryIndex::from_u32(memory_index);
let data_index = DataIndex::from_u32(data_index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
instance.memory_init(memory_index, data_index, dst, src, len)
};
if let Err(trap) = result {
@@ -429,7 +429,7 @@ pub unsafe extern "C" fn wasmtime_memory_init(
/// Implementation of `data.drop`.
pub unsafe extern "C" fn wasmtime_data_drop(vmctx: *mut VMContext, data_index: u32) {
let data_index = DataIndex::from_u32(data_index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance_mut();
instance.data_drop(data_index)
}
@@ -447,9 +447,8 @@ pub unsafe extern "C" fn wasmtime_activations_table_insert_with_gc(
externref: *mut u8,
) {
let externref = VMExternRef::clone_from_raw(externref);
let instance = (&mut *vmctx).instance();
let activations_table = &**instance.externref_activations_table();
let module_info_lookup = &**instance.module_info_lookup();
let instance = (*vmctx).instance();
let (activations_table, module_info_lookup) = (*instance.store()).externref_activations_table();
activations_table.insert_with_gc(externref, module_info_lookup);
}
@@ -459,14 +458,14 @@ pub unsafe extern "C" fn wasmtime_externref_global_get(
index: u32,
) -> *mut u8 {
let index = GlobalIndex::from_u32(index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance();
let global = instance.defined_or_imported_global_ptr(index);
match (*global).as_externref().clone() {
None => ptr::null_mut(),
Some(externref) => {
let raw = externref.as_raw();
let activations_table = &**instance.externref_activations_table();
let module_info_lookup = &**instance.module_info_lookup();
let (activations_table, module_info_lookup) =
(*instance.store()).externref_activations_table();
activations_table.insert_with_gc(externref, module_info_lookup);
raw
}
@@ -486,7 +485,7 @@ pub unsafe extern "C" fn wasmtime_externref_global_set(
};
let index = GlobalIndex::from_u32(index);
let instance = (&mut *vmctx).instance();
let instance = (*vmctx).instance();
let global = instance.defined_or_imported_global_ptr(index);
// Swap the new `externref` value into the global before we drop the old
@@ -583,6 +582,9 @@ pub unsafe extern "C" fn wasmtime_imported_memory_atomic_wait64(
}
/// Hook for when an instance runs out of fuel.
pub unsafe extern "C" fn wasmtime_out_of_gas(_vmctx: *mut VMContext) {
crate::traphandlers::out_of_gas()
pub unsafe extern "C" fn wasmtime_out_of_gas(vmctx: *mut VMContext) {
match (*(*vmctx).instance().store()).out_of_gas() {
Ok(()) => {}
Err(err) => crate::traphandlers::raise_user_trap(err),
}
}

View File

@@ -7,11 +7,7 @@ use crate::vmcontext::VMMemoryDefinition;
use crate::ResourceLimiter;
use anyhow::{bail, Result};
use more_asserts::{assert_ge, assert_le};
use std::cell::{Cell, RefCell};
use std::cmp::min;
use std::convert::TryFrom;
use std::ptr;
use std::rc::Rc;
use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE};
/// A memory allocator
@@ -31,7 +27,7 @@ impl RuntimeMemoryCreator for DefaultMemoryCreator {
}
/// A linear memory
pub trait RuntimeLinearMemory {
pub trait RuntimeLinearMemory: Send + Sync {
/// Returns the number of allocated wasm pages.
fn size(&self) -> u32;
@@ -43,7 +39,7 @@ pub trait RuntimeLinearMemory {
///
/// Returns `None` if memory can't be grown by the specified amount
/// of wasm pages.
fn grow(&self, delta: u32) -> Option<u32>;
fn grow(&mut self, delta: u32) -> Option<u32>;
/// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
fn vmmemory(&self) -> VMMemoryDefinition;
@@ -53,7 +49,7 @@ pub trait RuntimeLinearMemory {
#[derive(Debug)]
pub struct MmapMemory {
// The underlying allocation.
mmap: RefCell<WasmMmap>,
mmap: WasmMmap,
// The optional maximum size in wasm pages of this linear memory.
maximum: Option<u32>,
@@ -108,7 +104,7 @@ impl MmapMemory {
impl RuntimeLinearMemory for MmapMemory {
/// Returns the number of allocated wasm pages.
fn size(&self) -> u32 {
self.mmap.borrow().size
self.mmap.size
}
/// Returns the maximum number of pages the memory can grow to.
@@ -121,19 +117,18 @@ impl RuntimeLinearMemory for MmapMemory {
///
/// Returns `None` if memory can't be grown by the specified amount
/// of wasm pages.
fn grow(&self, delta: u32) -> Option<u32> {
fn grow(&mut self, delta: u32) -> Option<u32> {
// Optimization of memory.grow 0 calls.
let mut mmap = self.mmap.borrow_mut();
if delta == 0 {
return Some(mmap.size);
return Some(self.mmap.size);
}
let new_pages = match mmap.size.checked_add(delta) {
let new_pages = match self.mmap.size.checked_add(delta) {
Some(new_pages) => new_pages,
// Linear memory size overflow.
None => return None,
};
let prev_pages = mmap.size;
let prev_pages = self.mmap.size;
if let Some(maximum) = self.maximum {
if new_pages > maximum {
@@ -145,7 +140,7 @@ impl RuntimeLinearMemory for MmapMemory {
// Wasm linear memories are never allowed to grow beyond what is
// indexable. If the memory has no maximum, enforce the greatest
// limit here.
if new_pages >= WASM_MAX_PAGES {
if new_pages > WASM_MAX_PAGES {
// Linear memory size would exceed the index range.
return None;
}
@@ -154,7 +149,7 @@ impl RuntimeLinearMemory for MmapMemory {
let prev_bytes = usize::try_from(prev_pages).unwrap() * WASM_PAGE_SIZE as usize;
let new_bytes = usize::try_from(new_pages).unwrap() * WASM_PAGE_SIZE as usize;
if new_bytes > mmap.alloc.len() - self.offset_guard_size {
if new_bytes > self.mmap.alloc.len() - self.offset_guard_size {
// If the new size is within the declared maximum, but needs more memory than we
// have on hand, it's a dynamic heap and it can move.
let guard_bytes = self.offset_guard_size;
@@ -162,48 +157,59 @@ impl RuntimeLinearMemory for MmapMemory {
let mut new_mmap = Mmap::accessible_reserved(new_bytes, request_bytes).ok()?;
let copy_len = mmap.alloc.len() - self.offset_guard_size;
new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&mmap.alloc.as_slice()[..copy_len]);
let copy_len = self.mmap.alloc.len() - self.offset_guard_size;
new_mmap.as_mut_slice()[..copy_len]
.copy_from_slice(&self.mmap.alloc.as_slice()[..copy_len]);
mmap.alloc = new_mmap;
self.mmap.alloc = new_mmap;
} else if delta_bytes > 0 {
// Make the newly allocated pages accessible.
mmap.alloc.make_accessible(prev_bytes, delta_bytes).ok()?;
self.mmap
.alloc
.make_accessible(prev_bytes, delta_bytes)
.ok()?;
}
mmap.size = new_pages;
self.mmap.size = new_pages;
Some(prev_pages)
}
/// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
fn vmmemory(&self) -> VMMemoryDefinition {
let mmap = self.mmap.borrow();
VMMemoryDefinition {
base: mmap.alloc.as_mut_ptr(),
current_length: mmap.size as usize * WASM_PAGE_SIZE as usize,
base: self.mmap.alloc.as_mut_ptr(),
current_length: self.mmap.size as usize * WASM_PAGE_SIZE as usize,
}
}
}
enum MemoryStorage {
/// Representation of a runtime wasm linear memory.
pub enum Memory {
/// A "static" memory where the lifetime of the backing memory is managed
/// elsewhere. Currently used with the pooling allocator.
Static {
base: *mut u8,
size: Cell<u32>,
maximum: u32,
/// The memory in the host for this wasm memory. The length of this
/// slice is the maximum size of the memory that can be grown to.
base: &'static mut [u8],
/// The current size, in wasm pages, of this memory.
size: u32,
/// A callback which makes portions of `base` accessible for when memory
/// is grown. Otherwise it's expected that accesses to `base` will
/// fault.
make_accessible: fn(*mut u8, usize) -> Result<()>,
/// Stores the pages in the linear memory that have faulted as guard pages when using the `uffd` feature.
/// These pages need their protection level reset before the memory can grow.
#[cfg(all(feature = "uffd", target_os = "linux"))]
guard_page_faults: RefCell<Vec<(*mut u8, usize, fn(*mut u8, usize) -> Result<()>)>>,
guard_page_faults: Vec<(usize, usize, fn(*mut u8, usize) -> Result<()>)>,
},
Dynamic(Box<dyn RuntimeLinearMemory>),
}
/// Represents an instantiation of a WebAssembly memory.
pub struct Memory {
storage: MemoryStorage,
limiter: Option<Rc<dyn ResourceLimiter>>,
/// A "dynamic" memory whose data is managed at runtime and lifetime is tied
/// to this instance.
Dynamic(Box<dyn RuntimeLinearMemory>),
}
impl Memory {
@@ -211,40 +217,45 @@ impl Memory {
pub fn new_dynamic(
plan: &MemoryPlan,
creator: &dyn RuntimeMemoryCreator,
limiter: Option<&Rc<dyn ResourceLimiter>>,
limiter: Option<&mut dyn ResourceLimiter>,
) -> Result<Self> {
Self::new(
plan,
MemoryStorage::Dynamic(creator.new_memory(plan)?),
limiter,
)
Self::limit_new(plan, limiter)?;
Ok(Memory::Dynamic(creator.new_memory(plan)?))
}
/// Create a new static (immovable) memory instance for the specified plan.
pub fn new_static(
plan: &MemoryPlan,
base: *mut u8,
maximum: u32,
base: &'static mut [u8],
make_accessible: fn(*mut u8, usize) -> Result<()>,
limiter: Option<&Rc<dyn ResourceLimiter>>,
limiter: Option<&mut dyn ResourceLimiter>,
) -> Result<Self> {
let storage = MemoryStorage::Static {
base,
size: Cell::new(plan.memory.minimum),
maximum: min(plan.memory.maximum.unwrap_or(maximum), maximum),
make_accessible,
#[cfg(all(feature = "uffd", target_os = "linux"))]
guard_page_faults: RefCell::new(Vec::new()),
Self::limit_new(plan, limiter)?;
let base = match plan.memory.maximum {
Some(max) if (max as usize) < base.len() / (WASM_PAGE_SIZE as usize) => {
&mut base[..(max * WASM_PAGE_SIZE) as usize]
}
_ => base,
};
Self::new(plan, storage, limiter)
if plan.memory.minimum > 0 {
make_accessible(
base.as_mut_ptr(),
plan.memory.minimum as usize * WASM_PAGE_SIZE as usize,
)?;
}
Ok(Memory::Static {
base,
size: plan.memory.minimum,
make_accessible,
#[cfg(all(feature = "uffd", target_os = "linux"))]
guard_page_faults: Vec::new(),
})
}
fn new(
plan: &MemoryPlan,
storage: MemoryStorage,
limiter: Option<&Rc<dyn ResourceLimiter>>,
) -> Result<Self> {
fn limit_new(plan: &MemoryPlan, limiter: Option<&mut dyn ResourceLimiter>) -> Result<()> {
if let Some(limiter) = limiter {
if !limiter.memory_growing(0, plan.memory.minimum, plan.memory.maximum) {
bail!(
@@ -253,32 +264,14 @@ impl Memory {
);
}
}
if let MemoryStorage::Static {
base,
make_accessible,
..
} = &storage
{
if plan.memory.minimum > 0 {
make_accessible(
*base,
plan.memory.minimum as usize * WASM_PAGE_SIZE as usize,
)?;
}
}
Ok(Self {
storage,
limiter: limiter.cloned(),
})
Ok(())
}
/// Returns the number of allocated wasm pages.
pub fn size(&self) -> u32 {
match &self.storage {
MemoryStorage::Static { size, .. } => size.get(),
MemoryStorage::Dynamic(mem) => mem.size(),
match self {
Memory::Static { size, .. } => *size,
Memory::Dynamic(mem) => mem.size(),
}
}
@@ -289,15 +282,15 @@ impl Memory {
/// The runtime maximum may not be equal to the maximum from the linear memory's
/// Wasm type when it is being constrained by an instance allocator.
pub fn maximum(&self) -> Option<u32> {
match &self.storage {
MemoryStorage::Static { maximum, .. } => Some(*maximum),
MemoryStorage::Dynamic(mem) => mem.maximum(),
match self {
Memory::Static { base, .. } => Some((base.len() / (WASM_PAGE_SIZE as usize)) as u32),
Memory::Dynamic(mem) => mem.maximum(),
}
}
/// Returns whether or not the underlying storage of the memory is "static".
pub(crate) fn is_static(&self) -> bool {
if let MemoryStorage::Static { .. } = &self.storage {
if let Memory::Static { .. } = self {
true
} else {
false
@@ -317,57 +310,65 @@ impl Memory {
///
/// Generally, prefer using `InstanceHandle::memory_grow`, which encapsulates
/// this unsafety.
pub unsafe fn grow(&self, delta: u32) -> Option<u32> {
pub unsafe fn grow(
&mut self,
delta: u32,
limiter: Option<&mut dyn ResourceLimiter>,
) -> Option<u32> {
let old_size = self.size();
if delta == 0 {
return Some(old_size);
}
let new_size = old_size.checked_add(delta)?;
let maximum = self.maximum();
if let Some(limiter) = &self.limiter {
if !limiter.memory_growing(old_size, new_size, self.maximum()) {
if let Some(limiter) = limiter {
if !limiter.memory_growing(old_size, new_size, maximum) {
return None;
}
}
match &self.storage {
MemoryStorage::Static {
#[cfg(all(feature = "uffd", target_os = "linux"))]
{
if self.is_static() {
// Reset any faulted guard pages before growing the memory.
self.reset_guard_pages().ok()?;
}
}
match self {
Memory::Static {
base,
size,
maximum,
make_accessible,
..
} => {
// Reset any faulted guard pages before growing the memory.
#[cfg(all(feature = "uffd", target_os = "linux"))]
self.reset_guard_pages().ok()?;
if new_size > *maximum || new_size >= WASM_MAX_PAGES {
if new_size > maximum.unwrap_or(WASM_MAX_PAGES) {
return None;
}
let start = usize::try_from(old_size).unwrap() * WASM_PAGE_SIZE as usize;
let len = usize::try_from(delta).unwrap() * WASM_PAGE_SIZE as usize;
make_accessible(base.add(start), len).ok()?;
make_accessible(base.as_mut_ptr().add(start), len).ok()?;
size.set(new_size);
*size = new_size;
Some(old_size)
}
MemoryStorage::Dynamic(mem) => mem.grow(delta),
Memory::Dynamic(mem) => mem.grow(delta),
}
}
/// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
pub fn vmmemory(&self) -> VMMemoryDefinition {
match &self.storage {
MemoryStorage::Static { base, size, .. } => VMMemoryDefinition {
base: *base,
current_length: size.get() as usize * WASM_PAGE_SIZE as usize,
match self {
Memory::Static { base, size, .. } => VMMemoryDefinition {
base: base.as_ptr() as *mut _,
current_length: *size as usize * WASM_PAGE_SIZE as usize,
},
MemoryStorage::Dynamic(mem) => mem.vmmemory(),
Memory::Dynamic(mem) => mem.vmmemory(),
}
}
@@ -378,20 +379,18 @@ impl Memory {
/// This function will panic if called on a dynamic memory.
#[cfg(all(feature = "uffd", target_os = "linux"))]
pub(crate) fn record_guard_page_fault(
&self,
&mut self,
page_addr: *mut u8,
size: usize,
reset: fn(*mut u8, usize) -> Result<()>,
) {
match &self.storage {
MemoryStorage::Static {
match self {
Memory::Static {
guard_page_faults, ..
} => {
guard_page_faults
.borrow_mut()
.push((page_addr, size, reset));
guard_page_faults.push((page_addr as usize, size, reset));
}
MemoryStorage::Dynamic(_) => {
Memory::Dynamic(_) => {
unreachable!("dynamic memories should not have guard page faults")
}
}
@@ -403,17 +402,16 @@ impl Memory {
///
/// This function will panic if called on a dynamic memory.
#[cfg(all(feature = "uffd", target_os = "linux"))]
pub(crate) fn reset_guard_pages(&self) -> Result<()> {
match &self.storage {
MemoryStorage::Static {
pub(crate) fn reset_guard_pages(&mut self) -> Result<()> {
match self {
Memory::Static {
guard_page_faults, ..
} => {
let mut faults = guard_page_faults.borrow_mut();
for (addr, len, reset) in faults.drain(..) {
reset(addr, len)?;
for (addr, len, reset) in guard_page_faults.drain(..) {
reset(addr as *mut u8, len)?;
}
}
MemoryStorage::Dynamic(_) => {
Memory::Dynamic(_) => {
unreachable!("dynamic memories should not have guard page faults")
}
}
@@ -425,20 +423,12 @@ impl Memory {
// The default memory representation is an empty memory that cannot grow.
impl Default for Memory {
fn default() -> Self {
fn make_accessible(_ptr: *mut u8, _len: usize) -> Result<()> {
unreachable!()
}
Self {
storage: MemoryStorage::Static {
base: ptr::null_mut(),
size: Cell::new(0),
maximum: 0,
make_accessible,
#[cfg(all(feature = "uffd", target_os = "linux"))]
guard_page_faults: RefCell::new(Vec::new()),
},
limiter: None,
Memory::Static {
base: &mut [],
size: 0,
make_accessible: |_, _| unreachable!(),
#[cfg(all(feature = "uffd", target_os = "linux"))]
guard_page_faults: Vec::new(),
}
}
}

View File

@@ -5,12 +5,9 @@
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
use crate::{ResourceLimiter, Trap, VMExternRef};
use anyhow::{bail, Result};
use std::cell::{Cell, RefCell};
use std::cmp::min;
use std::convert::{TryFrom, TryInto};
use std::ops::Range;
use std::ptr;
use std::rc::Rc;
use wasmtime_environ::wasm::TableElementType;
use wasmtime_environ::{ir, TablePlan};
@@ -25,6 +22,11 @@ pub enum TableElement {
ExternRef(Option<VMExternRef>),
}
// The usage of `*mut VMCallerCheckedAnyfunc` is safe w.r.t. thread safety, this
// just relies on thread-safety of `VMExternRef` itself.
unsafe impl Send for TableElement where VMExternRef: Send {}
unsafe impl Sync for TableElement where VMExternRef: Sync {}
impl TableElement {
/// Consumes the given raw pointer into a table element.
///
@@ -33,13 +35,13 @@ impl TableElement {
/// This is unsafe as it will *not* clone any externref, leaving the reference count unchanged.
///
/// This should only be used if the raw pointer is no longer in use.
unsafe fn from_raw(ty: TableElementType, ptr: *mut u8) -> Self {
unsafe fn from_raw(ty: TableElementType, ptr: usize) -> Self {
match ty {
TableElementType::Func => Self::FuncRef(ptr as _),
TableElementType::Val(_) => Self::ExternRef(if ptr.is_null() {
TableElementType::Val(_) => Self::ExternRef(if ptr == 0 {
None
} else {
Some(VMExternRef::from_raw(ptr))
Some(VMExternRef::from_raw(ptr as *mut u8))
}),
}
}
@@ -49,13 +51,13 @@ impl TableElement {
/// # Safety
///
/// This is unsafe as it will clone any externref, incrementing the reference count.
unsafe fn clone_from_raw(ty: TableElementType, ptr: *mut u8) -> Self {
unsafe fn clone_from_raw(ty: TableElementType, ptr: usize) -> Self {
match ty {
TableElementType::Func => Self::FuncRef(ptr as _),
TableElementType::Val(_) => Self::ExternRef(if ptr.is_null() {
TableElementType::Val(_) => Self::ExternRef(if ptr == 0 {
None
} else {
Some(VMExternRef::clone_from_raw(ptr))
Some(VMExternRef::clone_from_raw(ptr as *mut u8))
}),
}
}
@@ -68,10 +70,10 @@ impl TableElement {
/// the reference count.
///
/// Use `from_raw` to properly drop any table elements stored as raw pointers.
unsafe fn into_raw(self) -> *mut u8 {
unsafe fn into_raw(self) -> usize {
match self {
Self::FuncRef(e) => e as _,
Self::ExternRef(e) => e.map_or(ptr::null_mut(), |e| e.into_raw()),
Self::ExternRef(e) => e.map_or(0, |e| e.into_raw() as usize),
}
}
}
@@ -94,71 +96,68 @@ impl From<VMExternRef> for TableElement {
}
}
enum TableStorage {
/// Represents an instance's table.
pub enum Table {
/// A "static" table where storage space is managed externally, currently
/// used with the pooling allocator.
Static {
data: *mut *mut u8,
size: Cell<u32>,
/// Where data for this table is stored. The length of this list is the
/// maximum size of the table.
data: &'static mut [usize],
/// The current size of the table.
size: u32,
/// The type of this table.
ty: TableElementType,
maximum: u32,
},
/// A "dynamic" table where table storage space is dynamically allocated via
/// `malloc` (aka Rust's `Vec`).
Dynamic {
elements: RefCell<Vec<*mut u8>>,
/// Dynamically managed storage space for this table. The length of this
/// vector is the current size of the table.
elements: Vec<usize>,
/// The type of this table.
ty: TableElementType,
/// Maximum size that `elements` can grow to.
maximum: Option<u32>,
},
}
/// Represents an instance's table.
pub struct Table {
storage: TableStorage,
limiter: Option<Rc<dyn ResourceLimiter>>,
}
impl Table {
/// Create a new dynamic (movable) table instance for the specified table plan.
pub fn new_dynamic(
plan: &TablePlan,
limiter: Option<&Rc<dyn ResourceLimiter>>,
limiter: Option<&mut dyn ResourceLimiter>,
) -> Result<Self> {
let elements = RefCell::new(vec![ptr::null_mut(); plan.table.minimum as usize]);
Self::limit_new(plan, limiter)?;
let elements = vec![0; plan.table.minimum as usize];
let ty = plan.table.ty.clone();
let maximum = plan.table.maximum;
let storage = TableStorage::Dynamic {
Ok(Table::Dynamic {
elements,
ty,
maximum,
};
Self::new(plan, storage, limiter)
})
}
/// Create a new static (immovable) table instance for the specified table plan.
pub fn new_static(
plan: &TablePlan,
data: *mut *mut u8,
maximum: u32,
limiter: Option<&Rc<dyn ResourceLimiter>>,
data: &'static mut [usize],
limiter: Option<&mut dyn ResourceLimiter>,
) -> Result<Self> {
let size = Cell::new(plan.table.minimum);
Self::limit_new(plan, limiter)?;
let size = plan.table.minimum;
let ty = plan.table.ty.clone();
let maximum = min(plan.table.maximum.unwrap_or(maximum), maximum);
let storage = TableStorage::Static {
data,
size,
ty,
maximum,
let data = match plan.table.maximum {
Some(max) if (max as usize) < data.len() => &mut data[..max as usize],
_ => data,
};
Self::new(plan, storage, limiter)
Ok(Table::Static { data, size, ty })
}
fn new(
plan: &TablePlan,
storage: TableStorage,
limiter: Option<&Rc<dyn ResourceLimiter>>,
) -> Result<Self> {
fn limit_new(plan: &TablePlan, limiter: Option<&mut dyn ResourceLimiter>) -> Result<()> {
if let Some(limiter) = limiter {
if !limiter.table_growing(0, plan.table.minimum, plan.table.maximum) {
bail!(
@@ -167,24 +166,20 @@ impl Table {
);
}
}
Ok(Self {
storage,
limiter: limiter.cloned(),
})
Ok(())
}
/// Returns the type of the elements in this table.
pub fn element_type(&self) -> TableElementType {
match &self.storage {
TableStorage::Static { ty, .. } => *ty,
TableStorage::Dynamic { ty, .. } => *ty,
match self {
Table::Static { ty, .. } => *ty,
Table::Dynamic { ty, .. } => *ty,
}
}
/// Returns whether or not the underlying storage of the table is "static".
pub(crate) fn is_static(&self) -> bool {
if let TableStorage::Static { .. } = &self.storage {
if let Table::Static { .. } = self {
true
} else {
false
@@ -193,9 +188,9 @@ impl Table {
/// Returns the number of allocated elements.
pub fn size(&self) -> u32 {
match &self.storage {
TableStorage::Static { size, .. } => size.get(),
TableStorage::Dynamic { elements, .. } => elements.borrow().len().try_into().unwrap(),
match self {
Table::Static { size, .. } => *size,
Table::Dynamic { elements, .. } => elements.len().try_into().unwrap(),
}
}
@@ -206,9 +201,9 @@ impl Table {
/// The runtime maximum may not be equal to the maximum from the table's Wasm type
/// when it is being constrained by an instance allocator.
pub fn maximum(&self) -> Option<u32> {
match &self.storage {
TableStorage::Static { maximum, .. } => Some(*maximum),
TableStorage::Dynamic { maximum, .. } => maximum.clone(),
match self {
Table::Static { data, .. } => Some(data.len() as u32),
Table::Dynamic { maximum, .. } => maximum.clone(),
}
}
@@ -216,32 +211,31 @@ impl Table {
///
/// Returns a trap error on out-of-bounds accesses.
pub fn init_funcs(
&self,
&mut self,
dst: u32,
items: impl ExactSizeIterator<Item = *mut VMCallerCheckedAnyfunc>,
) -> Result<(), Trap> {
assert!(self.element_type() == TableElementType::Func);
self.with_elements_mut(|elements| {
let elements = match elements
.get_mut(usize::try_from(dst).unwrap()..)
.and_then(|s| s.get_mut(..items.len()))
{
Some(elements) => elements,
None => return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds)),
};
let elements = match self
.elements_mut()
.get_mut(usize::try_from(dst).unwrap()..)
.and_then(|s| s.get_mut(..items.len()))
{
Some(elements) => elements,
None => return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds)),
};
for (item, slot) in items.zip(elements) {
*slot = item as *mut u8;
}
Ok(())
})
for (item, slot) in items.zip(elements) {
*slot = item as usize;
}
Ok(())
}
/// Fill `table[dst..dst + len]` with `val`.
///
/// Returns a trap error on out-of-bounds accesses.
pub fn fill(&self, dst: u32, val: TableElement, len: u32) -> Result<(), Trap> {
pub fn fill(&mut self, dst: u32, val: TableElement, len: u32) -> Result<(), Trap> {
let start = dst as usize;
let end = start
.checked_add(len as usize)
@@ -253,19 +247,16 @@ impl Table {
debug_assert!(self.type_matches(&val));
self.with_elements_mut(|elements| {
if let Some((last, elements)) = elements[start..end].split_last_mut() {
let ty = self.element_type();
for e in elements {
Self::set_raw(ty, e, val.clone());
}
Self::set_raw(self.element_type(), last, val);
let ty = self.element_type();
if let Some((last, elements)) = self.elements_mut()[start..end].split_last_mut() {
for e in elements {
Self::set_raw(ty, e, val.clone());
}
Ok(())
})
Self::set_raw(ty, last, val);
}
Ok(())
}
/// Grow table by the specified amount of elements.
@@ -284,11 +275,16 @@ impl Table {
///
/// Generally, prefer using `InstanceHandle::table_grow`, which encapsulates
/// this unsafety.
pub unsafe fn grow(&self, delta: u32, init_value: TableElement) -> Option<u32> {
pub unsafe fn grow(
&mut self,
delta: u32,
init_value: TableElement,
limiter: Option<&mut dyn ResourceLimiter>,
) -> Option<u32> {
let old_size = self.size();
let new_size = old_size.checked_add(delta)?;
if let Some(limiter) = &self.limiter {
if let Some(limiter) = limiter {
if !limiter.table_growing(old_size, new_size, self.maximum()) {
return None;
}
@@ -303,13 +299,15 @@ impl Table {
debug_assert!(self.type_matches(&init_value));
// First resize the storage and then fill with the init value
match &self.storage {
TableStorage::Static { size, .. } => {
size.set(new_size);
match self {
Table::Static { size, data, .. } => {
debug_assert!(data[*size as usize..new_size as usize]
.iter()
.all(|x| *x == 0));
*size = new_size;
}
TableStorage::Dynamic { elements, .. } => {
let mut elements = elements.borrow_mut();
elements.resize(new_size as usize, ptr::null_mut());
Table::Dynamic { elements, .. } => {
elements.resize(new_size as usize, 0);
}
}
@@ -323,11 +321,9 @@ impl Table {
///
/// Returns `None` if the index is out of bounds.
pub fn get(&self, index: u32) -> Option<TableElement> {
self.with_elements(|elements| {
elements
.get(index as usize)
.map(|p| unsafe { TableElement::clone_from_raw(self.element_type(), *p) })
})
self.elements()
.get(index as usize)
.map(|p| unsafe { TableElement::clone_from_raw(self.element_type(), *p) })
}
/// Set reference to the specified element.
@@ -336,16 +332,15 @@ impl Table {
///
/// Returns an error if `index` is out of bounds or if this table type does
/// not match the element type.
pub fn set(&self, index: u32, elem: TableElement) -> Result<(), ()> {
pub fn set(&mut self, index: u32, elem: TableElement) -> Result<(), ()> {
if !self.type_matches(&elem) {
return Err(());
}
self.with_elements_mut(|elements| {
let e = elements.get_mut(index as usize).ok_or(())?;
Self::set_raw(self.element_type(), e, elem);
Ok(())
})
let ty = self.element_type();
let e = self.elements_mut().get_mut(index as usize).ok_or(())?;
Self::set_raw(ty, e, elem);
Ok(())
}
/// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`.
@@ -354,9 +349,9 @@ impl Table {
///
/// Returns an error if the range is out of bounds of either the source or
/// destination tables.
pub fn copy(
dst_table: &Self,
src_table: &Self,
pub unsafe fn copy(
dst_table: *mut Self,
src_table: *mut Self,
dst_index: u32,
src_index: u32,
len: u32,
@@ -365,16 +360,16 @@ impl Table {
if src_index
.checked_add(len)
.map_or(true, |n| n > src_table.size())
.map_or(true, |n| n > (*src_table).size())
|| dst_index
.checked_add(len)
.map_or(true, |m| m > dst_table.size())
.map_or(true, |m| m > (*dst_table).size())
{
return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds));
}
debug_assert!(
dst_table.element_type() == src_table.element_type(),
(*dst_table).element_type() == (*src_table).element_type(),
"table element type mismatch"
);
@@ -383,9 +378,9 @@ impl Table {
// Check if the tables are the same as we cannot mutably borrow and also borrow the same `RefCell`
if ptr::eq(dst_table, src_table) {
Self::copy_elements_within(dst_table, dst_range, src_range);
(*dst_table).copy_elements_within(dst_range, src_range);
} else {
Self::copy_elements(dst_table, src_table, dst_range, src_range);
Self::copy_elements(&mut *dst_table, &*src_table, dst_range, src_range);
}
Ok(())
@@ -393,18 +388,15 @@ impl Table {
/// Return a `VMTableDefinition` for exposing the table to compiled wasm code.
pub fn vmtable(&self) -> VMTableDefinition {
match &self.storage {
TableStorage::Static { data, size, .. } => VMTableDefinition {
base: *data as _,
current_elements: size.get(),
match self {
Table::Static { data, size, .. } => VMTableDefinition {
base: data.as_ptr() as *mut _,
current_elements: *size,
},
Table::Dynamic { elements, .. } => VMTableDefinition {
base: elements.as_ptr() as _,
current_elements: elements.len().try_into().unwrap(),
},
TableStorage::Dynamic { elements, .. } => {
let elements = elements.borrow();
VMTableDefinition {
base: elements.as_ptr() as _,
current_elements: elements.len().try_into().unwrap(),
}
}
}
}
@@ -416,37 +408,21 @@ impl Table {
}
}
fn with_elements<F, R>(&self, f: F) -> R
where
F: FnOnce(&[*mut u8]) -> R,
{
match &self.storage {
TableStorage::Static { data, size, .. } => unsafe {
f(std::slice::from_raw_parts(*data, size.get() as usize))
},
TableStorage::Dynamic { elements, .. } => {
let elements = elements.borrow();
f(elements.as_slice())
}
fn elements(&self) -> &[usize] {
match self {
Table::Static { data, size, .. } => &data[..*size as usize],
Table::Dynamic { elements, .. } => &elements[..],
}
}
fn with_elements_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut [*mut u8]) -> R,
{
match &self.storage {
TableStorage::Static { data, size, .. } => unsafe {
f(std::slice::from_raw_parts_mut(*data, size.get() as usize))
},
TableStorage::Dynamic { elements, .. } => {
let mut elements = elements.borrow_mut();
f(elements.as_mut_slice())
}
fn elements_mut(&mut self) -> &mut [usize] {
match self {
Table::Static { data, size, .. } => &mut data[..*size as usize],
Table::Dynamic { elements, .. } => &mut elements[..],
}
}
fn set_raw(ty: TableElementType, elem: &mut *mut u8, val: TableElement) {
fn set_raw(ty: TableElementType, elem: &mut usize, val: TableElement) {
unsafe {
let old = *elem;
*elem = val.into_raw();
@@ -457,7 +433,7 @@ impl Table {
}
fn copy_elements(
dst_table: &Self,
dst_table: &mut Self,
src_table: &Self,
dst_range: Range<usize>,
src_range: Range<usize>,
@@ -470,47 +446,43 @@ impl Table {
match ty {
TableElementType::Func => {
// `funcref` are `Copy`, so just do a mempcy
dst_table.with_elements_mut(|dst| {
src_table.with_elements(|src| dst[dst_range].copy_from_slice(&src[src_range]))
});
dst_table.elements_mut()[dst_range]
.copy_from_slice(&src_table.elements()[src_range]);
}
TableElementType::Val(_) => {
// We need to clone each `externref`
dst_table.with_elements_mut(|dst| {
src_table.with_elements(|src| {
for (s, d) in src_range.zip(dst_range) {
let elem = unsafe { TableElement::clone_from_raw(ty, src[s]) };
Self::set_raw(ty, &mut dst[d], elem);
}
})
});
let dst = dst_table.elements_mut();
let src = src_table.elements();
for (s, d) in src_range.zip(dst_range) {
let elem = unsafe { TableElement::clone_from_raw(ty, src[s]) };
Self::set_raw(ty, &mut dst[d], elem);
}
}
}
}
fn copy_elements_within(table: &Self, dst_range: Range<usize>, src_range: Range<usize>) {
let ty = table.element_type();
fn copy_elements_within(&mut self, dst_range: Range<usize>, src_range: Range<usize>) {
let ty = self.element_type();
let dst = self.elements_mut();
match ty {
TableElementType::Func => {
// `funcref` are `Copy`, so just do a memmove
table.with_elements_mut(|dst| dst.copy_within(src_range, dst_range.start));
dst.copy_within(src_range, dst_range.start);
}
TableElementType::Val(_) => {
// We need to clone each `externref` while handling overlapping ranges
table.with_elements_mut(|dst| {
if dst_range.start <= src_range.start {
for (s, d) in src_range.zip(dst_range) {
let elem = unsafe { TableElement::clone_from_raw(ty, dst[s]) };
Self::set_raw(ty, &mut dst[d], elem);
}
} else {
for (s, d) in src_range.rev().zip(dst_range.rev()) {
let elem = unsafe { TableElement::clone_from_raw(ty, dst[s]) };
Self::set_raw(ty, &mut dst[d], elem);
}
// We need to clone each `externref` while handling overlapping
// ranges
if dst_range.start <= src_range.start {
for (s, d) in src_range.zip(dst_range) {
let elem = unsafe { TableElement::clone_from_raw(ty, dst[s]) };
Self::set_raw(ty, &mut dst[d], elem);
}
});
} else {
for (s, d) in src_range.rev().zip(dst_range.rev()) {
let elem = unsafe { TableElement::clone_from_raw(ty, dst[s]) };
Self::set_raw(ty, &mut dst[d], elem);
}
}
}
}
}
@@ -526,25 +498,19 @@ impl Drop for Table {
}
// Properly drop any table elements stored in the table
self.with_elements(|elements| {
for element in elements.iter() {
let _ = unsafe { TableElement::from_raw(ty, *element) };
}
});
for element in self.elements() {
drop(unsafe { TableElement::from_raw(ty, *element) });
}
}
}
// The default table representation is an empty funcref table that cannot grow.
impl Default for Table {
fn default() -> Self {
Self {
storage: TableStorage::Static {
data: std::ptr::null_mut(),
size: Cell::new(0),
ty: TableElementType::Func,
maximum: 0,
},
limiter: None,
Table::Static {
data: &mut [],
size: 0,
ty: TableElementType::Func,
}
}
}

View File

@@ -1,7 +1,7 @@
//! WebAssembly trap handling, which is built on top of the lower-level
//! signalhandling mechanisms.
use crate::VMInterrupts;
use crate::{VMContext, VMInterrupts};
use backtrace::Backtrace;
use std::any::Any;
use std::cell::{Cell, UnsafeCell};
@@ -15,10 +15,12 @@ use wasmtime_environ::ir;
pub use self::tls::TlsRestore;
extern "C" {
#[allow(improper_ctypes)]
fn RegisterSetjmp(
jmp_buf: *mut *const u8,
callback: extern "C" fn(*mut u8),
callback: extern "C" fn(*mut u8, *mut VMContext),
payload: *mut u8,
callee: *mut VMContext,
) -> i32;
fn Unwind(jmp_buf: *const u8) -> !;
}
@@ -52,7 +54,7 @@ static mut IS_WASM_PC: fn(usize) -> bool = |_| false;
/// This function must not only be called globally once before entering
/// WebAssembly but it must also be called once-per-thread that enters
/// WebAssembly. Currently in wasmtime's integration this function is called on
/// creation of a `Store`.
/// creation of a `Engine`.
///
/// The `is_wasm_pc` argument is used when a trap happens to determine if a
/// program counter is the pc of an actual wasm trap or not. This is then used
@@ -165,76 +167,42 @@ impl Trap {
/// returning them as a `Result`.
///
/// Highly unsafe since `closure` won't have any dtors run.
pub unsafe fn catch_traps<F>(trap_info: &impl TrapInfo, mut closure: F) -> Result<(), Trap>
pub unsafe fn catch_traps<'a, F>(
vminterrupts: *mut VMInterrupts,
signal_handler: Option<*const SignalHandler<'static>>,
callee: *mut VMContext,
mut closure: F,
) -> Result<(), Trap>
where
F: FnMut(),
F: FnMut(*mut VMContext),
{
return CallThreadState::new(trap_info).with(|cx| {
return CallThreadState::new(signal_handler).with(vminterrupts, |cx| {
RegisterSetjmp(
cx.jmp_buf.as_ptr(),
call_closure::<F>,
&mut closure as *mut F as *mut u8,
callee,
)
});
extern "C" fn call_closure<F>(payload: *mut u8)
extern "C" fn call_closure<F>(payload: *mut u8, callee: *mut VMContext)
where
F: FnMut(),
F: FnMut(*mut VMContext),
{
unsafe { (*(payload as *mut F))() }
unsafe { (*(payload as *mut F))(callee) }
}
}
/// Runs `func` with the last `trap_info` object registered by `catch_traps`.
///
/// Calls `func` with `None` if `catch_traps` wasn't previously called from this
/// stack frame.
pub fn with_last_info<R>(func: impl FnOnce(Option<&dyn Any>) -> R) -> R {
tls::with(|state| func(state.map(|s| s.trap_info.as_any())))
}
/// Invokes the contextually-defined context's out-of-gas function.
///
/// (basically delegates to `wasmtime::Store::out_of_gas`)
pub fn out_of_gas() {
tls::with(|state| state.unwrap().trap_info.out_of_gas())
}
/// Temporary state stored on the stack which is registered in the `tls` module
/// below for calls into wasm.
pub struct CallThreadState<'a> {
pub struct CallThreadState {
unwind: UnsafeCell<MaybeUninit<UnwindReason>>,
jmp_buf: Cell<*const u8>,
handling_trap: Cell<bool>,
trap_info: &'a (dyn TrapInfo + 'a),
signal_handler: Option<*const SignalHandler<'static>>,
prev: Cell<tls::Ptr>,
}
/// A package of functionality needed by `catch_traps` to figure out what to do
/// when handling a trap.
///
/// Note that this is an `unsafe` trait at least because it's being run in the
/// context of a synchronous signal handler, so it needs to be careful to not
/// access too much state in answering these queries.
pub unsafe trait TrapInfo {
/// Converts this object into an `Any` to dynamically check its type.
fn as_any(&self) -> &dyn Any;
/// Uses `call` to call a custom signal handler, if one is specified.
///
/// Returns `true` if `call` returns true, otherwise returns `false`.
fn custom_signal_handler(&self, call: &dyn Fn(&SignalHandler) -> bool) -> bool;
/// Callback invoked whenever WebAssembly has entirely consumed the fuel
/// that it was allotted.
///
/// This function may return, and it may also `raise_lib_trap`.
fn out_of_gas(&self);
/// Returns the VM interrupts to use for interrupting Wasm code.
fn interrupts(&self) -> &VMInterrupts;
}
enum UnwindReason {
Panic(Box<dyn Any + Send>),
UserTrap(Box<dyn Error + Send + Sync>),
@@ -242,19 +210,23 @@ enum UnwindReason {
JitTrap { backtrace: Backtrace, pc: usize },
}
impl<'a> CallThreadState<'a> {
impl CallThreadState {
#[inline]
fn new(trap_info: &'a (dyn TrapInfo + 'a)) -> CallThreadState<'a> {
fn new(signal_handler: Option<*const SignalHandler<'static>>) -> CallThreadState {
CallThreadState {
unwind: UnsafeCell::new(MaybeUninit::uninit()),
jmp_buf: Cell::new(ptr::null()),
handling_trap: Cell::new(false),
trap_info,
signal_handler,
prev: Cell::new(ptr::null()),
}
}
fn with(self, closure: impl FnOnce(&CallThreadState) -> i32) -> Result<(), Trap> {
fn with(
self,
interrupts: *mut VMInterrupts,
closure: impl FnOnce(&CallThreadState) -> i32,
) -> Result<(), Trap> {
let ret = tls::set(&self, || closure(&self))?;
if ret != 0 {
return Ok(());
@@ -263,9 +235,9 @@ impl<'a> CallThreadState<'a> {
UnwindReason::UserTrap(data) => Err(Trap::User(data)),
UnwindReason::LibTrap(trap) => Err(trap),
UnwindReason::JitTrap { backtrace, pc } => {
let interrupts = self.trap_info.interrupts();
let maybe_interrupted =
interrupts.stack_limit.load(SeqCst) == wasmtime_environ::INTERRUPTED;
let maybe_interrupted = unsafe {
(*interrupts).stack_limit.load(SeqCst) == wasmtime_environ::INTERRUPTED
};
Err(Trap::Jit {
pc,
backtrace,
@@ -322,8 +294,10 @@ impl<'a> CallThreadState<'a> {
// First up see if any instance registered has a custom trap handler,
// in which case run them all. If anything handles the trap then we
// return that the trap was handled.
if self.trap_info.custom_signal_handler(&call_handler) {
return 1 as *const _;
if let Some(handler) = self.signal_handler {
if unsafe { call_handler(&*handler) } {
return 1 as *const _;
}
}
// If this fault wasn't in wasm code, then it's not our problem
@@ -366,7 +340,6 @@ impl<T: Copy> Drop for ResetCell<'_, T> {
mod tls {
use super::CallThreadState;
use crate::Trap;
use std::mem;
use std::ptr;
pub use raw::Ptr;
@@ -388,7 +361,7 @@ mod tls {
use std::cell::Cell;
use std::ptr;
pub type Ptr = *const CallThreadState<'static>;
pub type Ptr = *const CallThreadState;
// The first entry here is the `Ptr` which is what's used as part of the
// public interface of this module. The second entry is a boolean which
@@ -460,10 +433,11 @@ mod tls {
/// Configures thread local state such that for the duration of the
/// execution of `closure` any call to `with` will yield `ptr`, unless this
/// is recursively called again.
pub fn set<R>(state: &CallThreadState<'_>, closure: impl FnOnce() -> R) -> Result<R, Trap> {
struct Reset<'a, 'b>(&'a CallThreadState<'b>);
#[inline]
pub fn set<R>(state: &CallThreadState, closure: impl FnOnce() -> R) -> Result<R, Trap> {
struct Reset<'a>(&'a CallThreadState);
impl Drop for Reset<'_, '_> {
impl Drop for Reset<'_> {
#[inline]
fn drop(&mut self) {
raw::replace(self.0.prev.replace(ptr::null()))
@@ -471,13 +445,7 @@ mod tls {
}
}
// Note that this extension of the lifetime to `'static` should be
// safe because we only ever access it below with an anonymous
// lifetime, meaning `'static` never leaks out of this module.
let ptr = unsafe {
mem::transmute::<*const CallThreadState<'_>, *const CallThreadState<'static>>(state)
};
let prev = raw::replace(ptr)?;
let prev = raw::replace(state)?;
state.prev.set(prev);
let _reset = Reset(state);
Ok(closure())
@@ -485,7 +453,7 @@ mod tls {
/// Returns the last pointer configured with `set` above. Panics if `set`
/// has not been previously called.
pub fn with<R>(closure: impl FnOnce(Option<&CallThreadState<'_>>) -> R) -> R {
pub fn with<R>(closure: impl FnOnce(Option<&CallThreadState>) -> R) -> R {
let p = raw::get();
unsafe { closure(if p.is_null() { None } else { Some(&*p) }) }
}

View File

@@ -149,7 +149,7 @@ use mach_addons::*;
pub enum Void {}
/// For now this is basically unused, we don't expose this any more for
/// Wasmtime on macOS.
pub type SignalHandler<'a> = dyn Fn(Void) -> bool + 'a;
pub type SignalHandler<'a> = dyn Fn(Void) -> bool + Send + Sync + 'a;
/// Process-global port that we use to route thread-level exceptions to.
static mut WASMTIME_PORT: mach_port_name_t = MACH_PORT_NULL;

View File

@@ -7,7 +7,7 @@ use std::ptr::{self, null_mut};
/// Function which may handle custom signals while processing traps.
pub type SignalHandler<'a> =
dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + 'a;
dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + Send + Sync + 'a;
static mut PREV_SIGSEGV: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();

View File

@@ -6,7 +6,8 @@ use winapi::um::winnt::*;
use winapi::vc::excpt::*;
/// Function which may handle custom signals while processing traps.
pub type SignalHandler<'a> = dyn Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool + 'a;
pub type SignalHandler<'a> =
dyn Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool + Send + Sync + 'a;
pub unsafe fn platform_init() {
// our trap handler needs to go first, so that we can recover from

View File

@@ -5,6 +5,7 @@ use crate::externref::VMExternRef;
use crate::instance::Instance;
use std::any::Any;
use std::cell::UnsafeCell;
use std::marker;
use std::ptr::NonNull;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::u32;
@@ -21,6 +22,11 @@ pub struct VMFunctionImport {
pub vmctx: *mut VMContext,
}
// Declare that this type is send/sync, it's the responsibility of users of
// `VMFunctionImport` to uphold this guarantee.
unsafe impl Send for VMFunctionImport {}
unsafe impl Sync for VMFunctionImport {}
#[cfg(test)]
mod test_vmfunction_import {
use super::VMFunctionImport;
@@ -77,6 +83,11 @@ pub struct VMTableImport {
pub vmctx: *mut VMContext,
}
// Declare that this type is send/sync, it's the responsibility of users of
// `VMTableImport` to uphold this guarantee.
unsafe impl Send for VMTableImport {}
unsafe impl Sync for VMTableImport {}
#[cfg(test)]
mod test_vmtable_import {
use super::VMTableImport;
@@ -115,6 +126,11 @@ pub struct VMMemoryImport {
pub vmctx: *mut VMContext,
}
// Declare that this type is send/sync, it's the responsibility of users of
// `VMMemoryImport` to uphold this guarantee.
unsafe impl Send for VMMemoryImport {}
unsafe impl Sync for VMMemoryImport {}
#[cfg(test)]
mod test_vmmemory_import {
use super::VMMemoryImport;
@@ -150,6 +166,11 @@ pub struct VMGlobalImport {
pub from: *mut VMGlobalDefinition,
}
// Declare that this type is send/sync, it's the responsibility of users of
// `VMGlobalImport` to uphold this guarantee.
unsafe impl Send for VMGlobalImport {}
unsafe impl Sync for VMGlobalImport {}
#[cfg(test)]
mod test_vmglobal_import {
use super::VMGlobalImport;
@@ -259,7 +280,7 @@ mod test_vmtable_definition {
///
/// TODO: Pack the globals more densely, rather than using the same size
/// for every type.
#[derive(Debug, Copy, Clone)]
#[derive(Debug)]
#[repr(C, align(16))]
pub struct VMGlobalDefinition {
storage: [u8; 16],
@@ -524,6 +545,9 @@ pub struct VMCallerCheckedAnyfunc {
// If more elements are added here, remember to add offset_of tests below!
}
unsafe impl Send for VMCallerCheckedAnyfunc {}
unsafe impl Sync for VMCallerCheckedAnyfunc {}
#[cfg(test)]
mod test_vmcaller_checked_anyfunc {
use super::VMCallerCheckedAnyfunc;
@@ -682,6 +706,16 @@ pub struct VMInterrupts {
pub fuel_consumed: UnsafeCell<i64>,
}
// The `VMInterrupts` type is a pod-type with no destructor, and we only access
// `stack_limit` from other threads, so add in these trait impls which are
// otherwise not available due to the `fuel_consumed` variable in
// `VMInterrupts`.
//
// Note that users of `fuel_consumed` understand that the unsafety encompasses
// ensuring that it's only mutated/accessed from one thread dynamically.
unsafe impl Send for VMInterrupts {}
unsafe impl Sync for VMInterrupts {}
impl VMInterrupts {
/// Flag that an interrupt should occur
pub fn interrupt(&self) {
@@ -728,7 +762,17 @@ mod test_vminterrupts {
/// TODO: We could move the globals into the `vmctx` allocation too.
#[derive(Debug)]
#[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside
pub struct VMContext {}
pub struct VMContext {
/// There's some more discussion about this within `wasmtime/src/lib.rs` but
/// the idea is that we want to tell the compiler that this contains
/// pointers which transitively refers to itself, to suppress some
/// optimizations that might otherwise assume this doesn't exist.
///
/// The self-referential pointer we care about is the `*mut Store` pointer
/// early on in this context, which if you follow through enough levels of
/// nesting, eventually can refer back to this `VMContext`
pub _marker: marker::PhantomPinned,
}
impl VMContext {
/// Return a mutable reference to the associated `Instance`.
@@ -742,6 +786,11 @@ impl VMContext {
&*((self as *const Self as *mut u8).offset(-Instance::vmctx_offset()) as *const Instance)
}
#[inline]
pub(crate) unsafe fn instance_mut(&mut self) -> &mut Instance {
&mut *((self as *const Self as *mut u8).offset(-Instance::vmctx_offset()) as *mut Instance)
}
/// Return a reference to the host state associated with this `Instance`.
///
/// # Safety

View File

@@ -1,8 +1,8 @@
use anyhow::Context;
use std::path::Path;
use wasi_common::pipe::WritePipe;
use wasmtime::{Linker, Module, Store};
use wasmtime_wasi::sync::{Wasi, WasiCtxBuilder};
use wasmtime::{Engine, Linker, Module, Store};
use wasmtime_wasi::sync::{add_to_linker, WasiCtxBuilder};
pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
run(data, bin_name, workspace, false)
@@ -25,7 +25,10 @@ fn run(
let stderr = WritePipe::new_in_memory();
let r = {
let store = Store::default();
let engine = Engine::default();
let module = Module::new(&engine, &data).context("failed to create wasm module")?;
let mut linker = Linker::new(&engine);
add_to_linker(&mut linker, |cx| cx)?;
// Create our wasi context.
// Additionally register any preopened directories if we have them.
@@ -53,16 +56,10 @@ fn run(
// cap-std-sync does not yet support the sync family of fdflags
builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?;
let wasi = Wasi::new(&store, builder.build());
let mut linker = Linker::new(&store);
wasi.add_to_linker(&mut linker)?;
let module = Module::new(store.engine(), &data).context("failed to create wasm module")?;
let instance = linker.instantiate(&module)?;
let start = instance.get_typed_func::<(), ()>("_start")?;
start.call(()).map_err(anyhow::Error::from)
let mut store = Store::new(&engine, builder.build());
let instance = linker.instantiate(&mut store, &module)?;
let start = instance.get_typed_func::<(), (), _>(&mut store, "_start")?;
start.call(&mut store, ()).map_err(anyhow::Error::from)
};
match r {

View File

@@ -2,7 +2,7 @@ use anyhow::Context;
use std::path::Path;
use wasi_common::pipe::WritePipe;
use wasmtime::{Config, Engine, Linker, Module, Store};
use wasmtime_wasi::tokio::{Wasi, WasiCtxBuilder};
use wasmtime_wasi::tokio::{add_to_linker, WasiCtxBuilder};
pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
run(data, bin_name, workspace, false)
@@ -31,9 +31,10 @@ fn run(
.block_on(async move {
let mut config = Config::new();
config.async_support(true);
Wasi::add_to_config(&mut config);
let engine = Engine::new(&config)?;
let store = Store::new(&engine);
let module = Module::new(&engine, &data).context("failed to create wasm module")?;
let mut linker = Linker::new(&engine);
add_to_linker(&mut linker, |cx| cx)?;
// Create our wasi context.
let mut builder = WasiCtxBuilder::new();
@@ -62,15 +63,14 @@ fn run(
// does not.
builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?;
Wasi::set_context(&store, builder.build())
.map_err(|_| anyhow::anyhow!("wasi set_context failed"))?;
let mut store = Store::new(&engine, builder.build());
let module =
Module::new(store.engine(), &data).context("failed to create wasm module")?;
let linker = Linker::new(&store);
let instance = linker.instantiate_async(&module).await?;
let start = instance.get_typed_func::<(), ()>("_start")?;
start.call_async(()).await.map_err(anyhow::Error::from)
let instance = linker.instantiate_async(&mut store, &module).await?;
let start = instance.get_typed_func::<(), (), _>(&mut store, "_start")?;
start
.call_async(&mut store, ())
.await
.map_err(anyhow::Error::from)
});
match r {

View File

@@ -106,7 +106,7 @@ impl Dir {
}
}
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl WasiDir for Dir {
fn as_any(&self) -> &dyn Any {
self

View File

@@ -20,7 +20,7 @@ impl File {
}
}
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl WasiFile for File {
fn as_any(&self) -> &dyn Any {
self
@@ -119,10 +119,10 @@ impl WasiFile for File {
async fn num_ready_bytes(&self) -> Result<u64, Error> {
Ok(self.0.num_ready_bytes()?)
}
async fn readable(&mut self) -> Result<(), Error> {
async fn readable(&self) -> Result<(), Error> {
Err(Error::badf())
}
async fn writable(&mut self) -> Result<(), Error> {
async fn writable(&self) -> Result<(), Error> {
Err(Error::badf())
}
}

View File

@@ -42,9 +42,7 @@ pub use clocks::clocks_ctx;
pub use sched::sched_ctx;
use cap_rand::RngCore;
use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;
use wasi_common::{table::Table, Error, WasiCtx, WasiFile};
pub struct WasiCtxBuilder(WasiCtx);
@@ -55,7 +53,7 @@ impl WasiCtxBuilder {
random_ctx(),
clocks_ctx(),
sched_ctx(),
Rc::new(RefCell::new(Table::new())),
Table::new(),
))
}
pub fn env(mut self, var: &str, value: &str) -> Result<Self, wasi_common::StringArrayError> {
@@ -124,6 +122,6 @@ impl WasiCtxBuilder {
}
}
pub fn random_ctx() -> RefCell<Box<dyn RngCore>> {
RefCell::new(Box::new(unsafe { cap_rand::rngs::OsRng::default() }))
pub fn random_ctx() -> Box<dyn RngCore + Send + Sync> {
Box::new(unsafe { cap_rand::rngs::OsRng::default() })
}

View File

@@ -21,7 +21,7 @@ impl SyncSched {
Self {}
}
}
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl WasiSched for SyncSched {
async fn poll_oneoff<'a>(&self, poll: &mut Poll<'a>) -> Result<(), Error> {
poll_oneoff(poll).await

View File

@@ -22,7 +22,7 @@ pub fn stdin() -> Stdin {
Stdin(std::io::stdin())
}
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl WasiFile for Stdin {
fn as_any(&self) -> &dyn Any {
self
@@ -103,10 +103,10 @@ impl WasiFile for Stdin {
async fn num_ready_bytes(&self) -> Result<u64, Error> {
Ok(self.0.num_ready_bytes()?)
}
async fn readable(&mut self) -> Result<(), Error> {
async fn readable(&self) -> Result<(), Error> {
Err(Error::badf())
}
async fn writable(&mut self) -> Result<(), Error> {
async fn writable(&self) -> Result<(), Error> {
Err(Error::badf())
}
}
@@ -125,7 +125,7 @@ impl AsRawFd for Stdin {
macro_rules! wasi_file_write_impl {
($ty:ty) => {
#[async_trait::async_trait(?Send)]
#[async_trait::async_trait]
impl WasiFile for $ty {
fn as_any(&self) -> &dyn Any {
self
@@ -209,10 +209,10 @@ macro_rules! wasi_file_write_impl {
async fn num_ready_bytes(&self) -> Result<u64, Error> {
Ok(0)
}
async fn readable(&mut self) -> Result<(), Error> {
async fn readable(&self) -> Result<(), Error> {
Err(Error::badf())
}
async fn writable(&mut self) -> Result<(), Error> {
async fn writable(&self) -> Result<(), Error> {
Err(Error::badf())
}
}

View File

@@ -6,25 +6,23 @@ use crate::string_array::{StringArray, StringArrayError};
use crate::table::Table;
use crate::Error;
use cap_rand::RngCore;
use std::cell::{RefCell, RefMut};
use std::path::{Path, PathBuf};
use std::rc::Rc;
pub struct WasiCtx {
pub args: StringArray,
pub env: StringArray,
pub random: RefCell<Box<dyn RngCore>>,
pub random: Box<dyn RngCore + Send + Sync>,
pub clocks: WasiClocks,
pub sched: Box<dyn WasiSched>,
pub table: Rc<RefCell<Table>>,
pub table: Table,
}
impl WasiCtx {
pub fn new(
random: RefCell<Box<dyn RngCore>>,
random: Box<dyn RngCore + Send + Sync>,
clocks: WasiClocks,
sched: Box<dyn WasiSched>,
table: Rc<RefCell<Table>>,
table: Table,
) -> Self {
let mut s = WasiCtx {
args: StringArray::new(),
@@ -40,13 +38,13 @@ impl WasiCtx {
s
}
pub fn insert_file(&self, fd: u32, file: Box<dyn WasiFile>, caps: FileCaps) {
pub fn insert_file(&mut self, fd: u32, file: Box<dyn WasiFile>, caps: FileCaps) {
self.table()
.insert_at(fd, Box::new(FileEntry::new(caps, file)));
}
pub fn insert_dir(
&self,
&mut self,
fd: u32,
dir: Box<dyn WasiDir>,
caps: DirCaps,
@@ -59,8 +57,8 @@ impl WasiCtx {
);
}
pub fn table(&self) -> RefMut<Table> {
self.table.borrow_mut()
pub fn table(&mut self) -> &mut Table {
&mut self.table
}
pub fn push_arg(&mut self, arg: &str) -> Result<(), StringArrayError> {

View File

@@ -2,8 +2,6 @@ use crate::file::{FdFlags, FileCaps, FileType, Filestat, OFlags, WasiFile};
use crate::{Error, ErrorExt, SystemTimeSpec};
use bitflags::bitflags;
use std::any::Any;
use std::cell::Ref;
use std::ops::Deref;
use std::path::PathBuf;
#[wiggle::async_trait]
@@ -113,14 +111,14 @@ impl DirEntry {
}
}
pub trait DirEntryExt<'a> {
fn get_cap(self, caps: DirCaps) -> Result<Ref<'a, dyn WasiDir>, Error>;
pub trait DirEntryExt {
fn get_cap(&self, caps: DirCaps) -> Result<&dyn WasiDir, Error>;
}
impl<'a> DirEntryExt<'a> for Ref<'a, DirEntry> {
fn get_cap(self, caps: DirCaps) -> Result<Ref<'a, dyn WasiDir>, Error> {
impl DirEntryExt for DirEntry {
fn get_cap(&self, caps: DirCaps) -> Result<&dyn WasiDir, Error> {
self.capable_of_dir(caps)?;
Ok(Ref::map(self, |r| r.dir.deref()))
Ok(&*self.dir)
}
}
@@ -152,17 +150,17 @@ pub struct DirFdStat {
}
pub(crate) trait TableDirExt {
fn get_dir(&self, fd: u32) -> Result<Ref<DirEntry>, Error>;
fn get_dir(&self, fd: u32) -> Result<&DirEntry, Error>;
fn is_preopen(&self, fd: u32) -> bool;
}
impl TableDirExt for crate::table::Table {
fn get_dir(&self, fd: u32) -> Result<Ref<DirEntry>, Error> {
fn get_dir(&self, fd: u32) -> Result<&DirEntry, Error> {
self.get(fd)
}
fn is_preopen(&self, fd: u32) -> bool {
if self.is::<DirEntry>(fd) {
let dir_entry: std::cell::Ref<DirEntry> = self.get(fd).unwrap();
let dir_entry: &DirEntry = self.get(fd).unwrap();
dir_entry.preopen_path.is_some()
} else {
false

View File

@@ -1,11 +1,9 @@
use crate::{Error, ErrorExt, SystemTimeSpec};
use bitflags::bitflags;
use std::any::Any;
use std::cell::{Ref, RefMut};
use std::ops::{Deref, DerefMut};
#[wiggle::async_trait]
pub trait WasiFile: Send {
pub trait WasiFile: Send + Sync {
fn as_any(&self) -> &dyn Any;
async fn datasync(&self) -> Result<(), Error>; // write op
async fn sync(&self) -> Result<(), Error>; // file op
@@ -37,8 +35,8 @@ pub trait WasiFile: Send {
async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error>; // read op
async fn num_ready_bytes(&self) -> Result<u64, Error>; // read op
async fn readable(&mut self) -> Result<(), Error>;
async fn writable(&mut self) -> Result<(), Error>;
async fn readable(&self) -> Result<(), Error>;
async fn writable(&self) -> Result<(), Error>;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -86,14 +84,14 @@ pub struct Filestat {
}
pub(crate) trait TableFileExt {
fn get_file(&self, fd: u32) -> Result<Ref<FileEntry>, Error>;
fn get_file_mut(&self, fd: u32) -> Result<RefMut<FileEntry>, Error>;
fn get_file(&self, fd: u32) -> Result<&FileEntry, Error>;
fn get_file_mut(&mut self, fd: u32) -> Result<&mut FileEntry, Error>;
}
impl TableFileExt for crate::table::Table {
fn get_file(&self, fd: u32) -> Result<Ref<FileEntry>, Error> {
fn get_file(&self, fd: u32) -> Result<&FileEntry, Error> {
self.get(fd)
}
fn get_file_mut(&self, fd: u32) -> Result<RefMut<FileEntry>, Error> {
fn get_file_mut(&mut self, fd: u32) -> Result<&mut FileEntry, Error> {
self.get_mut(fd)
}
}
@@ -131,24 +129,20 @@ impl FileEntry {
}
}
pub trait FileEntryExt<'a> {
fn get_cap(self, caps: FileCaps) -> Result<Ref<'a, dyn WasiFile>, Error>;
pub trait FileEntryExt {
fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error>;
fn get_cap_mut(&mut self, caps: FileCaps) -> Result<&mut dyn WasiFile, Error>;
}
impl<'a> FileEntryExt<'a> for Ref<'a, FileEntry> {
fn get_cap(self, caps: FileCaps) -> Result<Ref<'a, dyn WasiFile>, Error> {
impl FileEntryExt for FileEntry {
fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error> {
self.capable_of(caps)?;
Ok(Ref::map(self, |r| r.file.deref()))
Ok(&*self.file)
}
}
pub trait FileEntryMutExt<'a> {
fn get_cap(self, caps: FileCaps) -> Result<RefMut<'a, dyn WasiFile>, Error>;
}
impl<'a> FileEntryMutExt<'a> for RefMut<'a, FileEntry> {
fn get_cap(self, caps: FileCaps) -> Result<RefMut<'a, dyn WasiFile>, Error> {
fn get_cap_mut(&mut self, caps: FileCaps) -> Result<&mut dyn WasiFile, Error> {
self.capable_of(caps)?;
Ok(RefMut::map(self, |r| r.file.deref_mut()))
Ok(&mut *self.file)
}
}

View File

@@ -23,15 +23,13 @@ use std::sync::{Arc, RwLock};
/// A variety of `From` impls are provided so that common pipe types are easy to create. For example:
///
/// ```no_run
/// # use std::rc::Rc;
/// # use std::cell::RefCell;
/// use wasi_common::{pipe::ReadPipe, WasiCtx, Table};
/// let stdin = ReadPipe::from("hello from stdin!");
/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):
/// let random = todo!();
/// let clocks = todo!();
/// let sched = todo!();
/// let table = Rc::new(RefCell::new(Table::new()));
/// let table = Table::new();
/// let mut ctx = WasiCtx::new(random, clocks, sched, table);
/// ctx.set_stdin(Box::new(stdin.clone()));
/// ```
@@ -182,10 +180,10 @@ impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {
async fn num_ready_bytes(&self) -> Result<u64, Error> {
Ok(0)
}
async fn readable(&mut self) -> Result<(), Error> {
async fn readable(&self) -> Result<(), Error> {
Err(Error::badf())
}
async fn writable(&mut self) -> Result<(), Error> {
async fn writable(&self) -> Result<(), Error> {
Err(Error::badf())
}
}
@@ -193,15 +191,13 @@ impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {
/// A virtual pipe write end.
///
/// ```no_run
/// # use std::rc::Rc;
/// # use std::cell::RefCell;
/// use wasi_common::{pipe::WritePipe, WasiCtx, Table};
/// let stdout = WritePipe::new_in_memory();
/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):
/// let random = todo!();
/// let clocks = todo!();
/// let sched = todo!();
/// let table = Rc::new(RefCell::new(Table::new()));
/// let table = Table::new();
/// let mut ctx = WasiCtx::new(random, clocks, sched, table);
/// ctx.set_stdout(Box::new(stdout.clone()));
/// // use ctx in an instance, then make sure it is dropped:
@@ -340,10 +336,10 @@ impl<W: Write + Any + Send + Sync> WasiFile for WritePipe<W> {
async fn num_ready_bytes(&self) -> Result<u64, Error> {
Ok(0)
}
async fn readable(&mut self) -> Result<(), Error> {
async fn readable(&self) -> Result<(), Error> {
Err(Error::badf())
}
async fn writable(&mut self) -> Result<(), Error> {
async fn writable(&self) -> Result<(), Error> {
Err(Error::badf())
}
}

View File

@@ -10,7 +10,7 @@ pub use subscription::{
};
#[wiggle::async_trait]
pub trait WasiSched {
pub trait WasiSched: Send + Sync {
async fn poll_oneoff<'a>(&self, poll: &mut Poll<'a>) -> Result<(), Error>;
async fn sched_yield(&self) -> Result<(), Error>;
async fn sleep(&self, duration: Duration) -> Result<(), Error>;
@@ -56,11 +56,11 @@ impl<'a> Poll<'a> {
ud,
));
}
pub fn subscribe_read(&mut self, file: &'a mut dyn WasiFile, ud: Userdata) {
pub fn subscribe_read(&mut self, file: &'a dyn WasiFile, ud: Userdata) {
self.subs
.push((Subscription::Read(RwSubscription::new(file)), ud));
}
pub fn subscribe_write(&mut self, file: &'a mut dyn WasiFile, ud: Userdata) {
pub fn subscribe_write(&mut self, file: &'a dyn WasiFile, ud: Userdata) {
self.subs
.push((Subscription::Write(RwSubscription::new(file)), ud));
}

View File

@@ -3,7 +3,6 @@ use crate::file::WasiFile;
use crate::Error;
use bitflags::bitflags;
use cap_std::time::{Duration, Instant};
use std::cell::Cell;
bitflags! {
pub struct RwEventFlags: u32 {
@@ -12,24 +11,21 @@ bitflags! {
}
pub struct RwSubscription<'a> {
pub file: &'a mut dyn WasiFile,
status: Cell<Option<Result<(u64, RwEventFlags), Error>>>,
pub file: &'a dyn WasiFile,
status: Option<Result<(u64, RwEventFlags), Error>>,
}
impl<'a> RwSubscription<'a> {
pub fn new(file: &'a mut dyn WasiFile) -> Self {
Self {
file,
status: Cell::new(None),
}
pub fn new(file: &'a dyn WasiFile) -> Self {
Self { file, status: None }
}
pub fn complete(&self, size: u64, flags: RwEventFlags) {
self.status.set(Some(Ok((size, flags))))
pub fn complete(&mut self, size: u64, flags: RwEventFlags) {
self.status = Some(Ok((size, flags)))
}
pub fn error(&self, error: Error) {
self.status.set(Some(Err(error)))
pub fn error(&mut self, error: Error) {
self.status = Some(Err(error))
}
pub fn result(&self) -> Option<Result<(u64, RwEventFlags), Error>> {
pub fn result(&mut self) -> Option<Result<(u64, RwEventFlags), Error>> {
self.status.take()
}
}
@@ -72,8 +68,8 @@ pub enum SubscriptionResult {
impl SubscriptionResult {
pub fn from_subscription(s: Subscription) -> Option<SubscriptionResult> {
match s {
Subscription::Read(s) => s.result().map(SubscriptionResult::Read),
Subscription::Write(s) => s.result().map(SubscriptionResult::Write),
Subscription::Read(mut s) => s.result().map(SubscriptionResult::Read),
Subscription::Write(mut s) => s.result().map(SubscriptionResult::Write),
Subscription::MonotonicClock(s) => s.result().map(SubscriptionResult::MonotonicClock),
}
}

View File

@@ -1,4 +1,4 @@
use crate::file::{FileCaps, FileEntryExt, FileEntryMutExt, TableFileExt, WasiFile};
use crate::file::{FileCaps, FileEntryExt, TableFileExt};
use crate::sched::{
subscription::{RwEventFlags, SubscriptionResult},
Poll, Userdata,
@@ -7,7 +7,6 @@ use crate::snapshots::preview_1::types as snapshot1_types;
use crate::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
use crate::{Error, ErrorExt, WasiCtx};
use cap_std::time::Duration;
use std::cell::RefMut;
use std::collections::HashSet;
use std::convert::{TryFrom, TryInto};
use std::io::{IoSlice, IoSliceMut};
@@ -19,6 +18,7 @@ wiggle::from_witx!({
witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"],
errors: { errno => Error },
async: *,
wasmtime: false,
});
impl wiggle::GuestErrorType for types::Errno {
@@ -28,7 +28,7 @@ impl wiggle::GuestErrorType for types::Errno {
}
impl types::UserErrorConversion for WasiCtx {
fn errno_from_error(&self, e: Error) -> Result<types::Errno, wiggle::Trap> {
fn errno_from_error(&mut self, e: Error) -> Result<types::Errno, wiggle::Trap> {
debug!("Error: {:?}", e);
e.try_into()
.map_err(|e| wiggle::Trap::String(format!("{:?}", e)))
@@ -338,35 +338,35 @@ convert_flags_bidirectional!(
#[wiggle::async_trait]
impl wasi_unstable::WasiUnstable for WasiCtx {
async fn args_get<'a>(
&self,
&mut self,
argv: &GuestPtr<'a, GuestPtr<'a, u8>>,
argv_buf: &GuestPtr<'a, u8>,
) -> Result<(), Error> {
Snapshot1::args_get(self, argv, argv_buf).await
}
async fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> {
Snapshot1::args_sizes_get(self).await
}
async fn environ_get<'a>(
&self,
&mut self,
environ: &GuestPtr<'a, GuestPtr<'a, u8>>,
environ_buf: &GuestPtr<'a, u8>,
) -> Result<(), Error> {
Snapshot1::environ_get(self, environ, environ_buf).await
}
async fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> {
Snapshot1::environ_sizes_get(self).await
}
async fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, Error> {
async fn clock_res_get(&mut self, id: types::Clockid) -> Result<types::Timestamp, Error> {
Snapshot1::clock_res_get(self, id.into()).await
}
async fn clock_time_get(
&self,
&mut self,
id: types::Clockid,
precision: types::Timestamp,
) -> Result<types::Timestamp, Error> {
@@ -374,7 +374,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn fd_advise(
&self,
&mut self,
fd: types::Fd,
offset: types::Filesize,
len: types::Filesize,
@@ -384,7 +384,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn fd_allocate(
&self,
&mut self,
fd: types::Fd,
offset: types::Filesize,
len: types::Filesize,
@@ -392,24 +392,28 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
Snapshot1::fd_allocate(self, fd.into(), offset, len).await
}
async fn fd_close(&self, fd: types::Fd) -> Result<(), Error> {
async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> {
Snapshot1::fd_close(self, fd.into()).await
}
async fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> {
async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), Error> {
Snapshot1::fd_datasync(self, fd.into()).await
}
async fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, Error> {
async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result<types::Fdstat, Error> {
Ok(Snapshot1::fd_fdstat_get(self, fd.into()).await?.into())
}
async fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> {
async fn fd_fdstat_set_flags(
&mut self,
fd: types::Fd,
flags: types::Fdflags,
) -> Result<(), Error> {
Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into()).await
}
async fn fd_fdstat_set_rights(
&self,
&mut self,
fd: types::Fd,
fs_rights_base: types::Rights,
fs_rights_inheriting: types::Rights,
@@ -423,12 +427,12 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.await
}
async fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> {
async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result<types::Filestat, Error> {
Ok(Snapshot1::fd_filestat_get(self, fd.into()).await?.into())
}
async fn fd_filestat_set_size(
&self,
&mut self,
fd: types::Fd,
size: types::Filesize,
) -> Result<(), Error> {
@@ -436,7 +440,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn fd_filestat_set_times(
&self,
&mut self,
fd: types::Fd,
atim: types::Timestamp,
mtim: types::Timestamp,
@@ -454,7 +458,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
// representation to a std::io::IoSlice(Mut) representation.
async fn fd_read<'a>(
&self,
&mut self,
fd: types::Fd,
iovs: &types::IovecArray<'a>,
) -> Result<types::Size, Error> {
@@ -480,7 +484,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn fd_pread<'a>(
&self,
&mut self,
fd: types::Fd,
iovs: &types::IovecArray<'a>,
offset: types::Filesize,
@@ -509,7 +513,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn fd_write<'a>(
&self,
&mut self,
fd: types::Fd,
ciovs: &types::CiovecArray<'a>,
) -> Result<types::Size, Error> {
@@ -535,7 +539,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn fd_pwrite<'a>(
&self,
&mut self,
fd: types::Fd,
ciovs: &types::CiovecArray<'a>,
offset: types::Filesize,
@@ -563,12 +567,12 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
Ok(types::Size::try_from(bytes_written)?)
}
async fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> {
async fn fd_prestat_get(&mut self, fd: types::Fd) -> Result<types::Prestat, Error> {
Ok(Snapshot1::fd_prestat_get(self, fd.into()).await?.into())
}
async fn fd_prestat_dir_name<'a>(
&self,
&mut self,
fd: types::Fd,
path: &GuestPtr<'a, u8>,
path_max_len: types::Size,
@@ -576,12 +580,12 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len).await
}
async fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
async fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
Snapshot1::fd_renumber(self, from.into(), to.into()).await
}
async fn fd_seek(
&self,
&mut self,
fd: types::Fd,
offset: types::Filedelta,
whence: types::Whence,
@@ -589,16 +593,16 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
Snapshot1::fd_seek(self, fd.into(), offset, whence.into()).await
}
async fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> {
async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), Error> {
Snapshot1::fd_sync(self, fd.into()).await
}
async fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, Error> {
async fn fd_tell(&mut self, fd: types::Fd) -> Result<types::Filesize, Error> {
Snapshot1::fd_tell(self, fd.into()).await
}
async fn fd_readdir<'a>(
&self,
&mut self,
fd: types::Fd,
buf: &GuestPtr<'a, u8>,
buf_len: types::Size,
@@ -608,7 +612,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_create_directory<'a>(
&self,
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), Error> {
@@ -616,7 +620,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_filestat_get<'a>(
&self,
&mut self,
dirfd: types::Fd,
flags: types::Lookupflags,
path: &GuestPtr<'a, str>,
@@ -629,7 +633,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_filestat_set_times<'a>(
&self,
&mut self,
dirfd: types::Fd,
flags: types::Lookupflags,
path: &GuestPtr<'a, str>,
@@ -650,7 +654,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_link<'a>(
&self,
&mut self,
src_fd: types::Fd,
src_flags: types::Lookupflags,
src_path: &GuestPtr<'a, str>,
@@ -669,7 +673,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_open<'a>(
&self,
&mut self,
dirfd: types::Fd,
dirflags: types::Lookupflags,
path: &GuestPtr<'a, str>,
@@ -693,7 +697,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_readlink<'a>(
&self,
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
buf: &GuestPtr<'a, u8>,
@@ -703,7 +707,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_remove_directory<'a>(
&self,
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), Error> {
@@ -711,7 +715,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_rename<'a>(
&self,
&mut self,
src_fd: types::Fd,
src_path: &GuestPtr<'a, str>,
dest_fd: types::Fd,
@@ -721,7 +725,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_symlink<'a>(
&self,
&mut self,
src_path: &GuestPtr<'a, str>,
dirfd: types::Fd,
dest_path: &GuestPtr<'a, str>,
@@ -730,7 +734,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn path_unlink_file<'a>(
&self,
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), Error> {
@@ -745,7 +749,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
// The bodies of these functions is mostly about converting the GuestPtr and types::-based
// representation to use the Poll abstraction.
async fn poll_oneoff<'a>(
&self,
&mut self,
subs: &GuestPtr<'a, types::Subscription>,
events: &GuestPtr<'a, types::Event>,
nsubscriptions: types::Size,
@@ -779,11 +783,11 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
}
let table = self.table();
let table = &mut self.table;
let mut sub_fds: HashSet<types::Fd> = HashSet::new();
// We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside
let mut read_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new();
let mut write_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new();
let mut reads: Vec<(u32, Userdata)> = Vec::new();
let mut writes: Vec<(u32, Userdata)> = Vec::new();
let mut poll = Poll::new();
let subs = subs.as_array(nsubscriptions);
@@ -828,10 +832,10 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
} else {
sub_fds.insert(fd);
}
let file_ref = table
table
.get_file_mut(u32::from(fd))?
.get_cap(FileCaps::POLL_READWRITE)?;
read_refs.push((file_ref, sub.userdata.into()));
.get_cap_mut(FileCaps::POLL_READWRITE)?;
reads.push((u32::from(fd), sub.userdata.into()));
}
types::SubscriptionU::FdWrite(writesub) => {
let fd = writesub.file_descriptor;
@@ -841,10 +845,10 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
} else {
sub_fds.insert(fd);
}
let file_ref = table
table
.get_file_mut(u32::from(fd))?
.get_cap(FileCaps::POLL_READWRITE)?;
write_refs.push((file_ref, sub.userdata.into()));
.get_cap_mut(FileCaps::POLL_READWRITE)?;
writes.push((u32::from(fd), sub.userdata.into()));
}
}
}
@@ -924,20 +928,20 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
Ok(num_results.try_into().expect("results fit into memory"))
}
async fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap {
async fn proc_exit(&mut self, status: types::Exitcode) -> wiggle::Trap {
Snapshot1::proc_exit(self, status).await
}
async fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> {
async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), Error> {
Err(Error::trap("proc_raise unsupported"))
}
async fn sched_yield(&self) -> Result<(), Error> {
async fn sched_yield(&mut self) -> Result<(), Error> {
Snapshot1::sched_yield(self).await
}
async fn random_get<'a>(
&self,
&mut self,
buf: &GuestPtr<'a, u8>,
buf_len: types::Size,
) -> Result<(), Error> {
@@ -945,7 +949,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn sock_recv<'a>(
&self,
&mut self,
_fd: types::Fd,
_ri_data: &types::IovecArray<'a>,
_ri_flags: types::Riflags,
@@ -954,7 +958,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
}
async fn sock_send<'a>(
&self,
&mut self,
_fd: types::Fd,
_si_data: &types::CiovecArray<'a>,
_si_flags: types::Siflags,
@@ -962,7 +966,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
Err(Error::trap("sock_send unsupported"))
}
async fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
async fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
Err(Error::trap("sock_shutdown unsupported"))
}
}

View File

@@ -1,8 +1,8 @@
use crate::{
dir::{DirCaps, DirEntry, DirEntryExt, DirFdStat, ReaddirCursor, ReaddirEntity, TableDirExt},
file::{
Advice, FdFlags, FdStat, FileCaps, FileEntry, FileEntryExt, FileEntryMutExt, FileType,
Filestat, OFlags, TableFileExt, WasiFile,
Advice, FdFlags, FdStat, FileCaps, FileEntry, FileEntryExt, FileType, Filestat, OFlags,
TableFileExt, WasiFile,
},
sched::{
subscription::{RwEventFlags, SubscriptionResult},
@@ -12,7 +12,6 @@ use crate::{
};
use anyhow::Context;
use cap_std::time::{Duration, SystemClock};
use std::cell::{Ref, RefMut};
use std::collections::HashSet;
use std::convert::{TryFrom, TryInto};
use std::io::{IoSlice, IoSliceMut};
@@ -26,7 +25,8 @@ wiggle::from_witx!({
// Note: not every function actually needs to be async, however, nearly all of them do, and
// keeping that set the same in this macro and the wasmtime_wiggle / lucet_wiggle macros is
// tedious, and there is no cost to having a sync function be async in this case.
async: *
async: *,
wasmtime: false
});
impl wiggle::GuestErrorType for types::Errno {
@@ -36,7 +36,7 @@ impl wiggle::GuestErrorType for types::Errno {
}
impl types::UserErrorConversion for WasiCtx {
fn errno_from_error(&self, e: Error) -> Result<types::Errno, wiggle::Trap> {
fn errno_from_error(&mut self, e: Error) -> Result<types::Errno, wiggle::Trap> {
debug!("Error: {:?}", e);
e.try_into()
.map_err(|e| wiggle::Trap::String(format!("{:?}", e)))
@@ -196,30 +196,30 @@ impl TryFrom<std::io::Error> for types::Errno {
#[wiggle::async_trait]
impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
async fn args_get<'b>(
&self,
&mut self,
argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
argv_buf: &GuestPtr<'b, u8>,
) -> Result<(), Error> {
self.args.write_to_guest(argv_buf, argv)
}
async fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> {
Ok((self.args.number_elements(), self.args.cumulative_size()))
}
async fn environ_get<'b>(
&self,
&mut self,
environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
environ_buf: &GuestPtr<'b, u8>,
) -> Result<(), Error> {
self.env.write_to_guest(environ_buf, environ)
}
async fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> {
async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> {
Ok((self.env.number_elements(), self.env.cumulative_size()))
}
async fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, Error> {
async fn clock_res_get(&mut self, id: types::Clockid) -> Result<types::Timestamp, Error> {
let resolution = match id {
types::Clockid::Realtime => Ok(self.clocks.system.resolution()),
types::Clockid::Monotonic => Ok(self.clocks.monotonic.resolution()),
@@ -231,7 +231,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn clock_time_get(
&self,
&mut self,
id: types::Clockid,
precision: types::Timestamp,
) -> Result<types::Timestamp, Error> {
@@ -256,7 +256,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_advise(
&self,
&mut self,
fd: types::Fd,
offset: types::Filesize,
len: types::Filesize,
@@ -271,7 +271,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_allocate(
&self,
&mut self,
fd: types::Fd,
offset: types::Filesize,
len: types::Filesize,
@@ -284,8 +284,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Ok(())
}
async fn fd_close(&self, fd: types::Fd) -> Result<(), Error> {
let mut table = self.table();
async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> {
let table = self.table();
let fd = u32::from(fd);
// Fail fast: If not present in table, Badf
@@ -297,7 +297,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
let _ = table.delete(fd);
} else if table.is::<DirEntry>(fd) {
// We cannot close preopened directories
let dir_entry: Ref<DirEntry> = table.get(fd).unwrap();
let dir_entry: &DirEntry = table.get(fd).unwrap();
if dir_entry.preopen_path().is_some() {
return Err(Error::not_supported().context("cannot close propened directory"));
}
@@ -310,7 +310,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Ok(())
}
async fn fd_datasync(&self, fd: types::Fd) -> Result<(), Error> {
async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), Error> {
self.table()
.get_file(u32::from(fd))?
.get_cap(FileCaps::DATASYNC)?
@@ -319,15 +319,15 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Ok(())
}
async fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, Error> {
async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result<types::Fdstat, Error> {
let table = self.table();
let fd = u32::from(fd);
if table.is::<FileEntry>(fd) {
let file_entry: Ref<FileEntry> = table.get(fd)?;
let file_entry: &FileEntry = table.get(fd)?;
let fdstat = file_entry.get_fdstat().await?;
Ok(types::Fdstat::from(&fdstat))
} else if table.is::<DirEntry>(fd) {
let dir_entry: Ref<DirEntry> = table.get(fd)?;
let dir_entry: &DirEntry = table.get(fd)?;
let dir_fdstat = dir_entry.get_dir_fdstat();
Ok(types::Fdstat::from(&dir_fdstat))
} else {
@@ -335,16 +335,20 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
}
async fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> {
async fn fd_fdstat_set_flags(
&mut self,
fd: types::Fd,
flags: types::Fdflags,
) -> Result<(), Error> {
self.table()
.get_file_mut(u32::from(fd))?
.get_cap(FileCaps::FDSTAT_SET_FLAGS)?
.get_cap_mut(FileCaps::FDSTAT_SET_FLAGS)?
.set_fdflags(FdFlags::from(flags))
.await
}
async fn fd_fdstat_set_rights(
&self,
&mut self,
fd: types::Fd,
fs_rights_base: types::Rights,
fs_rights_inheriting: types::Rights,
@@ -352,11 +356,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
let table = self.table();
let fd = u32::from(fd);
if table.is::<FileEntry>(fd) {
let mut file_entry: RefMut<FileEntry> = table.get_mut(fd)?;
let file_entry: &mut FileEntry = table.get_mut(fd)?;
let file_caps = FileCaps::from(&fs_rights_base);
file_entry.drop_caps_to(file_caps)
} else if table.is::<DirEntry>(fd) {
let mut dir_entry: RefMut<DirEntry> = table.get_mut(fd)?;
let dir_entry: &mut DirEntry = table.get_mut(fd)?;
let dir_caps = DirCaps::from(&fs_rights_base);
let file_caps = FileCaps::from(&fs_rights_inheriting);
dir_entry.drop_caps_to(dir_caps, file_caps)
@@ -365,7 +369,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
}
async fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, Error> {
async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result<types::Filestat, Error> {
let table = self.table();
let fd = u32::from(fd);
if table.is::<FileEntry>(fd) {
@@ -388,7 +392,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_filestat_set_size(
&self,
&mut self,
fd: types::Fd,
size: types::Filesize,
) -> Result<(), Error> {
@@ -401,7 +405,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_filestat_set_times(
&self,
&mut self,
fd: types::Fd,
atim: types::Timestamp,
mtim: types::Timestamp,
@@ -438,7 +442,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_read<'a>(
&self,
&mut self,
fd: types::Fd,
iovs: &types::IovecArray<'a>,
) -> Result<types::Size, Error> {
@@ -464,7 +468,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_pread<'a>(
&self,
&mut self,
fd: types::Fd,
iovs: &types::IovecArray<'a>,
offset: types::Filesize,
@@ -493,7 +497,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_write<'a>(
&self,
&mut self,
fd: types::Fd,
ciovs: &types::CiovecArray<'a>,
) -> Result<types::Size, Error> {
@@ -519,7 +523,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_pwrite<'a>(
&self,
&mut self,
fd: types::Fd,
ciovs: &types::CiovecArray<'a>,
offset: types::Filesize,
@@ -547,9 +551,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Ok(types::Size::try_from(bytes_written)?)
}
async fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, Error> {
async fn fd_prestat_get(&mut self, fd: types::Fd) -> Result<types::Prestat, Error> {
let table = self.table();
let dir_entry: Ref<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::badf())?;
let dir_entry: &DirEntry = table.get(u32::from(fd)).map_err(|_| Error::badf())?;
if let Some(ref preopen) = dir_entry.preopen_path() {
let path_str = preopen.to_str().ok_or_else(|| Error::not_supported())?;
let pr_name_len = u32::try_from(path_str.as_bytes().len())?;
@@ -560,13 +564,13 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_prestat_dir_name<'a>(
&self,
&mut self,
fd: types::Fd,
path: &GuestPtr<'a, u8>,
path_max_len: types::Size,
) -> Result<(), Error> {
let table = self.table();
let dir_entry: Ref<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?;
let dir_entry: &DirEntry = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?;
if let Some(ref preopen) = dir_entry.preopen_path() {
let path_bytes = preopen
.to_str()
@@ -583,8 +587,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Err(Error::not_supported())
}
}
async fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
let mut table = self.table();
async fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> {
let table = self.table();
let from = u32::from(from);
let to = u32::from(to);
if !table.contains_key(from) {
@@ -601,7 +605,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_seek(
&self,
&mut self,
fd: types::Fd,
offset: types::Filedelta,
whence: types::Whence,
@@ -628,7 +632,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Ok(newoffset)
}
async fn fd_sync(&self, fd: types::Fd) -> Result<(), Error> {
async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), Error> {
self.table()
.get_file(u32::from(fd))?
.get_cap(FileCaps::SYNC)?
@@ -637,7 +641,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Ok(())
}
async fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, Error> {
async fn fd_tell(&mut self, fd: types::Fd) -> Result<types::Filesize, Error> {
// XXX should this be stream_position?
let offset = self
.table()
@@ -649,7 +653,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn fd_readdir<'a>(
&self,
&mut self,
fd: types::Fd,
buf: &GuestPtr<'a, u8>,
buf_len: types::Size,
@@ -703,7 +707,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_create_directory<'a>(
&self,
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), Error> {
@@ -715,7 +719,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_filestat_get<'a>(
&self,
&mut self,
dirfd: types::Fd,
flags: types::Lookupflags,
path: &GuestPtr<'a, str>,
@@ -733,7 +737,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_filestat_set_times<'a>(
&self,
&mut self,
dirfd: types::Fd,
flags: types::Lookupflags,
path: &GuestPtr<'a, str>,
@@ -761,7 +765,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_link<'a>(
&self,
&mut self,
src_fd: types::Fd,
src_flags: types::Lookupflags,
src_path: &GuestPtr<'a, str>,
@@ -791,7 +795,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_open<'a>(
&self,
&mut self,
dirfd: types::Fd,
dirflags: types::Lookupflags,
path: &GuestPtr<'a, str>,
@@ -800,7 +804,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
fs_rights_inheriting: types::Rights,
fdflags: types::Fdflags,
) -> Result<types::Fd, Error> {
let mut table = self.table();
let table = self.table();
let dirfd = u32::from(dirfd);
if table.is::<FileEntry>(dirfd) {
return Err(Error::not_dir());
@@ -850,7 +854,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_readlink<'a>(
&self,
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
buf: &GuestPtr<'a, u8>,
@@ -876,7 +880,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_remove_directory<'a>(
&self,
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), Error> {
@@ -888,7 +892,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_rename<'a>(
&self,
&mut self,
src_fd: types::Fd,
src_path: &GuestPtr<'a, str>,
dest_fd: types::Fd,
@@ -911,7 +915,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_symlink<'a>(
&self,
&mut self,
src_path: &GuestPtr<'a, str>,
dirfd: types::Fd,
dest_path: &GuestPtr<'a, str>,
@@ -924,7 +928,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn path_unlink_file<'a>(
&self,
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), Error> {
@@ -936,7 +940,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn poll_oneoff<'a>(
&self,
&mut self,
subs: &GuestPtr<'a, types::Subscription>,
events: &GuestPtr<'a, types::Event>,
nsubscriptions: types::Size,
@@ -970,11 +974,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
}
let table = self.table();
let table = &mut self.table;
let mut sub_fds: HashSet<types::Fd> = HashSet::new();
// We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside
let mut read_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new();
let mut write_refs: Vec<(RefMut<'_, dyn WasiFile>, Userdata)> = Vec::new();
let mut read_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
let mut write_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
let mut poll = Poll::new();
let subs = subs.as_array(nsubscriptions);
@@ -1020,7 +1024,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
sub_fds.insert(fd);
}
let file_ref = table
.get_file_mut(u32::from(fd))?
.get_file(u32::from(fd))?
.get_cap(FileCaps::POLL_READWRITE)?;
read_refs.push((file_ref, sub.userdata.into()));
}
@@ -1033,7 +1037,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
sub_fds.insert(fd);
}
let file_ref = table
.get_file_mut(u32::from(fd))?
.get_file(u32::from(fd))?
.get_cap(FileCaps::POLL_READWRITE)?;
write_refs.push((file_ref, sub.userdata.into()));
}
@@ -1041,10 +1045,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
for (f, ud) in read_refs.iter_mut() {
poll.subscribe_read(f.deref_mut(), *ud);
poll.subscribe_read(*f, *ud);
}
for (f, ud) in write_refs.iter_mut() {
poll.subscribe_write(f.deref_mut(), *ud);
poll.subscribe_write(*f, *ud);
}
self.sched.poll_oneoff(&mut poll).await?;
@@ -1122,7 +1126,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Ok(num_results.try_into().expect("results fit into memory"))
}
async fn proc_exit(&self, status: types::Exitcode) -> wiggle::Trap {
async fn proc_exit(&mut self, status: types::Exitcode) -> wiggle::Trap {
// Check that the status is within WASI's range.
if status < 126 {
wiggle::Trap::I32Exit(status as i32)
@@ -1131,26 +1135,26 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
}
async fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> {
async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), Error> {
Err(Error::trap("proc_raise unsupported"))
}
async fn sched_yield(&self) -> Result<(), Error> {
async fn sched_yield(&mut self) -> Result<(), Error> {
self.sched.sched_yield().await
}
async fn random_get<'a>(
&self,
&mut self,
buf: &GuestPtr<'a, u8>,
buf_len: types::Size,
) -> Result<(), Error> {
let mut buf = buf.as_array(buf_len).as_slice_mut()?;
self.random.borrow_mut().try_fill_bytes(buf.deref_mut())?;
self.random.try_fill_bytes(buf.deref_mut())?;
Ok(())
}
async fn sock_recv<'a>(
&self,
&mut self,
_fd: types::Fd,
_ri_data: &types::IovecArray<'a>,
_ri_flags: types::Riflags,
@@ -1159,7 +1163,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
}
async fn sock_send<'a>(
&self,
&mut self,
_fd: types::Fd,
_si_data: &types::CiovecArray<'a>,
_si_flags: types::Siflags,
@@ -1167,7 +1171,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
Err(Error::trap("sock_send unsupported"))
}
async fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
async fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> {
Err(Error::trap("sock_shutdown unsupported"))
}
}

View File

@@ -1,6 +1,5 @@
use crate::{Error, ErrorExt};
use std::any::Any;
use std::cell::{Ref, RefCell, RefMut};
use std::collections::HashMap;
/// The `Table` type is designed to map u32 handles to resources. The table is now part of the
@@ -11,7 +10,7 @@ use std::collections::HashMap;
/// The `Table` type is intended to model how the Interface Types concept of Resources is shaping
/// up. Right now it is just an approximation.
pub struct Table {
map: HashMap<u32, RefCell<Box<dyn Any>>>,
map: HashMap<u32, Box<dyn Any + Send + Sync>>,
next_key: u32,
}
@@ -25,12 +24,12 @@ impl Table {
}
/// Insert a resource at a certain index.
pub fn insert_at(&mut self, key: u32, a: Box<dyn Any>) {
self.map.insert(key, RefCell::new(a));
pub fn insert_at(&mut self, key: u32, a: Box<dyn Any + Send + Sync>) {
self.map.insert(key, a);
}
/// Insert a resource at the next available index.
pub fn push(&mut self, a: Box<dyn Any>) -> Result<u32, Error> {
pub fn push(&mut self, a: Box<dyn Any + Send + Sync>) -> Result<u32, Error> {
// NOTE: The performance of this new key calculation could be very bad once keys wrap
// around.
if self.map.len() == u32::MAX as usize {
@@ -42,7 +41,7 @@ impl Table {
if self.map.contains_key(&key) {
continue;
}
self.map.insert(key, RefCell::new(a));
self.map.insert(key, a);
return Ok(key);
}
}
@@ -55,12 +54,8 @@ impl Table {
/// Check if the resource at a given index can be downcast to a given type.
/// Note: this will always fail if the resource is already borrowed.
pub fn is<T: Any + Sized>(&self, key: u32) -> bool {
if let Some(refcell) = self.map.get(&key) {
if let Ok(refmut) = refcell.try_borrow_mut() {
refmut.is::<T>()
} else {
false
}
if let Some(r) = self.map.get(&key) {
r.is::<T>()
} else {
false
}
@@ -69,17 +64,10 @@ impl Table {
/// Get an immutable reference to a resource of a given type at a given index. Multiple
/// immutable references can be borrowed at any given time. Borrow failure
/// results in a trapping error.
pub fn get<T: Any + Sized>(&self, key: u32) -> Result<Ref<T>, Error> {
if let Some(refcell) = self.map.get(&key) {
if let Ok(r) = refcell.try_borrow() {
if r.is::<T>() {
Ok(Ref::map(r, |r| r.downcast_ref::<T>().unwrap()))
} else {
Err(Error::badf().context("element is a different type"))
}
} else {
Err(Error::trap("table get of mutably borrowed element"))
}
pub fn get<T: Any + Sized>(&self, key: u32) -> Result<&T, Error> {
if let Some(r) = self.map.get(&key) {
r.downcast_ref::<T>()
.ok_or_else(|| Error::badf().context("element is a different type"))
} else {
Err(Error::badf().context("key not in table"))
}
@@ -87,17 +75,10 @@ impl Table {
/// Get a mutable reference to a resource of a given type at a given index. Only one mutable
/// reference can be borrowed at any given time. Borrow failure results in a trapping error.
pub fn get_mut<T: Any + Sized>(&self, key: u32) -> Result<RefMut<T>, Error> {
if let Some(refcell) = self.map.get(&key) {
if let Ok(r) = refcell.try_borrow_mut() {
if r.is::<T>() {
Ok(RefMut::map(r, |r| r.downcast_mut::<T>().unwrap()))
} else {
Err(Error::badf().context("element is a different type"))
}
} else {
Err(Error::trap("table get_mut of borrowed element"))
}
pub fn get_mut<T: Any + Sized>(&mut self, key: u32) -> Result<&mut T, Error> {
if let Some(r) = self.map.get_mut(&key) {
r.downcast_mut::<T>()
.ok_or_else(|| Error::badf().context("element is a different type"))
} else {
Err(Error::badf().context("key not in table"))
}
@@ -105,7 +86,7 @@ impl Table {
/// Remove a resource at a given index from the table. Returns the resource
/// if it was present.
pub fn delete(&mut self, key: u32) -> Option<Box<dyn Any>> {
self.map.remove(&key).map(|rc| RefCell::into_inner(rc))
pub fn delete(&mut self, key: u32) -> Option<Box<dyn Any + Send + Sync>> {
self.map.remove(&key)
}
}

View File

@@ -112,7 +112,7 @@ macro_rules! wasi_file_impl {
}
#[cfg(not(windows))]
async fn readable(&mut self) -> Result<(), Error> {
async fn readable(&self) -> Result<(), Error> {
// The Inner impls OwnsRaw, which asserts exclusive use of the handle by the owned object.
// AsyncFd needs to wrap an owned `impl std::os::unix::io::AsRawFd`. Rather than introduce
// mutability to let it own the `Inner`, we are depending on the `&mut self` bound on this
@@ -135,14 +135,14 @@ macro_rules! wasi_file_impl {
}
}
#[cfg(windows)]
async fn readable(&mut self) -> Result<(), Error> {
async fn readable(&self) -> Result<(), Error> {
// Windows uses a rawfd based scheduler :(
use wasi_common::ErrorExt;
Err(Error::badf())
}
#[cfg(not(windows))]
async fn writable(&mut self) -> Result<(), Error> {
async fn writable(&self) -> Result<(), Error> {
// The Inner impls OwnsRaw, which asserts exclusive use of the handle by the owned object.
// AsyncFd needs to wrap an owned `impl std::os::unix::io::AsRawFd`. Rather than introduce
// mutability to let it own the `Inner`, we are depending on the `&mut self` bound on this
@@ -165,7 +165,7 @@ macro_rules! wasi_file_impl {
}
}
#[cfg(windows)]
async fn writable(&mut self) -> Result<(), Error> {
async fn writable(&self) -> Result<(), Error> {
// Windows uses a rawfd based scheduler :(
use wasi_common::ErrorExt;
Err(Error::badf())

View File

@@ -3,10 +3,8 @@ mod file;
pub mod sched;
pub mod stdio;
use std::cell::RefCell;
use std::future::Future;
use std::path::Path;
use std::rc::Rc;
pub use wasi_cap_std_sync::{clocks_ctx, random_ctx};
use wasi_common::{Error, Table, WasiCtx, WasiFile};
@@ -23,7 +21,7 @@ impl WasiCtxBuilder {
random_ctx(),
clocks_ctx(),
sched_ctx(),
Rc::new(RefCell::new(Table::new())),
Table::new(),
))
}
pub fn env(mut self, var: &str, value: &str) -> Result<Self, wasi_common::StringArrayError> {

View File

@@ -9,13 +9,13 @@ use wasi_common::{
Context as _, Error,
};
struct FirstReady<'a, T>(Vec<Pin<Box<dyn Future<Output = T> + 'a>>>);
struct FirstReady<'a, T>(Vec<Pin<Box<dyn Future<Output = T> + Send + 'a>>>);
impl<'a, T> FirstReady<'a, T> {
fn new() -> Self {
FirstReady(Vec::new())
}
fn push(&mut self, f: impl Future<Output = T> + 'a) {
fn push(&mut self, f: impl Future<Output = T> + Send + 'a) {
self.0.push(Box::pin(f));
}
}

View File

@@ -15,7 +15,6 @@ edition = "2018"
anyhow = "1.0"
wasi-crypto = { path = "spec/implementations/hostcalls/rust", version = "0.1.4" }
wasmtime = { path = "../wasmtime", version = "0.27.0", default-features = false }
wasmtime-wiggle = { path = "../wiggle/wasmtime", version = "0.27.0" }
wiggle = { path = "../wiggle", version = "0.27.0" }
[badges]

View File

@@ -2,30 +2,14 @@ mod wiggle_interfaces;
pub use wiggle_interfaces::WasiCryptoCtx;
wasmtime_wiggle::wasmtime_integration!({
target: wiggle_interfaces::wasi_modules,
witx: ["$CARGO_MANIFEST_DIR/spec/witx/wasi_ephemeral_crypto.witx"],
ctx: WasiCryptoCtx,
modules: {
wasi_ephemeral_crypto_common =>
{
name: WasiCryptoCommon,
docs: "wasi-crypto - Common module."
},
wasi_ephemeral_crypto_asymmetric_common =>
{
name: WasiCryptoAsymmetricCommon,
docs: "wasi-crypto - Common module for asymmetric operations."
},
wasi_ephemeral_crypto_signatures =>
{
name: WasiCryptoSignatures,
docs: "wasi-crypto - Signature module."
},
wasi_ephemeral_crypto_symmetric =>
{
name: WasiCryptoSymmetric,
docs: "wasi-crypto - Symmetric cryptography module."
}
}
});
pub fn add_to_linker<T>(
linker: &mut wasmtime::Linker<T>,
get_cx: impl Fn(&mut T) -> &mut WasiCryptoCtx + Send + Sync + Copy + 'static,
) -> anyhow::Result<()> {
use wiggle_interfaces::wasi_modules as w;
w::wasi_ephemeral_crypto_common::add_to_linker(linker, get_cx)?;
w::wasi_ephemeral_crypto_asymmetric_common::add_to_linker(linker, get_cx)?;
w::wasi_ephemeral_crypto_signatures::add_to_linker(linker, get_cx)?;
w::wasi_ephemeral_crypto_symmetric::add_to_linker(linker, get_cx)?;
Ok(())
}

View File

@@ -11,7 +11,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
// --- keypair_manager
fn keypair_generate_managed(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
alg_type: guest_types::AlgorithmType,
alg_str: &wiggle::GuestPtr<'_, str>,
@@ -22,7 +22,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None,
};
Ok(self
Ok((&*self)
.keypair_generate_managed(
secrets_manager_handle.into(),
alg_type.into(),
@@ -33,14 +33,14 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
}
fn keypair_store_managed(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
kp_handle: guest_types::Keypair,
kp_id_ptr: &wiggle::GuestPtr<'_, u8>,
kp_id_max_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> {
let key_id_buf = &mut *kp_id_ptr.as_array(kp_id_max_len).as_slice_mut()?;
Ok(self.keypair_store_managed(
Ok((&*self).keypair_store_managed(
secrets_manager_handle.into(),
kp_handle.into(),
key_id_buf,
@@ -48,12 +48,12 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
}
fn keypair_replace_managed(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
kp_old_handle: guest_types::Keypair,
kp_new_handle: guest_types::Keypair,
) -> Result<guest_types::Version, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.keypair_replace_managed(
secrets_manager_handle.into(),
kp_old_handle.into(),
@@ -63,14 +63,14 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
}
fn keypair_from_id(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
kp_id_ptr: &wiggle::GuestPtr<'_, u8>,
kp_id_len: guest_types::Size,
kp_version: guest_types::Version,
) -> Result<guest_types::Keypair, guest_types::CryptoErrno> {
let kp_id = &*kp_id_ptr.as_array(kp_id_len).as_slice()?;
Ok(self
Ok((&*self)
.keypair_from_id(secrets_manager_handle.into(), kp_id, Version(kp_version))?
.into())
}
@@ -78,7 +78,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
// --- keypair
fn keypair_generate(
&self,
&mut self,
alg_type: guest_types::AlgorithmType,
alg_str: &wiggle::GuestPtr<'_, str>,
options_handle: &guest_types::OptOptions,
@@ -88,13 +88,13 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None,
};
Ok(self
Ok((&*self)
.keypair_generate(alg_type.into(), alg_str, options_handle.map(Into::into))?
.into())
}
fn keypair_import(
&self,
&mut self,
alg_type: guest_types::AlgorithmType,
alg_str: &wiggle::GuestPtr<'_, str>,
encoded_ptr: &wiggle::GuestPtr<'_, u8>,
@@ -103,52 +103,52 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
) -> Result<guest_types::Keypair, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?;
let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?;
Ok(self
Ok((&*self)
.keypair_import(alg_type.into(), alg_str, encoded, encoding.into())?
.into())
}
fn keypair_id(
&self,
&mut self,
kp_handle: guest_types::Keypair,
kp_id_ptr: &wiggle::GuestPtr<'_, u8>,
kp_id_max_len: guest_types::Size,
) -> Result<(guest_types::Size, guest_types::Version), guest_types::CryptoErrno> {
let kp_id_buf = &mut *kp_id_ptr.as_array(kp_id_max_len as _).as_slice_mut()?;
let (kp_id, version) = self.keypair_id(kp_handle.into())?;
let (kp_id, version) = (&*self).keypair_id(kp_handle.into())?;
ensure!(kp_id.len() <= kp_id_buf.len(), CryptoError::Overflow.into());
kp_id_buf.copy_from_slice(&kp_id);
Ok((kp_id.len().try_into()?, version.0))
}
fn keypair_export(
&self,
&mut self,
kp_handle: guest_types::Keypair,
encoding: guest_types::KeypairEncoding,
) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.keypair_export(kp_handle.into(), encoding.into())?
.into())
}
fn keypair_publickey(
&self,
&mut self,
kp_handle: guest_types::Keypair,
) -> Result<guest_types::Publickey, guest_types::CryptoErrno> {
Ok(self.keypair_publickey(kp_handle.into())?.into())
Ok((&*self).keypair_publickey(kp_handle.into())?.into())
}
fn keypair_close(
&self,
&mut self,
kp_handle: guest_types::Keypair,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.keypair_close(kp_handle.into())?)
Ok((&*self).keypair_close(kp_handle.into())?)
}
// --- publickey
fn publickey_import(
&self,
&mut self,
alg_type: guest_types::AlgorithmType,
alg_str: &wiggle::GuestPtr<'_, str>,
encoded_ptr: &wiggle::GuestPtr<'_, u8>,
@@ -157,46 +157,46 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
) -> Result<guest_types::Publickey, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?;
let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?;
Ok(self
Ok((&*self)
.publickey_import(alg_type.into(), alg_str, encoded, encoding.into())?
.into())
}
fn publickey_export(
&self,
&mut self,
pk_handle: guest_types::Publickey,
encoding: guest_types::PublickeyEncoding,
) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.publickey_export(pk_handle.into(), encoding.into())?
.into())
}
fn publickey_from_secretkey(
&self,
&mut self,
sk_handle: guest_types::Secretkey,
) -> Result<guest_types::Publickey, guest_types::CryptoErrno> {
Ok(self.keypair_publickey(sk_handle.into())?.into())
Ok((&*self).keypair_publickey(sk_handle.into())?.into())
}
fn publickey_verify(
&self,
&mut self,
pk_handle: guest_types::Publickey,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.publickey_verify(pk_handle.into())?)
Ok((&*self).publickey_verify(pk_handle.into())?)
}
fn publickey_close(
&self,
&mut self,
pk_handle: guest_types::Publickey,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.publickey_close(pk_handle.into())?)
Ok((&*self).publickey_close(pk_handle.into())?)
}
// --- secretkey
fn secretkey_import(
&self,
&mut self,
alg_type: guest_types::AlgorithmType,
alg_str: &wiggle::GuestPtr<'_, str>,
encoded_ptr: &wiggle::GuestPtr<'_, u8>,
@@ -205,43 +205,43 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
) -> Result<guest_types::Secretkey, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?;
let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?;
Ok(self
Ok((&*self)
.secretkey_import(alg_type.into(), alg_str, encoded, encoding.into())?
.into())
}
fn secretkey_export(
&self,
&mut self,
sk_handle: guest_types::Secretkey,
encoding: guest_types::SecretkeyEncoding,
) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.secretkey_export(sk_handle.into(), encoding.into())?
.into())
}
fn secretkey_close(
&self,
&mut self,
sk_handle: guest_types::Secretkey,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.secretkey_close(sk_handle.into())?)
Ok((&*self).secretkey_close(sk_handle.into())?)
}
fn keypair_from_pk_and_sk(
&self,
&mut self,
pk_handle: guest_types::Publickey,
sk_handle: guest_types::Secretkey,
) -> Result<guest_types::Keypair, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.keypair_from_pk_and_sk(pk_handle.into(), sk_handle.into())?
.into())
}
fn keypair_secretkey(
&self,
&mut self,
kp_handle: guest_types::Keypair,
) -> Result<guest_types::Secretkey, guest_types::CryptoErrno> {
Ok(self.keypair_secretkey(kp_handle.into())?.into())
Ok((&*self).keypair_secretkey(kp_handle.into())?.into())
}
}

View File

@@ -7,21 +7,21 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
// --- options
fn options_open(
&self,
&mut self,
options_type: guest_types::AlgorithmType,
) -> Result<guest_types::Options, guest_types::CryptoErrno> {
Ok(self.options_open(options_type.into())?.into())
Ok((&*self).options_open(options_type.into())?.into())
}
fn options_close(
&self,
&mut self,
options_handle: guest_types::Options,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.options_close(options_handle.into())?)
Ok((&*self).options_close(options_handle.into())?)
}
fn options_set(
&self,
&mut self,
options_handle: guest_types::Options,
name_str: &wiggle::GuestPtr<'_, str>,
value_ptr: &wiggle::GuestPtr<'_, u8>,
@@ -29,11 +29,11 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
) -> Result<(), guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?;
let value: &[u8] = { &*value_ptr.as_array(value_len).as_slice()? };
Ok(self.options_set(options_handle.into(), name_str, value)?)
Ok((&*self).options_set(options_handle.into(), name_str, value)?)
}
fn options_set_guest_buffer(
&self,
&mut self,
options_handle: guest_types::Options,
name_str: &wiggle::GuestPtr<'_, str>,
buffer_ptr: &wiggle::GuestPtr<'_, u8>,
@@ -42,38 +42,38 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
let name_str: &str = &*name_str.as_str()?;
let buffer: &'static mut [u8] =
unsafe { std::mem::transmute(&mut *buffer_ptr.as_array(buffer_len).as_slice_mut()?) };
Ok(self.options_set_guest_buffer(options_handle.into(), name_str, buffer)?)
Ok((&*self).options_set_guest_buffer(options_handle.into(), name_str, buffer)?)
}
fn options_set_u64(
&self,
&mut self,
options_handle: guest_types::Options,
name_str: &wiggle::GuestPtr<'_, str>,
value: u64,
) -> Result<(), guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?;
Ok(self.options_set_u64(options_handle.into(), name_str, value)?)
Ok((&*self).options_set_u64(options_handle.into(), name_str, value)?)
}
// --- array
fn array_output_len(
&self,
&mut self,
array_output_handle: guest_types::ArrayOutput,
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.array_output_len(array_output_handle.into())?
.try_into()?)
}
fn array_output_pull(
&self,
&mut self,
array_output_handle: guest_types::ArrayOutput,
buf_ptr: &wiggle::GuestPtr<'_, u8>,
buf_len: guest_types::Size,
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let buf: &mut [u8] = { &mut *buf_ptr.as_array(buf_len).as_slice_mut()? };
Ok(self
Ok((&*self)
.array_output_pull(array_output_handle.into(), buf)?
.try_into()?)
}
@@ -81,34 +81,34 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
// --- secrets_manager
fn secrets_manager_open(
&self,
&mut self,
options_handle: &guest_types::OptOptions,
) -> Result<guest_types::SecretsManager, guest_types::CryptoErrno> {
let options_handle = match *options_handle {
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None,
};
Ok(self
Ok((&*self)
.secrets_manager_open(options_handle.map(Into::into))?
.into())
}
fn secrets_manager_close(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.secrets_manager_close(secrets_manager_handle.into())?)
Ok((&*self).secrets_manager_close(secrets_manager_handle.into())?)
}
fn secrets_manager_invalidate(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
key_id_ptr: &wiggle::GuestPtr<'_, u8>,
key_id_len: guest_types::Size,
key_version: guest_types::Version,
) -> Result<(), guest_types::CryptoErrno> {
let key_id: &[u8] = { &*key_id_ptr.as_array(key_id_len).as_slice()? };
Ok(self.secrets_manager_invalidate(
Ok((&*self).secrets_manager_invalidate(
secrets_manager_handle.into(),
key_id,
Version(key_version),

View File

@@ -4,26 +4,27 @@ impl super::wasi_ephemeral_crypto_kx::WasiEphemeralCryptoKx for WasiCryptoCtx {
// --- key exchange
fn kx_dh(
&self,
&mut self,
pk_handle: guest_types::Publickey,
sk_handle: guest_types::Secretkey,
) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> {
Ok(self.kx_dh(pk_handle.into(), sk_handle.into())?.into())
Ok((&*self).kx_dh(pk_handle.into(), sk_handle.into())?.into())
}
// --- Key encapsulation
fn kx_encapsulate(
&self,
&mut self,
pk_handle: guest_types::Publickey,
) -> Result<(guest_types::ArrayOutput, guest_types::ArrayOutput), guest_types::CryptoErrno>
{
let (secret_handle, encapsulated_secret_handle) = self.kx_encapsulate(pk_handle.into())?;
let (secret_handle, encapsulated_secret_handle) =
(&*self).kx_encapsulate(pk_handle.into())?;
Ok((secret_handle.into(), encapsulated_secret_handle.into()))
}
fn kx_decapsulate(
&self,
&mut self,
sk_handle: guest_types::Secretkey,
encapsulated_secret_ptr: &wiggle::GuestPtr<'_, u8>,
encapsulated_secret_len: guest_types::Size,
@@ -31,7 +32,7 @@ impl super::wasi_ephemeral_crypto_kx::WasiEphemeralCryptoKx for WasiCryptoCtx {
let encapsulated_secret = &*encapsulated_secret_ptr
.as_array(encapsulated_secret_len)
.as_slice()?;
Ok(self
Ok((&*self)
.kx_decapsulate(sk_handle.into(), encapsulated_secret)?
.into())
}

View File

@@ -6,17 +6,17 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for
// --- signature
fn signature_export(
&self,
&mut self,
signature_handle: guest_types::Signature,
encoding: guest_types::SignatureEncoding,
) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.signature_export(signature_handle.into(), encoding.into())?
.into())
}
fn signature_import(
&self,
&mut self,
alg_str: &wiggle::GuestPtr<'_, str>,
encoded_ptr: &wiggle::GuestPtr<'_, u8>,
encoded_len: guest_types::Size,
@@ -24,86 +24,89 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for
) -> Result<guest_types::Signature, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?;
let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?;
Ok(self
Ok((&*self)
.signature_import(alg_str, encoded, encoding.into())?
.into())
}
fn signature_state_open(
&self,
&mut self,
kp_handle: guest_types::Keypair,
) -> Result<guest_types::SignatureState, guest_types::CryptoErrno> {
Ok(self.signature_state_open(kp_handle.into())?.into())
Ok((&*self).signature_state_open(kp_handle.into())?.into())
}
fn signature_state_update(
&self,
&mut self,
state_handle: guest_types::SignatureState,
input_ptr: &wiggle::GuestPtr<'_, u8>,
input_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> {
let input = &*input_ptr.as_array(input_len).as_slice()?;
Ok(self.signature_state_update(state_handle.into(), input)?)
Ok((&*self).signature_state_update(state_handle.into(), input)?)
}
fn signature_state_sign(
&self,
&mut self,
signature_state_handle: guest_types::SignatureState,
) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.signature_state_sign(signature_state_handle.into())?
.into())
}
fn signature_state_close(
&self,
&mut self,
signature_state_handle: guest_types::SignatureState,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.signature_state_close(signature_state_handle.into())?)
Ok((&*self).signature_state_close(signature_state_handle.into())?)
}
fn signature_verification_state_open(
&self,
&mut self,
pk_handle: guest_types::Publickey,
) -> Result<guest_types::SignatureVerificationState, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.signature_verification_state_open(pk_handle.into())?
.into())
}
fn signature_verification_state_update(
&self,
&mut self,
verification_state_handle: guest_types::SignatureVerificationState,
input_ptr: &wiggle::GuestPtr<'_, u8>,
input_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> {
let input: &[u8] = &*input_ptr.as_array(input_len).as_slice()?;
Ok(self.signature_verification_state_update(verification_state_handle.into(), input)?)
Ok(
(&*self)
.signature_verification_state_update(verification_state_handle.into(), input)?,
)
}
fn signature_verification_state_verify(
&self,
&mut self,
verification_state_handle: guest_types::SignatureVerificationState,
signature_handle: guest_types::Signature,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.signature_verification_state_verify(
Ok((&*self).signature_verification_state_verify(
verification_state_handle.into(),
signature_handle.into(),
)?)
}
fn signature_verification_state_close(
&self,
&mut self,
verification_state_handle: guest_types::SignatureVerificationState,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.signature_verification_state_close(verification_state_handle.into())?)
Ok((&*self).signature_verification_state_close(verification_state_handle.into())?)
}
fn signature_close(
&self,
&mut self,
signature_handle: guest_types::Signature,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.signature_close(signature_handle.into())?)
Ok((&*self).signature_close(signature_handle.into())?)
}
}

View File

@@ -7,7 +7,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
// --- secrets_manager
fn symmetric_key_generate_managed(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
alg_str: &wiggle::GuestPtr<'_, str>,
options_handle: &guest_types::OptOptions,
@@ -17,7 +17,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None,
};
Ok(self
Ok((&*self)
.symmetric_key_generate_managed(
secrets_manager_handle.into(),
alg_str,
@@ -27,7 +27,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
}
fn symmetric_key_store_managed(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
symmetric_key_handle: guest_types::SymmetricKey,
symmetric_key_id_ptr: &wiggle::GuestPtr<'_, u8>,
@@ -36,7 +36,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
let key_id_buf = &mut *symmetric_key_id_ptr
.as_array(symmetric_key_id_max_len)
.as_slice_mut()?;
Ok(self.symmetric_key_store_managed(
Ok((&*self).symmetric_key_store_managed(
secrets_manager_handle.into(),
symmetric_key_handle.into(),
key_id_buf,
@@ -44,12 +44,12 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
}
fn symmetric_key_replace_managed(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
symmetric_key_old_handle: guest_types::SymmetricKey,
symmetric_key_new_handle: guest_types::SymmetricKey,
) -> Result<guest_types::Version, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.symmetric_key_replace_managed(
secrets_manager_handle.into(),
symmetric_key_old_handle.into(),
@@ -59,7 +59,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
}
fn symmetric_key_from_id(
&self,
&mut self,
secrets_manager_handle: guest_types::SecretsManager,
symmetric_key_id_ptr: &wiggle::GuestPtr<'_, u8>,
symmetric_key_id_len: guest_types::Size,
@@ -68,7 +68,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
let symmetric_key_id = &*symmetric_key_id_ptr
.as_array(symmetric_key_id_len)
.as_slice()?;
Ok(self
Ok((&*self)
.symmetric_key_from_id(
secrets_manager_handle.into(),
symmetric_key_id,
@@ -80,7 +80,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
// --- key
fn symmetric_key_generate(
&self,
&mut self,
alg_str: &wiggle::GuestPtr<'_, str>,
options_handle: &guest_types::OptOptions,
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
@@ -89,33 +89,33 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None,
};
Ok(self
Ok((&*self)
.symmetric_key_generate(alg_str, options_handle.map(Into::into))?
.into())
}
fn symmetric_key_import(
&self,
&mut self,
alg_str: &wiggle::GuestPtr<'_, str>,
raw_ptr: &wiggle::GuestPtr<'_, u8>,
raw_len: guest_types::Size,
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?;
let raw = &*raw_ptr.as_array(raw_len).as_slice()?;
Ok(self.symmetric_key_import(alg_str, raw)?.into())
Ok((&*self).symmetric_key_import(alg_str, raw)?.into())
}
fn symmetric_key_export(
&self,
&mut self,
symmetric_key_handle: guest_types::SymmetricKey,
) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.symmetric_key_export(symmetric_key_handle.into())?
.into())
}
fn symmetric_key_id(
&self,
&mut self,
symmetric_key_handle: guest_types::SymmetricKey,
symmetric_key_id_ptr: &wiggle::GuestPtr<'_, u8>,
symmetric_key_id_max_len: guest_types::Size,
@@ -123,7 +123,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
let key_id_buf = &mut *symmetric_key_id_ptr
.as_array(symmetric_key_id_max_len)
.as_slice_mut()?;
let (key_id, version) = self.symmetric_key_id(symmetric_key_handle.into())?;
let (key_id, version) = (&*self).symmetric_key_id(symmetric_key_handle.into())?;
ensure!(
key_id.len() <= key_id_buf.len(),
CryptoError::Overflow.into()
@@ -133,16 +133,16 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
}
fn symmetric_key_close(
&self,
&mut self,
key_handle: guest_types::SymmetricKey,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.symmetric_key_close(key_handle.into())?)
Ok((&*self).symmetric_key_close(key_handle.into())?)
}
// --- state
fn symmetric_state_open(
&self,
&mut self,
alg_str: &wiggle::GuestPtr<'_, str>,
key_handle: &guest_types::OptSymmetricKey,
options_handle: &guest_types::OptOptions,
@@ -156,7 +156,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None,
};
Ok(self
Ok((&*self)
.symmetric_state_open(
alg_str,
key_handle.map(Into::into),
@@ -166,7 +166,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
}
fn symmetric_state_options_get(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
name_str: &wiggle::GuestPtr<'_, str>,
value_ptr: &wiggle::GuestPtr<'_, u8>,
@@ -174,78 +174,78 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?;
let value = &mut *value_ptr.as_array(value_max_len).as_slice_mut()?;
Ok(self
Ok((&*self)
.options_get(symmetric_state_handle.into(), name_str, value)?
.try_into()?)
}
fn symmetric_state_options_get_u64(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
name_str: &wiggle::GuestPtr<'_, str>,
) -> Result<u64, guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?;
Ok(self.options_get_u64(symmetric_state_handle.into(), name_str)?)
Ok((&*self).options_get_u64(symmetric_state_handle.into(), name_str)?)
}
fn symmetric_state_close(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.symmetric_state_close(symmetric_state_handle.into())?)
Ok((&*self).symmetric_state_close(symmetric_state_handle.into())?)
}
fn symmetric_state_absorb(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
data_ptr: &wiggle::GuestPtr<'_, u8>,
data_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> {
let data = &*data_ptr.as_array(data_len).as_slice()?;
Ok(self.symmetric_state_absorb(symmetric_state_handle.into(), data)?)
Ok((&*self).symmetric_state_absorb(symmetric_state_handle.into(), data)?)
}
fn symmetric_state_squeeze(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
out_ptr: &wiggle::GuestPtr<'_, u8>,
out_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?;
Ok(self.symmetric_state_squeeze(symmetric_state_handle.into(), out)?)
Ok((&*self).symmetric_state_squeeze(symmetric_state_handle.into(), out)?)
}
fn symmetric_state_squeeze_tag(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
) -> Result<guest_types::SymmetricTag, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.symmetric_state_squeeze_tag(symmetric_state_handle.into())?
.into())
}
fn symmetric_state_squeeze_key(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
alg_str: &wiggle::GuestPtr<'_, str>,
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?;
Ok(self
Ok((&*self)
.symmetric_state_squeeze_key(symmetric_state_handle.into(), alg_str)?
.into())
}
fn symmetric_state_max_tag_len(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.symmetric_state_max_tag_len(symmetric_state_handle.into())?
.try_into()?)
}
fn symmetric_state_encrypt(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
out_ptr: &wiggle::GuestPtr<'_, u8>,
out_len: guest_types::Size,
@@ -254,13 +254,13 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?;
let data = &*data_ptr.as_array(data_len).as_slice()?;
Ok(self
Ok((&*self)
.symmetric_state_encrypt(symmetric_state_handle.into(), out, data)?
.try_into()?)
}
fn symmetric_state_encrypt_detached(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
out_ptr: &wiggle::GuestPtr<'_, u8>,
out_len: guest_types::Size,
@@ -269,13 +269,13 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
) -> Result<guest_types::SymmetricTag, guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?;
let data = &*data_ptr.as_array(data_len).as_slice()?;
Ok(self
Ok((&*self)
.symmetric_state_encrypt_detached(symmetric_state_handle.into(), out, data)?
.into())
}
fn symmetric_state_decrypt(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
out_ptr: &wiggle::GuestPtr<'_, u8>,
out_len: guest_types::Size,
@@ -284,13 +284,13 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?;
let data = &*data_ptr.as_array(data_len).as_slice()?;
Ok(self
Ok((&*self)
.symmetric_state_decrypt(symmetric_state_handle.into(), out, data)?
.try_into()?)
}
fn symmetric_state_decrypt_detached(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
out_ptr: &wiggle::GuestPtr<'_, u8>,
out_len: guest_types::Size,
@@ -302,55 +302,55 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?;
let data = &*data_ptr.as_array(data_len).as_slice()?;
let raw_tag: &[u8] = &*raw_tag_ptr.as_array(raw_tag_len).as_slice()?;
Ok(self
Ok((&*self)
.symmetric_state_decrypt_detached(symmetric_state_handle.into(), out, data, raw_tag)?
.try_into()?)
}
fn symmetric_state_ratchet(
&self,
&mut self,
symmetric_state_handle: guest_types::SymmetricState,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.symmetric_state_ratchet(symmetric_state_handle.into())?)
Ok((&*self).symmetric_state_ratchet(symmetric_state_handle.into())?)
}
// --- tag
fn symmetric_tag_len(
&self,
&mut self,
symmetric_tag_handle: guest_types::SymmetricTag,
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
Ok(self
Ok((&*self)
.symmetric_tag_len(symmetric_tag_handle.into())?
.try_into()?)
}
fn symmetric_tag_pull(
&self,
&mut self,
symmetric_tag_handle: guest_types::SymmetricTag,
buf_ptr: &wiggle::GuestPtr<'_, u8>,
buf_len: guest_types::Size,
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let buf = &mut *buf_ptr.as_array(buf_len).as_slice_mut()?;
Ok(self
Ok((&*self)
.symmetric_tag_pull(symmetric_tag_handle.into(), buf)?
.try_into()?)
}
fn symmetric_tag_verify(
&self,
&mut self,
symmetric_tag_handle: guest_types::SymmetricTag,
expected_raw_ptr: &wiggle::GuestPtr<'_, u8>,
expected_raw_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> {
let expected_raw = &*expected_raw_ptr.as_array(expected_raw_len).as_slice()?;
Ok(self.symmetric_tag_verify(symmetric_tag_handle.into(), expected_raw)?)
Ok((&*self).symmetric_tag_verify(symmetric_tag_handle.into(), expected_raw)?)
}
fn symmetric_tag_close(
&self,
&mut self,
symmetric_tag_handle: guest_types::SymmetricTag,
) -> Result<(), guest_types::CryptoErrno> {
Ok(self.symmetric_tag_close(symmetric_tag_handle.into())?)
Ok((&*self).symmetric_tag_close(symmetric_tag_handle.into())?)
}
}

View File

@@ -17,7 +17,6 @@ anyhow = "1.0"
log = { version = "0.4", default-features = false }
wasmtime = { path = "../wasmtime", version = "0.27.0", default-features = false }
wasmtime-runtime = { path = "../runtime", version = "0.27.0" }
wasmtime-wiggle = { path = "../wiggle/wasmtime", version = "0.27.0" }
wasmtime-wasi = { path = "../wasi", version = "0.27.0" }
wiggle = { path = "../wiggle", version = "0.27.0" }

View File

@@ -28,7 +28,7 @@ pub enum UsageError {
impl<'a> WasiEphemeralNn for WasiNnCtx {
fn load<'b>(
&self,
&mut self,
builders: &GraphBuilderArray<'_>,
encoding: GraphEncoding,
target: ExecutionTarget,
@@ -76,7 +76,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx {
Ok(id)
}
fn init_execution_context(&self, graph: Graph) -> Result<GraphExecutionContext> {
fn init_execution_context(&mut self, graph: Graph) -> Result<GraphExecutionContext> {
let request =
if let Some((_, executable_graph)) = self.ctx.borrow_mut().graphs.get_mut(graph) {
executable_graph.create_infer_request()?
@@ -90,7 +90,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx {
}
fn set_input<'b>(
&self,
&mut self,
context: GraphExecutionContext,
index: u32,
tensor: &Tensor<'b>,
@@ -135,7 +135,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx {
Ok(())
}
fn compute(&self, context: GraphExecutionContext) -> Result<()> {
fn compute(&mut self, context: GraphExecutionContext) -> Result<()> {
if let Some(execution) = self.ctx.borrow_mut().executions.get_mut(context) {
Ok(execution.request.infer()?)
} else {
@@ -144,7 +144,7 @@ impl<'a> WasiEphemeralNn for WasiNnCtx {
}
fn get_output<'b>(
&self,
&mut self,
context: GraphExecutionContext,
index: u32,
out_buffer: &GuestPtr<'_, u8>,

View File

@@ -3,21 +3,4 @@ mod r#impl;
mod witx;
pub use ctx::WasiNnCtx;
// Defines a `struct WasiNn` with member fields and appropriate APIs for dealing with all the
// various WASI exports.
wasmtime_wiggle::wasmtime_integration!({
// The wiggle code to integrate with lives here:
target: witx,
// This must be the same witx document as used above:
witx: ["$WASI_ROOT/phases/ephemeral/witx/wasi_ephemeral_nn.witx"],
// This must be the same ctx type as used for the target:
ctx: WasiNnCtx,
// This macro will emit a struct to represent the instance, with this name and docs:
modules: {
wasi_ephemeral_nn => {
name: WasiNn,
docs: "An instantiated instance of the wasi-nn exports.",
}
},
});
pub use witx::wasi_ephemeral_nn::add_to_linker;

View File

@@ -11,7 +11,7 @@ wiggle::from_witx!({
use types::NnErrno;
impl<'a> types::UserErrorConversion for WasiNnCtx {
fn nn_errno_from_wasi_nn_error(&self, e: WasiNnError) -> Result<NnErrno, wiggle::Trap> {
fn nn_errno_from_wasi_nn_error(&mut self, e: WasiNnError) -> Result<NnErrno, wiggle::Trap> {
eprintln!("Host error: {:?}", e);
match e {
WasiNnError::OpenvinoSetupError(_) => unimplemented!(),

View File

@@ -16,12 +16,11 @@ build = "build.rs"
wasi-common = { path = "../wasi-common", version = "0.27.0" }
wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync", version = "0.27.0", optional = true }
wasi-tokio = { path = "../wasi-common/tokio", version = "0.27.0", optional = true }
wiggle = { path = "../wiggle", default-features = false, version = "0.27.0" }
wasmtime-wiggle = { path = "../wiggle/wasmtime", default-features = false, version = "0.27.0" }
wiggle = { path = "../wiggle", default-features = false, version = "0.27.0", features = ["wasmtime_integration"] }
wasmtime = { path = "../wasmtime", default-features = false, version = "0.27.0" }
anyhow = "1.0"
[features]
default = ["sync"]
sync = ["wasi-cap-std-sync"]
tokio = ["wasi-tokio", "wasmtime/async", "wasmtime-wiggle/async"]
tokio = ["wasi-tokio", "wasmtime/async", "wiggle/wasmtime_async"]

View File

@@ -28,7 +28,7 @@ pub use sync::*;
#[cfg(feature = "tokio")]
pub mod tokio {
pub use wasi_tokio::*;
super::define_wasi!(async);
super::define_wasi!(async T: Send);
}
// The only difference between these definitions for sync vs async is whether
@@ -38,100 +38,41 @@ pub mod tokio {
#[doc(hidden)]
#[macro_export]
macro_rules! define_wasi {
($async_mode: tt) => {
($async_mode:tt $($bounds:tt)*) => {
use std::cell::RefCell;
use std::rc::Rc;
use wasmtime::{Config, Linker, Store};
use wasi_common::WasiCtx;
use wasmtime::Linker;
/// An instantiated instance of all available wasi exports. Presently includes
/// both the "preview1" snapshot and the "unstable" (preview0) snapshot.
pub struct Wasi {
preview_1: snapshots::preview_1::Wasi,
preview_0: snapshots::preview_0::Wasi,
}
impl Wasi {
pub fn new(store: &Store, context: WasiCtx) -> Self {
let context = Rc::new(RefCell::new(context));
let preview_1 = snapshots::preview_1::Wasi::new(store, context.clone());
let preview_0 = snapshots::preview_0::Wasi::new(store, context);
Self {
preview_1,
preview_0,
}
}
pub fn add_to_linker(&self, linker: &mut Linker) -> Result<(), anyhow::Error> {
self.preview_1.add_to_linker(linker)?;
self.preview_0.add_to_linker(linker)?;
Ok(())
}
pub fn add_to_config(config: &mut Config) {
snapshots::preview_1::Wasi::add_to_config(config);
snapshots::preview_0::Wasi::add_to_config(config);
}
pub fn set_context(store: &Store, context: WasiCtx) -> Result<(), WasiCtx> {
// It doesn't matter which underlying `Wasi` type this gets called on as the
// implementations are identical
snapshots::preview_1::Wasi::set_context(store, context)
}
pub fn add_to_linker<T>(
linker: &mut Linker<T>,
get_cx: impl Fn(&mut T) -> &mut crate::WasiCtx + Send + Sync + Copy + 'static,
) -> anyhow::Result<()>
where $($bounds)*
{
snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(linker, get_cx)?;
snapshots::preview_0::add_wasi_unstable_to_linker(linker, get_cx)?;
Ok(())
}
pub mod snapshots {
pub mod preview_1 {
use wasi_common::WasiCtx;
// Defines a `struct Wasi` with member fields and appropriate APIs for dealing
// with all the various WASI exports.
wasmtime_wiggle::wasmtime_integration!({
wiggle::wasmtime_integration!({
// The wiggle code to integrate with lives here:
target: wasi_common::snapshots::preview_1,
// This must be the same witx document as used above. This should be ensured by
// the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`.
witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"],
// This must be the same ctx type as used for the target:
ctx: WasiCtx,
// This macro will emit a struct to represent the instance,
// with this name and docs:
modules: { wasi_snapshot_preview1 =>
{ name: Wasi,
docs: "An instantiated instance of the wasi exports.
This represents a wasi module which can be used to instantiate other wasm
modules. This structure exports all that various fields of the wasi instance
as fields which can be used to implement your own instantiation logic, if
necessary. Additionally [`Wasi::get_export`] can be used to do name-based
resolution.",
},
},
errors: { errno => Error },
$async_mode: *
});
}
pub mod preview_0 {
use wasi_common::WasiCtx;
// Defines a `struct Wasi` with member fields and appropriate APIs for dealing
// with all the various WASI exports.
wasmtime_wiggle::wasmtime_integration!({
wiggle::wasmtime_integration!({
// The wiggle code to integrate with lives here:
target: wasi_common::snapshots::preview_0,
// This must be the same witx document as used above. This should be ensured by
// the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`.
witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"],
// This must be the same ctx type as used for the target:
ctx: WasiCtx,
// This macro will emit a struct to represent the instance,
// with this name and docs:
modules: { wasi_unstable =>
{ name: Wasi,
docs: "An instantiated instance of the wasi exports.
This represents a wasi module which can be used to instantiate other wasm
modules. This structure exports all that various fields of the wasi instance
as fields which can be used to implement your own instantiation logic, if
necessary. Additionally [`Wasi::get_export`] can be used to do name-based
resolution.",
},
},
errors: { errno => Error },
$async_mode: *
});
}

View File

@@ -1,16 +1,12 @@
use crate::memory::MemoryCreator;
use crate::trampoline::MemoryCreatorProxy;
use crate::{func::HostFunc, Caller, FuncType, IntoFunc, Trap, Val, WasmRet, WasmTy};
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use std::cmp;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt;
use std::future::Future;
#[cfg(feature = "cache")]
use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;
use wasmparser::WasmFeatures;
#[cfg(feature = "cache")]
@@ -273,97 +269,6 @@ impl Default for InstanceAllocationStrategy {
}
}
/// This type is used for storing host functions in a `Config`.
///
/// The module and function names are interned for more compact storage.
#[derive(Clone)]
struct HostFuncMap {
index_map: HashMap<Arc<str>, usize>,
strings: Vec<Arc<str>>,
funcs: HashMap<(usize, usize), (Arc<HostFunc>, bool)>,
}
impl HostFuncMap {
fn new() -> Self {
Self {
index_map: HashMap::new(),
strings: Vec::new(),
funcs: HashMap::new(),
}
}
fn insert(&mut self, module: &str, name: &str, async_required: bool, func: HostFunc) {
let key = (self.intern_str(module), self.intern_str(name));
self.funcs.insert(key, (Arc::new(func), async_required));
}
fn get(&self, module: &str, name: &str) -> Option<&HostFunc> {
let key = (
self.index_map.get(module).cloned()?,
self.index_map.get(name).cloned()?,
);
self.funcs.get(&key).map(|f| f.0.as_ref())
}
fn intern_str(&mut self, string: &str) -> usize {
if let Some(idx) = self.index_map.get(string) {
return *idx;
}
let string: Arc<str> = string.into();
let idx = self.strings.len();
self.strings.push(string.clone());
self.index_map.insert(string, idx);
idx
}
fn async_required(&self) -> bool {
self.funcs.values().any(|f| f.1)
}
fn iter(&self) -> impl Iterator<Item = &HostFunc> {
self.funcs.values().map(|v| &*v.0)
}
}
macro_rules! generate_wrap_async_host_func {
($num:tt $($args:ident)*) => (paste::paste!{
/// Same as [`Config::wrap_host_func`], except the closure asynchronously produces
/// its result. For more information see the [`Func`](crate::Func) documentation.
///
/// Note: creating an engine will fail if an async host function is defined and
/// [async support](Config::async_support) is not enabled.
#[allow(non_snake_case)]
#[cfg(feature = "async")]
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
pub fn [<wrap $num _host_func_async>]<$($args,)* R>(
&mut self,
module: &str,
name: &str,
func: impl for <'a> Fn(Caller<'a>, $($args),*) -> Box<dyn Future<Output = R> + 'a> + Send + Sync + 'static,
)
where
$($args: WasmTy,)*
R: WasmRet,
{
// Defer the check for async support until engine creation time to not introduce an order dependency
self.host_funcs.insert(
module,
name,
true,
HostFunc::wrap(move |caller: Caller<'_>, $($args: $args),*| {
let store = caller.store().clone();
debug_assert!(store.async_support());
let mut future = Pin::from(func(caller, $($args),*));
match store.block_on(future.as_mut()) {
Ok(ret) => ret.into_fallible(),
Err(e) => R::fallible_from_trap(e),
}
})
);
}
})
}
/// Global configuration options used to create an [`Engine`](crate::Engine)
/// and customize its behavior.
///
@@ -385,7 +290,6 @@ pub struct Config {
pub(crate) wasm_backtrace_details_env_used: bool,
#[cfg(feature = "async")]
pub(crate) async_stack_size: usize,
host_funcs: HostFuncMap,
pub(crate) async_support: bool,
}
@@ -421,7 +325,6 @@ impl Config {
features: WasmFeatures::default(),
#[cfg(feature = "async")]
async_stack_size: 2 << 20,
host_funcs: HostFuncMap::new(),
async_support: false,
};
ret.cranelift_debug_verifier(false);
@@ -1190,107 +1093,6 @@ impl Config {
self
}
/// Defines a host function for the [`Config`] for the given callback.
///
/// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function.
///
/// Note that the implementation of `func` must adhere to the `ty`
/// signature given, error or traps may occur if it does not respect the
/// `ty` signature.
///
/// Additionally note that this is quite a dynamic function since signatures
/// are not statically known. For performance reasons, it's recommended
/// to use [`Config::wrap_host_func`] if you can because with statically known
/// signatures the engine can optimize the implementation much more.
///
/// The callback must be `Send` and `Sync` as it is shared between all engines created
/// from the `Config`. For more relaxed bounds, use [`Func::new`](crate::Func::new) to define the function.
pub fn define_host_func(
&mut self,
module: &str,
name: &str,
ty: FuncType,
func: impl Fn(Caller<'_>, &[Val], &mut [Val]) -> Result<(), Trap> + Send + Sync + 'static,
) {
self.host_funcs
.insert(module, name, false, HostFunc::new(self, ty, func));
}
/// Defines an async host function for the [`Config`] for the given callback.
///
/// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function.
///
/// This function is the asynchronous analogue of [`Config::define_host_func`] and much of
/// that documentation applies to this as well.
///
/// Additionally note that this is quite a dynamic function since signatures
/// are not statically known. For performance reasons, it's recommended
/// to use `Config::wrap$N_host_func_async` if you can because with statically known
/// signatures the engine can optimize the implementation much more.
///
/// The callback must be `Send` and `Sync` as it is shared between all engines created
/// from the `Config`. For more relaxed bounds, use [`Func::new_async`](crate::Func::new_async) to define the function.
///
/// Note: creating an engine will fail if an async host function is defined and [async support](Config::async_support)
/// is not enabled.
#[cfg(feature = "async")]
#[cfg_attr(nightlydoc, doc(cfg(feature = "async")))]
pub fn define_host_func_async<F>(&mut self, module: &str, name: &str, ty: FuncType, func: F)
where
F: for<'a> Fn(
Caller<'a>,
&'a [Val],
&'a mut [Val],
) -> Box<dyn Future<Output = Result<(), Trap>> + 'a>
+ Send
+ Sync
+ 'static,
{
// Defer the check for async support until engine creation time to not introduce an order dependency
self.host_funcs.insert(
module,
name,
true,
HostFunc::new(self, ty, move |caller, params, results| {
let store = caller.store().clone();
debug_assert!(store.async_support());
let mut future = Pin::from(func(caller, params, results));
match store.block_on(future.as_mut()) {
Ok(Ok(())) => Ok(()),
Ok(Err(trap)) | Err(trap) => Err(trap),
}
}),
);
}
/// Defines a host function for the [`Config`] from the given Rust closure.
///
/// Use [`Store::get_host_func`](crate::Store::get_host_func) to get a [`Func`](crate::Func) representing the function.
///
/// See [`Func::wrap`](crate::Func::wrap) for information about accepted parameter and result types for the closure.
///
/// The closure must be `Send` and `Sync` as it is shared between all engines created
/// from the `Config`. For more relaxed bounds, use [`Func::wrap`](crate::Func::wrap) to wrap the closure.
pub fn wrap_host_func<Params, Results>(
&mut self,
module: &str,
name: &str,
func: impl IntoFunc<Params, Results> + Send + Sync,
) {
self.host_funcs
.insert(module, name, false, HostFunc::wrap(func));
}
for_each_function_signature!(generate_wrap_async_host_func);
pub(crate) fn host_funcs(&self) -> impl Iterator<Item = &HostFunc> {
self.host_funcs.iter()
}
pub(crate) fn get_host_func(&self, module: &str, name: &str) -> Option<&HostFunc> {
self.host_funcs.get(module, name)
}
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
self.isa_flags
.clone()
@@ -1303,18 +1105,6 @@ impl Config {
self.isa_flags.clone().finish(settings::Flags::new(flags))
}
pub(crate) fn validate(&self) -> Result<()> {
// This is used to validate that the config is internally consistent prior to
// creating an engine using this config.
// Check that there isn't a host function defined that requires async support enabled
if self.host_funcs.async_required() && !self.async_support {
bail!("an async host function cannot be defined without async support enabled in the config");
}
Ok(())
}
pub(crate) fn build_compiler(&self, allocator: &dyn InstanceAllocator) -> Compiler {
let isa = self.target_isa();
let mut tunables = self.tunables.clone();

View File

@@ -1,47 +1,17 @@
use crate::signatures::{SignatureCollection, SignatureRegistry};
use crate::signatures::SignatureRegistry;
use crate::Config;
use anyhow::Result;
use std::collections::HashMap;
use std::sync::Arc;
#[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig;
use wasmtime_jit::Compiler;
use wasmtime_runtime::{debug_builtins, InstanceAllocator, InstanceHandle, VMCallerCheckedAnyfunc};
/// This is used as a Send+Sync wrapper around two data structures relating to
/// host functions defined on `Config`:
///
/// * `anyfuncs` - this stores a mapping between the host function instance and
/// a `VMCallerCheckedAnyfunc` that can be used as the function's value in Wasmtime's ABI.
/// The address of the anyfunc needs to be stable, thus the boxed value.
///
/// * `signatures` - this stores the collection of shared signatures registered for every
/// usable host functions with this engine.
struct EngineHostFuncs {
anyfuncs: HashMap<InstanceHandle, Box<VMCallerCheckedAnyfunc>>,
signatures: SignatureCollection,
}
impl EngineHostFuncs {
fn new(registry: &SignatureRegistry) -> Self {
Self {
anyfuncs: HashMap::new(),
signatures: SignatureCollection::new(registry),
}
}
}
// This is safe for send and sync as it is read-only once the
// engine is constructed and the host functions live with the config,
// which the engine keeps a strong reference to.
unsafe impl Send for EngineHostFuncs {}
unsafe impl Sync for EngineHostFuncs {}
use wasmtime_runtime::{debug_builtins, InstanceAllocator};
/// An `Engine` which is a global context for compilation and management of wasm
/// modules.
///
/// An engine can be safely shared across threads and is a cheap cloneable
/// handle to the actual engine. The engine itself will be deallocate once all
/// handle to the actual engine. The engine itself will be deallocated once all
/// references to it have gone away.
///
/// Engines store global configuration preferences such as compilation settings,
@@ -69,31 +39,19 @@ struct EngineInner {
compiler: Compiler,
allocator: Box<dyn InstanceAllocator>,
signatures: SignatureRegistry,
host_funcs: EngineHostFuncs,
}
impl Engine {
/// Creates a new [`Engine`] with the specified compilation and
/// configuration settings.
pub fn new(config: &Config) -> Result<Engine> {
// Ensure that wasmtime_runtime's signal handlers are configured. This
// is the per-program initialization required for handling traps, such
// as configuring signals, vectored exception handlers, etc.
wasmtime_runtime::init_traps(crate::module::GlobalModuleRegistry::is_wasm_pc);
debug_builtins::ensure_exported();
config.validate()?;
let allocator = config.build_allocator()?;
let registry = SignatureRegistry::new();
let mut host_funcs = EngineHostFuncs::new(&registry);
// Register all the host function signatures with the collection
for func in config.host_funcs() {
let sig = host_funcs
.signatures
.register(func.ty.as_wasm_func_type(), func.trampoline);
// Cloning the instance handle is safe as host functions outlive the engine
host_funcs.anyfuncs.insert(
unsafe { func.instance.clone() },
Box::new(func.anyfunc(sig)),
);
}
Ok(Engine {
inner: Arc::new(EngineInner {
@@ -101,7 +59,6 @@ impl Engine {
compiler: config.build_compiler(allocator.as_ref()),
allocator,
signatures: registry,
host_funcs,
}),
})
}
@@ -134,21 +91,6 @@ impl Engine {
&self.inner.signatures
}
pub(crate) fn host_func_signatures(&self) -> &SignatureCollection {
&self.inner.host_funcs.signatures
}
pub(crate) fn host_func_anyfunc(
&self,
instance: &InstanceHandle,
) -> Option<&VMCallerCheckedAnyfunc> {
self.inner
.host_funcs
.anyfuncs
.get(instance)
.map(AsRef::as_ref)
}
/// Ahead-of-time (AOT) compiles a WebAssembly module.
///
/// The `bytes` provided must be in one of two formats:

Some files were not shown because too many files have changed in this diff Show More