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:
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
@@ -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
37
Cargo.lock
generated
@@ -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]]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
9
build.rs
9
build.rs
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
264
crates/c-api/include/wasmtime/config.h
Normal file
264
crates/c-api/include/wasmtime/config.h
Normal 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
|
||||
|
||||
55
crates/c-api/include/wasmtime/error.h
Normal file
55
crates/c-api/include/wasmtime/error.h
Normal 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
|
||||
172
crates/c-api/include/wasmtime/extern.h
Normal file
172
crates/c-api/include/wasmtime/extern.h
Normal 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
|
||||
|
||||
179
crates/c-api/include/wasmtime/func.h
Normal file
179
crates/c-api/include/wasmtime/func.h
Normal 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
|
||||
91
crates/c-api/include/wasmtime/global.h
Normal file
91
crates/c-api/include/wasmtime/global.h
Normal 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
|
||||
158
crates/c-api/include/wasmtime/instance.h
Normal file
158
crates/c-api/include/wasmtime/instance.h
Normal 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
|
||||
239
crates/c-api/include/wasmtime/linker.h
Normal file
239
crates/c-api/include/wasmtime/linker.h
Normal 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
|
||||
90
crates/c-api/include/wasmtime/memory.h
Normal file
90
crates/c-api/include/wasmtime/memory.h
Normal 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
|
||||
172
crates/c-api/include/wasmtime/module.h
Normal file
172
crates/c-api/include/wasmtime/module.h
Normal 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
|
||||
203
crates/c-api/include/wasmtime/store.h
Normal file
203
crates/c-api/include/wasmtime/store.h
Normal 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
|
||||
|
||||
126
crates/c-api/include/wasmtime/table.h
Normal file
126
crates/c-api/include/wasmtime/table.h
Normal 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
|
||||
|
||||
60
crates/c-api/include/wasmtime/trap.h
Normal file
60
crates/c-api/include/wasmtime/trap.h
Normal 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
|
||||
158
crates/c-api/include/wasmtime/val.h
Normal file
158
crates/c-api/include/wasmtime/val.h
Normal 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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
|
||||
@@ -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, ¶ms, &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(¶ms, &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(¶ms)));
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
f.call(func.ext.store.context_mut(), ¶ms)
|
||||
}));
|
||||
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, ¶ms)));
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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()), |()| {})
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 }));
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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>) {}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>) {}
|
||||
|
||||
@@ -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>>,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(¶ms).map_err(|e| e.downcast::<Trap>().unwrap());
|
||||
let this_result = f
|
||||
.call(&mut store, ¶ms)
|
||||
.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(¶ms);
|
||||
let _ = f.call(store, ¶ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)) => {}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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>>;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }) }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() })
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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())?)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!(),
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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: *
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(®istry);
|
||||
|
||||
// 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
Reference in New Issue
Block a user