mirror of
https://github.com/extism/extism.git
synced 2026-01-11 14:58:01 -05:00
Compare commits
13 Commits
v1.4.1
...
feat/expos
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
566372c736 | ||
|
|
f87eec2214 | ||
|
|
985f66472f | ||
|
|
60da76cf37 | ||
|
|
f68ba112bc | ||
|
|
b7fa319cb9 | ||
|
|
d04e2e42bf | ||
|
|
6d2735cec7 | ||
|
|
b1d0f335b3 | ||
|
|
3a7768ffd5 | ||
|
|
ee8c41ab26 | ||
|
|
8312e98463 | ||
|
|
17a546b2db |
@@ -81,10 +81,10 @@ mod tests {
|
||||
c: true,
|
||||
};
|
||||
let raw = Raw(&x).to_bytes().unwrap();
|
||||
let y = Raw::from_bytes(&raw).unwrap();
|
||||
let y = Raw::from_bytes(raw).unwrap();
|
||||
assert_eq!(&x, y.0);
|
||||
|
||||
let y: Result<Raw<[u8; std::mem::size_of::<TestRaw>()]>, Error> = Raw::from_bytes(&raw);
|
||||
let y: Result<Raw<[u8; std::mem::size_of::<TestRaw>()]>, Error> = Raw::from_bytes(raw);
|
||||
assert!(y.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,7 +546,8 @@ pub unsafe fn reset() {
|
||||
MemoryRoot::new().reset()
|
||||
}
|
||||
|
||||
/// Set the error message offset
|
||||
/// Set the error message offset, the handle passed to this
|
||||
/// function should not be freed after this call
|
||||
#[no_mangle]
|
||||
pub unsafe fn error_set(h: Handle) {
|
||||
let root = MemoryRoot::new();
|
||||
|
||||
@@ -279,7 +279,7 @@ pub struct Manifest {
|
||||
/// the path on disk to the path it should be available inside the plugin.
|
||||
/// For example, `".": "/tmp"` would mount the current directory as `/tmp` inside the module
|
||||
#[serde(default)]
|
||||
pub allowed_paths: Option<BTreeMap<PathBuf, PathBuf>>,
|
||||
pub allowed_paths: Option<BTreeMap<String, PathBuf>>,
|
||||
|
||||
/// The plugin timeout in milliseconds
|
||||
#[serde(default)]
|
||||
@@ -337,8 +337,7 @@ impl Manifest {
|
||||
}
|
||||
|
||||
/// Add a path to `allowed_paths`
|
||||
pub fn with_allowed_path(mut self, src: impl AsRef<Path>, dest: impl AsRef<Path>) -> Self {
|
||||
let src = src.as_ref().to_path_buf();
|
||||
pub fn with_allowed_path(mut self, src: String, dest: impl AsRef<Path>) -> Self {
|
||||
let dest = dest.as_ref().to_path_buf();
|
||||
match &mut self.allowed_paths {
|
||||
Some(p) => {
|
||||
@@ -355,7 +354,7 @@ impl Manifest {
|
||||
}
|
||||
|
||||
/// Set `allowed_paths`
|
||||
pub fn with_allowed_paths(mut self, paths: impl Iterator<Item = (PathBuf, PathBuf)>) -> Self {
|
||||
pub fn with_allowed_paths(mut self, paths: impl Iterator<Item = (String, PathBuf)>) -> Self {
|
||||
self.allowed_paths = Some(paths.collect());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
wasmtime = ">= 20.0.0, < 22.0.0"
|
||||
wasi-common = ">= 20.0.0, < 22.0.0"
|
||||
wasmtime = ">= 20.0.0, < 23.0.0"
|
||||
wasi-common = ">= 20.0.0, < 23.0.0"
|
||||
wiggle = ">= 20.0.0, < 23.0.0"
|
||||
anyhow = "1"
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
serde_json = "1"
|
||||
|
||||
@@ -12,7 +12,7 @@ To use the `extism` crate, you can add it to your Cargo file:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
extism = "1.2.0"
|
||||
extism = "1.4.1"
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
|
||||
@@ -35,6 +35,21 @@ pub fn create_plugin(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn create_plugin_no_cache(c: &mut Criterion) {
|
||||
let mut g = c.benchmark_group("create");
|
||||
g.noise_threshold(1.0);
|
||||
g.significance_level(0.2);
|
||||
g.bench_function("create_plugin_no_cache", |b| {
|
||||
b.iter(|| {
|
||||
let _plugin = PluginBuilder::new(COUNT_VOWELS)
|
||||
.with_cache_disabled()
|
||||
.with_wasi(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
||||
struct Count {
|
||||
count: u32,
|
||||
@@ -251,6 +266,7 @@ criterion_group!(
|
||||
reflect_linked,
|
||||
basic,
|
||||
create_plugin,
|
||||
create_plugin_no_cache,
|
||||
count_vowels
|
||||
);
|
||||
criterion_main!(benches);
|
||||
|
||||
33
runtime/examples/fs.rs
Normal file
33
runtime/examples/fs.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use extism::*;
|
||||
fn main() {
|
||||
let url = Wasm::file("../wasm/read_write.wasm");
|
||||
let manifest = Manifest::new([url])
|
||||
.with_allowed_path("ro:src/tests/data".to_string(), "/data")
|
||||
.with_config_key("path", "/data/data.txt");
|
||||
|
||||
let mut plugin = PluginBuilder::new(manifest)
|
||||
.with_wasi(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
println!("trying to read file: ");
|
||||
|
||||
let res = plugin.call::<&str, &str>("try_read", "").unwrap();
|
||||
|
||||
println!("{:?}", res);
|
||||
|
||||
println!("-----------------------------------------------------");
|
||||
|
||||
println!("trying to write file: ");
|
||||
let line = format!(
|
||||
"Hello World at {:?}\n",
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
);
|
||||
let res2 = plugin.call::<&str, &str>("try_write", &line).unwrap();
|
||||
|
||||
println!("{:?}", res2);
|
||||
|
||||
println!("done!");
|
||||
}
|
||||
@@ -160,10 +160,10 @@ impl CurrentPlugin {
|
||||
}
|
||||
|
||||
pub fn memory_bytes_mut(&mut self, handle: MemoryHandle) -> Result<&mut [u8], Error> {
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
if let Some(mem) = linker.get(&mut store, EXTISM_ENV_MODULE, "memory") {
|
||||
let (linker, store) = self.linker_and_store();
|
||||
if let Some(mem) = linker.get(&mut *store, EXTISM_ENV_MODULE, "memory") {
|
||||
let mem = mem.into_memory().unwrap();
|
||||
let ptr = unsafe { mem.data_ptr(&store).add(handle.offset() as usize) };
|
||||
let ptr = unsafe { mem.data_ptr(&*store).add(handle.offset() as usize) };
|
||||
if ptr.is_null() {
|
||||
return Ok(&mut []);
|
||||
}
|
||||
@@ -174,10 +174,10 @@ impl CurrentPlugin {
|
||||
}
|
||||
|
||||
pub fn memory_bytes(&mut self, handle: MemoryHandle) -> Result<&[u8], Error> {
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
if let Some(mem) = linker.get(&mut store, EXTISM_ENV_MODULE, "memory") {
|
||||
let (linker, store) = self.linker_and_store();
|
||||
if let Some(mem) = linker.get(&mut *store, EXTISM_ENV_MODULE, "memory") {
|
||||
let mem = mem.into_memory().unwrap();
|
||||
let ptr = unsafe { mem.data_ptr(&store).add(handle.offset() as usize) };
|
||||
let ptr = unsafe { mem.data_ptr(&*store).add(handle.offset() as usize) };
|
||||
if ptr.is_null() {
|
||||
return Ok(&[]);
|
||||
}
|
||||
@@ -188,17 +188,17 @@ impl CurrentPlugin {
|
||||
}
|
||||
|
||||
pub fn host_context<T: Clone + 'static>(&mut self) -> Result<T, Error> {
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
let Some(Extern::Global(xs)) = linker.get(&mut store, EXTISM_ENV_MODULE, "extism_context")
|
||||
let (linker, store) = self.linker_and_store();
|
||||
let Some(Extern::Global(xs)) = linker.get(&mut *store, EXTISM_ENV_MODULE, "extism_context")
|
||||
else {
|
||||
anyhow::bail!("unable to locate an extism kernel global: extism_context",)
|
||||
};
|
||||
|
||||
let Val::ExternRef(Some(xs)) = xs.get(&mut 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(&mut store)?.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",),
|
||||
}
|
||||
@@ -216,7 +216,7 @@ impl CurrentPlugin {
|
||||
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "alloc") {
|
||||
f.into_func()
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(n as i64)], output)?;
|
||||
.call(&mut *store, &[Val::I64(n as i64)], output)?;
|
||||
} else {
|
||||
anyhow::bail!("{} unable to allocate memory", self.id);
|
||||
}
|
||||
@@ -238,11 +238,11 @@ impl CurrentPlugin {
|
||||
|
||||
/// Free a block of Extism plugin memory
|
||||
pub fn memory_free(&mut self, handle: MemoryHandle) -> Result<(), Error> {
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "free") {
|
||||
let (linker, store) = self.linker_and_store();
|
||||
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "free") {
|
||||
f.into_func()
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(handle.offset as i64)], &mut [])?;
|
||||
.call(&mut *store, &[Val::I64(handle.offset as i64)], &mut [])?;
|
||||
} else {
|
||||
anyhow::bail!("unable to locate an extism kernel function: free",)
|
||||
}
|
||||
@@ -250,12 +250,12 @@ impl CurrentPlugin {
|
||||
}
|
||||
|
||||
pub fn memory_length(&mut self, offs: u64) -> Result<u64, Error> {
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
let (linker, store) = self.linker_and_store();
|
||||
let output = &mut [Val::I64(0)];
|
||||
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "length") {
|
||||
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "length") {
|
||||
f.into_func()
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(offs as i64)], output)?;
|
||||
.call(&mut *store, &[Val::I64(offs as i64)], output)?;
|
||||
} else {
|
||||
anyhow::bail!("unable to locate an extism kernel function: length",)
|
||||
}
|
||||
@@ -270,12 +270,12 @@ impl CurrentPlugin {
|
||||
}
|
||||
|
||||
pub fn memory_length_unsafe(&mut self, offs: u64) -> Result<u64, Error> {
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
let (linker, store) = self.linker_and_store();
|
||||
let output = &mut [Val::I64(0)];
|
||||
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "length_unsafe") {
|
||||
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "length_unsafe") {
|
||||
f.into_func()
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(offs as i64)], output)?;
|
||||
.call(&mut *store, &[Val::I64(offs as i64)], output)?;
|
||||
} else {
|
||||
anyhow::bail!("unable to locate an extism kernel function: length_unsafe",)
|
||||
}
|
||||
@@ -320,9 +320,20 @@ impl CurrentPlugin {
|
||||
|
||||
if let Some(a) = &manifest.allowed_paths {
|
||||
for (k, v) in a.iter() {
|
||||
let file = Box::new(wasi_common::sync::dir::Dir::from_cap_std(
|
||||
wasi_common::sync::Dir::open_ambient_dir(k, auth)?,
|
||||
));
|
||||
let readonly = k.starts_with("ro:");
|
||||
|
||||
let dir_path = if readonly { &k[3..] } else { k };
|
||||
|
||||
let dir = wasi_common::sync::dir::Dir::from_cap_std(
|
||||
wasi_common::sync::Dir::open_ambient_dir(dir_path, auth)?,
|
||||
);
|
||||
|
||||
let file: Box<dyn wasi_common::dir::WasiDir> = if readonly {
|
||||
Box::new(readonly_dir::ReadOnlyDir::new(dir))
|
||||
} else {
|
||||
Box::new(dir)
|
||||
};
|
||||
|
||||
ctx.push_preopened_dir(file, v)?;
|
||||
}
|
||||
}
|
||||
@@ -403,12 +414,12 @@ impl CurrentPlugin {
|
||||
/// Clear the current plugin error
|
||||
pub fn clear_error(&mut self) {
|
||||
trace!(plugin = self.id.to_string(), "CurrentPlugin::clear_error");
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "error_set") {
|
||||
let (linker, store) = self.linker_and_store();
|
||||
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "error_set") {
|
||||
let res = f
|
||||
.into_func()
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(0)], &mut []);
|
||||
.call(&mut *store, &[Val::I64(0)], &mut []);
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
plugin = self.id.to_string(),
|
||||
@@ -440,10 +451,10 @@ impl CurrentPlugin {
|
||||
let s = s.as_ref();
|
||||
debug!(plugin = self.id.to_string(), "set error: {:?}", s);
|
||||
let handle = self.current_plugin_mut().memory_new(s)?;
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "error_set") {
|
||||
let (linker, store) = self.linker_and_store();
|
||||
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "error_set") {
|
||||
f.into_func().unwrap().call(
|
||||
&mut store,
|
||||
&mut *store,
|
||||
&[Val::I64(handle.offset() as i64)],
|
||||
&mut [],
|
||||
)?;
|
||||
@@ -454,10 +465,10 @@ impl CurrentPlugin {
|
||||
}
|
||||
|
||||
pub(crate) fn get_error_position(&mut self) -> (u64, u64) {
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
let (linker, store) = self.linker_and_store();
|
||||
let output = &mut [Val::I64(0)];
|
||||
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "error_get") {
|
||||
if let Err(e) = f.into_func().unwrap().call(&mut store, &[], output) {
|
||||
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "error_get") {
|
||||
if let Err(e) = f.into_func().unwrap().call(&mut *store, &[], output) {
|
||||
error!(
|
||||
plugin = self.id.to_string(),
|
||||
"unable to call extism:host/env::error_get: {:?}", e
|
||||
|
||||
@@ -279,7 +279,7 @@ impl Function {
|
||||
/// For example, the following defines a host function named `add_newline` that takes a
|
||||
/// string parameter and returns a string result:
|
||||
/// ```rust
|
||||
/// extism::host_fn!(add_newline(_user_data: (), a: String) -> String { Ok(a + "\n") });
|
||||
/// extism::host_fn!(add_newline(_user_data: (); a: String) -> String { Ok(a + "\n") });
|
||||
/// ```
|
||||
/// A few things worth noting:
|
||||
/// - The function always returns a `Result` that wraps the specified return type
|
||||
|
||||
@@ -18,6 +18,7 @@ pub(crate) mod manifest;
|
||||
pub(crate) mod pdk;
|
||||
mod plugin;
|
||||
mod plugin_builder;
|
||||
mod readonly_dir;
|
||||
mod timer;
|
||||
|
||||
/// Extism C API
|
||||
|
||||
@@ -377,3 +377,14 @@ pub(crate) fn log_error(
|
||||
) -> Result<(), Error> {
|
||||
log(tracing::Level::ERROR, caller, input, _output)
|
||||
}
|
||||
|
||||
/// Write to logs (trace)
|
||||
/// Params: i64 (offset)
|
||||
/// Returns: none
|
||||
pub(crate) fn log_trace(
|
||||
caller: Caller<CurrentPlugin>,
|
||||
input: &[Val],
|
||||
_output: &mut [Val],
|
||||
) -> Result<(), Error> {
|
||||
log(tracing::Level::TRACE, caller, input, _output)
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ pub struct Plugin {
|
||||
pub(crate) store_needs_reset: bool,
|
||||
|
||||
pub(crate) debug_options: DebugOptions,
|
||||
|
||||
pub(crate) error_msg: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Plugin {}
|
||||
@@ -242,6 +244,7 @@ fn relink(
|
||||
log_info(I64);
|
||||
log_debug(I64);
|
||||
log_error(I64);
|
||||
log_trace(I64);
|
||||
);
|
||||
|
||||
let mut linked = BTreeSet::new();
|
||||
@@ -361,6 +364,7 @@ impl Plugin {
|
||||
store_needs_reset: false,
|
||||
debug_options,
|
||||
_functions: imports,
|
||||
error_msg: None,
|
||||
};
|
||||
|
||||
plugin.current_plugin_mut().store = &mut plugin.store;
|
||||
@@ -608,17 +612,17 @@ impl Plugin {
|
||||
|
||||
// Initialize the guest runtime
|
||||
pub(crate) fn initialize_guest_runtime(&mut self) -> Result<(), Error> {
|
||||
let mut store = &mut self.store;
|
||||
let store = &mut self.store;
|
||||
if let Some(runtime) = &self.runtime {
|
||||
trace!(plugin = self.id.to_string(), "Plugin::initialize_runtime");
|
||||
match runtime {
|
||||
GuestRuntime::Haskell { init, reactor_init } => {
|
||||
if let Some(reactor_init) = reactor_init {
|
||||
reactor_init.call(&mut store, &[], &mut [])?;
|
||||
reactor_init.call(&mut *store, &[], &mut [])?;
|
||||
}
|
||||
let mut results = vec![Val::I32(0); init.ty(&store).results().len()];
|
||||
let mut results = vec![Val::I32(0); init.ty(&*store).results().len()];
|
||||
init.call(
|
||||
&mut store,
|
||||
&mut *store,
|
||||
&[Val::I32(0), Val::I32(0)],
|
||||
results.as_mut_slice(),
|
||||
)?;
|
||||
@@ -628,7 +632,7 @@ impl Plugin {
|
||||
);
|
||||
}
|
||||
GuestRuntime::Wasi { init } => {
|
||||
init.call(&mut store, &[], &mut [])?;
|
||||
init.call(&mut *store, &[], &mut [])?;
|
||||
debug!(plugin = self.id.to_string(), "initialied WASI runtime");
|
||||
}
|
||||
}
|
||||
@@ -641,20 +645,20 @@ impl Plugin {
|
||||
fn output_memory_position(&mut self) -> Result<(u64, u64), Error> {
|
||||
let out = &mut [Val::I64(0)];
|
||||
let out_len = &mut [Val::I64(0)];
|
||||
let mut store = &mut self.store;
|
||||
let store = &mut self.store;
|
||||
if let Some(f) = self
|
||||
.linker
|
||||
.get(&mut store, EXTISM_ENV_MODULE, "output_offset")
|
||||
.get(&mut *store, EXTISM_ENV_MODULE, "output_offset")
|
||||
{
|
||||
f.into_func().unwrap().call(&mut store, &[], out)?;
|
||||
f.into_func().unwrap().call(&mut *store, &[], out)?;
|
||||
} else {
|
||||
anyhow::bail!("unable to set output")
|
||||
}
|
||||
if let Some(f) = self
|
||||
.linker
|
||||
.get(&mut store, EXTISM_ENV_MODULE, "output_length")
|
||||
.get(&mut *store, EXTISM_ENV_MODULE, "output_length")
|
||||
{
|
||||
f.into_func().unwrap().call(&mut store, &[], out_len)?;
|
||||
f.into_func().unwrap().call(&mut *store, &[], out_len)?;
|
||||
} else {
|
||||
anyhow::bail!("unable to set output length")
|
||||
}
|
||||
@@ -953,11 +957,11 @@ impl Plugin {
|
||||
|
||||
pub(crate) fn clear_error(&mut self) -> Result<(), Error> {
|
||||
trace!(plugin = self.id.to_string(), "clearing error");
|
||||
let (linker, mut store) = self.linker_and_store();
|
||||
if let Some(f) = linker.get(&mut store, EXTISM_ENV_MODULE, "error_set") {
|
||||
let (linker, store) = self.linker_and_store();
|
||||
if let Some(f) = linker.get(&mut *store, EXTISM_ENV_MODULE, "error_set") {
|
||||
f.into_func()
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(0)], &mut [])?;
|
||||
.call(store, &[Val::I64(0)], &mut [])?;
|
||||
Ok(())
|
||||
} else {
|
||||
anyhow::bail!("Plugin::clear_error failed, extism:host/env::error_set not found")
|
||||
|
||||
109
runtime/src/readonly_dir.rs
Normal file
109
runtime/src/readonly_dir.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use crate::*;
|
||||
|
||||
use wasi_common::{Error, ErrorExt};
|
||||
|
||||
pub struct ReadOnlyDir<D: wasi_common::WasiDir> {
|
||||
inner: std::sync::Arc<D>,
|
||||
}
|
||||
|
||||
impl<D: wasi_common::WasiDir> ReadOnlyDir<D> {
|
||||
pub fn new(inner: D) -> Self {
|
||||
ReadOnlyDir {
|
||||
inner: std::sync::Arc::new(inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wiggle::async_trait]
|
||||
impl<D: wasi_common::WasiDir> wasi_common::WasiDir for ReadOnlyDir<D> {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self.inner.as_any()
|
||||
}
|
||||
|
||||
async fn open_file(
|
||||
&self,
|
||||
symlink_follow: bool,
|
||||
path: &str,
|
||||
oflags: wasi_common::file::OFlags,
|
||||
read: bool,
|
||||
write: bool,
|
||||
fdflags: wasi_common::file::FdFlags,
|
||||
) -> Result<wasi_common::dir::OpenResult, Error> {
|
||||
if write {
|
||||
return Err(Error::not_supported());
|
||||
}
|
||||
self.inner
|
||||
.open_file(symlink_follow, path, oflags, read, false, fdflags)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_dir(&self, _path: &str) -> Result<(), Error> {
|
||||
Err(Error::not_supported())
|
||||
}
|
||||
|
||||
async fn readdir(
|
||||
&self,
|
||||
cursor: wasi_common::dir::ReaddirCursor,
|
||||
) -> Result<
|
||||
Box<dyn Iterator<Item = Result<wasi_common::dir::ReaddirEntity, Error>> + Send>,
|
||||
Error,
|
||||
> {
|
||||
self.inner.readdir(cursor).await
|
||||
}
|
||||
|
||||
async fn symlink(&self, _old_path: &str, _new_path: &str) -> Result<(), Error> {
|
||||
Err(Error::not_supported())
|
||||
}
|
||||
|
||||
async fn remove_dir(&self, _path: &str) -> Result<(), Error> {
|
||||
Err(Error::not_supported())
|
||||
}
|
||||
|
||||
async fn unlink_file(&self, _path: &str) -> Result<(), Error> {
|
||||
Err(Error::not_supported())
|
||||
}
|
||||
|
||||
async fn read_link(&self, path: &str) -> Result<std::path::PathBuf, Error> {
|
||||
self.inner.read_link(path).await
|
||||
}
|
||||
|
||||
async fn get_filestat(&self) -> Result<wasi_common::file::Filestat, Error> {
|
||||
self.inner.get_filestat().await
|
||||
}
|
||||
|
||||
async fn get_path_filestat(
|
||||
&self,
|
||||
path: &str,
|
||||
follow_symlinks: bool,
|
||||
) -> Result<wasi_common::file::Filestat, Error> {
|
||||
self.inner.get_path_filestat(path, follow_symlinks).await
|
||||
}
|
||||
|
||||
async fn rename(
|
||||
&self,
|
||||
_path: &str,
|
||||
_dest_dir: &dyn wasi_common::WasiDir,
|
||||
_dest_path: &str,
|
||||
) -> Result<(), Error> {
|
||||
Err(wasi_common::Error::not_supported())
|
||||
}
|
||||
|
||||
async fn hard_link(
|
||||
&self,
|
||||
_path: &str,
|
||||
_target_dir: &dyn wasi_common::WasiDir,
|
||||
_target_path: &str,
|
||||
) -> Result<(), Error> {
|
||||
Err(wasi_common::Error::not_supported())
|
||||
}
|
||||
|
||||
async fn set_times(
|
||||
&self,
|
||||
_path: &str,
|
||||
_atime: std::option::Option<wasi_common::SystemTimeSpec>,
|
||||
_mtime: std::option::Option<wasi_common::SystemTimeSpec>,
|
||||
_follow_symlinks: bool,
|
||||
) -> Result<(), Error> {
|
||||
Err(wasi_common::Error::not_supported())
|
||||
}
|
||||
}
|
||||
@@ -505,6 +505,8 @@ pub unsafe extern "C" fn extism_plugin_call_with_host_context(
|
||||
let lock = plugin.instance.clone();
|
||||
let mut lock = lock.lock().unwrap();
|
||||
|
||||
plugin.error_msg = None;
|
||||
|
||||
// Get function name
|
||||
let name = std::ffi::CStr::from_ptr(func_name);
|
||||
let name = match name.to_str() {
|
||||
@@ -551,10 +553,19 @@ pub unsafe extern "C" fn extism_plugin_error(plugin: *mut Plugin) -> *const c_ch
|
||||
return std::ptr::null();
|
||||
}
|
||||
|
||||
plugin
|
||||
let offs = plugin.output.error_offset;
|
||||
|
||||
let ptr = plugin.current_plugin_mut().memory_ptr().add(offs as usize) as *const _;
|
||||
|
||||
let len = plugin
|
||||
.current_plugin_mut()
|
||||
.memory_ptr()
|
||||
.add(plugin.output.error_offset as usize) as *const _
|
||||
.memory_length(offs)
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut data = std::slice::from_raw_parts(ptr, len as usize).to_vec();
|
||||
data.push(0);
|
||||
plugin.error_msg = Some(data);
|
||||
plugin.error_msg.as_ref().unwrap().as_ptr() as *const _
|
||||
}
|
||||
|
||||
/// Get the length of a plugin's output data
|
||||
|
||||
1
runtime/src/tests/data/data.txt
Normal file
1
runtime/src/tests/data/data.txt
Normal file
@@ -0,0 +1 @@
|
||||
hello world!
|
||||
@@ -3,36 +3,32 @@ use quickcheck::*;
|
||||
|
||||
const KERNEL: &[u8] = include_bytes!("../extism-runtime.wasm");
|
||||
|
||||
fn extism_alloc<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, n: u64) -> u64 {
|
||||
fn extism_alloc<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, n: u64) -> u64 {
|
||||
let out_alloc = &mut [Val::I64(0)];
|
||||
instance
|
||||
.get_func(&mut store, "alloc")
|
||||
.get_func(&mut *store, "alloc")
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(n as i64)], out_alloc)
|
||||
.call(store, &[Val::I64(n as i64)], out_alloc)
|
||||
.unwrap();
|
||||
out_alloc[0].unwrap_i64() as u64
|
||||
}
|
||||
|
||||
fn extism_length<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
|
||||
fn extism_length<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
|
||||
let out = &mut [Val::I64(0)];
|
||||
instance
|
||||
.get_func(&mut store, "length")
|
||||
.get_func(&mut *store, "length")
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(p as i64)], out)
|
||||
.call(store, &[Val::I64(p as i64)], out)
|
||||
.unwrap();
|
||||
out[0].unwrap_i64() as u64
|
||||
}
|
||||
|
||||
fn extism_length_unsafe<T>(
|
||||
mut store: &mut wasmtime::Store<T>,
|
||||
instance: &mut Instance,
|
||||
p: u64,
|
||||
) -> u64 {
|
||||
fn extism_length_unsafe<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
|
||||
let out = &mut [Val::I64(0)];
|
||||
instance
|
||||
.get_func(&mut store, "length_unsafe")
|
||||
.get_func(&mut *store, "length_unsafe")
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(p as i64)], out)
|
||||
.call(store, &[Val::I64(p as i64)], out)
|
||||
.unwrap();
|
||||
out[0].unwrap_i64() as u64
|
||||
}
|
||||
@@ -47,122 +43,96 @@ fn extism_load_u8<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance
|
||||
out[0].unwrap_i32() as u8
|
||||
}
|
||||
|
||||
fn extism_load_u64<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
|
||||
fn extism_load_u64<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u64 {
|
||||
let out = &mut [Val::I32(0)];
|
||||
instance
|
||||
.get_func(&mut store, "load_u64")
|
||||
.get_func(&mut *store, "load_u64")
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(p as i64)], out)
|
||||
.call(store, &[Val::I64(p as i64)], out)
|
||||
.unwrap();
|
||||
out[0].unwrap_i64() as u64
|
||||
}
|
||||
|
||||
fn extism_input_load_u8<T>(
|
||||
mut store: &mut wasmtime::Store<T>,
|
||||
instance: &mut Instance,
|
||||
p: u64,
|
||||
) -> u8 {
|
||||
fn extism_input_load_u8<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) -> u8 {
|
||||
let out = &mut [Val::I32(0)];
|
||||
instance
|
||||
.get_func(&mut store, "input_load_u8")
|
||||
.get_func(&mut *store, "input_load_u8")
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(p as i64)], out)
|
||||
.call(store, &[Val::I64(p as i64)], out)
|
||||
.unwrap();
|
||||
out[0].unwrap_i32() as u8
|
||||
}
|
||||
|
||||
fn extism_input_load_u64<T>(
|
||||
mut store: &mut wasmtime::Store<T>,
|
||||
store: &mut wasmtime::Store<T>,
|
||||
instance: &mut Instance,
|
||||
p: u64,
|
||||
) -> u64 {
|
||||
let out = &mut [Val::I32(0)];
|
||||
instance
|
||||
.get_func(&mut store, "input_load_u64")
|
||||
.get_func(&mut *store, "input_load_u64")
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(p as i64)], out)
|
||||
.call(store, &[Val::I64(p as i64)], out)
|
||||
.unwrap();
|
||||
out[0].unwrap_i64() as u64
|
||||
}
|
||||
|
||||
fn extism_store_u8<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64, x: u8) {
|
||||
fn extism_store_u8<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64, x: u8) {
|
||||
instance
|
||||
.get_func(&mut store, "store_u8")
|
||||
.get_func(&mut *store, "store_u8")
|
||||
.unwrap()
|
||||
.call(
|
||||
&mut store,
|
||||
&[Val::I64(p as i64), Val::I32(x as i32)],
|
||||
&mut [],
|
||||
)
|
||||
.call(store, &[Val::I64(p as i64), Val::I32(x as i32)], &mut [])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn extism_store_u64<T>(
|
||||
mut store: &mut wasmtime::Store<T>,
|
||||
instance: &mut Instance,
|
||||
p: u64,
|
||||
x: u64,
|
||||
) {
|
||||
fn extism_store_u64<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64, x: u64) {
|
||||
instance
|
||||
.get_func(&mut store, "store_u64")
|
||||
.get_func(&mut *store, "store_u64")
|
||||
.unwrap()
|
||||
.call(
|
||||
&mut store,
|
||||
&[Val::I64(p as i64), Val::I64(x as i64)],
|
||||
&mut [],
|
||||
)
|
||||
.call(store, &[Val::I64(p as i64), Val::I64(x as i64)], &mut [])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn extism_free<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) {
|
||||
fn extism_free<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) {
|
||||
instance
|
||||
.get_func(&mut store, "free")
|
||||
.get_func(&mut *store, "free")
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(p as i64)], &mut [])
|
||||
.call(store, &[Val::I64(p as i64)], &mut [])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn extism_error_set<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) {
|
||||
fn extism_error_set<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64) {
|
||||
instance
|
||||
.get_func(&mut store, "error_set")
|
||||
.get_func(&mut *store, "error_set")
|
||||
.unwrap()
|
||||
.call(&mut store, &[Val::I64(p as i64)], &mut [])
|
||||
.call(store, &[Val::I64(p as i64)], &mut [])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn extism_error_get<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance) -> u64 {
|
||||
fn extism_error_get<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance) -> u64 {
|
||||
let out = &mut [Val::I64(0)];
|
||||
instance
|
||||
.get_func(&mut store, "error_get")
|
||||
.get_func(&mut *store, "error_get")
|
||||
.unwrap()
|
||||
.call(&mut store, &[], out)
|
||||
.call(store, &[], out)
|
||||
.unwrap();
|
||||
|
||||
out[0].unwrap_i64() as u64
|
||||
}
|
||||
|
||||
fn extism_reset<T>(mut store: &mut wasmtime::Store<T>, instance: &mut Instance) {
|
||||
fn extism_reset<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance) {
|
||||
instance
|
||||
.get_func(&mut store, "reset")
|
||||
.get_func(&mut *store, "reset")
|
||||
.unwrap()
|
||||
.call(&mut store, &[], &mut [])
|
||||
.call(store, &[], &mut [])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn extism_input_set<T>(
|
||||
mut store: &mut wasmtime::Store<T>,
|
||||
instance: &mut Instance,
|
||||
p: u64,
|
||||
l: u64,
|
||||
) {
|
||||
fn extism_input_set<T>(store: &mut wasmtime::Store<T>, instance: &mut Instance, p: u64, l: u64) {
|
||||
instance
|
||||
.get_func(&mut store, "input_set")
|
||||
.get_func(&mut *store, "input_set")
|
||||
.unwrap()
|
||||
.call(
|
||||
&mut store,
|
||||
&[Val::I64(p as i64), Val::I64(l as i64)],
|
||||
&mut [],
|
||||
)
|
||||
.call(store, &[Val::I64(p as i64), Val::I64(l as i64)], &mut [])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ 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_FS: &[u8] = include_bytes!("../../../wasm/read_write.wasm");
|
||||
|
||||
host_fn!(pub hello_world (a: String) -> String { Ok(a) });
|
||||
|
||||
@@ -750,3 +751,26 @@ fn test_linking() {
|
||||
assert_eq!(plugin.call::<&str, i64>("run", "Hello, world!").unwrap(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_readonly_dirs() {
|
||||
let wasm = Wasm::data(WASM_FS);
|
||||
let manifest = Manifest::new([wasm])
|
||||
.with_allowed_path("ro:src/tests/data".to_string(), "/data")
|
||||
.with_config_key("path", "/data/data.txt");
|
||||
|
||||
let mut plugin = PluginBuilder::new(manifest)
|
||||
.with_wasi(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let res = plugin.call::<&str, &str>("try_read", "").unwrap();
|
||||
assert_eq!(res, "hello world!");
|
||||
|
||||
let line = "hello world 2";
|
||||
let res2 = plugin.call::<&str, &str>("try_write", line);
|
||||
assert!(
|
||||
res2.is_err(),
|
||||
"Expected try_write to fail, but it succeeded."
|
||||
);
|
||||
}
|
||||
|
||||
BIN
wasm/read_write.wasm
Normal file
BIN
wasm/read_write.wasm
Normal file
Binary file not shown.
Reference in New Issue
Block a user