前回の「Javascipt:関数が終了した後でもそのローカル変数が参照できる」を発展させて、関数に eval(getChain()); と記述するだけで、トレース出来るようにしたものです。
機能は以下になります。
- arguments, caller, callee を保存
- 終了時の変数の値を個別・一括で取得
- 複数の関数の終了時の状態を起動順序ごとに配列へ蓄積
ie 5.0, 5.5, 6.0, 7.0
ff 1.0, 1.5, 2.0
ns 7.1
※ opera は caller が無いため動作しません。
参考:http://www.opera.com/docs/specs/js/ecma/
■ 注意点
対象とする関数が起動されるたびに延々と蓄積し続けます。
Array.shift() などを使って配列を詰めてもいいかも知れません。
■2009/04/24追記
変数を監視する場合はこちら
javascript:変数を監視する関数
ソース
■ トレース用関数の本体
function getChain(){
if(!getChain.data) getChain.data = [];
var cle = arguments.callee.caller;
var cnt = getChain.data.length;
getChain.data[cnt] = {
callee : cle,
caller : cle.arguments.callee.caller,
arguments : cle.arguments,
getValue : void(0),
getValues : function (){
var vn = getValueName("" + cle), val = {}, tmp;
for(var i = 0; vn.length > i; i++){
tmp = getChain.data[cnt].getValue(vn[i]);
if(typeof(tmp) != 'undefined') val[vn[i]] = tmp;
}
return val;
}
}
return 'getChain.data[' + cnt + '].getValue = function (val){try{return eval(val);}catch(e){}}';
}
if(!getChain.data) getChain.data = [];
var cle = arguments.callee.caller;
var cnt = getChain.data.length;
getChain.data[cnt] = {
callee : cle,
caller : cle.arguments.callee.caller,
arguments : cle.arguments,
getValue : void(0),
getValues : function (){
var vn = getValueName("" + cle), val = {}, tmp;
for(var i = 0; vn.length > i; i++){
tmp = getChain.data[cnt].getValue(vn[i]);
if(typeof(tmp) != 'undefined') val[vn[i]] = tmp;
}
return val;
}
}
return 'getChain.data[' + cnt + '].getValue = function (val){try{return eval(val);}catch(e){}}';
}
arguments 他は、呼び出し先からでも取得できますが、関数内の変数を参照する関数は、参照したい関数内で eval する必要があります。このため、eval 用の文字列を返します。
■ 関数内の変数名を取得する関数
function getValueName(str){
var vn = [];
var i = str.match(/(var¥s)[^;]*⁄gi);
if(!(i instanceof Array)) i = [];
str = i.concat(str.match(/¥([^{}¥(]*¥)/gi)[0].replace('(', '').replace(')', ''));
for(var i = 0; i < str.length; i++){
str[i] = str[i].replace('var ', '').split(',');
for(var t = 0; t < str[i].length; t++){
str[i][t] = str[i][t].split('=');
vn[vn.length] = str[i][t][0].replace(/^¥s+|¥s+$/g, "");
}
}
return vn;
}
var vn = [];
var i = str.match(/(var¥s)[^;]*⁄gi);
if(!(i instanceof Array)) i = [];
str = i.concat(str.match(/¥([^{}¥(]*¥)/gi)[0].replace('(', '').replace(')', ''));
for(var i = 0; i < str.length; i++){
str[i] = str[i].replace('var ', '').split(',');
for(var t = 0; t < str[i].length; t++){
str[i][t] = str[i][t].split('=');
vn[vn.length] = str[i][t][0].replace(/^¥s+|¥s+$/g, "");
}
}
return vn;
}
関数(ソースコード)から正規表現で変数名を取得する関数です。
var 〜 ;を見つけると、「,」で split、さらに「=」で split して変数名を取得します。引数は、コードの最初に現われる「()」内を検索します。おそらくこれで十分だと思いますが、精査してません。
正規表現でコードから引っ張るのは、まったくもってスマートじゃないのですが、Activation オブジェクトにはアクセスできないので他に方法は無いかなと思います。
使い方
サンプルはこちら
■ トレース
トレースしたい幾つかの関数内のトップに以下を挿入します。
eval(getChain());
■ 参照
配列 getChain.data[n] に関数の終了時の情報が蓄積されます。
n = 0; が最初です。
・ arguments の取得
getChain.data[n].arguments を参照します。
・ callee の取得
getChain.data[n].callee を参照します。
・ caller の取得
getChain.data[n].caller を参照します。
・ 変数値の取得
ret = getChain.data[n].getValue('変数名');
ret に 変数の値が返されます。
・ 変数値の取得(一括)
ret = getChain.data[n].getValues();
ret に {変数名1:変数値1, 変数名2:変数値2, ・・・}などの連想配列様のオブジェクトが返されます。
雑感
やり始めたら面白くって作ってみましたが、徒労感が・・・。これってデバッグ以外に用途あるのか!?と思いつつも、認めたくなかった事実に襲われています。
タイトルに御幣があるかなぁ。簡潔に表現するのって難しい。
この記事へのコメント