2013-03-23 130 views
0

說我想獲得某種堆棧跟蹤,獲取在當前函數之前調用的所有函數的名稱。遍歷arguments.callee.caller導致無限循環

我做這樣的事情:

 var callee; 
     var caller; 
     var _args = arguments; 
     var check = 0; 
     do { 
        check++; 

        callee = _args.callee; 
        caller = callee.caller; 

        var msg = 'Check ' + check + ' - ' + callee.name 
          + ' has been called by: ' + caller.name; 
        console.log(msg); 

        if (caller) { 
         // Get this caller's arguments 
         _args = caller.arguments; 
        } else { 
         reached_end = true; 
        } 

     } while (!reached_end); 

這工作得很好,大部分時間。但有時它會陷入無限循環,我想知道:這可能怎麼樣?我能做些什麼呢?

這裏是我的無限循環的輸出:

Check 1 - __parent__ has been called by: add 
    Check 2 - add has been called by: afterComponentStartup 
    Check 3 - afterComponentStartup has been called by: _launchComponents [arg0:"startup"] 
    Check 4 - _launchComponents has been called by: beforeActionNext 
    Check 5 - beforeActionNext has been called by: beforeAction 
    Check 6 - beforeAction has been called by: afterComponentInitialize 
    Check 7 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"] 
    Check 8 - _launchComponents has been called by: beforeActionNext 
    Check 9 - beforeActionNext has been called by: beforeAction 
    Check 10 - beforeAction has been called by: afterComponentInitialize 
    Check 11 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"] 
    Check 12 - _launchComponents has been called by: beforeActionNext 
    Check 13 - beforeActionNext has been called by: beforeAction 
    Check 14 - beforeAction has been called by: afterComponentInitialize 
    Check 15 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"] 
    Check 16 - _launchComponents has been called by: beforeActionNext 
    Check 17 - beforeActionNext has been called by: beforeAction 
    Check 18 - beforeAction has been called by: afterComponentInitialize 
    Check 19 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"] 
    Check 20 - _launchComponents has been called by: beforeActionNext 
    Check 21 - beforeActionNext has been called by: beforeAction 
    Check 22 - beforeAction has been called by: afterComponentInitialize 
    Check 23 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"] 
    Check 24 - _launchComponents has been called by: beforeActionNext 
    Check 25 - beforeActionNext has been called by: beforeAction 
    Check 26 - beforeAction has been called by: afterComponentInitialize 
+0

