せっかくだから探検に行く
目標地点があると探検し甲斐がありますよね。
ソースコードを手に入れる
https://developer.mozilla.org/ja/Developer_Guide/Source_Code/Mercurial
# 数百メガバイトの履歴が .hg フォルダにダウンロードされるため、しばらく時間がかかります。
時間かかりすぎて諦めた。よく見たらアーカイブもあるらしい。
https://developer.mozilla.org/ja/Developer_Guide/Source_Code/Downloading_Source_Archives
今使ってるのは3.5.9なので…
ftp://ftp.mozilla.org/pub/mozilla.org/firefox/releases/3.5.9/source/
名前 | サイズ | 最終更新日時 |
firefox-3.5.9.source.tar.bz2 | 46350 KB | 2010/03/15 7:46:00 |
ダウンロードする。大きいなあ。
ビルドする
説明があるのでこれを見ながら進める。
https://developer.mozilla.org/ja/Developer_Guide/Build_Instructions
Windows版のビルドに必要なものを説明した文書があるので読みながら道具をそろえる。
https://developer.mozilla.org/ja/Developer_Guide/Build_Instructions/Windows_Build_Prerequisites
親切だなあ。他はあるのでMozillaBuildだけダウンロードしてインストールする。
これで必要なものがそろったみたい。
- Microsoft Visual C++ 2008 Express Edition
- Microsoft Platform SDK for Windows Server 2003 R2
- MozillaBuild 1.4
ソースアーカイブを展開。ファイル数が4万近い。
標準オプションの状態で configure および make を実行しても、動作するビルドを作り上げることはできません。 .mozconfig ファイルを使って、相応のリリースビルドを入手してください。
まだ準備がいるらしい。
https://developer.mozilla.org/ja/Configuring_Build_Options
ここを見ながら .mozconfig を書いてソースディレクトリに入れる。
.mozconfig に次の行を追加することでオブジェクトディレクトリが有効になります。
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../obj-@CONFIG_GUESS@
Firefox以外ビルドする予定ないけど @TOPSRCDIR@/../mozilla-ff-obj-@CONFIG_GUESS@ にしておく。
ブラウザ (Firefox)
.mozconfig ファイルで Firefox の デフォルトの mozconfig ファイル を読み込んでください。
. $topsrcdir/browser/config/mozconfig
ここに書いてある通りに .mozconfig に書く。
ac_add_options --disable-optimize
最適化を無効にします。デバッガでの解析を簡易化します。
デバッガ使うかもしれないので --disable-optimize 入れる。
ac_add_options --enable-debug
デバッグマクロや他のデバッグ専用コードを有効にします。ビルドは著しく遅くなりますが、パッチを書く際に大変有用です。
ビルドの時間が長くなるのは嫌なので --enable-debug 入れない。
ac_add_options --disable-tests
デフォルトでは多くの補助的なテストアプリケーションがビルドされます。これらはデバッグや mozilla のソースにパッチを当てるのに役立ちます。これらのテストアプリケーションを無効にすることでかなりビルド時間を短縮し、ディスクスペースを減らすことができます。
テストは無効にしておこう。あとはデフォルトのままにしておく。
こんな .mozconfig ファイルができた。
# My first mozilla config mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../mozilla-ff-obj-@CONFIG_GUESS@ . $topsrcdir/browser/config/mozconfig ac_add_options --disable-optimize ac_add_options --disable-tests ac_add_options --disable-mochitest
準備完了。
2008はVC9らしいので C:\mozilla-build\start-msvc9.bat を起動。けっこう時間かかる。
ソースは D:\Projects\mozilla\mozilla-1.9.1 に入れたのでそこに移動してからビルド。
$ cd /d/Projects/mozilla/mozilla-1.9.1/ $ make -f client.mk build
はい、失敗。
nspr4.dllのビルドがうまくいってない。
どうやらバグらしい。
https://bugzilla.mozilla.org/show_bug.cgi?id=338224
--disable-optimize を指定していて --enable-debug を指定していないという場合にビルドできない。
.mozconfig を修正。
# My first mozilla config mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../mozilla-ff-obj-@CONFIG_GUESS@ . $topsrcdir/browser/config/mozconfig # https://bugzilla.mozilla.org/show_bug.cgi?id=338224 ac_add_options --disable-optimize ac_add_options --enable-debug ac_add_options --disable-tests ac_add_options --disable-mochitest
今度は成功。makeを動かしてから終了するまで一時間半ぐらいかかる。
プロファイルを作る
基本的には普段使っているFirefoxと同じものなのでそのまま動かしてもいいんですが、念のためにプロファイルを分けることにする。
"C:\Program Files\Mozilla Firefox\firefox.exe" -ProfileManager
これでプロファイルマネージャが起動できるので新しいプロファイルを作成し、起動時に毎回選択するように設定しておく。
普段使ってるFirefoxを起動したときはこう。
今回ビルドしたのを起動したときはこんな感じ。
もちろん文字化けもする。
探しに行こう
途方もない量のソースコードから目的の場所を見つけるため、適当な言葉でgrepする。
手がかりは…
- 文字化けがおきているのはエラーコンソール
- JavaScriptの例外は化けるけど、他のエラーは化けない
- 上位バイトが0になっているということは途中でwchar_tからcharにキャストしてるんじゃないだろうか
"error console"という文字列を求めてxpconnectを歩き回ったりlayoutに迷い込んだり、どこをどう進んだかわからなくなった頃、js/src/jsexn.cppの下の方で遭遇。
JSBool js_ReportUncaughtException(JSContext *cx) { jsval exn; JSObject *exnObject;
jsというディレクトリはSpiderMonkeyと同じなのでここはJavaScriptのコード。「キャッチされなかった例外を報告」という関数名はエラーコンソールに例外を出力する処理のように思える。
ここからたどっていくと…
js_ReportUncaughtException └→ js_GetStringBytes └→ js_DeflateString
jsstr.cppの中のjs_DeflateString
char * js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars) { ... for (i = 0; i < nbytes; i++) bytes[i] = (char) chars[i]; ... return bytes; }
キャストしているところを発見。実にあやしい。
jscharって何だろう。JavaScriptのcharってことかな。定義をたどっていくと…
// js/src/jspubtd.h typedef uint16 jschar; // js/src/jsotypes.h typedef JSUint16 uint16; // js/src/jstypes.h typedef uint16_t JSUint16; // VCにはuint16_tが無いみたいなので // js/src/jsstdint.h typedef unsigned __int16 uint16_t;
というわけで符号なし16ビット整数。
jsapi.hのコメントには"uint16/jschar ECMA uint16, Unicode char"という記述があるのでJavaScriptで文字列を作るときに使う16ビット値で間違いない。もし最初に書いた「やめてー!」がここを通ったら上位8ビットが0になって下位8ビットだけが残る形の文字化けをするはず。
ソースコードを書き換えてみる
js_DeflateStringが今回の文字化けに関係しているかどうか確認するためにちょっといじる。
char * js_DeflateString(JSContext *cx, const jschar *chars, size_t nchars) { ... for (i = 0; i < nbytes; i++) { if (chars[i] > 0xFF) { bytes[i] = 'E'; } else { bytes[i] = (char) chars[i]; } } ... return bytes; }
文字化け解消を目指して進む
文字化け解消にたどり着くための道筋が見えてきた。
- js_DeflateStringのすぐ上に、これと対になりそうなjs_InflateStringという関数がある
- charとjscharの文字列を相互に変換する関数のようだ
- どちらもjs_CStringsAreUTF8という変数の真偽で動作が変わるようになっている
- js_CStringsAreUTF8を真にすればcharの側がUTF-8じなって文字化けが解消できるんじゃないか
js_CStringsAreUTF8について調べる。
js/src/jsprvtd.h
#ifdef JS_C_STRINGS_ARE_UTF8 # define js_CStringsAreUTF8 JS_TRUE #else extern JSBool js_CStringsAreUTF8; #endif
js/src/jsapi.cpp
#ifndef JS_C_STRINGS_ARE_UTF8 JSBool js_CStringsAreUTF8 = JS_FALSE; #endif JS_PUBLIC_API(JSBool) JS_CStringsAreUTF8() { return js_CStringsAreUTF8; } JS_PUBLIC_API(void) JS_SetCStringsAreUTF8() { JS_ASSERT(!js_NewRuntimeWasCalled); #ifndef JS_C_STRINGS_ARE_UTF8 js_CStringsAreUTF8 = JS_TRUE; #endif }
よくわからないけど、どうやらJS_C_STRINGS_ARE_UTF8を使って切り替えるみたいだ。JS_C_STRINGS_ARE_UTF8を#defineするconfigureオプションがあればいいんだけど。
ない。JS_C_STRINGS_ARE_UTF8を定義するとどうなるのかよく調べないといけない。
js_DeflateStringで化けた文字列がどこに行くのかデバッガでトレースして調べる。
js_ReportUncaughtException └→js_ReportErrorAgain └→JSContext::errorReporter errorReporterは関数ポインタ。NS_ScriptErrorReporterが入ってた。 └→NS_ScriptErrorReporter └→nsAutoString::AssignWithConversion └→…(略)…→CopyASCIItoUTF16
js_InflateStringには行かない!
途中で他に気になる部分があったので調べる。js_CStringsAreUTF8は諦める。
void NS_ScriptErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { ... const PRUnichar *m = reinterpret_cast<const PRUnichar*> (report->ucmessage); if (m) { msg.Assign(m); } if (msg.IsEmpty() && message) { msg.AssignWithConversion(message); }
messageに化けた文字列が入ってる。report->ucmessageに何か入ってればそっちを使うんじゃないかな。ucってのがUnicodeっぽいので文字化けしないかも。
js/src/jsapi.h
struct JSErrorReport { ... const jschar *ucmessage; /* the (default) error message */
エラーメッセージ?これか?
js_ReportUncaughtExceptionの方を見ると、report->ucmessageは0になってる。ucmessageを設定してみる。
JSBool js_ReportUncaughtException(JSContext *cx) { ... if (!reportp && exnObject && OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) { const char *filename; uint32 lineno; const jschar *chars; // 追加 ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]); if (!ok) goto out; if (JSVAL_IS_STRING(roots[2])) { bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2])); chars = js_GetStringChars(cx, JSVAL_TO_STRING(roots[2])); // 追加 ... reportp = &report; memset(&report, 0, sizeof report); report.filename = filename; report.lineno = (uintN) lineno; report.ucmessage = chars; // 追加 } ... js_ReportErrorAgain(cx, bytes, reportp); ...
bytesは化けた文字列。charsは正しい文字列のはず。
文字化け直った。
目標地点に到着、終了。
最新版では既に直ってるみたい
Mercurialのリポジトリは大きすぎるのでWeb上でソースを見てたんですが、関係しそうな変更を発見。
http://hg.mozilla.org/mozilla-central/rev/34eb552d42f5
Bug 557346
https://bugzilla.mozilla.org/show_bug.cgi?id=557346
3.6.4と3.5.10で修正されているみたいですね。
例外のメッセージは遠慮なく日本語で書くことにする。