MoonBitを触ってみる回
Download page (English / Chinese)
Tutorial
moon new
で生成されるファイルがもうすでにだいぶRust風でおもしろい。ここからはCloudflare Workerから1文字でも返せたら成功くらいの気持ちで進めていきます
JavaScriptを挟むならこれ見れば良さそう
とりあえずmainをいちばんシンプルにする
fn main {
}
buildからのwabt
moon build
wasm2wat target/wasm/release/build/main/main.wasm
何も入っていない 無
(module
(type (;0;) (func))
(func (;0;) (type 0))
(table (;0;) 0 0 funcref)
(memory (;0;) 1)
(export "moonbit.memory" (memory 0))
(export "_start" (func 0))
(elem (;0;) (i32.const 0) func))
今度はハロワだけしてみる
fn main {
println("Hello, world!")
}
Denoのスクリプトで実行してみる
const wasm = "target/wasm/release/build/main/main.wasm";
// spectest
let output = "";
const importModule = {
spectest: {
print_char: (char: number) => {
output += String.fromCharCode(char);
},
},
};
// Load wasm
const wasmCode = await Deno.readFile(wasm);
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule, importModule);
// Run
const main = wasmInstance.exports["_start"] as CallableFunction;
main();
console.log(output);
$ moon build
$ deno run -A test.ts
Hello, world!
ドキュメントにはまだ記載がないっぽいが、lib単体でビルドして関数を公開する方法はすでにあるらしい。現時点ではこれが参考になります👇
こう書き換えておく
pub fn hello() -> String {
"Hello, world!"
}
{
"link": {
"wasm": {
"exports": ["hello"]
}
}
}
wasm全然わからないがこうすると動く 👊 これもdeno
これを実施することで「wasmから吐き出された文字列を受け取る」まで実行できました。あとはこれを素直にworkerに乗せてやれば終わり…
const wasm = "target/wasm/release/build/lib/lib.wasm";
// Load wasm
const wasmCode = await Deno.readFile(wasm);
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule);
// Run
const hello = wasmInstance.exports["hello"] as CallableFunction;
const memory = wasmInstance.exports["moonbit.memory"] as WebAssembly.Memory;
const memoryBuffer = new Uint8Array(memory.buffer);
const result = hello();
// Print
const encoder = new TextDecoder("utf-8");
const length = memoryBuffer[result];
const str = encoder.decode(new Uint8Array(memory.buffer, result + 1, length));
console.log(str);
終わりではあるのですが、jsラッパーなしのwasm単体で実行してくれるとカッコイイのでちょっと調べています
wasiの話が出てくるスレッド
要約が欲しい人向けのAI要約
この議論は、wasmtime、wasmedge、またはwazeroなどのサーバーサイドのWASMランタイムで、Moonbitコードを実行する可能性について話し合っています。主な要点は次のとおりです。
- 現在、MoonbitからホストのFunctionを呼び出す唯一の方法は、wazeroとwasmtimeの例のように関数を渡すことです。
- 将来的にMoonbitはWASI標準をサポートする可能性があり、MoonbitCLIを必要とせずにWASI準拠のランタイムで実行できるようになります。
- WASIとWASM-GC(WASMのためのガベージコレクション提案)はまだプレビュー/実験段階にあり、現時点でMoonbitをWASIランタイムで実行するのが難しくなっています。
- DenoはWASIとWASM-GCの両方をサポートしている唯一のランタイムですが、面倒な回避策が必要です。
- ユーザーはMoonbitCLIや追加のツーリングを必要とせずに、任意のWASIランタイムで実行できる単一のデプロイ可能なWASMファイルを希望しています。
- WASIとWASM-GCが成熟すれば、WASMのポータビリティ目標に沿って、サーバーサイドのWASMランタイムでMoonbitコードをデプロイして実行するのが容易になると期待されています。
要約すると、この議論ではサーバーサイドのWASMランタイムでMoonbitを実行する際の現在の制限と、ポータブルでシンプルな方法での将来の期待について話し合われています。
自分の知識だとどうにもならない感を察したので、方向性を変えて遊んでいきます
説明をめちゃくちゃ省くと、このリポジトリを参考にしてwasi準拠な振る舞いをするwasmをビルドしてやれば、
npx wrangler@wasm dev target/wasm-gc/release/build/main/main.wasm
でレスポンス返せるところまでは来ました
ただこの実装をそのまま使うと、UTF-8ではなくてUTF-16 LEになってしまう。たぶんfd_writeの書き込みが固定長になってるせいだと思うけどまだちゃんと見れてないです
𝕐𝕆𝕌 𝕎𝕀ℕ
pub fn utf8_bytes(str : String) -> Bytes {
let mut length = 0
let chars : Array[Char] = Array::make(str.length(), ' ')
for i = 0; i < str.length(); i = i + 1 {
let char : Char = str[i]
let code : Int = char.to_int()
chars[i] = char
if code < 0x80 {
length = length + 1
} else if code < 0x0800 {
length = length + 2
} else if code < 0x010000 {
length = length + 3
} else if code < 0x110000 {
length = length + 4
}
}
let result = Bytes::make(length, 0)
let mut j : Int = 0
for i = 0; i < str.length(); i = i + 1 {
let c = chars[i]
j = j + result.set_utf8_char(j, c)
}
result
}
fn main {
let bs = @lib.utf8_bytes("<body>Hello, MoonBit!</body>")
@wasi.stdout.write(bs).unwrap() |> ignore
}
$ npx wrangler@wasm dev target/wasm-gc/release/build/main/main.wasm
$ curl http://localhost:8787
<body>Hello, MoonBit!</body>⏎