ページの上のパリティ

raytracer は三つの backend で同じ画像をレンダリングしていた。その次の自然な置き場所はブラウザのタブだ。そこへ届けるには frontend FFI 初のピクセル出力面 —— canvas の塗り色を設定し矩形を塗る二つの新しい extern —— が要った。そして嬉しいことに、コンパイラの変更はゼロ。extern の機構は五引数の import を、昔からあったかのように受け入れた。検証はブラウザを開く前に済んだ。headless のハーネスが DOM glue をスタブし、すべての canvas 呼び出しを捕獲し、そこから画像ファイルを再構築して、native backend の書いたものとバイト単位で比較する。そしてページ自身が自分の仕事を見せる —— status 行がレンダの checksum を印字する。interpreter と C backend が印字するのと同じ数を。テストスイートの backend 横断の規律が、絵の下に、見える形で座っている。

merewasmbrowserffidogfood

raytracer は checksum を印字して画像を書くファイルとして存在していた。三つの backend がその全バイトで 一致していた。そういうプログラムの次の自然な置き場所はブラウザのタブだ。絵をただ眺められる場所。 そして playground には基盤が既にあった。Wasm モジュールを読み込むページ、extern fn 宣言を実際の DOM 操作に配線する小さな glue ファイル、それを通して矢印キーを読む 2048。frontend FFI に無かったのは、 画面にピクセルを置く手段だけだった。その語彙はすべてテキストだった —— 要素を取得し、textContent を 設定し、クリックとキーを聴く。カウンタと文字で描くタイルゲームには十分。画像には無力。

二つの関数と、ゼロのコンパイラ変更

欠けていた面は二つの関数だと分かった。dom_canvas_fill_style は CSS 文字列から canvas コンテキストの 塗り色を設定し、dom_canvas_fill_rect は矩形を塗る。ピクセルの色を計算し終えたレンダラは、前者を rgb(r,g,b) で、後者を 1×1 の矩形で呼ぶ。JavaScript 側では glue が canvas の 2d コンテキストを遅延で 取得し、要素にキャッシュする。報告に値するのは、コンパイラ側で何が要ったかだ。何も要らなかった。 二つ目の extern は五つの引数を取る —— ハンドルと四つの整数 —— そして一・二引数の import のために 作られた extern 機構は、五引数の呼び出し連鎖を一切の変更なしにコンパイルした。宣言は contrib ライブラリに入り、二つの実装は glue に入り、言語は最初から出来上がっていた。

ブラウザを開く前に検証済み

ブラウザページのテストは普通、ブラウザを開くことを意味する。この FFI の形はもっと厳密なやり方を 許した。glue をスタブする headless ハーネス —— すべての canvas 呼び出しは描かれる代わりに捕獲され、 すべての塗り色はパースされ、すべての 1×1 矩形はピクセルグリッドに記録される —— そしてそのグリッドを、 native の example が生むのと同じ画像形式で書き出す。比較は「正しく見えるか」ではなく cmp だ。 捕獲した FFI 呼び出しから再構築したファイルは、C backend が書いたファイルとバイト単位で同一。 五千百八十四回の矩形呼び出し、ピクセルにつき一回、そのどれもがずれていない。FFI の経路全体 —— Wasm の算術、CSS 色のための文字列構築、import の境界 —— が、ページが一度も読み込まれる前に ground truth に対して検証された。

絵の下の checksum

ページは、私が絵そのものより大事に思っている一つの細部とともに公開された。canvas の下、status 行が レンダの Adler checksum を印字する —— そしてそれは interpreter が印字するのと同じ数、C backend が 印字するのと同じ数、headless ハーネスが検査するのと同じ数だ。テストスイートは八十リリースにわたって backend 同士を留めてきた。その規律は普段、誰も見ない CI の出力に住んでいる。ここではそれが公開ページに、 レンダリングされた画像の下に、一行のテキストとして座っている。この絵は、あなたのマシンで、あなたの ブラウザで、最後のビットまで同じ絵だ。眺められるものとしてのパリティ。レンダが 96×54 で CSS 拡大なのは、 中のすべての float がまだ box され、メモリがまだ固定だからだ —— 二話前の崖は動いていない。このページを もっと大きく描きたいという欲求が、いまやメモリ成長の作業をやる、立ちっぱなしの具体的な理由になった。 次の dogfood への食欲を作って終わる dogfood は、方法が意図どおりに働いているということだ。

← Back to Mere: 言語を作る