Photo by Markus Winkler on Unsplash
Extism & WebAssembly Plugins
Write Wasm plugins with Extism and TinyGo
Extism is a set of SDK projects that allow you to develop applications that run WebAssembly plugins and develop WebAssembly plugins themselves.
Extism provides multiple SDKs for building host applications (those that load and execute WebAssembly plugins) in various languages such as Rust, Go, Ruby, PHP, JavaScript, Java, Erlang, Haskell, Zig, .Net, C, Swift, and OCaml.
As mentioned earlier, Extism also provides Plugin Development Kits (PDKs) for developing WebAssembly plugins in Go, Rust, Haskell, C, Zig, AssemblyScript, and JavaScript.
Wasi
You need to use the Wasi specification to execute WebAssembly plugins from non-browser host applications. WebAssembly runtimes like WasmEdge, WasmTime, Wazero, Wasmer, and others implement this specification and provide SDKs for creating host applications. However, the Wasi specification is still a work in progress and has some limitations.
For example, functions in a Wasm program can only accept numbers as parameters and can only return a single number. This means that using strings as parameters and return values is not straightforward. It's worth noting that manipulating the shared memory between the host and the Wasm program can work around this limitation (and it can be a valuable learning experience).
Another example of a limitation is that a Wasm function cannot make HTTP requests or write to the console (display a result). The workaround is to create functions in the host application to perform these operations and expose them to the Wasm module for it to use. Again, this is not a trivial task.
Fortunately, we have Extism!
Extism provides all the necessary "plumbing" to overcome the limitations of the Wasi specification, making it easy to develop, for instance, a Go application that can execute Wasm plugins developed in Go, Rust, and even JavaScript (and other languages, of course).
Extism also comes with a CLI that allows you to test your plugins. So, today, we will focus solely on developing WebAssembly plugins.
Prerequisites
To reproduce the examples in this article, you will need:
Go (v1.20) & TinyGo (v0.28.1)
Node.js (v19.9.0)
Extism 0.4.0 & Extism-js PDK for building Wasm modules with JavaScript
Install Extism-js PDK (we won't use it in this blog post)
But let's see how to create our first Wasm plugin.
The first plugin in Go
Start by creating a go.mod
file with the command go mod init simple-go-plugin
, and then create a main.go
file with the following content:
package main
import (
"github.com/extism/go-pdk"
)
//export say_hello 1️⃣
func say_hello() int32 {
// read function argument from the memory
input := pdk.Input() //2️⃣
output := "👋 Hello " + string(input)
mem := pdk.AllocateString(output) //3️⃣
// copy output to host memory
pdk.OutputMemory(mem) //4️⃣
return 0
}
func main() {}
Remarks:
1: The
//export say_hello
annotation is necessary for thesay_hello
function to be "visible" to the host application (the Extism CLI).2:
pdk.Input()
allows reading the shared memory between the Wasm module and the host application to extract a buffer ([]byte
) containing the parameter sent by the host function.3: Allocate memory for the return value.
4: Copy the value into memory (it will be usable by the host application).
Compile the Wasm plugin
To compile the program, use TinyGo and the following command, which will produce a simple.wasm
file:
tinygo build -scheduler=none --no-debug \
-o simple.wasm \
-target wasi main.go
Execute the "say_hello
" function of the Wasm plugin
To do this, we will use the Extism CLI (we will see how to develop our host application in a future article).
To execute the say_hello
function with the string parameter "Lisa"
, use the following command:
extism call ./simple.wasm \
say_hello --input "Lisa" \
--wasi
And you will get the output:
👋 Hello Lisa
That's all for today. In the following articles, we will see:
How to use the "ready-to-use" host functions provided by Extism
How to create a Wasm plugin with JavaScript
How to develop a host application in Go
And probably more 😉