Slide 1

Slide 1 text

WebAssembly by Rust 明日からプロダクトに使える WebAssembly pixiv Inc. 森内 建太 2019.2.19

Slide 2

Slide 2 text

2 自己紹介 ● 主にフロントエンドエンジニア ● Ruby on Rails、iOS アプリ開発もやる ● ECMAScript とか DOM API とかの仕様まわりを調 べるのが好き ● 今好きな HTTP レスポンスヘッダは Report-To petamoriken 福岡オフィスプロジェクト エンジニア

Slide 3

Slide 3 text

WebAssembly とは 3

Slide 4

Slide 4 text

4 ● アセンブリではないバイナリ形式(32bit) ● ブラウザでなくても実行できる ○ モダンブラウザ Chrome / Firefox / Safari / Edge で扱える ○ Node.js v8 以降で使える(AWS Lambda, BigQuery...) ● JavaScript から WebAssembly を呼ぶインターフェースも定義されている ○ 初期化時に函数などの受け渡しが可能(後述) WebAssembly とは

Slide 5

Slide 5 text

シンプルな例 5 #[no_mangle] pub extern "C" fn succ(x: i32) -> i32 { x + 1 } const { instance } = await WebAssembly.instantiateStreaming(fetch("foo.wasm")); const exports = instance.exports; console.log(exports.succ(42)); // 43

Slide 6

Slide 6 text

函数の受け渡しの例 6 extern { fn console_log(x: i32); } #[no_mangle] pub extern "C" fn succ_log(x: i32) { unsafe { console_log(x + 1); } } const imports = { env: { console_log: console.log } }; const { instance } = await WebAssembly.instantiateStreaming(fetch("foo.wasm"), imports); const exports = instance.exports; exports.succ_log(42); // console.log(42 + 1)

Slide 7

Slide 7 text

7 ● 受け渡す函数は引数で数値型( i32, i64, f32, f64)のみ扱える ● その他受け渡せるもの ○ Memory ■ WebAssembly で使われるヒープ。バッファを渡したいときにはこれに積んでか らその先頭ポインタ(i32 と同等)を函数に渡す ○ Table (今のところ Function Table のみ) ○ Global Variable (数値型のみ。Chrome, Firefox のみ対応) 初期化時の受け渡しについて

Slide 8

Slide 8 text

バッファの受け渡しの例 8 use std::{mem, alloc as std_alloc}; #[no_mangle] pub unsafe extern "C" fn alloc(size: usize) -> *mut u8 { let layout = std_alloc::Layout::from_size_align(size, mem::align_of::()).unwrap(); std_alloc::alloc(layout) } #[no_mangle] pub unsafe extern "C" fn dealloc(ptr: *mut u8, size: usize) { if size == 0 { return } let layout = std_alloc::Layout::from_size_align_unchecked(size, mem::align_of::()); std_alloc::dealloc(ptr, layout) }

Slide 9

Slide 9 text

バッファの受け渡しの例 9 const { instance } = await WebAssembly.instantiateStreaming(fetch("foo.wasm")); const exports = instance.exports; const ptr = exports.alloc(8); new Uint8Array(exports.memory.buffer).set([0, 1, 2, 3, 4, 5, 6, 7], ptr); exports.foo(ptr, 8); exports.dealloc(ptr, 8); use std::slice; #[no_mangle] pub unsafe extern "C" fn foo(ptr: *const u8, size: usize) { let buf = slice::from_raw_parts(ptr, size); // ... }

Slide 10

Slide 10 text

wasm-bindgen 10

Slide 11

Slide 11 text

11

Slide 12

Slide 12 text

12 ● Rust (WebAssembly) と JavaScript 間のラッパー ● JavaScript の函数の型を書くとインポートしてくれるし、 String 型の変換もする ○ 多くの JavaScript の型が Rust 上では JsValue という型になる ■ Optional unwrap 祭りになってそれはそれで辛い ● 標準函数は自動でインポートすることが出来る(力技) ○ js-sys ■ ECMAScript の函数を全て Rust でラップするクレート ○ web-sys ■ DOM API の函数を全て(ry wasm-bindgen とは

Slide 13

Slide 13 text

13 ● JavaScript をラップするだけではなく TypeScript の型ファイルも作ってくれる ● wasm-pack ○ package.json を吐き出すので npm にそのまま publish 出来る ○ wasm-pack-plugin を使うと webpack であたかも普通のモジュールかのように扱 える Rust における wasm-bindgen と wasm-pack と cargo-web と stdweb の違い https://qiita.com/legokichi/items/5d6344314ab6d6633554 wasm-bindgen とは

Slide 14

Slide 14 text

14 ● Rust で書く WebAssembly はもうプロダクトに使えそう ○ wasm-bindgen (web-sys) を使うと DOM API にアクセスすることが出来るがあまり メリットはない ○ 要所での重い処理(バッファの変換とか)に向いている ■ WebP のデコードとか 詳しくは個人の Scrapbox にまとめてますので興味があれば https://scrapbox.io/petamoriken/WebAssembly_by_Rust まとめ