zed/crates/plugin_runtime
2022-07-08 21:28:35 +02:00
..
src Remove debug statements 2022-07-08 21:28:35 +02:00
build.rs Add basic support for precompiling plugins 2022-07-07 16:21:27 +02:00
Cargo.toml Fix errors resulting from rebase 2022-07-07 18:14:16 +02:00
README.md Add timing instrumentation 2022-07-07 15:32:43 +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.

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].