mirror of
https://github.com/extism/extism.git
synced 2026-01-11 14:58:01 -05:00
Compare commits
2 Commits
g4vi/wasi-
...
userdata
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d704a5068e | ||
|
|
054a29e91d |
@@ -25,7 +25,6 @@ extism-manifest = { workspace = true }
|
||||
extism-convert = { workspace = true, features = ["extism-path"] }
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
libc = "0.2"
|
||||
wasi-common = "14.0.0"
|
||||
|
||||
[features]
|
||||
default = ["http", "register-http", "register-filesystem"]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -81,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()),
|
||||
@@ -101,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(
|
||||
@@ -126,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()),
|
||||
@@ -150,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 {}
|
||||
|
||||
@@ -180,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>,
|
||||
@@ -211,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())))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ pub(crate) mod pdk;
|
||||
mod plugin;
|
||||
mod plugin_builder;
|
||||
mod timer;
|
||||
mod wasi_command;
|
||||
|
||||
/// Extism C API
|
||||
pub mod sdk;
|
||||
@@ -30,7 +29,6 @@ pub use extism_manifest::{Manifest, Wasm, WasmMetadata};
|
||||
pub use function::{Function, UserData, Val, ValType, PTR};
|
||||
pub use plugin::{CancelHandle, Plugin, WasmInput, EXTISM_ENV_MODULE, EXTISM_USER_MODULE};
|
||||
pub use plugin_builder::{DebugOptions, PluginBuilder};
|
||||
pub use wasi_command::WASICommand;
|
||||
|
||||
pub(crate) use internal::{Internal, Wasi};
|
||||
pub(crate) use timer::{Timer, TimerAction};
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -9,7 +9,6 @@ const WASM_LOOP: &[u8] = include_bytes!("../../../wasm/loop.wasm");
|
||||
const WASM_GLOBALS: &[u8] = include_bytes!("../../../wasm/globals.wasm");
|
||||
const WASM_REFLECT: &[u8] = include_bytes!("../../../wasm/reflect.wasm");
|
||||
const WASM_HTTP: &[u8] = include_bytes!("../../../wasm/http.wasm");
|
||||
const WASM_COMMAND: &[u8] = include_bytes!("../../../wasm/command.wasm");
|
||||
|
||||
host_fn!(pub hello_world (a: String) -> String { Ok(a) });
|
||||
|
||||
@@ -454,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();
|
||||
@@ -471,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],
|
||||
@@ -687,26 +687,3 @@ fn test_linking() {
|
||||
assert_eq!(plugin.call::<&str, i64>("run", "Hello, world!").unwrap(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wasi_command() {
|
||||
let cwasm = WASICommand::from_bytes(WASM_COMMAND);
|
||||
let envs: Vec<(String, String)> = vec![
|
||||
(String::from("var1"), String::from("value1")),
|
||||
(String::from("var2"), String::from("value2")),
|
||||
];
|
||||
let args: Vec<String> = vec![String::from("zero"), String::from("one")];
|
||||
let (output, stderr): (String, Vec<u8>) = cwasm.run(&envs, &args, "test").unwrap();
|
||||
println!("stdout: {}", output);
|
||||
eprintln!("stderr as bytes: {:?}", stderr);
|
||||
eprintln!("stderr as String: {}", String::from_utf8(stderr).unwrap());
|
||||
|
||||
let (output, stderr): (String, Vec<u8>) = cwasm.run(&envs, &args, "test").unwrap();
|
||||
println!("2| stdout: {}", output);
|
||||
eprintln!("2| stderr as bytes: {:?}", stderr);
|
||||
eprintln!(
|
||||
"2| stderr as String: {}",
|
||||
String::from_utf8(stderr).unwrap()
|
||||
);
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
use crate::Error;
|
||||
use crate::FromBytesOwned;
|
||||
use crate::ToBytes;
|
||||
use wasi_common::pipe::{ReadPipe, WritePipe};
|
||||
|
||||
pub struct WASICommand {
|
||||
engine: wasmtime::Engine,
|
||||
module: wasmtime::Module,
|
||||
}
|
||||
|
||||
impl WASICommand {
|
||||
pub fn from(engine: wasmtime::Engine, module: wasmtime::Module) -> WASICommand {
|
||||
WASICommand { engine, module }
|
||||
}
|
||||
|
||||
pub fn from_bytes(data: &[u8]) -> WASICommand {
|
||||
let config = wasmtime::Config::new();
|
||||
let engine = wasmtime::Engine::new(&config).unwrap();
|
||||
let module = wasmtime::Module::from_binary(&engine, data).unwrap();
|
||||
WASICommand::from(engine, module)
|
||||
}
|
||||
|
||||
pub fn run<'a, T: ToBytes<'a>, U: FromBytesOwned, V: FromBytesOwned>(
|
||||
&self,
|
||||
envs: &[(String, String)],
|
||||
args: &[String],
|
||||
stdin: T,
|
||||
) -> Result<(U, V), Error> {
|
||||
let bytes = stdin.to_bytes()?;
|
||||
let stdin = ReadPipe::from(bytes.as_ref());
|
||||
let stdout = WritePipe::new_in_memory();
|
||||
let stderr = WritePipe::new_in_memory();
|
||||
let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new()
|
||||
.args(args)?
|
||||
.envs(envs)?
|
||||
.stdin(Box::new(stdin.clone()))
|
||||
.stdout(Box::new(stdout.clone()))
|
||||
.stderr(Box::new(stderr.clone()))
|
||||
.build();
|
||||
|
||||
let mut store = wasmtime::Store::new(&self.engine, wasi_ctx);
|
||||
let mut linker = wasmtime::Linker::new(&self.engine);
|
||||
wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?;
|
||||
let instance = linker.instantiate(&mut store, &self.module)?;
|
||||
let function_name = "_start";
|
||||
let f = instance
|
||||
.get_func(&mut store, function_name)
|
||||
.expect("function exists");
|
||||
f.call(&mut store, &[], &mut []).unwrap();
|
||||
drop(store);
|
||||
let contents: Vec<u8> = stdout.try_into_inner().unwrap().into_inner();
|
||||
let stderr_contents: Vec<u8> = stderr.try_into_inner().unwrap().into_inner();
|
||||
let stdout_output = U::from_bytes_owned(&contents)?;
|
||||
let stderr_output = V::from_bytes_owned(&stderr_contents)?;
|
||||
Ok((stdout_output, stderr_output))
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
puts("test stdout");
|
||||
printf("argc %d\n", argc);
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
printf("argv[%d]: %s\n", i, argv[i]);
|
||||
}
|
||||
char **s = environ;
|
||||
for (; *s; s++)
|
||||
{
|
||||
printf("env: %s\n", *s);
|
||||
}
|
||||
printf("stdin: ");
|
||||
int c;
|
||||
while ((c = getchar()) != EOF)
|
||||
{
|
||||
putchar(c);
|
||||
}
|
||||
fprintf(stderr, "test stderr\n");
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user