zed/crates/plugin_runtime
2022-07-07 15:22:17 +02:00
..
src Documentation pass 2022-07-07 15:22:17 +02:00
build.rs Add async host functions 2022-07-07 15:22:17 +02:00
Cargo.toml Add build.rs to rebuild plugins, and a test plugin 2022-07-07 15:22:17 +02:00
README.md Remove non-WASI code 2022-07-07 15:20:25 +02:00

Zed's Plugin Runner

Wasm plugins can be run through wasmtime, with supported for sandboxed system integration through WASI. There are three plugin crates that implement different things:

  1. plugin_runtime loads and runs compiled Wasm plugins, and handles setting up system bindings.

  2. plugin is the crate that Rust Wasm plugins should depend on. It re-exports some required crates (e.g. serde, bincode) and provides some necessary macros for generating bindings that plugin_runtime can hook into.

  3. plugin_macros implements the proc macros required by plugin, like the #[bind] attribute macro.

ABI

The interface between the host Rust runtime ('Runtime') and plugins implemented in Wasm ('Plugin') is pretty simple.

Buffer is a pair of two 4-byte (u32) fields:

struct Buffer {
    ptr: u32,
    len: u32,
}

All functions that Plugin exports must have the following properties:

  • Have the signature fn(ptr: u32, len: u32) -> u32, where the return type is a pointer to a Buffer, and the arguments are the component parts of a Buffer.

    • The input Buffer will contain the input arguments serialized to bincode.
    • The output Buffer will contain the output arguments serialized to bincode.
  • Have a name starting with two underscores.

Additionally, Plugin must export an:

  • __alloc_buffer function that, given a u32 length, returns a u32 pointer to a buffer of that length.

Note that all of these requirements are automatically fullfilled for any Rust Wasm plugin that uses the plugin crate, and imports the prelude.

Here's an example Rust Wasm plugin that doubles the value of every float in a Vec<f64> passed into it:

use plugin::prelude::*;

#[bind]
pub fn double(mut x: Vec<f64>) -> Vec<f64> {
    x.into_iter().map(|x| x * 2.0).collect()
}

All the serialization code is automatically generated by #[bind].