愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

Aardwolfにみるブラウザに頼らないブレークポイントの実装

weinreっていう、iPhoneやらAndroidやらの実機(別に実機じゃなくてもいいんだけど)で見ているWeb画面のJavaScriptをリモートdebugできるツールがあって、これは割と有名。

http://alpha.mixi.co.jp/blog/?p=3248

有名なんだけど、試してみると普通にブレークポイント貼ってブレークするとか、debugger;って書いてブレークするとか、そういういわゆるデバッガ的な操作がどうやら出来ないっぽい。(ひょっとしたら出来るのかもしれない、知ってたら誰か教えて下さい)

Web InspectorっぽいUIからHTMLをリアルタイム編集できたり、Consoleからいわゆるevalが出来るのはとってもステキなんだけど、「デバッグできます!」と謳っているのにブレークポイント貼ってブレークできないのは、なんか惜しい感じである。


で、今日か昨日にTwitterで見たNode.js製のAardwolfとかいうカッコええ名前の何かを試してみた。

http://lexandera.com/aardwolf/

サイトを見ると、どうやらweinreと同じような路線でかつブレークポイント貼れるっぽい。


で、試してみたら、確かにiPhoneエミュレータでもブレークできた!



ウェーイ



とか言ってる場合じゃなくて、AardWolfがどうやって外側からブレークポイントを実現しているのか気になったので調べた。


結論から言うとサーバサイドでクライアントに渡す前にJSのコード書き換えてる。


例えば、以下のような"/homura.js"というパスで読み込まれるJSがあったとする。

  var a = 1;
  var b = 2;
  var c = 3;

Aardwolfは以下のように書き換えてからクライアントにそれを渡す(実際はこれに加えてtryとか入るのでちょっと違う)

Aardwolf.updatePosition("/homura.js", 1, false, aardwolfEvalFunc); var a = 1;
Aardwolf.updatePosition("/homura.js", 2, false, aardwolfEvalFunc); var a = 2;
Aardwolf.updatePosition("/homura.js", 3, false, aardwolfEvalFunc); var a = 3;

Aardwolf.updatePositionの中身は以下

    this.updatePosition = function(file, line, isDebuggerStatement, evalScopeFunc) {
        /* Webkit's exceptions don't contain any useful file and line data,
           so we keep track of this manually for exception reporting purposes. */
        lastFile = file;
        lastLine = line;

        while (true) {
            var shouldBreak = (breakpoints[file] && breakpoints[file][line]) || isDebuggerStatement || breakOnNext;
            if (!shouldBreak) {
                return;
            }

            dropCommandConnection();
            var cmd = sendToServer('/breakpoint', { command: 'report-breakpoint', file: file, line: line, stack: getStack().slice(1) });
            listenToServer();
            if (!cmd) {
                return;
            }

            if (cmd.command == 'eval') {
                doEval(evalScopeFunc, cmd);
            }
            else {
                var isInternalCommand = processCommand(cmd);
                if (!isInternalCommand) {
                    return;
                }
            }
        }
    };

…お分かりだろうか。ブレークの実体はwhile無限ループだったのだあ!じゃじゃん!

もうちょっとちゃんと説明すると、updatePositionはコールされて「ブレークすべし」というフラグがどれか立っていればwhileを使った「サーバからのコマンド待ちループ」に入る。サーバから来るコマンドを見て、次のブレーク条件をonにしたり(break on next)、returnでwhileループを抜けたり(continue)、といった風に動作を変える。


タネが分かると「なんだそうかあ」ってなるけど、その発想はあまりなかった。


Aardwolfは、ソース書き換えを行う都合上すべてのJSファイルをAardwolf経由でserveする必要がある。これはこれで仕方ないけどちょっと面倒か。あとデバッガ部分がオレオレWeb UIなので、やっぱり慣れてるWeb Inspector or FirebugなUIだったらいいのになー、とか。

Web Inspector UIなAardwolfがあればいいのになー、とかとか。



オチはありません。