1731185429
2024-11-09 18:14:00
Jaws は、Rust で書かれた JavaScript から WebAssembly へのコンパイラーです。に似ています 紫 ある意味では、インタプリタなしで実行できるスタンドアロンの WASM バイナリも生成されますが、実装アプローチは異なります。
これは実験的なツールであり、製品化の準備ができていません。多くの言語機能と組み込み型が欠落しているか不完全です。とはいえ、私の目標は最終的には言語を 100% サポートすることです。
私はストレス テスト ツールの開発中にこのプロジェクトを開始しました。 カラス WebAssembly シナリオを実行します。現時点では、Rust から WASM にコンパイルされたコードのみをサポートしています。私は Rust を書くのが大好きですが、Rust が広く普及している言語ではないことも知っています。さらに、小さなテストはインタプリタ型言語で書く方が簡単な場合が多いです。問題は、WASM 上でスクリプト言語を実行することが現時点では理想的ではないことです。バイナリのサイズが自動的に少なくとも数 MB になり、メモリ使用量がさらに大きくなるインタプリタを含めるか、ターゲット言語のバリエーション (Go の代わりに TinyGo、TypeScript の代わりに AssemblyScript など) を使用する必要があります。 /JavaScript)。
WASM ランタイムはすでにインタープリターであるため、最新の WASM 提案では、コンパイル済みインタープリターを使用することなく JavaScript 機能を 100% 実装することが可能であると私は信じています。
それが起こるのを見たい場合は、検討してください 私の作品をスポンサーしてくれる
最終的には言語を 100% 実装したいと考えているため、実行可能であることを 100% 確信したいので、組み込みと文法を 100% 実行するのではなく、最初にセマンティクスを実装することに意図的に重点を置いています。
実装が難しいと思われる 4 つのリストを用意しており、それらをすべて実装した後、さらに文法と組み込みに焦点を当てます。これらは:
- スコープ/クロージャ
- トライ/キャッチ
- 非同期/待機
- 発電機
最後の 2 つは、ジェネレーターを機能させるという点で似ており、1 つは基本的に async await を機能させるためのツールを備えていますが、それでも区別したいと思いました。現時点では、Jaws は (ほとんど) 適切なスコープをサポートするクロージャーを使用してコードをコンパイルでき、try/catch が可能で、(限定的) を実装できます。 Promise APIと async (しかしそうではありません await まだ)。たとえば、次のスクリプトは出力します。 error: foo:
let value = "foo";
async function foo() {
throw value;
}
foo().then(
function () {},
function (v) {
console.log("error", v);
},
);
機能するはずの他のものの完全なリストではありません:
- 宣言と代入:
var、let、const while- 文字列リテラル、文字列リテラルの追加
- 数値と基本的な演算子 (
+、-、*、/) - ブール値と基本的なブール演算子
- 配列リテラル
- オブジェクトリテラル
newキーワード
Jaws はいくつかの比較的最近の WASM 提案を使用して構築されているため、生成されたバイナリはまだランタイム間で移植可能ではありません。 WASIp2 を念頭に置いて実装することを目指していますが、コンポーネントと WASIp2 を実行できる唯一のランタイムです。 Wasmtime は、WASM GC プロポーザルの一部や例外処理など、私が使用する他のいくつかのものをサポートしていません。
ランタイムが標準化された提案に追いつく前に開発を容易にするために、必要な WASIp2 機能に Javascript ポリフィルを備えた V8 (Chromium または Node 経由) を使用することにしました。スクリプトがあります run.js Jaws によって生成されたバイナリを実行できるリポジトリ内。最終的には、WASM GC、例外処理、および WASIp2 API を実装するランタイム上でそれらを実行できるようになるはずです。
貢献したくない場合は、おそらく貢献すべきではありませんが、リポジトリのクローンを作成した後は、 execute.sh 次のようなスクリプト:
./execute.sh --cargo-run path/to/script.js
WAT ファイルを生成し、バイナリにコンパイルして、Node.js を使用して実行します。
Rust が必要です cargo、比較的新しいバージョンの wasm-tools Node.js v23.0.0 以降。通過 --cargo-run スクリプトで使用するようになります cargo run コマンドを使用して、最初にプロジェクトをコンパイルしてから実行します。それ以外の場合は、リリース ビルドを実行しようとします (そのため、次のコマンドを実行する必要があります)。 cargo build --release 走る前に ./execute.sh それなし --cargo-run オプション)
私の計画では、最初に「実装が難しい」機能をすべて実装し終えることです。そのため、次はジェネレーターと await キーワードのサポート。理想的には、 スタックスイッチング await とジェネレーターの両方の提案ですが、残念ながらフェーズ 2 にすぎず、最小限のランタイム サポートしかありません (Chromium 開発グループでいくつかの言及を見つけることができましたが、機能させることはできませんでした)。スタック切り替えがない場合、継続をシミュレートするために CPS 変換を使用することに取り組んでいます。
それが完了したら、文法 (for ループ、スイッチなど) から始めて、組み込み型と API まで、欠けている部分をすべてゆっくりと実装していきます。
このプロジェクトは基本的に、WASM GC によって追加された命令、例外処理、末尾呼び出しの最適化提案を利用して、JavaScript 構文を WASM 命令に変換しています。 JavaScript コードを変換する Rust コードの上に、JavaScript セマンティクスを WASM に変換するために必要なすべての配管を備えた約 3,000 行の WAT コードがあります。
例として、スコープとクロージャを考えてみましょう。 WASM は関数参照の受け渡し、構造体と配列のサポートを備えていますが、JavaScript のようなスコープ セマンティクスは備えていません。したがって、追加の WASM コードを追加して、スコープがどのように機能するかをシミュレートする必要があります。次の JavaScript コードを想像してください。
let a = "foo";
function bar() {
console.log(a);
}
bar();
JavaScript では、関数定義はそれが定義されているスコープを継承するため、 bar() 関数はにアクセスできます a 変数。したがって、このスクリプトは次の文字列を出力する必要があります。 "foo"。これを大まかに次の疑似コードに変換できます。
// first we create a global scope, that has no parents
let scope = newScope(null);
// then we set the variable `a` on the scope
declareVariable(scope, "a", "foo");
// now we define the bar function saving a reference to the function
let func = function(parentScope: Scope, arguments: JSArguments, this: Any) -> Any {
// inside a function declaration we start a new scope, but keeping
// a reference to the parentScope
let scope = newScope(parentScope);
// now we translate console.log call retreiving the variable from the scope
// this will search for the `a` variable on the current scope and all of the
// parent scopes
console.log(retrieve(scope, "a"));
}
// when running a function we have to consider the scope
// in which it was defined
let fObject = createFunctionObject(func, scope);
// and now we also set `bar` on the current scope
declareVariable(scope, "bar", fObject)
// now we need to fetch the `bar` function from the scop
// and run it
let f = retrieve(scope, "bar");
call(f);
これを機能させるために必要なヘルパーはすべて WAT 形式で手書きされています。より効率的にする方法についてはいくつかのアイデアがありますが、主要な機能をすべて検証する前に、サイド クエストにあまり時間を費やしたくありませんでした。特に WASM GC を考慮すると、WAT を手動で記述することもそれほど難しくありません。
コードは Apache 2.0 ライセンスに基づいてライセンスされています
#drogusjaws #JavaScript #から #WASM #へのコンパイラー