Recursive functions and arguments.callee.caller

Discussion in 'Javascript' started by Csaba Gabor, Mar 19, 2006.

  1. Csaba  Gabor

    Csaba Gabor Guest

    Inside a function, I'd like to know the call stack. By this I mean
    that I'd like to know the function that called this one, that one's
    caller and so on.

    So I thought to do:
    <script type='text/javascript'>
    function myFunc(lev) {
    // if (lev) return myFunc(lev-1);
    var aStack=[];
    nextFunc = arguments.callee;
    while (nextFunc) {
    aStack.push(nextFunc.name ? nextFunc.name : nextFunc.toString());
    nextFunc = nextFunc.caller;
    }
    return aStack;
    }
    function foo() { return myFunc(1); }
    function bar() { return foo("frob"); }
    alert (bar("baz"));
    </script>


    This works as expected (on FF 1.5+ / IE 6 on my WinXP Pro). However,
    if I uncomment the first line of myFunc to make it recursive (making
    sure I don't have any unsaved work or other browser windows open), the
    function stalls out because arguments.callee.caller==arguments.callee,
    so the script goes into an infinite loop.

    In other words, myFunc at the lowest level knows it was called by
    myFunc (hence I can detect recursion), but I don't see how to determine
    how many levels deep myFunc is, nor its original caller. Anyone know
    an alternate approach?

    Thanks,
    Csaba Gabor from Vienna

    I did try an approach of getting to the arguments.callee of the caller
    (since arguments.callee.caller in [the outer call, ] myFunc(1) is
    correct, returning foo), but no dice. I got the same results (of an
    infinite loop) after adding the following two lines after nextFunc =
    nextFunc.caller;

    if (!nextFunc) break;
    var nextFunc = nextFunc.arguments.callee;
     
    Csaba Gabor, Mar 19, 2006
    #1
    1. Advertising

  2. Csaba  Gabor

    RobG Guest

    Csaba Gabor said on 20/03/2006 8:10 AM AEST:
    > Inside a function, I'd like to know the call stack. By this I mean
    > that I'd like to know the function that called this one, that one's
    > caller and so on.
    >
    > So I thought to do:
    > <script type='text/javascript'>
    > function myFunc(lev) {
    > // if (lev) return myFunc(lev-1);
    > var aStack=[];
    > nextFunc = arguments.callee;
    > while (nextFunc) {
    > aStack.push(nextFunc.name ? nextFunc.name : nextFunc.toString());
    > nextFunc = nextFunc.caller;
    > }
    > return aStack;
    > }
    > function foo() { return myFunc(1); }
    > function bar() { return foo("frob"); }
    > alert (bar("baz"));
    > </script>
    >
    >
    > This works as expected (on FF 1.5+ / IE 6 on my WinXP Pro). However,
    > if I uncomment the first line of myFunc to make it recursive (making
    > sure I don't have any unsaved work or other browser windows open), the
    > function stalls out because arguments.callee.caller==arguments.callee,
    > so the script goes into an infinite loop.
    >
    > In other words, myFunc at the lowest level knows it was called by
    > myFunc (hence I can detect recursion), but I don't see how to determine
    > how many levels deep myFunc is, nor its original caller. Anyone know
    > an alternate approach?
    >
    > Thanks,
    > Csaba Gabor from Vienna
    >
    > I did try an approach of getting to the arguments.callee of the caller
    > (since arguments.callee.caller in [the outer call, ] myFunc(1) is
    > correct, returning foo), but no dice. I got the same results (of an
    > infinite loop) after adding the following two lines after nextFunc =
    > nextFunc.caller;


    Not all browsers give function objects a caller property, and I think
    the name property of function objects is unique to Gecko. So whatever
    you get to work in Firefox/Mozilla/Netscape probably will not work
    elsewhere.

    >
    > if (!nextFunc) break;
    > var nextFunc = nextFunc.arguments.callee;


    The script below is a bit dirty, it works in Firefox but not IE.

    I've used the function object's name property but it isn't widely
    supported, nor is the name property of arguments.callee (which I guess
    is the same thing as arguments.callee is a reference to the current
    function object).

    You could use toString() and just parse out the function name to get
    around lack of support for fnObj.name, it's a bit harder to get around
    lack of support for the caller property, but not impossible.


    function callFoo(){ foo(); }
    function foo(){ bar(); }

    function bar()
    {
    var callObj = arguments.callee;
    var callName = callObj.name;
    var callChain = [callName];

    while ('string' == typeof callName) {
    if ( (callObj = callObj.caller) ){
    callName = callObj.name;
    callChain.push(callName);
    } else {
    callName = null;
    }
    }

    // Gives top-down order, leave as-is if bottom-up is required
    callChain.reverse();
    alert(callChain.join('\n'));
    }

    callFoo();

    The bar() function could be turned into a showCallChain() function by
    passing it arguments.callee and using that as the initial value of callObj.

    It seems to me you are better off having each function call the next
    with its arguments.callee.name property as a parameter - again,
    callee.name may not be (in fact probably isn't) supported in all browsers.


    --
    Rob
     
    RobG, Mar 20, 2006
    #2
    1. Advertising

  3. Csaba  Gabor

    RobG Guest

    RobG said on 20/03/2006 3:51 PM AEST:
    > Csaba Gabor said on 20/03/2006 8:10 AM AEST:
    >
    >> Inside a function, I'd like to know the call stack. By this I mean
    >> that I'd like to know the function that called this one, that one's
    >> caller and so on.


    Sorry for my above answer, I missed your point. The problem is that for
    recursive functions, the callee and caller properties refer to the same
    object.

    Try this, loopMe() recurses 'depth' times, then compares callee to caller:

    function loopMe(depth, idx){
    if (idx<depth){
    loopMe(depth, ++idx);
    } else {
    var obj = arguments.callee;
    alert(idx + ': ' + (obj == obj.caller));
    }
    }

    loopMe(5, 0);


    You can even do something like:

    alert(arguments.callee.caller.caller.caller.caller.name)


    after two loops, you'll keep getting 'loopMe'. To keep track of
    recursion depth, you'll need to pass ++idx to recursive calls. Once you
    enter a recursive function, probably the only way to get the scope chain
    using your while algorithm is to pass a reference to the function that
    calls the recursive function and start from there.


    The ECMAScript Section 10.1.8 says:

    "A property is created with name callee and property
    attributes { DontEnum }. The initial value of this property
    is the Function object being executed. This allows anonymous
    functions to be recursive."


    And Section 10.2.3 on function code:

    "The scope chain is initialised to contain the activation
    object followed by the objects in the scope chain stored
    in the [[Scope]] property of the Function object."

    Based on that and testing, it seems that callee and caller refer to the
    original function object, not the activation object that actually calls
    the function. This makes sense if you want to use a recursive function
    because you want a new 'copy' of the original function object.

    Note that arguments.caller was deprecated in Netsacpe's JavaScript
    version 1.3, but the function object's caller property is still supported.

    The following links offer minimal help:

    MSDN:
    <URL:
    http://msdn.microsoft.com/library/d...html/ae210853-7160-4102-9cfd-ab489f180ce1.asp
    >


    MozDev:
    <URL:
    http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Function:caller
    >



    --
    Rob
     
    RobG, Mar 20, 2006
    #3
  4. Csaba  Gabor

    Csaba Gabor Guest

    RobG wrote:
    > RobG said on 20/03/2006 3:51 PM AEST:
    > > Csaba Gabor said on 20/03/2006 8:10 AM AEST:
    > >> Inside a function, I'd like to know the call stack. By this I mean
    > >> that I'd like to know the function that called this one, that one's
    > >> caller and so on.

    ....
    >
    > The ECMAScript Section 10.1.8 says:
    >
    > "A property is created with name callee and property
    > attributes { DontEnum }. The initial value of this property
    > is the Function object being executed. This allows anonymous
    > functions to be recursive."
    >
    > And Section 10.2.3 on function code:
    >
    > "The scope chain is initialised to contain the activation
    > object followed by the objects in the scope chain stored
    > in the [[Scope]] property of the Function object."
    >
    > Based on that and testing, it seems that callee and caller refer to the
    > original function object, not the activation object that actually calls
    > the function. This makes sense if you want to use a recursive function
    > because you want a new 'copy' of the original function object.


    Thanks for your comments, Rob. I didn't follow your conclusion about
    it making sense and wanting the copies. Are you saying that it makes
    sense as is? Seems to me, I should expect to be able to backtrack up
    the call stack. I didn't find any references to
    arguments.callee.caller coupled with recursive functions in my
    searches, but I did see mentioned __caller__ in one old FF bug report
    (https://bugzilla.mozilla.org/show_bug.cgi?id=65683), but I didn't get
    so much as "peep" in trying to use it.

    So I'm really wondering whether I'm missing an angle on this, cause I
    just don't see the rationale. It's does not seem to be a security
    issue because arguments.callee.caller of the first call into the
    recursive function can reach out to its caller. By the same token,
    it's not a (stack) space issue since that stack exists anyway (since
    each function higher up the stack has its own, extant and distinct
    arguments object). And it's also not on account of possible tail
    recursion optimisation because in separate testing I inserted code to
    forestall tail recursion.

    Csaba Gabor from Vienna
     
    Csaba Gabor, Mar 20, 2006
    #4
  5. Csaba  Gabor

    RobG Guest

    Csaba Gabor said on 21/03/2006 4:03 AM AEST:
    > RobG wrote:
    >
    >>RobG said on 20/03/2006 3:51 PM AEST:
    >>
    >>>Csaba Gabor said on 20/03/2006 8:10 AM AEST:
    >>>
    >>>>Inside a function, I'd like to know the call stack. By this I mean
    >>>>that I'd like to know the function that called this one, that one's
    >>>>caller and so on.

    >
    > ...
    >
    >>The ECMAScript Section 10.1.8 says:
    >>
    >> "A property is created with name callee and property
    >> attributes { DontEnum }. The initial value of this property
    >> is the Function object being executed. This allows anonymous
    >> functions to be recursive."
    >>
    >>And Section 10.2.3 on function code:
    >>
    >> "The scope chain is initialised to contain the activation
    >> object followed by the objects in the scope chain stored
    >> in the [[Scope]] property of the Function object."
    >>
    >>Based on that and testing, it seems that callee and caller refer to the
    >>original function object, not the activation object that actually calls
    >>the function. This makes sense if you want to use a recursive function
    >>because you want a new 'copy' of the original function object.

    >
    >
    > Thanks for your comments, Rob. I didn't follow your conclusion about
    > it making sense and wanting the copies. Are you saying that it makes
    > sense as is? Seems to me, I should expect to be able to backtrack up
    > the call stack. I didn't find any references to
    > arguments.callee.caller coupled with recursive functions in my
    > searches, but I did see mentioned __caller__ in one old FF bug report
    > (https://bugzilla.mozilla.org/show_bug.cgi?id=65683), but I didn't get
    > so much as "peep" in trying to use it.


    Ah, I think that holds the key. The caller property references the
    function object, not the activation object that the now removed
    __caller__ property referred to.


    Following is a Brendan Eich comment on Mozilla 0.9 (2001-01-10):

    "All traces of a caller property were removed a while ago,
    to follow ECMA-262 and to avoid any chance of a security exploit.
    The removal seems to me to have been overzealous, because it axed
    the caller property of function objects *and* the
    much-more-exploitable caller (or __caller__) property of
    activation objects.

    "Confirming, we should consider restoring the caller property
    of function objects for old time's sake (JScript imitated it
    faithfully in cloning JS1.1, where it originated)."


    I suppose that for a recursive function, each recursion has the same
    function object but different activation object. If you could reference
    the activation object, then you could climb any stack. But Brendan's
    comment says that support for __caller__ was removed long ago.


    > So I'm really wondering whether I'm missing an angle on this, cause I
    > just don't see the rationale. It's does not seem to be a security
    > issue because arguments.callee.caller of the first call into the
    > recursive function can reach out to its caller.


    I think getting a reference to the activation object was seen as a
    potential security exploit, hence caller was reinstated but not __caller__.

    [...]


    --
    Rob
     
    RobG, Mar 21, 2006
    #5
  6. Csaba  Gabor

    VK Guest

    RobG wrote:
    > Following is a Brendan Eich comment on Mozilla 0.9 (2001-01-10):
    >
    > "All traces of a caller property were removed a while ago,
    > to follow ECMA-262 and to avoid any chance of a security exploit.
    > The removal seems to me to have been overzealous, because it axed
    > the caller property of function objects *and* the
    > much-more-exploitable caller (or __caller__) property of
    > activation objects.
    >
    > "Confirming, we should consider restoring the caller property
    > of function objects for old time's sake (JScript imitated it
    > faithfully in cloning JS1.1, where it originated)."
    >
    >
    > I suppose that for a recursive function, each recursion has the same
    > function object but different activation object. If you could reference
    > the activation object, then you could climb any stack. But Brendan's
    > comment says that support for __caller__ was removed long ago.
    >
    >
    > > So I'm really wondering whether I'm missing an angle on this, cause I
    > > just don't see the rationale. It's does not seem to be a security
    > > issue because arguments.callee.caller of the first call into the
    > > recursive function can reach out to its caller.

    >
    > I think getting a reference to the activation object was seen as a
    > potential security exploit, hence caller was reinstated but not __caller__.


    A year or so ago I was writing a similar caller chain tracing program
    but for another purpose (to get the literal name of the instance). Some
    discrepancies and bizarrities I had to bypass for that how have some
    explanation grace to Rob.

    Given a function f(), "arguments" object with all its properties is
    created not for the function f() itself, but for the given
    instance/heap of the function f() - that is what confusingly reffered
    in ECMAScript as Activation object.
    I would stay with the "heap" term here because "instance" implies wrong
    analogies with the higher level mechanics, while here it is merely a
    lower level execution stack mechanics.
    There can be multiple heaps created by the same function existing at
    once, each with its own argument object. In this heap arguments.caller
    either blocked or looped to the "creator" (function f). Otherwise
    indeed one could penetrate right into execution stack using caller as
    starting point with all possible security problems.
    It is interesting to notice that in this concerne:

    function f() {
    f.caller != arguments.caller;
    }

    f.caller refers to the function forced function f to create the current
    heap. It is really should be explained better than I did - and
    definitely better than it's done in ECMA Books.

    Some code:

    <html>
    <head>
    <title>callee, caller and stuff...</title>
    <meta http-equiv="Content-Type"
    content="text/html; charset=iso-8859-1">
    <style type="text/css">
    body {
    margin: 0px 0px;
    padding: 10px 10px;
    color: #000000;
    background-color: #FFFFFF}
    </style>

    <script type="text/javascript">
    var output = null;
    function init() {
    output = document.forms['frm01'].elements['output'];
    }
    window.onload = init;

    function test1(caller) {
    var out = 'Function in intrinsic event handler:\n\n'
    out+= (caller)? caller : 'undefined';
    output.value = out;
    }

    function test2(caller) {
    var out = 'test2(): function reference behavior:\n\n'
    out+= 'arguments.callee = ' + arguments.callee + '\n\n';
    out+= 'test2.toString() = ' + test2.toString();
    output.value = out;
    }

    function test3() {
    var out = 'arguments.caller = ' + arguments.caller + '\n\n';
    out+= 'test3.caller = ' + test3.caller + '\n\n';
    out+= (arguments.caller == test3.caller) + '\n\n';
    out+= arguments.caller.callee;
    output.value = out;
    }
    function test3_helper() {
    test3();
    } test3_helper.fid = true;

    </script>

    </head>

    <body>

    <form name="frm01" action="">
    <fieldset>
    <legend>Test</legend><br>
    <textarea name="output" id="output"
    cols="64" rows="10"></textarea><br>
    <input type="button" value="Test 1"
    onClick="test1(arguments.callee)">
    <input type="button" value="Test 2"
    onClick="test2(arguments.callee)">
    <input type="button" value="Test 3" onClick="test3_helper()">
    </fieldset>
    </form>

    </body>
    </html>
     
    VK, Mar 21, 2006
    #6
  7. VK wrote:

    > function f() {
    > f.caller != arguments.caller;
    > }
    >
    > f.caller refers to the function forced function f to
    > create the current heap.


    No, it does not. There is only one heap for the entire program.

    > It is really should be explained better than I did -


    Which is not too hard.

    > and definitely better than it's done in ECMA Books.


    There are no ECMA Books.


    PointedEars
     
    Thomas 'PointedEars' Lahn, Mar 21, 2006
    #7
  8. Csaba  Gabor

    Csaba Gabor Guest

    RobG wrote:
    > Csaba Gabor said on 21/03/2006 4:03 AM AEST:
    > >>RobG said on 20/03/2006 3:51 PM AEST:
    > >>>Csaba Gabor said on 20/03/2006 8:10 AM AEST:
    > >>>>Inside a function, I'd like to know the call stack. By this I mean
    > >>>>that I'd like to know the function that called this one, that one's
    > >>>>caller and so on.

    > > ...

    > Ah, I think that holds the key. The caller property references the
    > function object, not the activation object that the now removed
    > __caller__ property referred to.
    >
    > Following is a Brendan Eich comment on Mozilla 0.9 (2001-01-10):
    >
    > "All traces of a caller property were removed a while ago,
    > to follow ECMA-262 and to avoid any chance of a security exploit.
    > The removal seems to me to have been overzealous, because it axed
    > the caller property of function objects *and* the
    > much-more-exploitable caller (or __caller__) property of
    > activation objects.
    >
    > "Confirming, we should consider restoring the caller property
    > of function objects for old time's sake (JScript imitated it
    > faithfully in cloning JS1.1, where it originated)."
    >
    >
    > I suppose that for a recursive function, each recursion has the same
    > function object but different activation object. If you could reference
    > the activation object, then you could climb any stack. But Brendan's
    > comment says that support for __caller__ was removed long ago.
    >
    >
    > > So I'm really wondering whether I'm missing an angle on this, cause I
    > > just don't see the rationale. It's does not seem to be a security
    > > issue because arguments.callee.caller of the first call into the
    > > recursive function can reach out to its caller.

    >
    > I think getting a reference to the activation object was seen as a
    > potential security exploit, hence caller was reinstated but not __caller__.


    To be clear on what is happening [it is as you say], arguments.callee
    returns the original function object (and by that I mean the reference
    that you get when you define:
    function foo(...) {...}
    I tested this by calling a function and in the function doing
    arguments.callee.funcProp="frob" and checking foo.funcProp after
    finishing the function call and sure enough, there was "frob".

    The implication is that each time a call is made the function object
    has a property, .caller, placed on it that points to the calling
    function object ** for the duration of that call **. Sure enough, this
    checks out too.

    Frankly, this seems like a really whacked out way to go. I haven't
    seen anything so far to give me notions to the contrary. Appears to me
    that what is needed is a property of arguments that is essentially
    ..parent, which would point to the arguments object of the activation
    object that called this one. In this fashion there would not even need
    to be a .caller property anymore, considering that .caller on the
    original function object is a crutch anyway (ie. the interpreter is not
    making use of it).

    Csaba

    By the way, Eric Lippert has a really interesting read about activation
    objects, execution variables, scopes, this, and more at:
    http://blogs.msdn.com/ericlippert/archive/2005/05/04/414684.aspx
     
    Csaba Gabor, Mar 23, 2006
    #8
  9. Csaba  Gabor

    VK Guest

    Csaba Gabor wrote:
    > To be clear on what is happening [it is as you say], arguments.callee
    > returns the original function object (and by that I mean the reference
    > that you get when you define:
    > function foo(...) {...}


    Right in opposite order ;-)

    arguments.caller returns a reference to the current running instance of
    the function (and its functionality and reliability is partially locked
    for security reasons). FunctionName.caller returns the reference to the
    original function. Read my previous post again, it has some code to
    play with.

    Also you may try this (narrowed to illustrate this particular issue):

    <html>
    <head>
    <title>callee, caller and stuff</title>
    <meta http-equiv="Content-Type"
    content="text/html; charset=iso-8859-1">
    <script type="text/javascript">

    var out = null;

    function init() {
    out = document.forms[0].elements['out'];
    test();
    }
    // adding a property to init function object:
    init.marker = true;

    function test() {
    if (arguments.caller) {
    var s = 'arguments.caller.marker = '
    + arguments.caller.marker + '\n\n';
    s+= 'init.marker = ' + init.marker + '\n\n';
    out.value = s;
    }
    }
    </script>
    </head>

    <body onLoad="init()">

    <form action="">
    <fieldset>
    <legend>Output</legend>
    <textarea name="out" cols="64" rows="32"></textarea>
    </fieldset>
    </form>

    </body>
    </html>
     
    VK, Mar 25, 2006
    #9
  10. Csaba  Gabor

    VK Guest

    VK wrote:
    > arguments.caller returns a reference to the current running instance of
    > the function (and its functionality and reliability is partially locked
    > for security reasons). FunctionName.caller returns the reference to the
    > original function. Read my previous post again, it has some code to
    > play with.
    >
    > Also you may try this (narrowed to illustrate this particular issue):


    <snip>
    > s+= 'init.marker = ' + init.marker + '\n\n';

    <snip>

    Hum... Either I'm in everyone's ban list already, or everyone found
    some sense in it I'm not aware of :) Sorry I actually grabbed a wrong
    demo file - I produced a lot of them while trying to study this
    question. The right code (what I meant in my post is):

    <html>
    <head>
    <title>callee, caller and stuff</title>
    <meta http-equiv="Content-Type"
    content="text/html; charset=iso-8859-1">
    <script type="text/javascript">

    var out = null;

    function init() {
    out = document.forms[0].elements['out'];
    test();
    }

    // adding a property to init function object:
    init.marker = true;

    function test() {
    if (arguments.caller) {
    var s = 'arguments.caller.marker = '
    + arguments.caller.marker + '\n\n';
    // HERE IS THE CHANGE:
    s+= 'test.caller.marker = ' + test.caller.marker + '\n\n';
    out.value = s;
    }
    }

    </script>
    </head>

    <body onLoad="init()">

    <form action="">
    <fieldset>
    <legend>Output</legend>
    <textarea name="out" cols="64" rows="32"></textarea>
    </fieldset>
    </form>

    </body>
    </html>

    IE6 (JScript was not patched for caller) gives me:

    arguments.caller.marker = undefined
    test.caller.marker = true
     
    VK, Mar 27, 2006
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. jai hanuman

    caller / callee graph ...

    jai hanuman, Dec 16, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    676
    jai hanuman
    Dec 16, 2003
  2. John Dalberg
    Replies:
    0
    Views:
    742
    John Dalberg
    Jun 21, 2004
  3. Replies:
    19
    Views:
    457
    Ben Bacarisse
    Feb 22, 2006
  4. Replies:
    8
    Views:
    681
    James Kanze
    May 10, 2007
  5. Gildor Oronar
    Replies:
    6
    Views:
    162
    Gildor Oronar
    Sep 6, 2013
Loading...

Share This Page