mirror of
https://github.com/extism/extism.git
synced 2026-01-11 23:08:06 -05:00
Compare commits
1 Commits
externref-
...
userdata
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d704a5068e |
@@ -1,7 +1,7 @@
|
||||
use extism::*;
|
||||
|
||||
// pretend this is redis or something :)
|
||||
type KVStore = std::collections::BTreeMap<String, Vec<u8>>;
|
||||
type KVStore = std::sync::Arc<std::sync::Mutex<std::collections::BTreeMap<String, Vec<u8>>>>;
|
||||
|
||||
// When a first argument separated with a semicolon is provided to `host_fn` it is used as the
|
||||
// variable name and type for the `UserData` parameter
|
||||
|
||||
@@ -296,13 +296,7 @@ impl CurrentPlugin {
|
||||
|
||||
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()
|
||||
))
|
||||
})?;
|
||||
let d = wasmtime_wasi::Dir::open_ambient_dir(k, auth)?;
|
||||
ctx.preopened_dir(d, v)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,7 @@ pub struct CPtr {
|
||||
/// UserDataHandle is an untyped version of `UserData` that is stored inside `Function` to keep a live reference.
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum UserDataHandle {
|
||||
#[allow(dead_code)]
|
||||
C(Arc<CPtr>),
|
||||
#[allow(dead_code)]
|
||||
Rust(Arc<std::sync::Mutex<dyn std::any::Any>>),
|
||||
}
|
||||
|
||||
@@ -83,18 +81,18 @@ pub(crate) enum UserDataHandle {
|
||||
/// using `UserData::get`. The `C` data is stored as a pointer and cleanup function and isn't usable from Rust. The cleanup function
|
||||
/// will be called when the inner `CPtr` is dropped.
|
||||
#[derive(Debug)]
|
||||
pub enum UserData<T: Sized> {
|
||||
pub enum UserData<T: Sync + Clone + Sized> {
|
||||
C(Arc<CPtr>),
|
||||
Rust(Arc<std::sync::Mutex<T>>),
|
||||
Rust(T),
|
||||
}
|
||||
|
||||
impl<T: Default> Default for UserData<T> {
|
||||
impl<T: Default + Sync + Clone> Default for UserData<T> {
|
||||
fn default() -> Self {
|
||||
UserData::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for UserData<T> {
|
||||
impl<T: Sync + Clone> Clone for UserData<T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
UserData::C(ptr) => UserData::C(ptr.clone()),
|
||||
@@ -103,7 +101,7 @@ impl<T> Clone for UserData<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserData<T> {
|
||||
impl<T: Sync + Clone> UserData<T> {
|
||||
/// Create a new `UserData` from an existing pointer and free function, this is used
|
||||
/// by the C API to wrap C pointers into user data
|
||||
pub(crate) fn new_pointer(
|
||||
@@ -128,12 +126,11 @@ impl<T> UserData<T> {
|
||||
///
|
||||
/// This will wrap the provided value in a reference-counted mutex
|
||||
pub fn new(x: T) -> Self {
|
||||
let data = Arc::new(std::sync::Mutex::new(x));
|
||||
UserData::Rust(data)
|
||||
UserData::Rust(x)
|
||||
}
|
||||
|
||||
/// Get a copy of the inner value
|
||||
pub fn get(&self) -> Result<Arc<std::sync::Mutex<T>>, Error> {
|
||||
pub fn get(&self) -> Result<T, Error> {
|
||||
match self {
|
||||
UserData::C { .. } => anyhow::bail!("C UserData should not be used from Rust"),
|
||||
UserData::Rust(data) => Ok(data.clone()),
|
||||
@@ -152,8 +149,8 @@ impl Drop for CPtr {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for UserData<T> {}
|
||||
unsafe impl<T> Sync for UserData<T> {}
|
||||
unsafe impl<T: Sync + Clone> Send for UserData<T> {}
|
||||
unsafe impl<T: Sync + Clone> Sync for UserData<T> {}
|
||||
unsafe impl Send for CPtr {}
|
||||
unsafe impl Sync for CPtr {}
|
||||
|
||||
@@ -182,7 +179,7 @@ pub struct Function {
|
||||
|
||||
impl Function {
|
||||
/// Create a new host function
|
||||
pub fn new<T: 'static, F>(
|
||||
pub fn new<T: 'static + Sync + Clone, F>(
|
||||
name: impl Into<String>,
|
||||
args: impl IntoIterator<Item = ValType>,
|
||||
returns: impl IntoIterator<Item = ValType>,
|
||||
@@ -213,7 +210,9 @@ impl Function {
|
||||
namespace: None,
|
||||
_user_data: match &user_data {
|
||||
UserData::C(ptr) => UserDataHandle::C(ptr.clone()),
|
||||
UserData::Rust(x) => UserDataHandle::Rust(x.clone()),
|
||||
UserData::Rust(x) => {
|
||||
UserDataHandle::Rust(std::sync::Arc::new(std::sync::Mutex::new(x.clone())))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +47,9 @@ fn to_module(engine: &Engine, wasm: &extism_manifest::Wasm) -> Result<(String, M
|
||||
let name = meta.name.as_deref().unwrap_or(MAIN_KEY).to_string();
|
||||
|
||||
// Load file
|
||||
let buf = std::fs::read(path).map_err(|err| {
|
||||
Error::msg(format!(
|
||||
"Unable to load Wasm file \"{}\": {}",
|
||||
path.display(),
|
||||
err.kind()
|
||||
))
|
||||
})?;
|
||||
let mut buf = Vec::new();
|
||||
let mut file = std::fs::File::open(path)?;
|
||||
file.read_to_end(&mut buf)?;
|
||||
|
||||
check_hash(&meta.hash, &buf)?;
|
||||
Ok((name, Module::new(engine, buf)?))
|
||||
|
||||
@@ -668,12 +668,11 @@ impl Plugin {
|
||||
|
||||
// Implements the build of the `call` function, `raw_call` is also used in the SDK
|
||||
// code
|
||||
pub(crate) fn raw_call<T: 'static + Sync + Send>(
|
||||
pub(crate) fn raw_call(
|
||||
&mut self,
|
||||
lock: &mut std::sync::MutexGuard<Option<Instance>>,
|
||||
name: impl AsRef<str>,
|
||||
input: impl AsRef<[u8]>,
|
||||
arg: Option<T>,
|
||||
) -> Result<i32, (Error, i32)> {
|
||||
let name = name.as_ref();
|
||||
let input = input.as_ref();
|
||||
@@ -721,14 +720,7 @@ impl Plugin {
|
||||
|
||||
// Call the function
|
||||
let mut results = vec![wasmtime::Val::null(); n_results];
|
||||
let args = if func.ty(self.store()).params().count() == 0 {
|
||||
vec![]
|
||||
} else {
|
||||
let r = arg.map(wasmtime::ExternRef::new);
|
||||
vec![wasmtime::Val::ExternRef(r)]
|
||||
};
|
||||
|
||||
let mut res = func.call(self.store_mut(), args.as_slice(), results.as_mut_slice());
|
||||
let mut res = func.call(self.store_mut(), &[], results.as_mut_slice());
|
||||
|
||||
// Stop timer
|
||||
self.store
|
||||
@@ -881,21 +873,7 @@ 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, None)
|
||||
.map_err(|e| e.0)
|
||||
.and_then(move |_| self.output())
|
||||
}
|
||||
|
||||
pub fn call_with_arg<'a, 'b, T: ToBytes<'a>, U: FromBytes<'b>, V: 'static + Send + Sync>(
|
||||
&'b mut self,
|
||||
name: impl AsRef<str>,
|
||||
input: T,
|
||||
arg: V,
|
||||
) -> Result<U, Error> {
|
||||
let lock = self.instance.clone();
|
||||
let mut lock = lock.lock().unwrap();
|
||||
let data = input.to_bytes()?;
|
||||
self.raw_call(&mut lock, name, data, Some(arg))
|
||||
self.raw_call(&mut lock, name, data)
|
||||
.map_err(|e| e.0)
|
||||
.and_then(move |_| self.output())
|
||||
}
|
||||
@@ -914,7 +892,7 @@ impl Plugin {
|
||||
let lock = self.instance.clone();
|
||||
let mut lock = lock.lock().unwrap();
|
||||
let data = input.to_bytes().map_err(|e| (e, -1))?;
|
||||
self.raw_call::<()>(&mut lock, name, data, None)
|
||||
self.raw_call(&mut lock, name, data)
|
||||
.and_then(move |_| self.output().map_err(|e| (e, -1)))
|
||||
}
|
||||
|
||||
@@ -1023,7 +1001,7 @@ macro_rules! typed_plugin {
|
||||
impl $name {
|
||||
$(
|
||||
pub fn $f<'a, $( $( $lt $( : $clt )? ),+ )? >(&'a mut self, input: $input) -> Result<$output, $crate::Error> {
|
||||
self.0.call::<_, _>(stringify!($f), input)
|
||||
self.0.call(stringify!($f), input)
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ impl<'a> PluginBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Add a single host function
|
||||
pub fn with_function<T: 'static, F>(
|
||||
pub fn with_function<T: Sync + Clone + 'static, F>(
|
||||
mut self,
|
||||
name: impl Into<String>,
|
||||
args: impl IntoIterator<Item = ValType>,
|
||||
@@ -80,7 +80,7 @@ impl<'a> PluginBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Add a single host function in a specific namespace
|
||||
pub fn with_function_in_namespace<T: 'static, F>(
|
||||
pub fn with_function_in_namespace<T: Sync + Clone + 'static, F>(
|
||||
mut self,
|
||||
namespace: impl Into<String>,
|
||||
name: impl Into<String>,
|
||||
|
||||
@@ -485,7 +485,7 @@ pub unsafe extern "C" fn extism_plugin_call(
|
||||
name
|
||||
);
|
||||
let input = std::slice::from_raw_parts(data, data_len as usize);
|
||||
let res = plugin.raw_call::<()>(&mut lock, name, input, None);
|
||||
let res = plugin.raw_call(&mut lock, name, input);
|
||||
|
||||
match res {
|
||||
Err((e, rc)) => plugin.return_error(&mut lock, e, rc),
|
||||
@@ -671,7 +671,7 @@ unsafe fn set_log_buffer(filter: &str) -> Result<(), Error> {
|
||||
/// Calls the provided callback function for each buffered log line.
|
||||
/// This is only needed when `extism_log_custom` is used.
|
||||
pub unsafe extern "C" fn extism_log_drain(handler: ExtismLogDrainFunctionType) {
|
||||
if let Some(buf) = LOG_BUFFER.as_mut() {
|
||||
if let Some(buf) = &mut LOG_BUFFER {
|
||||
if let Ok(mut buf) = buf.buffer.lock() {
|
||||
for (line, len) in buf.drain(..) {
|
||||
handler(line.as_ptr(), len as u64);
|
||||
|
||||
@@ -453,7 +453,7 @@ fn hello_world_user_data(
|
||||
_plugin: &mut CurrentPlugin,
|
||||
inputs: &[Val],
|
||||
outputs: &mut [Val],
|
||||
user_data: UserData<std::fs::File>,
|
||||
user_data: UserData<std::sync::Arc<std::sync::Mutex<std::fs::File>>>,
|
||||
) -> Result<(), Error> {
|
||||
let data = user_data.get()?;
|
||||
let mut data = data.lock().unwrap();
|
||||
@@ -470,7 +470,8 @@ fn test_userdata() {
|
||||
if path.exists() {
|
||||
std::fs::remove_file(&path).unwrap();
|
||||
}
|
||||
let file = std::fs::File::create(&path).unwrap();
|
||||
let file =
|
||||
std::sync::Arc::new(std::sync::Mutex::new(std::fs::File::create(&path).unwrap()));
|
||||
let f = Function::new(
|
||||
"hello_world",
|
||||
[PTR],
|
||||
|
||||
Reference in New Issue
Block a user