['console.trace()'](http://www.nodejs.org/api/stdio.html#stdio_console_trace_label)? – 2013-03-23 02:33:15

回答

4

arguments.callee.caller指向功能參考,其中它只存在於每個調用堆棧每個函數中的一個。

每當一個函數被調用的caller屬性被設置,這意味着如果同樣的功能在調用堆棧被多次調用,如將與遞歸函數發生,以前的值會得到恢復,並caller會現在指向自己。這是什麼導致你的無限循環。

所以在你的算法中,如果你得到一個點,你需要爲了這個不發生而打破。

1

您需要添加呼叫者在數組中,並檢查下一位是在數組中。如果是這樣,那麼你有一個循環或遞歸調用結構。嘗試:

var args = arguments; 
var callee = args.callee; 
var caller = callee.caller; 

var stack = [callee]; 

while (caller) { 
    if (stack.indexOf(caller) < 0) { 
     stack.push(caller); 
     args = caller.arguments; 
     callee = args.callee; 
     caller = callee.caller; 
    } else break; 
} 

console.log(stack); 
0

我發現了一種方法來傳遞狀態信息(在這種情況下var .ua(Object))到遞歸函數的調用堆棧中;

代替

var fwa.animatedJavascriptControlCenter = { 
    ... 
    , 
    scanElements : function (el, scanResult) { 
    // a function that walks the DOM tree to find elements that require molding by my js framework 
    if (!scanResult) scanResult={}; 
    if (el.tagName.toLowerCase()!='svg') { 
     if (conditions_for_this_element_met(el)) { 
      scanResults[el.id] = el; 
     }; 
     if (el.children.length>0) { 
      for (var i=0; i < el.children.length; i++) { 
       if (el.children[i].tagName.toUpperCase()!=='IFRAME') { 
        scanResult = fwa.animatedJavascriptControlCenter.scanElements(el.children[i], scanResult); 
       } 
      } 
     } 
    } 
    return scanResult; 
    }, 
    ... 
} 

做:

var fwa.animatedJavascriptControlCenter = { 
    ... 
    , 
    scanElements : function (el, scanResult) { 
     if (!scanResult) scanResult={}; 
     if (el.tagName.toLowerCase()!='svg') { 
      if (conditions_for_this_element_met(el)) { 
       scanResults[el.id] = el; 
      } 
      if (el.children.length>0) { 
       for (var i=0; i < el.children.length; i++) { 
        if (el.children[i].tagName.toUpperCase()!=='IFRAME') { 
         var args = [el.children[i], scanResult]; 
         args.ua= tracer.findUA(arguments); 
         var passUAfunc = function(scanResult) { 
          return fwa.animatedJavascriptControlCenter.scanElements(el.children[i], scanResult); 
         } 
         passUAfunc.ua = args.ua; 
         scanResult = passUAfunc(scanResult); 
        } 
       } 
      } 
     } 
     return scanResult; 
    }, 
    ... 
} 

,使這項工作,你還需要:

// call this just after _completely_ defining yourFrameworkRootObject and yourSiteCodeRootObject 
tracer.traceAll (yourFrameworkRootObject, true); 
tracer.traceAll (yourSiteCodeRootObject, true); 

// Bonus : ;-) 
// Enable the passage of the 'this' object through the JavaScript timers 
// thanks to https://developer.mozilla.org/en/docs/DOM/window.setTimeout#Callback_arguments 
var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval; 
window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { 
    var 
    oThis = this, 
    ua = tracer.findUA(arguments), 
    aArgs = Array.prototype.slice.call(arguments, 2); 
    if (ua) aArgs.ua = ua; 
    //if (!ua) debugger; 
    return __nativeST__(vCallback instanceof Function ? function() { 
     vCallback.apply(oThis, aArgs); 
    } : vCallback, nDelay); 
}; 
window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { 
    var 
    oThis = this, 
    aArgs = Array.prototype.slice.call(arguments, 2), 
    ua = tracer.findUA(arguments); 
    if (ua) aArgs.ua = ua; 
    return __nativeSI__(vCallback instanceof Function ? function() { 
     vCallback.apply(oThis, aArgs); 
    } : vCallback, nDelay); 
}; 

var tracer = { 
/* object last modified : 14 May 2013, 04:46 CEST 

    original code by http://javascriptweblog.wordpress.com/2010/06/01/a-tracer-utility-in-2kb/ 

    modified by [email protected] with: 
    (1) http://stackoverflow.com/a/15582432/2379929 

    augmented by [email protected] with (among other things): 
    http://stacktracejs.com/ 

    this code is now used in and offered as part of the web-framework at http://fancywebapps.com 
    (component name : hipLog, to be released later) 
*/ 


    nativeCodeEx: /\[native code\]/, 
    tracing: [], 
    traced : [], 
    userActions : [], 

    findUA : function (arg) { 
     var p = arg; 
     if (p.ua) return p.ua; 

     var callee = arg.callee; 
     var caller = callee.caller; 

     var stack = [callee]; 

     while (caller) { 
      if (stack.indexOf(caller) < 0) { 
       stack.push(caller); 
       args = caller.arguments; 
       callee = args.callee; 
       caller = callee.caller; 
      } else break; 
     } 


     while (p = stack.shift()) { 
      if (p.ua) return p.ua; 
      if (p.arguments && p.arguments.ua) return p.arguments.ua; 

     }; 
     return false; 
    }, 

    traceMe: function(func, methodName, path) { 
     var traceOn = function() { 
      var 
      ua = tracer.findUA(arguments), 
      startTime = +new Date; 

      if (!ua){ 
       //debugger; 
       //ua = tracer.findUA(arguments); 
      }; 
      //if (path=='fwa.animatedJavascriptControlCenter.scanElements') debugger; 

      if (!ua) { 
       //debugger; 
       var 
       uaIdx = tracer.userActions.length, 
       ua = tracer.userActions[uaIdx] = { 
        uaIdx : uaIdx, 
        startTime : startTime, 
        path : path, 
        stackLevel : 0 
       }; 
       tracer.traced[uaIdx] = []; 
      } else { 
       var uaIdx = ua.uaIdx; 
       ua.stackLevel++; 
      } 

      arguments.ua = ua; 

      var idx = tracer.traced[uaIdx].length; 
      tracer.traced[uaIdx][idx] = { 
       path : path, 
       arguments : arguments 
      }; 

      var result = func.apply(this, arguments); 

      tracer.traced[uaIdx][idx].stacktrace = printStackTrace() // see http://stacktracejs.com, free, quite useful 
      tracer.traced[uaIdx][idx].result = result; 
      tracer.traced[uaIdx][idx].timed = new Date - startTime; 
      tracer.traced[uaIdx][idx].stackLevel = ua.stackLevel; 
      ua.stackLevel--; 
      return result; 
     }; 
     traceOn.traceOff = func; 
     for (var prop in func) { 
      traceOn[prop] = func[prop]; 
     } 
     console.log("tracing " + path); 
     return traceOn; 
    }, 

    traceAll: function(root, path, recurse) { 
     if ((root == window) || !((typeof root == 'object') || (typeof root == 'function'))) {return;} 
     for (var key in root) { 
      if ((root.hasOwnProperty(key)) && (root[key] != root)) { 
       var thisObj = root[key]; 
       if (typeof thisObj == 'function') { 
        if ((this != root) && !thisObj.traceOff && !this.nativeCodeEx.test(thisObj)) { 
         root[key] = this.traceMe(root[key], key, path+'.'+key); 
         this.tracing.push({obj:root,methodName:key, path:path+'.'+key}); 
        } 
       } 
       recurse && this.traceAll(thisObj, path+'.'+key, true); 
      } 
     } 
    }, 

    untraceAll: function() { 
     for (var i=0; i<this.tracing.length; ++i) { 
      var thisTracing = this.tracing[i]; 
      thisTracing.obj[thisTracing.methodName] = 
       thisTracing.obj[thisTracing.methodName].traceOff; 
     } 
     //console.log("tracing disabled"); 
     tracer.tracing = []; 
    } 
} 

像使用

僅供參考:此代碼取自我的js框架http://fancywebapps.com,並將成爲它的一部分hipLog組件,旨在向您顯示完整的執行路徑(帶或不帶往返服務器和/或setTimeout()s)的任何用戶行爲,如點擊,懸停或頁面加載。