Recursive functions and arguments.callee.caller

C

Csaba Gabor

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;
 
R

RobG

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.
 
R

RobG

RobG said on 20/03/2006 3:51 PM AEST:
Csaba Gabor said on 20/03/2006 8:10 AM AEST:

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
 
C

Csaba Gabor

RobG said:
RobG said on 20/03/2006 3:51 PM AEST:
Csaba Gabor said on 20/03/2006 8:10 AM AEST:
....

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
 
R

RobG

Csaba Gabor said on 21/03/2006 4:03 AM AEST:
RobG said:
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__.

[...]
 
V

VK

RobG said:
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.



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>
 
T

Thomas 'PointedEars' Lahn

VK said:
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
 
C

Csaba Gabor

RobG said:
Csaba Gabor said on 21/03/2006 4:03 AM AEST:
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.



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
 
V

VK

Csaba said:
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>
 
V

VK

VK said:
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):

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top