mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-18 18:08:07 +03:00
Documentation pass
This commit is contained in:
parent
4565f1a976
commit
f61ef446d3
@ -13,6 +13,7 @@ use wasmtime::{
|
||||
};
|
||||
use wasmtime_wasi::{Dir, WasiCtx, WasiCtxBuilder};
|
||||
|
||||
/// Represents a resource currently managed by the plugin, like a file descriptor.
|
||||
pub struct PluginResource(u32);
|
||||
|
||||
#[repr(C)]
|
||||
@ -34,6 +35,7 @@ impl WasiBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a typed WebAssembly function.
|
||||
pub struct WasiFn<A: Serialize, R: DeserializeOwned> {
|
||||
function: TypedFunc<u64, u64>,
|
||||
_function_type: PhantomData<fn(A) -> R>,
|
||||
@ -50,6 +52,10 @@ impl<A: Serialize, R: DeserializeOwned> Clone for WasiFn<A, R> {
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct is used to build a new [`Plugin`], using the builder pattern.
|
||||
/// Create a new default plugin with `PluginBuilder::new_with_default_ctx`,
|
||||
/// and add host-side exported functions using `host_function` and `host_function_async`.
|
||||
/// Finalize the plugin by calling [`init`].
|
||||
pub struct PluginBuilder {
|
||||
wasi_ctx: WasiCtx,
|
||||
engine: Engine,
|
||||
@ -57,6 +63,8 @@ pub struct PluginBuilder {
|
||||
}
|
||||
|
||||
impl PluginBuilder {
|
||||
/// Create a new [`PluginBuilder`] with the given WASI context.
|
||||
/// Using the default context is a safe bet, see [`new_with_default_context`].
|
||||
pub fn new(wasi_ctx: WasiCtx) -> Result<Self, Error> {
|
||||
let mut config = Config::default();
|
||||
config.async_support(true);
|
||||
@ -71,89 +79,17 @@ impl PluginBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new `PluginBuilder` that inherits the
|
||||
/// host processes' access to `stdout` and `stderr`.
|
||||
pub fn new_with_default_ctx() -> Result<Self, Error> {
|
||||
let wasi_ctx = WasiCtxBuilder::new()
|
||||
.inherit_stdin()
|
||||
.inherit_stdout()
|
||||
.inherit_stderr()
|
||||
.build();
|
||||
Self::new(wasi_ctx)
|
||||
}
|
||||
|
||||
// pub fn host_function_async<A: DeserializeOwned + Send, R: Serialize, F, Fut>(
|
||||
// mut self,
|
||||
// name: &str,
|
||||
// function: impl Fn(A) -> Pin<Box<dyn Future<Output = R> + Send + Sync>> + Sync + Send + 'static,
|
||||
// ) -> Result<Self, Error>
|
||||
// where
|
||||
// A: DeserializeOwned + Send,
|
||||
// R: Serialize + Send,
|
||||
// {
|
||||
// self.linker.func_wrap1_async(
|
||||
// "env",
|
||||
// &format!("__{}", name),
|
||||
// move |caller: Caller<'_, WasiCtxAlloc>, packed_buffer: u64| {
|
||||
// // let function = &function;
|
||||
// Box::new(async move {
|
||||
// // grab a handle to the memory
|
||||
// let mut plugin_memory = match caller.get_export("memory") {
|
||||
// Some(Extern::Memory(mem)) => mem,
|
||||
// _ => return Err(Trap::new("Could not grab slice of plugin memory"))?,
|
||||
// };
|
||||
|
||||
// let buffer = WasiBuffer::from_u64(packed_buffer);
|
||||
|
||||
// // get the args passed from Guest
|
||||
// let args = Wasi::buffer_to_type(&mut plugin_memory, &mut caller, &buffer)?;
|
||||
|
||||
// // Call the Host-side function
|
||||
// let result: R = function(args).await;
|
||||
|
||||
// // Serialize the result back to guest
|
||||
// let result = Wasi::serialize_to_bytes(result).map_err(|_| {
|
||||
// Trap::new("Could not serialize value returned from function")
|
||||
// })?;
|
||||
|
||||
// // Ok((buffer, plugin_memory, result))
|
||||
// Wasi::buffer_to_free(caller.data().free_buffer(), &mut caller, buffer).await?;
|
||||
|
||||
// let buffer = Wasi::bytes_to_buffer(
|
||||
// caller.data().alloc_buffer(),
|
||||
// &mut plugin_memory,
|
||||
// &mut caller,
|
||||
// result,
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
// Ok(buffer.into_u64())
|
||||
// })
|
||||
// },
|
||||
// )?;
|
||||
// Ok(self)
|
||||
// }
|
||||
|
||||
// pub fn host_function_async<F>(mut self, name: &str, function: F) -> Result<Self, Error>
|
||||
// where
|
||||
// F: Fn(u64) -> Pin<Box<dyn Future<Output = u64> + Send + Sync + 'static>>
|
||||
// + Send
|
||||
// + Sync
|
||||
// + 'static,
|
||||
// {
|
||||
// self.linker.func_wrap1_async(
|
||||
// "env",
|
||||
// &format!("__{}", name),
|
||||
// move |_: Caller<'_, WasiCtxAlloc>, _: u64| {
|
||||
// // let function = &function;
|
||||
// Box::new(async {
|
||||
// // let function = function;
|
||||
// // Call the Host-side function
|
||||
// let result: u64 = function(7).await;
|
||||
// Ok(result)
|
||||
// })
|
||||
// },
|
||||
// )?;
|
||||
// Ok(self)
|
||||
// }
|
||||
|
||||
/// Add an `async` host function. See [`host_function`] for details.
|
||||
pub fn host_function_async<F, A, R, Fut>(
|
||||
mut self,
|
||||
name: &str,
|
||||
@ -218,6 +154,22 @@ impl PluginBuilder {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Add a new host function to the given `PluginBuilder`.
|
||||
/// A host function is a function defined host-side, in Rust,
|
||||
/// that is accessible guest-side, in WebAssembly.
|
||||
/// You can specify host-side functions to import using
|
||||
/// the `#[input]` macro attribute:
|
||||
/// ```ignore
|
||||
/// #[input]
|
||||
/// fn total(counts: Vec<f64>) -> f64;
|
||||
/// ```
|
||||
/// When loading a plugin, you need to provide all host functions the plugin imports:
|
||||
/// ```ignore
|
||||
/// let plugin = PluginBuilder::new_with_default_context()
|
||||
/// .host_function("total", |counts| counts.iter().fold(0.0, |tot, n| tot + n))
|
||||
/// // and so on...
|
||||
/// ```
|
||||
/// And that's a wrap!
|
||||
pub fn host_function<A, R>(
|
||||
mut self,
|
||||
name: &str,
|
||||
@ -276,6 +228,8 @@ impl PluginBuilder {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Initializes a [`Plugin`] from a given compiled Wasm module.
|
||||
/// Both binary (`.wasm`) and text (`.wat`) module formats are supported.
|
||||
pub async fn init<T: AsRef<[u8]>>(self, module: T) -> Result<Plugin, Error> {
|
||||
Plugin::init(module.as_ref().to_vec(), self).await
|
||||
}
|
||||
@ -310,6 +264,8 @@ impl WasiCtxAlloc {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a WebAssembly plugin, with access to the WebAssembly System Inferface.
|
||||
/// Build a new plugin using [`PluginBuilder`].
|
||||
pub struct Plugin {
|
||||
engine: Engine,
|
||||
module: Module,
|
||||
@ -318,6 +274,8 @@ pub struct Plugin {
|
||||
}
|
||||
|
||||
impl Plugin {
|
||||
/// Dumps the *entirety* of Wasm linear memory to `stdout`.
|
||||
/// Don't call this unless you're debugging a memory issue!
|
||||
pub fn dump_memory(data: &[u8]) {
|
||||
for (i, byte) in data.iter().enumerate() {
|
||||
if i % 32 == 0 {
|
||||
@ -400,6 +358,8 @@ impl Plugin {
|
||||
}
|
||||
|
||||
/// Returns `true` if the resource existed and was removed.
|
||||
/// Currently the only resource we support is adding scoped paths (e.g. folders and files)
|
||||
/// to plugins using [`attach_path`].
|
||||
pub fn remove_resource(&mut self, resource: PluginResource) -> Result<(), Error> {
|
||||
self.store
|
||||
.data_mut()
|
||||
@ -410,16 +370,6 @@ impl Plugin {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// pub fn with_resource<T>(
|
||||
// &mut self,
|
||||
// resource: WasiResource,
|
||||
// callback: fn(&mut Self) -> Result<T, Error>,
|
||||
// ) -> Result<T, Error> {
|
||||
// let result = callback(self);
|
||||
// self.remove_resource(resource)?;
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// So this call function is kinda a dance, I figured it'd be a good idea to document it.
|
||||
// the high level is we take a serde type, serialize it to a byte array,
|
||||
// (we're doing this using bincode for now)
|
||||
@ -463,12 +413,14 @@ impl Plugin {
|
||||
// This isn't a problem because Wasm stops executing after the function returns,
|
||||
// so the heap is still valid for our inspection when we want to pull things out.
|
||||
|
||||
/// Serializes a given type to bytes.
|
||||
fn serialize_to_bytes<A: Serialize>(item: A) -> Result<Vec<u8>, Error> {
|
||||
// serialize the argument using bincode
|
||||
let bytes = bincode::serialize(&item)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// Deserializes a given type from bytes.
|
||||
fn deserialize_to_type<R: DeserializeOwned>(bytes: &[u8]) -> Result<R, Error> {
|
||||
// serialize the argument using bincode
|
||||
let bytes = bincode::deserialize(bytes)?;
|
||||
@ -522,6 +474,7 @@ impl Plugin {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// TODO: don't allocate a new `Vec`!
|
||||
/// Takes a `(ptr, len)` pair and returns the corresponding deserialized buffer.
|
||||
fn buffer_to_bytes<'a>(
|
||||
plugin_memory: &'a Memory,
|
||||
@ -570,9 +523,6 @@ impl Plugin {
|
||||
handle: &WasiFn<A, R>,
|
||||
arg: A,
|
||||
) -> Result<R, Error> {
|
||||
// dbg!(&handle.name);
|
||||
// dbg!(serde_json::to_string(&arg)).unwrap();
|
||||
|
||||
let mut plugin_memory = self
|
||||
.instance
|
||||
.get_memory(&mut self.store, "memory")
|
||||
|
@ -5,7 +5,7 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[import]
|
||||
fn command(string: &str) -> Option<String>;
|
||||
fn command(string: &str) -> Option<Vec<u8>>;
|
||||
|
||||
// #[no_mangle]
|
||||
// // TODO: switch len from usize to u32?
|
||||
|
Loading…
Reference in New Issue
Block a user