zed/crates/plugin_runtime
2022-07-11 16:40:12 +02:00
..
src Fix minor issues in plugin and project raised during review 2022-07-11 16:40:12 +02:00
build.rs Fix minor issues pointed out in the review 2022-07-11 15:54:03 +02:00
Cargo.toml Fix errors resulting from rebase 2022-07-07 18:14:16 +02:00
README.md Fix minor issues pointed out in the review 2022-07-11 15:54:03 +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, encoded as a single u64.

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

All functions that Plugin exports must have the following properties:

  • Have the signature fn(ptr: u64) -> u64, where both the argument and return types are 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.
  • __free_buffer function that, given a buffer encoded as a u64, frees the buffer at the given location, and does not return anything.

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::*;

#[export]
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 #[export].

You can specify functions that must be defined host-side by using the #[import] attribute. This attribute must be attached to a function signature:

#[import]
fn run(command: String) -> Vec<u8>;

The #[import] macro will generate a function body that performs the proper serialization/deserialization needed to call out to the host rust runtime. Note that the same ABI is used for both #[import] and #[export].