読者です 読者をやめる 読者になる 読者になる

0x90

一回休み

Rust+EmscriptenでJavascriptを呼ぶライブラリをかく その1

_emval*系の関数を使えばEmscriptenからJavascriptを呼べるということがわかったので、Rustから呼ぶライブラリを作ってみましょう。

まずは、どんな流れで関数を呼ぶかということをまとめておきたいと思います。基本的には

0x90.hatenablog.jp

の流れに沿ってRustコードを書いていくだけです。作成途中のものがこちら

github.com

技術的な詳細とTODOをまとめていきたい!です!

How to write C FFI in Rust

まずは、CとRustの橋渡しをどうするかという話*1

0x90.hatenablog.jp

に書いたように、Rust-bindgenを使ってC headerに対応する(関数定義だけの)Rustソースを作ります。Emscriptenの場合はC++ヘッダなので、手で優しくC部分を抜き出してきましょう。それが

github.com

こちら。慣習に従ってパッケージ名はhogehoge-sysとしておきます。本当はEmscriptenのXHR系の関数も持ってきたいんだけど、まだやってないです。というかそうするとemvalって名前も適切じゃないなあ、、とか。

How to call Emscripten native functions

やるだけ。やった。

どんなAPIが"rusty"か

これが一番難しいところ。まず、もとのC++コードは引数部にC++11の可変長テンプレートという黒魔術を使っています。 一方のRustは可変長引数すらサポートしていないので、ここは白魔術ことmacro_rules!に登場願いましょう。ということで呼び出し部はargs!マクロを作ってみました。

返り値も難しいところです。Rust歴 a few monthのぼくの推測ですが(笑)、turbofishと呼ばれる構文(要はHaskellで言うところの型識別子`read “100” :: Intみたいなの)を使うと良さそうなかんじです。

let i = hoge::<isize>();

みたいな。

ということで、今のところ、

let window = JSObj::global("window");
window.call_prop::<()>("alert", args!("Hello, world!"));

みたいな書き方ができるようにしてみました。サンプルソース

github.com

こんな感じ。

ただ、今のところ問題があって、それはJSSerializable traitのAssociated typeでdeserializeしたときの型を指定しているので、

let s = hoge.call_prop("fuga", args!(0));
let a = s.chars().collect::<Vec<_>>();

みたいなコードが動かないことです。具体的には

pub trait JSSerializable {
    type V;

    fn id() -> TYPEID;

    // For macro...
    fn instance_id(&self) -> TYPEID {
        Self::id()
    }

    fn serialize(&self) -> EM_GENERIC_WIRE_TYPE;

    fn deserialize(v: EM_GENERIC_WIRE_TYPE) -> Self::V;

    // 略
}

のdeserializeの型指定が良くないのだと思います。そこでおそらくここは新しくJSDeserializable traitを作るのがよいと思います。

pub trait JSSerializable {
    fn deserialize(v: EM_GENERIC_WIRE_TYPE) -> Self;
    // ...
}

これなら型推論が効く、、、はず。

ただそもそも、serde_deriveに沿ってやったほうがいいのかとかも悩み中ではあります。

ちょっと今週は今後の方針を考えながらちょろちょろ書いていきます。

*1:みんな知ってると思いますがC++は名前マングリングがあるので無理