FirefoxのJSエンジン、SpiderMonkeyのエラーメッセージを変更してみた
本記事は東京大学工学部電子情報工学科の「大規模ソフトウェアを手探る」という実験のレポートです。
・目次
はじめに
実験内容と動機
環境構築と全体の流れ
やったこと1
やったこと2
やったこと3
おわりに
はじめに
実験「大規模ソフトウェアを手探る」では、班をつくりその班で選んだオープンソースのソフトウェアに何らかの変更を加えることで、大きいソフトウェアの作成及び更新に必要な考え方及びスキルを身につけるというものである。詳細はリンク先の実験公式ホームページをご確認ください。
最初の日にはチュートリアルとしてgnuplotのplotコマンドを省略するというものを皆共通で行った。
内容と動機
僕の班ではFirefoxのJavascriptエンジンであるSpiderMonkeyのエラーメッセージを改善するということをテーマとした。この内容にした理由は、TA(ティーチングアシスタント)が普段からSpiderMonkeyにコミットしており、パッチをレビューしたりする権限を持っているので、自分のやったことが実験の範囲で終わらず実際に誰かの役に立ち得るからである。
環境構築と全体の流れ
環境構築に関しては前述したTAさんがまとめてくださったものを参考にjs shellのビルドを行い環境を構築した。
全体の流れは以下のようになる。
1.改善すべきエラーメッセージのまとまったページから修正したいものを選ぶ
2.変更箇所をDXRというサイトで探し、変更を加える
3.テストし、うまくいったらパッチを作成する
4.Bugzillaにパッチを投げ、レビューを受ける
5.レビューを受けて問題があったなら修正しパッチを投げる
6.try-runが通れば取り込まれるのを待つ
班のメンバーがqiitaにまとめてくれていたのでリンクを貼ります
テストするプログラムのjstests.pyの引数は引数を含むものをテストするとTAさんが言っていたので、修正したエラーを呼び出している関数が一意なら関数名を引数にするといいと思います。
やったこと1
DataViewを使用し配列の外側にアクセスしたときのエラーメッセージを修正した
バグレポートは以下のようであった。
https://bugzilla.mozilla.org/show_bug.cgi?id=1245495
問題としてはn番目の引数をとって引数nがおかしいというべきところを違う引数の番号がおかしいというエラーメッセージが出ていることであった。
基本的にはReporterが言うようにGoogle Chromeのと同様に変更した。
Situation a:
var arrayBuffer = new ArrayBuffer(16);
var dataView = new DataView(arrayBuffer, 17);
この場合では16個の要素の配列であるarrayBufferの17番目からの要素を読みだそうとしているので"start offset is outside the bounds of the buffer"というエラーメッセージを表示させるようにした。
Situation b:
var arrayBuffer = new ArrayBuffer(16);
var dataView = new DataView(arrayBuffer, 15, 20);
この場合では16個の要素の配列であるarrayBufferの15番目の要素から20個分要素を読みだそうとしているので、"invalid data view length"というエラーメッセージを表示させるようにした。
Situation c:
var arrayBuffer = new ArrayBuffer(16);
var dataView = new DataView(arrayBuffer);
dataView.getUint32(20);
この場合では16個の要素のDataViewオブジェクトの20番目にアクセスしようとしているので、"offset is outside the bounds of the DataView"というエラーメッセージを表示させるようにした。
パッチ
手探った流れとしては、
1.エラーメッセージで検索するとそれを記したファイルであるjs.msgを見つける
2.js.msg内に存在する、エラーメッセージ識別に用いられる文字列で検索してエラーメッセージを呼び出す部分を見つける
3.エラーメッセージが載っているファイルであるjs.msgに新しいエラーメッセージを加える
4.エラーメッセージを呼び出す部分を自分で作成したエラーメッセージを表示させるようにする
というものであった。
やったこと2
Bugzillaのページ
やったこと1で表示されていたエラーメッセージが使われている部分を修正し、元のエラーメッセージを使わないようにする
レビュアーでもあるTAさんから「このエラーメッセージイケてないから修正していいと思う」との内容のことを言われた(そのように記憶している)ので、「こういう問題があります」とBugzillaに報告(Fileすると言うらしい)して、それを折角なので自分で修正した。
コードを見てみるとエラーへの分岐点は以下のようになっていた。
if (!sizeGiven) {
struct stat st;
if (fstat(fileno(file), &st) < 0) {
省略
}
if (off_t(offset) >= st.st_size) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
return false;
}
オフセットがファイルサイズより大きいのが原因だと考えられるので"offset is larger than filesize"と表示されるようにした。
修正の流れは「やったこと1」と、修正したファイルが違うだけでほぼ同じであった。
やったこと3
DefineProperty関数の引数のディスクリプタがオブジェクトではないときに出てくるエラーメッセージの修正
最初はこれを含め同様のメッセージが表示されるバグについてやろうかと思っていたが時間の関係でこれ一つになった。
Bugzillaのページ
js> Object.defineProperty({},'key',1);
Actual results:
TypeError: ({}) is not a non-null object
問題は第3引数のディスクリプタなのに第一引数がおかしいと表示されてしまっている。
これを、
js> Object.defineProperty({},'key',1);
typein:1:1 TypeError: descriptor must be an object, got number '1'
のように表示させるようにした。
パッチ
変更の内容について
1.C++にObjectではないことを報告する関数があったのでそれに投げられるようにJS_FN関数を用いてjsの関数とをC++の関数を結び付けることにした。
2.C++側では他の引数に問題がないことを確認したのちにエラーを吐く関数に投げた。
3.js側ではC++に投げられるように形を整えた。
手探った流れは型がundefinedな引数のときの挙動に関してレビューを受けて大幅に路線を修正したために結果的に手探ったとは言いずらいようになってしまった。
おわりに
OSS、それもFirefoxという非常に有名なソフトウェアにコミットできたことはとても有益でした。また、「コードをきれいに書く」ということについても結構学べ、実りある実験であったと思っています。
JavaScriptをほとんどやったことがないことやこともあり、エラーメッセージを書くときは文字列を表示させるという部分においては誤りがあってもコンピュータは誤りを指摘してはくれないので、やったこと1や2のようなものでもしっかりその関数の挙動について間違いがないか確認するというのもあり時間がかかりました。
コードを必要分だけ、また実験時間中+αくらいしか見ていないので間違った解釈をしている可能性はありますのでその場合は優しくご指摘していただけると幸いです。
いろいろ面倒を見てくださったTAさん、この実験を用意してくださった先生への感謝でこの文章を締めさせていただきます。ありがとうございました。