How to Run a WebAssembly Program in F#
I’m currently building a programming language that compiles down to wasm.
If you need to run a wasm program while using F#, here is how you do that.
Install Wasmtime
From my brief searching, Wasmtime was the most supported library for running wasm programs.
There are numerous ways to install it into your application. Here are the Cli commands.
//install latest
dotnet add package Wasmtime
//install specific version
dotnet add package Wasmtime --version 22.0.0
Get Your Wasm/Wat Code
WebAssembly has two different formats:
- Text format - *.wat files
- Binary format - *.wasm files
Here is the program that we’ll be running in text format:
(module
(type $t0 (func (param i32 i32) (result i32)))
(type $t1 (func (result i32)))
(func $add (export "add") (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
(i32.add
(local.get $p0)
(local.get $p1)))
(func $main (export "main") (type $t1) (result i32)
(call $add
(i32.const 1)
(i32.const 2))))
It’s a simple program, has two functions, add
and main
. main
calls add
with two numbers and add
returns the numbers summed.
There are a variety of tools to convert *.wat <-> *.wasm files, I use wasm-tools.
F# Code
The code here is fairly self explanatory. I import the same code in both binary and text formats, and run them using the wasmtime
library.
module RunWasm
open Wasmtime
let private loadAddWat () =
System.IO.File.ReadAllText "./wasm/add.wat"
let private loadAddWasm () =
System.IO.File.ReadAllBytes "./wasm/add.wasm"
let runAddWat () =
let text = loadAddWat()
let engine = new Engine()
let modd =
Module.FromText(
engine,
"test",
text
)
let linker = new Linker(engine)
let store = new Store(engine)
let instance = linker.Instantiate(store, modd)
let main = instance.GetFunction<int32>("main")
let result = main.Invoke()
printfn "%d" result
let runAddWasm () =
let bytes = loadAddWasm()
let engine = new Engine()
let modd =
Module.FromBytes(
engine,
"test",
bytes
)
let linker = new Linker(engine)
let store = new Store(engine)
let instance = linker.Instantiate(store, modd)
let main = instance.GetFunction<int32>("main")
let result = main.Invoke()
printfn "%d" result
For each source, we:
- Load program from *.wat or *.wasm file
- Instantiate the needed classes from Wasmtime library.
- Declare the function signature of the wasm program
instance.GetFunction<int32>("main")
- This is you telling that you have a function called
main
, it takes no parameters but returns oneint32
.
- Invoke the function
- Print the result (if any)
Invoking with Other Parameters
The instance.GetFunction
can take any number of parameters, you just need to specify them generically.
Here are some other examples:
let func = instance.GetFunction<int32, int32>(funcName)
func.Invoke(param)
let func = instance.GetFunction<int32, int32, int32>(funcName)
func.Invoke(p1, p2)
There are also other useful method on instance
class when dealing with webassembly programs.
That’s the Gist
That’s the gist of it.
The code for this post is located in a github repo, that can be checked out and run.