468,771 Members | 1,837 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,771 developers. It's quick & easy.

Recursive functions and arguments.callee.caller

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;

Mar 19 '06 #1
9 15972
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
Mar 20 '06 #2
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/de...489f180ce1.asp
MozDev:
<URL:
http://developer.mozilla.org/en/docs...unction:caller

--
Rob
Mar 20 '06 #3
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

Mar 20 '06 #4
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
Mar 21 '06 #5
VK

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>

Mar 21 '06 #6
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
Mar 21 '06 #7
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/ar...04/414684.aspx

Mar 23 '06 #8
VK

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>

Mar 25 '06 #9
VK

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

Mar 27 '06 #10

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

7 posts views Thread by Aloo | last post: by
6 posts views Thread by jeniffer | last post: by
41 posts views Thread by Harry | last post: by
5 posts views Thread by Digital Puer | last post: by
34 posts views Thread by Jorge | last post: by
1 post views Thread by CARIGAR | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.