Я начинаю работать с инструментом динамического анализа для JS, и я хотел бы ненавязчиво профилировать всю среду. Я в основном пересекаю различные контексты, копаю глубоко в объекты, и каждый раз, когда я нажимаю на функцию, я подключаюсь к ней. Теперь это работает достаточно хорошо для того, что он влезает при работе с библиотеками, как за исключением JQuery/прототип и т.д.Javascript проигрывает контекст при перехвате рекурсивно
Это мой код до сих пор (прокомментировал в меру моих возможностей):
var __PROFILER_global_props = new Array(); // visited properties
/**
* Hook into a function
* @name the name of the function
* @fn the reference to the function
* @parent the parent object
*/
function __PROFILER_hook(name, fn, parent) {
//console.log('hooking ' + name + ' ' + fn + ' ' + parent);
if (typeof parent == 'undefined')
parent = window;
for (var i in parent) {
// find the right function
if (parent[i] === fn) {
// hook into it
console.log('--> hooking ' + name);
parent[i] = function() {
console.log('called ' + name);
return fn.apply(parent, arguments);
}
//parent[i] = fn; // <-- this works (obviously)
break;
}
}
}
/**
* Traverse object recursively, looking for functions or objects
* @obj the object we're going into
* @parent the parent (used for keeping a breadcrumb)
*/
function __PROFILER_traverse(obj, parent) {
for (i in obj) {
// get the toString object type
var oo = Object.prototype.toString.call(obj[i]);
// if we're NOT an object Object or an object Function
if (oo != '[object Object]' && oo != '[object Function]') {
console.log("...skipping " + i);
// skip
// ... the reason we do this is because Functions can have sub-functions and sub-objects (just like Objects)
continue;
}
if (__PROFILER_global_props.indexOf(i) == -1 // first we want to make sure we haven't already visited this property
&& (i != '__PROFILER_global_props' // we want to make sure we're not descending infinitely
&& i != '__PROFILER_traverse' // or recusrively hooking into our own hooking functions
&& i != '__PROFILER_hook' // ...
&& i != 'Event' // Event tends to be called a lot, so just skip it
&& i != 'log' // using FireBug for debugging, so again, avoid hooking into the logging functions
&& i != 'notifyFirebug')) { // another firebug quirk, skip this as well
// log the element we're looking at
console.log(parent+'.'+i);
// push it.. it's going to end up looking like '__PROFILER_BASE_.something.somethingElse.foo'
__PROFILER_global_props.push(parent+'.'+i);
try {
// traverse the property recursively
__PROFILER_traverse(obj[i], parent+'.'+i);
// hook into it (this function does nothing if obj[i] is not a function)
__PROFILER_hook(i, obj[i], obj);
} catch (err) {
// most likely a security exception. we don't care about this.
}
} else {
// some debugging
console.log(i + ' already visited');
}
}
}
это профиль, и это, как я вызываю его:
// traverse the window
__PROFILER_traverse(window, '__PROFILER_BASE_');
// testing this on jQuery.com
$("p.neat").addClass("ohmy").show("slow");
Обход работает отлично и закреплять отлично работает до тех пор, как функции просты и неанонимные (я думаю, зацепив в анонимные функции невозможно, поэтому я не тоже беспокоился об этом).
Это некоторый отрезанный выход из фазы предварительной обработки.
notifyFirebug already visited
...skipping firebug
...skipping userObjects
__PROFILER_BASE_.loadFirebugConsole
--> hooking loadFirebugConsole
...skipping location
__PROFILER_BASE_.$
__PROFILER_BASE_.$.fn
__PROFILER_BASE_.$.fn.init
--> hooking init
...skipping selector
...skipping jquery
...skipping length
__PROFILER_BASE_.$.fn.size
--> hooking size
__PROFILER_BASE_.$.fn.toArray
--> hooking toArray
__PROFILER_BASE_.$.fn.get
--> hooking get
__PROFILER_BASE_.$.fn.pushStack
--> hooking pushStack
__PROFILER_BASE_.$.fn.each
--> hooking each
__PROFILER_BASE_.$.fn.ready
--> hooking ready
__PROFILER_BASE_.$.fn.eq
--> hooking eq
__PROFILER_BASE_.$.fn.first
--> hooking first
__PROFILER_BASE_.$.fn.last
--> hooking last
__PROFILER_BASE_.$.fn.slice
--> hooking slice
__PROFILER_BASE_.$.fn.map
--> hooking map
__PROFILER_BASE_.$.fn.end
--> hooking end
__PROFILER_BASE_.$.fn.push
--> hooking push
__PROFILER_BASE_.$.fn.sort
--> hooking sort
__PROFILER_BASE_.$.fn.splice
--> hooking splice
__PROFILER_BASE_.$.fn.extend
--> hooking extend
__PROFILER_BASE_.$.fn.data
--> hooking data
__PROFILER_BASE_.$.fn.removeData
--> hooking removeData
__PROFILER_BASE_.$.fn.queue
Когда я исполняю $("p.neat").addClass("ohmy").show("slow");
на jQuery.com (через Firebug), я получаю соответствующий стек вызовов, но я, кажется, сойду контекст где-то по пути, потому что ничего не происходит, и я получаю сообщение об ошибке e is undefined
из JQuery (ясно, что крючок прикрутил что-то).
called init
called init
called find
called find
called pushStack
called pushStack
called init
called init
called isArray
called isArray
called merge
called merge
called addClass
called addClass
called isFunction
called isFunction
called show
called show
called each
called each
called isFunction
called isFunction
called animate
called animate
called speed
called speed
called isFunction
called isFunction
called isEmptyObject
called isEmptyObject
called queue
called queue
called each
called each
called each
called each
called isFunction
called isFunction
Проблема заключается в том, что я думаю, что при вызове
return fn.apply(parent, arguments);
Вот еще одна интересная особенность я теряю this
контекст. Если бы я крюк, прежде чем я траверс, то есть:
// hook into it (this function does nothing if obj[i] is not a function)
__PROFILER_hook(i, obj[i], obj);
// traverse the property recursively
__PROFILER_traverse(obj[i], parent+'.'+i);
.. приложение работает абсолютно нормально, но стек вызовов меняется (и не похоже, чтобы получить JQuery специфические функции) по какой-то причине:
called $
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called setInterval
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called getComputedStyle
called clearInterval
.. вместо animation
, show
, merge
и т.д. Прямо сейчас, все крюк делает это сказать called functionName
, но в конце концов я хотел бы сделать трассировку стека и временные функции (с помощью апплета Java).
Этот вопрос оказался огромным, и я прошу прощения, но любая помощь приветствуется!
Примечание: вышеуказанный код может привести к краху вашего браузера, если вы не будете осторожны. Справедливое предупреждение: P
Аналогичный вопрос: http://stackoverflow.com/questions/5033836/adding-console-log-to-every-function-automatically –