refactor!: update to wasmtime 20 or greater (#723)

- Breaking: No longer copies Extism config values into WASI environment
variables because the new interface doesn't allow for the environment to
be updated - these should be accessed using the Extism config functions
instead
- Requires wasmtime 20 or greater
- Enables wasm-gc
- Similar to https://github.com/extism/extism/pull/697 without sockets
or additional support for command modules
This commit is contained in:
zach
2024-06-05 12:57:25 -07:00
committed by GitHub
parent ecf18a2d81
commit 3f54892a39
6 changed files with 88 additions and 76 deletions

View File

@@ -9,8 +9,8 @@ repository.workspace = true
version.workspace = true
[dependencies]
wasmtime = ">= 14.0.0, < 18.0.0"
wasmtime-wasi = ">= 14.0.0, < 18.0.0"
wasmtime = ">= 20.0.0, < 22.0.0"
wasmtime-wasi = ">= 20.0.0, < 22.0.0"
anyhow = "1"
serde = {version = "1", features = ["derive"]}
serde_json = "1"

View File

@@ -194,11 +194,11 @@ impl CurrentPlugin {
anyhow::bail!("unable to locate an extism kernel global: extism_context",)
};
let Val::ExternRef(Some(xs)) = xs.get(store) else {
let Val::ExternRef(Some(xs)) = xs.get(&mut store) else {
anyhow::bail!("expected extism_context to be an externref value",)
};
match xs.data().downcast_ref::<T>().cloned() {
match xs.data(&mut store)?.downcast_ref::<T>().cloned() {
Some(xs) => Ok(xs.clone()),
None => anyhow::bail!("could not downcast extism_context",),
}
@@ -311,22 +311,22 @@ impl CurrentPlugin {
id: uuid::Uuid,
) -> Result<Self, Error> {
let wasi = if wasi {
let auth = wasmtime_wasi::ambient_authority();
let mut ctx = wasmtime_wasi::WasiCtxBuilder::new();
for (k, v) in manifest.config.iter() {
ctx.env(k, v)?;
}
// Disable sockets/DNS lookup
ctx.allow_ip_name_lookup(false)
.allow_tcp(false)
.allow_udp(false)
.allow_blocking_current_thread(true);
if let Some(a) = &manifest.allowed_paths {
for (k, v) in a.iter() {
let d = wasmtime_wasi::Dir::open_ambient_dir(k, auth).map_err(|err| {
Error::msg(format!(
"Unable to preopen directory \"{}\": {}",
k.display(),
err.kind()
))
})?;
ctx.preopened_dir(d, v)?;
ctx.preopened_dir(
k,
v.to_string_lossy(),
wasmtime_wasi::DirPerms::READ | wasmtime_wasi::DirPerms::MUTATE,
wasmtime_wasi::FilePerms::READ | wasmtime_wasi::FilePerms::WRITE,
)?;
}
}
@@ -335,7 +335,9 @@ impl CurrentPlugin {
ctx.inherit_stdout().inherit_stderr();
}
Some(Wasi { ctx: ctx.build() })
Some(Wasi {
ctx: ctx.build_p1(),
})
} else {
None
};

View File

@@ -38,8 +38,13 @@ impl From<wasmtime::ValType> for ValType {
F32 => ValType::F32,
F64 => ValType::F64,
V128 => ValType::V128,
FuncRef => ValType::FuncRef,
ExternRef => ValType::ExternRef,
Ref(t) => {
if t.heap_type().is_func() {
ValType::FuncRef
} else {
ValType::ExternRef
}
}
}
}
}
@@ -53,8 +58,8 @@ impl From<ValType> for wasmtime::ValType {
F32 => wasmtime::ValType::F32,
F64 => wasmtime::ValType::F64,
V128 => wasmtime::ValType::V128,
FuncRef => wasmtime::ValType::FuncRef,
ExternRef => wasmtime::ValType::ExternRef,
FuncRef => wasmtime::ValType::FUNCREF,
ExternRef => wasmtime::ValType::EXTERNREF,
}
}
}
@@ -171,8 +176,8 @@ pub struct Function {
/// Module name
pub(crate) namespace: Option<String>,
/// Function type
pub(crate) ty: wasmtime::FuncType,
pub(crate) params: Vec<ValType>,
pub(crate) results: Vec<ValType>,
/// Function handle
pub(crate) f: Arc<FunctionInner>,
@@ -185,8 +190,8 @@ impl Function {
/// Create a new host function
pub fn new<T: 'static, F>(
name: impl Into<String>,
args: impl IntoIterator<Item = ValType>,
returns: impl IntoIterator<Item = ValType>,
params: impl IntoIterator<Item = ValType>,
results: impl IntoIterator<Item = ValType>,
user_data: UserData<T>,
f: F,
) -> Function
@@ -198,13 +203,13 @@ impl Function {
{
let data = user_data.clone();
let name = name.into();
let args = args.into_iter().map(wasmtime::ValType::from);
let returns = returns.into_iter().map(wasmtime::ValType::from);
let ty = wasmtime::FuncType::new(args, returns);
trace!("Creating function {name}: type={ty:?}");
let params = params.into_iter().collect();
let results = results.into_iter().collect();
trace!("Creating function {name}: params={params:?}, results={results:?}");
Function {
name,
ty,
params,
results,
f: Arc::new(
move |mut caller: Caller<_>, inp: &[Val], outp: &mut [Val]| {
let x = data.clone();
@@ -219,6 +224,22 @@ impl Function {
}
}
pub(crate) fn ty(&self, engine: &wasmtime::Engine) -> wasmtime::FuncType {
wasmtime::FuncType::new(
engine,
self.params
.iter()
.cloned()
.map(wasmtime::ValType::from)
.collect::<Vec<_>>(),
self.results
.iter()
.cloned()
.map(wasmtime::ValType::from)
.collect::<Vec<_>>(),
)
}
/// Host function name
pub fn name(&self) -> &str {
&self.name
@@ -242,9 +263,14 @@ impl Function {
self
}
/// Get function type
pub fn ty(&self) -> &wasmtime::FuncType {
&self.ty
/// Get param types
pub fn params(&self) -> &[ValType] {
&self.params
}
/// Get result types
pub fn results(&self) -> &[ValType] {
&self.results
}
}

View File

@@ -3,7 +3,7 @@ use crate::*;
/// WASI context
pub struct Wasi {
/// wasi
pub ctx: wasmtime_wasi::WasiCtx,
pub ctx: wasmtime_wasi::preview1::WasiP1Ctx,
}
/// InternalExt provides a unified way of acessing `memory`, `store` and `internal` values

View File

@@ -224,7 +224,7 @@ fn relink(
macro_rules! add_funcs {
($($name:ident($($args:expr),*) $(-> $($r:expr),*)?);* $(;)?) => {
$(
let t = FuncType::new([$($args),*], [$($($r),*)?]);
let t = FuncType::new(&engine, [$($args),*], [$($($r),*)?]);
linker.func_new(EXTISM_ENV_MODULE, stringify!($name), t, pdk::$name)?;
)*
};
@@ -250,7 +250,7 @@ fn relink(
// If wasi is enabled then add it to the linker
if with_wasi {
wasmtime_wasi::add_to_linker(&mut linker, |x: &mut CurrentPlugin| {
wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |x: &mut CurrentPlugin| {
&mut x.wasi.as_mut().unwrap().ctx
})?;
}
@@ -259,7 +259,7 @@ fn relink(
let name = f.name();
let ns = f.namespace().unwrap_or(EXTISM_USER_MODULE);
unsafe {
linker.func_new(ns, name, f.ty().clone(), &*(f.f.as_ref() as *const _))?;
linker.func_new(ns, name, f.ty(engine).clone(), &*(f.f.as_ref() as *const _))?;
}
}
@@ -305,7 +305,8 @@ impl Plugin {
.coredump_on_trap(debug_options.coredump.is_some())
.profiler(debug_options.profiling_strategy)
.wasm_tail_call(true)
.wasm_function_references(true);
.wasm_function_references(true)
.wasm_gc(true);
match cache_dir {
Some(None) => (),
@@ -465,7 +466,7 @@ impl Plugin {
if let Some(f) = x.func() {
let (params, mut results) = (f.params(), f.results());
match (params.len(), results.len()) {
(0, 1) => results.next() == Some(wasmtime::ValType::I32),
(0, 1) => matches!(results.next(), Some(wasmtime::ValType::I32)),
(0, 0) => true,
_ => false,
}
@@ -481,7 +482,7 @@ impl Plugin {
&mut self,
input: *const u8,
mut len: usize,
host_context: Option<ExternRef>,
host_context: Option<Rooted<ExternRef>>,
) -> Result<(), Error> {
self.output = Output::default();
self.clear_error()?;
@@ -615,7 +616,7 @@ impl Plugin {
if let Some(reactor_init) = reactor_init {
reactor_init.call(&mut store, &[], &mut [])?;
}
let mut results = vec![Val::null(); init.ty(&store).results().len()];
let mut results = vec![Val::I32(0); init.ty(&store).results().len()];
init.call(
&mut store,
&[Val::I32(0), Val::I32(0)],
@@ -700,7 +701,7 @@ impl Plugin {
lock: &mut std::sync::MutexGuard<Option<Instance>>,
name: impl AsRef<str>,
input: impl AsRef<[u8]>,
host_context: Option<ExternRef>,
host_context: Option<Rooted<ExternRef>>,
) -> Result<i32, (Error, i32)> {
let name = name.as_ref();
let input = input.as_ref();
@@ -748,7 +749,7 @@ impl Plugin {
self.current_plugin_mut().start_time = std::time::Instant::now();
// Call the function
let mut results = vec![wasmtime::Val::null(); n_results];
let mut results = vec![wasmtime::Val::I32(0); n_results];
let mut res = func.call(self.store_mut(), &[], results.as_mut_slice());
// Stop timer
@@ -833,13 +834,7 @@ impl Plugin {
}
}
let wasi_exit_code = e
.downcast_ref::<wasmtime_wasi::I32Exit>()
.map(|e| e.0)
.or_else(|| {
e.downcast_ref::<wasmtime_wasi::preview2::I32Exit>()
.map(|e| e.0)
});
let wasi_exit_code = e.downcast_ref::<wasmtime_wasi::I32Exit>().map(|e| e.0);
if let Some(exit_code) = wasi_exit_code {
debug!(
plugin = self.id.to_string(),
@@ -921,7 +916,8 @@ impl Plugin {
let lock = self.instance.clone();
let mut lock = lock.lock().unwrap();
let data = input.to_bytes()?;
self.raw_call(&mut lock, name, data, Some(ExternRef::new(host_context)))
let ctx = ExternRef::new(&mut self.store, host_context)?;
self.raw_call(&mut lock, name, data, Some(ctx))
.map_err(|e| e.0)
.and_then(move |_| self.output())
}

View File

@@ -41,9 +41,9 @@ pub type ExtismFunctionType = extern "C" fn(
/// Log drain callback
pub type ExtismLogDrainFunctionType = extern "C" fn(data: *const std::ffi::c_char, size: Size);
impl From<&wasmtime::Val> for ExtismVal {
fn from(value: &wasmtime::Val) -> Self {
match value.ty() {
impl ExtismVal {
fn from_val(value: &wasmtime::Val, ctx: impl AsContext) -> Self {
match value.ty(ctx) {
wasmtime::ValType::I32 => ExtismVal {
t: ValType::I32,
v: ValUnion {
@@ -218,7 +218,11 @@ pub unsafe extern "C" fn extism_function_new(
output_types.clone(),
user_data,
move |plugin, inputs, outputs, user_data| {
let inputs: Vec<_> = inputs.iter().map(ExtismVal::from).collect();
let store = &*plugin.store;
let inputs: Vec<_> = inputs
.iter()
.map(|x| ExtismVal::from_val(x, store))
.collect();
let mut output_tmp: Vec<_> = output_types
.iter()
.map(|t| ExtismVal {
@@ -407,20 +411,6 @@ pub unsafe extern "C" fn extism_plugin_config(
}
};
let wasi = &mut plugin.current_plugin_mut().wasi;
if let Some(Wasi { ctx, .. }) = wasi {
for (k, v) in json.iter() {
match v {
Some(v) => {
let _ = ctx.push_env(k, v);
}
None => {
let _ = ctx.push_env(k, "");
}
}
}
}
let id = plugin.id;
let config = &mut plugin.current_plugin_mut().manifest.config;
for (k, v) in json.into_iter() {
@@ -528,13 +518,11 @@ pub unsafe extern "C" fn extism_plugin_call_with_host_context(
name
);
let input = std::slice::from_raw_parts(data, data_len as usize);
let res = plugin.raw_call(
&mut lock,
name,
input,
Some(ExternRef::new(CVoidContainer(host_context))),
);
let r = match ExternRef::new(&mut plugin.store, CVoidContainer(host_context)) {
Err(e) => return plugin.return_error(&mut lock, e, -1),
Ok(x) => x,
};
let res = plugin.raw_call(&mut lock, name, input, Some(r));
match res {
Err((e, rc)) => plugin.return_error(&mut lock, e, rc),
Ok(x) => x,