rustのプログラムでプラグイン機構を組み込むにはどうしたら良いか調べていたところ、rustでwasmを実行する方法が出てきたので試しに実装してみた。
開発環境構築
wasmをビルドするために、Scoopでllvmをインストール
scoop install llvm
clangと一緒にwasm用リンカとか色々一緒に入ってくる
wasmプログラムの開発
llvmのインストールが終わったらcでプログラム作成
#include <stdint.h>
__attribute__((visibility("default"))) // clangに関数をエクスポートするように指定
int add(int32_t a, int32_t b) {
return a + b;
}
int型2つ受け取って足し算した結果を返すだけの関数。
ビルドするためのMakefileも
all: wasm.wasm
wasm.wasm: wasm.o
clang -Wl,--no-entry -Wl,--export-dynamic -nostdlib --target=wasm32 -o wasm.wasm wasm.o
wasm.o: wasm.c
clang --target=wasm32 -c wasm.c -o wasm.o
あとはmakeでwasm.wasmを作成してwasmファイルの準備は完了。
rust側の開発
実際にwasmファイルを読んで実行するプログラムの開発。まずはcargoでプロジェクトの準備
cargo new --bin wasm_rt
次にCargo.tomlの準備。
[package]
name = "wasm_rt"
version = "0.1.0"
authors = ["mattyan <mattyan.mail@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wasmer = "1.0.0-alpha"
最初はwasmer 0.16を使ってたんだけど、どうしてもビルドができなかったので、1.0.0-alpha5に変更。
extern crate wasmer;
use wasmer::{Store, Module, Instance, imports, Value};
use std::fs::File;
use std::io::Read;
fn main() {
// wasmファイル読み込み
let mut wasm_file = File::open("./wasm.wasm").unwrap();
let mut wasm_file_buf = Vec::new();
let _ = wasm_file.read_to_end(&mut wasm_file_buf).unwrap();
// wasmファイル実行準備
let store = Store::default();
let module = Module::new(&store, &wasm_file_buf).unwrap();
let import_object = imports! {};
let instance = Instance::new(&module, &import_object).unwrap();
let add = instance.exports.get_function("add").unwrap();
// wasmの関数実行
let response = add.call(&[Value::I32(1), Value::I32(2)]).unwrap();
println!("1 + 2 = {}", response[0].to_string().parse::<i32>().unwrap());
}
wasm.wasmを読み込んでadd関数を取得。
それを呼び出して結果を取得している。
PS wasm_rt> cargo run Compiling wasm_rt v0.1.0 (wasm_rt) Finished dev [unoptimized + debuginfo] target(s) in 5.60s Running `target\debug\wasm_rt.exe` 1 + 2 = 3
cargo runでビルドしてcargo runしたら、無事に実行できた。
おまけ: 今回からプログラムのシンタックスハイライトに APH Prism Syntax Highlighterを導入してみた。
少し書くのが面倒だけど、対応言語が多いからこっち優先にしようと思う。