Functions are not frames

I
just realized that on my list of features missing from JScript.NET “fast mode” I forgot
about the caller property
of functions. “urn:schemas-microsoft-com:office:office” />In
compatibility mode you can say

 

function
foo(){bar();}

function
bar(){print(bar.caller);}

foo();

 

In
fast mode this prints null,
in compatibility mode it prints function
foo(){bar();}.

 

Eliminating
this feature does make it possible to generate faster code — keeping track of the
caller of every function at all times adds a fair amount of complexity to the code
generation. But just as importantly,
this feature is simply incredibly broken by its very design. The problem is that the
function object is completely the wrong object to put the
caller property
upon in the first place
. For
example:

 

function
foo(x){bar(x-1);}

function
bar(x)

{

if
(x > 0)

foo(x-1);

else

{

print(bar.caller.toString().substring(9,12));

print(bar.caller.caller.toString().substring(9,12));

print(bar.caller.caller.caller.toString().substring(9,12));

print(bar.caller.caller.caller.caller.toString().substring(9,12));

}

}

function
bla(){foo(3)}

blah();

 

This
silly example is pretty straightforward — the global scope calls bla. bla calls foo(3),
calls bar(2),
calls foo(1),
calls bar(0),
prints out the call stack. So the call
stack at this point should be foo, bar, foo, bla,
right? So why does this print out foo, bar, foo, bar?

 

Because
the caller property
is a property of the function object and it returns a function object. bar.caller and bar.caller.caller.caller are
the same object
, so of course they have the same caller property!
Clearly this is completely broken for recursive functions. What
about multi-threaded programs, where there may be multiple callers on multiple threads? Do
you make the caller property different on different threads?

 

These
problems apply to the arguments property
as well. Essentially the problem is that
the notion we want to manipulate is activation frame, not function object,
but function object is what we’ve got. To
implement this feature properly you need to access the stack of activation frames,
where an activation frame consists of a function object, an array of arguments, and
a caller, where the caller is another activation frame. Now
the problem goes away — each activation frame in a recursive, multi-threaded
program is unique
. To gain access
to the frame we’d need to add something like the this keyword — perhaps
a frame keyword that would give you the activation frame at the top of the
stack.

 

That’s
how I would have designed this feature,
but in the real world we’re stuck with being backwards compatible with the original
Netscape design. Fortunately, the .NET
reflection code lets you walk stack frames yourself if you need to. Though
it doesn’t integrate perfectly smoothly with the JScript .NET notion of functions
as objects, at least it manipulates frames reasonably well.

Tags JScript JScript .NET Scripting

Comments (3)

Cancel reply

You must be logged in to post a comment.

  1. Peter Torr says:Ahhh, you forget “arguments.caller”, which is per-frame. You also had a typo in the call to “bla” (“blah”) // ———-function bar(x)
    {
    if (x > 0)
    foo(x-1);
    else
    {
    print(arguments.caller.callee)
    print(“—–”)
    print(arguments.caller.caller.callee)
    print(“—–”)
    print(arguments.caller.caller.caller.callee)
    print(“—–”)
    print(arguments.caller.caller.caller.caller.callee)
    }
    }Log in to Reply
  2. (function (){foo(3)})()
  3. function foo(x){bar(x-1);}
  4. Try the slightly modified program (also using a tricky anonymous function) for fun and profit!:
  5. November 3, 2003 at 7:47 pm
  6. Peter Torr says:Here’s an anomoly between 5.x and .NET — run this program on both platforms and see the difference between how we alias ‘arguments’ and actual parameters in 5.x whereas we don’t in .NET:// ———-function ICantControlMyOwnData(theData)
    {
    print(“Before:”)
    print(“theData is ” + theData)
    print(“arguments[0] is ” + arguments[0])print(“—”)
    print(“After:”)
    print(“theData is ” + theData)
    print(“arguments[0] is ” + arguments[0])
    }Log in to Reply
  7. function IMessWithMyCaller()
    {
    arguments.caller[0] = “mwhahahahahahaaa”
    }
  8. IMessWithMyCaller()
  9. print(“I am running in JScript version ” + ScriptEngineMajorVersion())
    ICantControlMyOwnData(“Hello world”)
  10. (Oh and never write code like this — it will break!)
  11. November 3, 2003 at 7:56 pm
  12. Samuel Bronson says:Sigh. They *tried* to take this out of Mozilla, but put it back for legacy reasons: see bugzilla.mozilla.org/show_bug.cgi for some details. Too bad there doesn’t seem to be a *standard* way of achieving this…
  13. Log in to Reply
  14. June 28, 2010 at 12:12 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s