ある時Twitterのタイムラインを見ていたら、「JavaScriptをWasm化して動かす意味がわからない」というような意見を見かけました。JavaScriptはブラウザに搭載されているV8のようなJavaScriptエンジンによって高速に動作するので、わざわざWasm化してもパフォーマンスは劣化するのになぜなのか?という話なんですが、これは「Wasm化=パフォーマンスのため」という考えだと意義がわからないのでこの記事ではそれについて解説します。
JavaScriptをWasm化して動かすツールやライブラリとしては、Shopifyが開発しているJavyやquickjs-emscriptenなどがあります。JavaScriptをWasm化して動かすためには、ある特定のJavaScriptエンジンをWasm向けにビルドして動かす必要がありますが、そのような用途ではQuickJSというJavaScript処理系がよく使われています。
QuickJSには、ブラウザに搭載されているJavaScriptエンジンとは違って、高速化のためのJITコンパイラのような上等な物は搭載されていませんが、それよりもアプリケーションに埋め込んで使いやすいように小さなサイズに収めているのが特徴のJavaScript処理系です。
QuickJSのウェブサイトに載ってるベンチマークを見てみると、Chromeに搭載されているJavaScriptエンジンのV8はQuickJSと比べるとなんと30倍以上のパフォーマンスを出しています(ただし実行ファイルのサイズはQuickJSの方が圧倒的に小さい)。
ネイティブの状態でここまで差がついていると、ブラウザで直接実行する場合と比べてQuickJSをWasm化してもパフォーマンス出ないでしょうし意味がないように思えますが、パフォーマンス以外の理由があります。それはJavaScript処理系をWasm化すると信頼できないJavaScriptのコードを安全に評価するのに大変便利だからです。
信頼できないJavaScriptのコードを安全に評価したいのはなぜかというと、ユースケースのひとつとしてはプラグイン機構の実装があります。JavaScriptで構築しているウェブアプリケーションの中には、そのアプリケーションの動作をある程度自由に拡張するためのプラグイン機構が実装されることがありますが、そういったプラグイン機構はしばしばユーザーから受け取った信頼できないJavaScriptのコードを受け取って評価することによって実現されます。
ユーザーから受け取ったJavaScriptを評価するのに eval()
を使えばいいじゃないかと思う人もいるかもしれませんが、eval()
で評価したコードはそのeval()
を実行している側と同じ環境で動作するので、信頼できないコードを評価するとウェブアプリケーションのクレデンシャルやそのアプリケーションが保管しているユーザーの機密情報が盗まれる危険性があります。当たり前の話ですがそれではただのXSSになります。
信頼できないJavaScriptを安全に評価する方法はいくつかあって、iframeを使う方法やShadowRealm APIを使う方法やウェブアプリケーションのサーバー側にサンドボックス環境を作ってそこで評価する方法などいくつかありますがそれぞれ一長一短あり採用する方法によって技術的制約も加わります。
そういった中でここ数年で(僕が勝手に)注目しているのが、WasmでJavaScript処理系を動かしてその中でJavaScriptのコードを評価するという方法です。Wasmのセキュリティモデルについて簡単に触れると、Wasmのバイナリを読み込んで実行するホスト側に対して、Wasmで実行されるアプリケーションは、ホスト側が明示的に用意したインターフェイスを通じてしかホスト側の環境を読み込んだりすることはできません。Wasm側が勝手にホスト側のDOMツリーを読み出したり変更したり、HTTPリクエストを投げるようなことはできないし、それをするためにはホスト側がそのためのAPIをWasm側に対して明示的に用意してあげる必要があるので、信頼できないコードを安全に評価するのにうってつけのサンドボックス環境を提供してくれるわけです。
Wasm化したJavaScriptを使う事例としては、前述したJavyはREADMEを見ると、Shopify ScriptsというShopifyの挙動を拡張できるスクリプトを作成できるサービスのために開発されているようです。また、ブラウザ上で動作可能なデザインツールであるFigmaについても、公式のブログ記事によるとプラグイン機構の実装にWasm化したQuickJSを採用したとしています。
この記事ではJavaScriptのWasm化の意義について解説していますが、ここで書いていることはWasm上で動く他の言語処理系についても同様のことが言えます。例えばRubyやLuaといった言語処理系をWasm化している事例があるので、RubyやLuaのコードをブラウザで安全に評価したりこれらの言語を使ったプラグイン機構の実装もできるでしょう。
Wasmというと、元々はasm.jsというJavaScriptを高速化するプロジェクトに由来するので「Wasm化する=パフォーマンスのため」と思いがちなのですが、実際にはこういったパフォーマンス以外のメリットがありますよ、という話でした。