Ye olde arguments argument (was: Topic list - pending changes and issues for the ES3.1 spec)
On Tue, Sep 9, 2008 at 11:32 AM, Mark S. Miller <erights at google.com> wrote:
On Tue, Sep 9, 2008 at 9:21 AM, Mark S. Miller <erights at google.com> wrote:
How to restrict 'arguments' in strict functions? anticipation of ES-H-strict -- prohibit co-existence with splat arguments.callee joining? frozen?
Should strict 'arguments' be an array? I know we've over this before and that strong reasons were presented why we couldn't do this. But on the ES3.1 phone call this morning no one could remember or regenerate those reasons. Anyone? Pointers to previous threads are a fine answer. Thanks.
The arguments object could have the same methods as array, like a "subclass" of array, but not have the special [[Put]], and concat() could still work the way it does with today's arguments object (I don't actually prefer the current behavior)
(function(){ return [].concat(arguments).length; // result => 1 })(1,2,3);
Garrett
On Sep 9, 2008, at 1:49 PM, Garrett Smith wrote:
On Tue, Sep 9, 2008 at 11:32 AM, Mark S. Miller
<erights at google.com> wrote:Should strict 'arguments' be an array? I know we've over this before and that strong reasons were presented why we couldn't do this.
But on the ES3.1 phone call this morning no one could remember or regenerate those reasons. Anyone? Pointers to previous threads are a fine
answer.
There was a narrow-cast thread among ES3.1 principals, I replied
without enlarging it. Here's my last message in full, citing Allen's
agreement:
I only now noticed that this mail spun off from a narrowcast mail
from Pratap, but the topic (arguments' type) deserves a wider list.
es3.x-discuss at mozilla.org is fine. May be that we are done with
this thread, but if not (or if it comes back to life), it should
not be too narrowly distributed. There's always the TC39 reflector
for member-only stuff./be
On Aug 1, 2008, at 6:26 PM, Allen Wirfs-Brock wrote: OK, I generally buy this, that arguments is an Object but its
[[prototype]] is Array.prototype. What about the semantics of its
length property and other array specific semantics defined in Array
instances’ custom [[Put]] method ([[ThrowablePut]] in the ES3.1
spec.). I would think--yes, which means we will need to define a
custom [[ThrowablePut]] for argument objects. That is probably
also the appropriate place for defining the mapping to/from actual
parameters semantics.At least one thing in Array.prototype that doesn’t fit if arguments
is an Object and not an Array. That is the definition of
constructor in Array.prototype. We can fix that by explicitly
providing an over-riding constructor property on arguments
instances. Are there any other properties of Array.prototype that
need special consideration?Finally, this brings to mind a subtlety to think about. The
Object meta functions can expose whether or not a property is an
own property or an inherited property. When the speciation says
(as it does in several places) that an instance of a specific
constructor has certain properties are we implying that those
properties must be observably own properties of the instance? From
an implementation flexibility perspective I’d prefer that this was
left unspecified but from an interoperability perspective I suppose
we should pin it down.From: Brendan Eich [mailto:brendan at mozilla.org] Sent: Tuesday, July 29, 2008 7:34 AM To: Pratap Lakshman (VJ#SDK) Cc: crock at yahoo-inc.com; Mark S. Miller; Kris Zyp; Mike Cowlishaw;
Adam Peller; Sam Ruby; Lars Hansen; ggaren at apple.com; Allen Wirfs- Brock; Waldemar Horwat Subject: Re: ES3.1 WG phone conference 29 July 08:00 PTOn Jul 29, 2008, at 7:04 AM, Pratap Lakshman (VJ#SDK) wrote:
(3) arguments. I think we should make arguments a true ES array. We
should correct the specification error that allowed it to be
anything else.This is not possible without extra magic, due to the aliasing of
arguments[0] and x infunction f(x){arguments[0] = 42; return x}
It goes both ways of course:
function f(x){x = 42; return arguments[0]}
You can't require analysis to find the mutation (of either alias),
due to eval. We shouldn't require any kind of analysis in ES3.1 to
handle this aliasing. It has to be done as it has always been done,
by a magic arguments object whose element getters know how to find
the actual parameters, and whose element setters update those
actuals that correspond to declared formal parameters.Note that the last sentence requires the first call to f below to
return undefined, not 42:js> function f(a,b){arguments[1]=42;return b} js> f(1) js> f(1,2) 42
For these reasons, arguments cannot be a true array. Such a change
is also not backward compatible in ways I recall Lars found out
about when he was at Opera.The only harmonized solution is to make arguments' prototype be
Array.prototype. Let's please do that, or else leave arguments alone.(4) Edits based on review feedback from Oslo; schedule for
circulating the next draftSorry if I cannot attend this meeting -- I will try if my schedule
permits. I'm still traveling and I haven't had a chance to dig into
chapter 15 of the spec. I hope the above is clear./be
Back to the present:
Thanks.
There are other threads covering arguments, not hard to find with
site:mail.mozilla.org or nabble.com (although both have odd gaps in
message indexing -- I'll talk to mail.mozilla.org admins).
The arguments object could have the same methods as array, like a "subclass" of array, but not have the special [[Put]], and concat() could still work the way it does with today's arguments object (I don't actually prefer the current behavior)
The plan has been to make arguments objects delegate to Array.prototype.
On Sep 9, 2008, at 11:32 AM, Mark S. Miller wrote:
On Tue, Sep 9, 2008 at 9:21 AM, Mark S. Miller <erights at google.com>
wrote:How to restrict 'arguments' in strict functions? anticipation of ES-H-strict -- prohibit co-existence with splat arguments.callee joining? frozen?
If we were making the jump from ES3 directly to ES-Harmony, we would probably prohibit the magic 'arguments' object in ES-Harmony strict functions, since its functionality is better served by optional and rest parameters. However, ES3.1 won't have optional and rest parameters, so 'arguments' will remain as the only way to do var-args functions in ES3.1-strict code. Given that ES-H-strict is upwards compatible from ES3.1-strict, this prevents us from prohibiting 'arguments' in ES-H-strict.
And it might be an undue migration tax, but I agree based only on the
argument you make.
The most we can do (which we should do) is prohibit 'arguments' in strict ES-H functions which use optional and rest parameters. (This is upwards compatible, since these features are new.) However, ES-H-strict must still allow 'arguments' in other code. If strict 'arguments' is to be better behaved and upwards compatible, we must make the critical fixes in ES3.1-strict.
Probably so, but did you post on this topic yet:
(In a separate note, I'll explain why we want the otherwise-unspecified Function.caller, Function.arguments, and arguments.caller to throw for strict functions.)
I snipped your words about callee, but it's used quite a bit in Ajax
libraries, e.g, Prototype:
$ grep -w callee prottype.js replacement = arguments.callee.prepareReplacement(replacement); var self = arguments.callee; parts.add = arguments.callee.addPair;
(Prototype 1.5.1.1.)
In ES4 we proposed new syntax (that violate the holy Tennent) to get
callee: |this function|. ES4 proposed to deprecate arguments by
specifying only rest and optional parameters normatively, leaving
arguments (which has never been fully specified to be web-compatible)
to an informative annex.
With 3.1 and Harmony we may have to keep arguments "more normative"
but reform it in strict mode till it's vestigial, while adding rest
and optional params. Sounds like a plan. But arguments.callee use
cases deserve a look. They are out there, in great number.
The TIBET guys would want a replacement for arguments.caller, but I
would not miss it. But it's not in ES3 at all, right? Ban all such
caller variations in strict mode, I say!
Strict 'arguments' should not be joined to its function's parameters. In order to get better diagnostics for migrating old code to strict, should 'arguments' also be frozen?
That would catch assignments to arguments[i] for i < actual argument
count, but it would not catch expectations that x = y in function f
(x) {...; return arguments[0]} would return y.
Should strict 'arguments' be an array? I know we've over this before and that strong reasons were presented why we couldn't do this. But on the ES3.1 phone call this morning no one could remember or regenerate those reasons. Anyone? Pointers to previous threads are a fine answer. Thanks.
I followed up to no reply -- did my post help?
On Thu, Sep 11, 2008 at 2:34 PM, Brendan Eich <brendan at mozilla.org> wrote:
In ES4 we proposed new syntax (that violate the holy Tennent) to get callee: |this function|. ES4 proposed to deprecate arguments by specifying only rest and optional parameters normatively, leaving arguments (which has never been fully specified to be web-compatible) to an informative annex.
With 3.1 and Harmony we may have to keep arguments "more normative" but reform it in strict mode till it's vestigial, while adding rest and optional params. Sounds like a plan. But arguments.callee use cases deserve a look. They are out there, in great number.
I imagine the appeal of arguments.callee comes from the fact that it allows programmers to rename functions without having to change recursive calls within the body of the function. Leaving aside the matter of how onerous this renaming actually is, the ability to have an outer, public name, as well as an inner, private one already exists in ES3:
var foo = function bar(x) { ... bar(y) ... }
... works fine, so long as bar isn't shadowed in the function body. The programmer can change the public name ("foo") to anything else without worrying about breaking recursive call sites inside the function. (In this case, it happens to be considerably terser than arguments.callee, too.)
Recursion isn't the only possibility here. Higher-order use of the callee -- i.e., returning it, passing it to another function -- is also possible and is handled correctly by the idiom above.
Are there uses of arguments.callee that can't be handled this way?
On Sep 11, 2008, at 12:11 PM, Jon Zeppieri wrote:
var foo = function bar(x) { ... bar(y) ... }
... works fine, so long as bar isn't shadowed in the function body. The programmer can change the public name ("foo") to anything else without worrying about breaking recursive call sites inside the function. (In this case, it happens to be considerably terser than arguments.callee, too.)
The only drawback is ES3.1's flawed specification of this named
function expression binding using a new Object. But this is being
fixed in any proposed new edition.
Without the fix, named function expressions are noticeably slower to
evaluate in common implementations.
Recursion isn't the only possibility here. Higher-order use of the callee -- i.e., returning it, passing it to another function -- is also possible and is handled correctly by the idiom above.
Sure, that's why it was added (IIRC -- my participation in ES3 was
limited). Even then the carrot of a better new form was used to try
to get people off of arguments.callee (which was in ES1).
We need more carrots, fewer sticks.
Are there uses of arguments.callee that can't be handled this way?
No. The problem is simply that arguments.callee is used all over the
web.
2008/9/11 Jon Zeppieri <jaz at bu.edu>:
I imagine the appeal of arguments.callee comes from the fact that it allows programmers to rename functions without having to change recursive calls within the body of the function. Leaving aside the matter of how onerous this renaming actually is, the ability to have an outer, public name, as well as an inner, private one already exists in ES3:
var foo = function bar(x) { ... bar(y) ... }
... works fine, so long as bar isn't shadowed in the function body.
Does not quite work on the current web - JScript binds the "bar" name in the containing scope, not the contained scope as ES3 specs it. That means the inner name isn't really usable for recursion unless the script author actually makes sure it stays unmodified in the containing scope.
Are there uses of arguments.callee that can't be handled this way?
Except for the JScript bug, none that I can think of straight off the top of my head.
On Sep 11, 2008, at 12:56 PM, liorean wrote:
Does not quite work on the current web - JScript binds the "bar" name in the containing scope, not the contained scope as ES3 specs it. That means the inner name isn't really usable for recursion unless the script author actually makes sure it stays unmodified in the containing scope.
Is this bug (which I should have remembered, since I called it out on
LtU recently) fixed in IE8?
Not that it helps the IE7 and even 6 cohorts out there.
On Sep 11, 2008, at 12:56 PM, liorean wrote:
Does not quite work on the current web - JScript binds the "bar" name in the containing scope, not the contained scope as ES3 specs it. That means the inner name isn't really usable for recursion unless the script author actually makes sure it stays unmodified in the containing scope.
2008/9/11 Brendan Eich <brendan at mozilla.org>:
Is this bug (which I should have remembered, since I called it out on LtU recently) fixed in IE8?
var
f=function fn(){return fn;},
fn=null;
f(); // => null
Seems to not have been fixed in ie8b2 / JScript5.8.
Not that it helps the IE7 and even 6 cohorts out there.
And we're likely to have to deal with those for a long time still...
On Sep 11, 2008, at 1:09 PM, liorean wrote:
On Sep 11, 2008, at 12:56 PM, liorean wrote:
Does not quite work on the current web - JScript binds the "bar"
name in the containing scope, not the contained scope as ES3 specs it.
That means the inner name isn't really usable for recursion unless the script author actually makes sure it stays unmodified in the containing scope.2008/9/11 Brendan Eich <brendan at mozilla.org>:
Is this bug (which I should have remembered, since I called it out
on LtU recently) fixed in IE8?var f=function fn(){return fn;}, fn=null; f(); // => null
Seems to not have been fixed in ie8b2 / JScript5.8.
Heavy sigh.
This is why arguments.callee is used all over the web. Mark, suggest
you keep it in strict mode if you want strict mode to see use with
Prototype, etc.
Not that it helps the IE7 and even 6 cohorts out there.
And we're likely to have to deal with those for a long time still...
I'm sure, although other browsers promote and push new versions
better. It's the wave of the future (ignoring debian-stable and the
likes).
On Thu, Sep 11, 2008 at 12:11 PM, Jon Zeppieri <jaz at bu.edu> wrote:
[snip]
I imagine the appeal of arguments.callee comes from the fact that it allows programmers to rename functions without having to change recursive calls within the body of the function.
It makes recursive calls inside anonymous functions possible.
(I haven't been following this thread closely so someone may have mentioned that.)
[snip]
Peter
On Thu, Sep 11, 2008 at 5:59 PM, Peter Michaux <petermichaux at gmail.com> wrote:
On Thu, Sep 11, 2008 at 12:11 PM, Jon Zeppieri <jaz at bu.edu> wrote:
[snip]
I imagine the appeal of arguments.callee comes from the fact that it allows programmers to rename functions without having to change recursive calls within the body of the function.
It makes recursive calls inside anonymous functions possible.
No. See "fixpoint combinator."
But that's a somewhat flippant response. More to the point: if you want recursion, one way or another, you need to be able to refer to the function in question. 'arguments.callee' allows you to refer to it but violates the Tennent Correspondence. So, given that you can either refer to the function this way:
var foo = bar() { ... bar ...}
or this way:
function bar() { ... arguments.callee ... }
... it's not clear to me what advantage the latter has over the former (IE bugs aside, that is). Anonymity itself doesn't seem to be a virtue.
2008/9/12 Jon Zeppieri <jaz at bu.edu>:
But that's a somewhat flippant response. More to the point: if you want recursion, one way or another, you need to be able to refer to the function in question. 'arguments.callee' allows you to refer to it but violates the Tennent Correspondence. So, given that you can either refer to the function this way:
var foo = bar() { ... bar ...}
or this way:
function bar() { ... arguments.callee ... }
... it's not clear to me what advantage the latter has over the former (IE bugs aside, that is). Anonymity itself doesn't seem to be a virtue.
Well, having had a few moments to think about it, I actually think I can give you some examples of when arguments.callee works but not the function name:
- Functions created by function declarations do not provide a separate inner name, and as such are subject to potential name rebinding, especially with the shared global object between separate scripts.
- Functions created using "new Function(...)" do not have an inner name. (Well, some implementations use the name "anonymous", some use the name "anonymous" and assigns it as a local variable so that it actually can be used this way and some don't name the function at all.)
- Functions created using the string argument form of the setTimeout and setInterval APIs.
- Functions created using HTML event handler attributes.
On Thu, Sep 11, 2008 at 3:40 PM, Jon Zeppieri <jaz at bu.edu> wrote:
On Thu, Sep 11, 2008 at 5:59 PM, Peter Michaux <petermichaux at gmail.com> wrote:
On Thu, Sep 11, 2008 at 12:11 PM, Jon Zeppieri <jaz at bu.edu> wrote:
[snip]
I imagine the appeal of arguments.callee comes from the fact that it allows programmers to rename functions without having to change recursive calls within the body of the function.
It makes recursive calls inside anonymous functions possible.
No. See "fixpoint combinator."
I was thinking about that in particular and thinking that arguments.callee makes a complex concept like fixpoint combinator something that is very easy to understand and use.
But that's a somewhat flippant response. More to the point: if you want recursion, one way or another, you need to be able to refer to the function in question. 'arguments.callee' allows you to refer to it but violates the Tennent Correspondence.
Isn't Tennent Correspondence completely shot anyway in ECMAScript?
So, given that you can either refer to the function this way:
var foo = bar() { ... bar ...}
or this way:
function bar() { ... arguments.callee ... }
... it's not clear to me what advantage the latter has over the former (IE bugs aside, that is). Anonymity itself doesn't seem to be a virtue.
There is a large advantage in keeping code bulk down. There have been times that I've marveled at the utility of arguments.callee. Sometimes the variable that refers to a function value will be reassigned, arguments.callee just keeps working but your first example above breaks.
Peter
On Thu, Sep 11, 2008 at 7:07 PM, liorean <liorean at gmail.com> wrote:
Well, having had a few moments to think about it, I actually think I can give you some examples of when arguments.callee works but not the function name:
- Functions created by function declarations do not provide a separate inner name, and as such are subject to potential name rebinding, especially with the shared global object between separate scripts.
I think you misunderstood my argument. My original example was to rewrite:
function foo() { ... arguments.callee ... } as var foo = function bar() { ... bar ... }
The former is a function declaration, and the latter isn't. So, obviously, I was never claiming that you could rip arguments.callee out of code without changing from one to the other.
On Thu, Sep 11, 2008 at 4:09 PM, Peter Michaux <petermichaux at gmail.com> wrote:
Isn't Tennent Correspondence completely shot anyway in ECMAScript?
Since ES is not an expression language, we need to examine TC separately for expressions and statements. Given that ES3.1-strict does no implicit coercion of 'this', then the only remaining violation of TC for expressions in ES3.1-strict is arguments, which we'll be deprecating once we have spread.
In the argument-less subset of ES3.1-strict for any expression "X", "X" is equivalent to "(function(){ return X; }).call(this)". Given the compact function-expression syntax expected post ES3.1, this will be equivalent to the prettier "(function()X).call(this)". Obviously, if X does not itself mention "this" freely, it is also equivalent to "(function()X)()".
As for statements, I plan to reread the thread with Lex Spoon. I'm tentatively hopeful we can improve things.
On Thu, Sep 11, 2008 at 7:09 PM, Peter Michaux <petermichaux at gmail.com> wrote:
On Thu, Sep 11, 2008 at 3:40 PM, Jon Zeppieri <jaz at bu.edu> wrote:
On Thu, Sep 11, 2008 at 5:59 PM, Peter Michaux <petermichaux at gmail.com> wrote:
On Thu, Sep 11, 2008 at 12:11 PM, Jon Zeppieri <jaz at bu.edu> wrote:
[snip]
I imagine the appeal of arguments.callee comes from the fact that it allows programmers to rename functions without having to change recursive calls within the body of the function.
It makes recursive calls inside anonymous functions possible.
No. See "fixpoint combinator."
I was thinking about that in particular and thinking that arguments.callee makes a complex concept like fixpoint combinator something that is very easy to understand and use.
I don't see how. At best you could say that arguments.callee obviates the need to understand or use a fixpoint combinator. But the same can be said of plain old variable definitions in ES.
But that's a somewhat flippant response. More to the point: if you want recursion, one way or another, you need to be able to refer to the function in question. 'arguments.callee' allows you to refer to it but violates the Tennent Correspondence.
Isn't Tennent Correspondence completely shot anyway in ECMAScript?
[MarkM already responded to this.]
So, given that you can either refer to the function this way:
var foo = bar() { ... bar ...}
or this way:
function bar() { ... arguments.callee ... }
... it's not clear to me what advantage the latter has over the former (IE bugs aside, that is). Anonymity itself doesn't seem to be a virtue.
There is a large advantage in keeping code bulk down. There have been times that I've marveled at the utility of arguments.callee. Sometimes the variable that refers to a function value will be reassigned, arguments.callee just keeps working but your first example above breaks.
My example above is incorrect (sorry). It should be:
var foo = function bar() { ... bar() ... } vs. function foo() { ... arguments.callee ... }
In ES3 (though, apparently not in JScript 5.8, as liorean pointed out), bar is scoped to the function expression, not to the enclosing scope. If you do the following:
var foo = function bar() { ... bar() ... } bar = 5;
... the assignment to bar does not break the function.
Oops, I meant this for the whole list:
Brendan -
We long ago gave up on having arguments.caller (queue sound effect for
"wailing and gnashing of teeth"). As I remember, there was a lack of
desire to keep it in Mozilla after security problems around calling
web content from chrome JS. Of course, we always thought this fix was
a bit extreme and could've been handled in another way (i.e. not allow
web content to access 'caller' if it was calling into chrome - throw a
security exception or something). Anyway, IE's JScript kept this
property (and you implemented arguments.callee.caller, which you can
explain better here than I can ;-), and was sufficient for our
debugging needs - although it crippled us a bit as to what we really
wanted to accomplish - but that's life :-).
However, having '.callee' support, or equivalent, is absolutely
crucial moving forward. We don't tend to use it as originally intended
(although its really nice to be able to have a reference to an
anonymous function without an outside reference). We do things like:
foo = function () { bar(arguments); }
bar = function (theArgs) { alert('You came from: ' + theArgs.callee.toString()); }
Removing 'callee' with no substitute that can supply similar
functionality would be very crippling indeed.
On Sep 11, 2008, at 14:34 , Brendan Eich wrote:
I snipped your words about callee, but it's used quite a bit in Ajax libraries, e.g, Prototype:
$ grep -w callee prottype.js replacement = arguments.callee.prepareReplacement(replacement); var self = arguments.callee; parts.add = arguments.callee.addPair;
(Prototype 1.5.1.1.)
In Prototype, arguments.callee is used in two distinct cases:
- reading a property of the callee, and
- recursively calling the callee.
Case #1 is easily solved by storing the said property in a closure,
or by setting it elsewhere (where it probably would be a better fit,
anyway).
Case #2 is easily solved by using a named function instead. For the
particular case where it is used in Prototype, the anonymous function
lies inside of a closure, so there is no chance of triggering the
JScript scope bug nor of polluting the global namespace when naming it.
From what I understand, ES 3.1 strict mode compliance will be opt-in
and will require at least slight source code modifications (if only
to prepend it with the 'use strict'; expression). So current versions
of Prototype (or other libs, for that matter), will not be ES 3.1
'strict' compliant by default.
I'm not really worried about modifying Prototype to avoid use of the
arguments.callee property. As a matter of fact, we've already done so
in a development branch for Caja compliance.
Note that jQuery does not use arguments.callee at all.
Best,
Tobie
Great -- good to have library authors / maintainers on this list.
I hold no brief for callee. The only issue in its favor is the cost of
migrating to strict mode. A new version of Prototype that loses
internal arguments.callee uses and is otherwise compatible helps.
Dojo and other Ajax folks on the list, please pipe up.
/ be
Sent from my iPhone
I found it convenient when writing MochiKit for the same reasons that it's in Prototype but it would be rather simple to change things around to use named references to functions or whatever else was appropriate. It occurs 28 times in trunk.
Inside Google we have a few occurrences of arguments.callee. Some of these are from Prototype, MochiKit, jQuery and other third party code.
I agree with Tobie regarding the 2 use cases but there is one more related use case and that is arguments.callee.caller and I know a lot of developers here rely on and that to be able to generate a stack trace. This is crucial for fixing bugs in production systems. SpiderMonkey has the Error.prototype.stack property and if that was supported everywhere I don't think there would be any serious complaints about removing arguments.callee and arguments.callee.caller.
On Fri, Sep 12, 2008 at 9:05 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:
Inside Google we have a few occurrences of arguments.callee. Some of these are from Prototype, MochiKit, jQuery and other third party code.
I agree with Tobie regarding the 2 use cases but there is one more related use case and that is arguments.callee.caller and I know a lot of developers here rely on and that to be able to generate a stack trace. This is crucial for fixing bugs in production systems. SpiderMonkey has the Error.prototype.stack property and if that was supported everywhere I don't think there would be any serious complaints about removing arguments.callee and arguments.callee.caller.
+1 for error stack.
(it's actually an instance property generated dynamically, 'stack'in Error.prototype)
Garrett
We use it for debugging/backtrace. If it could be had in non-strict
or, as mentioned elsewhere in this thread, by constructing an error
object.
(Actually, it looks like our current debug compile inserts it's own
annotation to maintain a backtrace. So apparently we don't have to
have arguments.callee.)
(Actually, it looks like our current debug compile inserts it's own annotation to maintain a backtrace. So apparently we don't have to have arguments.callee.)
We serve optimized, obfuscated code to our users and if we get a runtime error we post the stacktrace to the server. This is crucial for ensuring high quality applications. Serving debug code is not really an option. I agree that for debug mode you don't need to be able to get the caller but if you are debugging you are usually better of using a debugger anyway.
On Sat, Sep 13, 2008 at 12:05 AM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:
Inside Google we have a few occurrences of arguments.callee. Some of these are from Prototype, MochiKit, jQuery and other third party code.
I agree with Tobie regarding the 2 use cases but there is one more related use case and that is arguments.callee.caller and I know a lot of developers here rely on and that to be able to generate a stack trace. This is crucial for fixing bugs in production systems. SpiderMonkey has the Error.prototype.stack property and if that was supported everywhere I don't think there would be any serious complaints about removing arguments.callee and arguments.callee.caller.
A standard way to get a stack trace would be very useful. A callee.caller-based stack trace will break under recursion, right? So it's not the right thing for traces anyway.
2008/9/14 Jon Zeppieri <jaz at bu.edu>:
A standard way to get a stack trace would be very useful. A callee.caller-based stack trace will break under recursion, right? So it's not the right thing for traces anyway.
Indeed, a better way to get a stack trace would be to have some way of serialising the current stack of activation frames. There's strong arguments against giving actual programmatical access to the activation frame stack, however.
El 13/09/2008, a las 2:08, Brendan Eich escribió:
I hold no brief for callee.
Where can I learn what's wrong with arguments.callee, what's the
reason why you all want to get rid of it... ?
TIA
El 13/09/2008, a las 2:08, Brendan Eich escribió:
I hold no brief for callee.
2008/9/14 Jorge Chamorro <jorge at jorgechamorro.com>:
Where can I learn what's wrong with arguments.callee, what's the reason why you all want to get rid of it... ? TIA
It's not callee that that everyone wants to get rid of. It's the arguments object itself.
On Sun, Sep 14, 2008 at 11:09 AM, liorean <liorean at gmail.com> wrote:
El 13/09/2008, a las 2:08, Brendan Eich escribió:
I hold no brief for callee.
2008/9/14 Jorge Chamorro <jorge at jorgechamorro.com>:
Where can I learn what's wrong with arguments.callee, what's the reason why you all want to get rid of it... ? TIA
The arguments object itself is often passed in order for function F to give function G access to the argument list F with which was called. This seemingly innocent operation should not also inadvertently provide G with the ability to call F. F might otherwise be encapsulated within some larger abstraction.
It's not callee that that everyone wants to get rid of. It's the arguments object itself.
Yes and no. Come Harmony strict, we will at least deprecate 'arguments'. We would certainly like to get rid of 'arguments' in strict mode. However, we're stuck by the following constraints:
- In the ES3.1 timeframe, we won't yet have optional and rest parameters (spread, etc...), so we can't prohibit arguments in ES3.1-strict.
- Correct ES3.1-strict programs should remain correct ES-H-strict programs.
- While we have left open the option of introducing a distinct ES-H-stricter, in order to enforce stronger constraints, we should desperately avoid exercising this option without compelling reason, as every mode switch and pragma contributes to a combinatorial explosion of cases and a testing nightmare. Killing 'arguments' by certainly not reason enough by itself to introduce such a distinction.
Since we can't get rid of 'arguments' come ES-H-strict, we should do what we can to clean it up in relatively painless ways for ES3.1-strict. From the reactions on es-discuss so far, it seems like killing arguments.callee in ES3.1-strict is painless enough. I think we'll proceed on that basis.
After ES3.1, I think I like the idea of providing stack trace information in a standardized way on Error objects. Anyone care to make a proposal?
El 14/09/2008, a las 21:30, Mark S. Miller escribió:
The arguments object itself is often passed in order for function F to give function G access to the argument list F with which was called. This seemingly innocent operation should not also inadvertently provide G with the ability to call F. F might otherwise be encapsulated within some larger abstraction.
Hmm, the problem is 'callee' being a property of 'arguments' ? or the functionality that 'callee' provides ?
IOW, could we have instead a ('standalone') 'callee' property (that
isn't to be innocently passed on) ?
TIA
On Mon, Sep 15, 2008 at 4:18 PM, Jorge Chamorro <jorge at jorgechamorro.com> wrote:
El 14/09/2008, a las 21:30, Mark S. Miller escribió:
The arguments object itself is often passed in order for function F to give function G access to the argument list F with which was called. This seemingly innocent operation should not also inadvertently provide G with the ability to call F. F might otherwise be encapsulated within some larger abstraction.
Hmm, the problem is 'callee' being a property of 'arguments' ? or the functionality that 'callee' provides ?
IOW, could we have instead a ('standalone') 'callee' property (that isn't to be innocently passed on) ?
...as a property of what?
Any magically bound variable breaks TC. The expression
callee
should have the same meaning as
(function() { return callee; })()
...and it clearly does not.
On Mon, Sep 15, 2008 at 4:32 PM, Jon Zeppieri <jaz at bu.edu> wrote:
Any magically bound variable breaks TC. The expression
callee
should have the same meaning as
(function() { return callee; })()
...and it clearly does not.
True, but it doesn't work for "arguments" or "arguments.callee" either, so I'm not sure it's much of a loss to do something like that here.
The important thing here seems to be decoupling "called function capture" from "arguments to this function", since people are meaning to do the latter and inadvertently doing the former.
Mike
On Mon, Sep 15, 2008 at 1:40 PM, Mike Shaver <mike.shaver at gmail.com> wrote:
The important thing here seems to be decoupling "called function capture" from "arguments to this function", since people are meaning to do the latter and inadvertently doing the former.
That's a good characterization. Fortunately, in any ES3 compliant implementation of ES3, nothing more is needed to provide the former. Just name your function. By itself, this is as convenient -- as well as more understandable -- as any of the "conveniences" that have been proposed to help with this non-problem.
El 15/09/2008, a las 22:32, Jon Zeppieri escribió:
IOW, could we have instead a ('standalone') 'callee' property (that isn't to be innocently passed on) ?
...as a property of what?
The activation object of the execution context.
On Mon, Sep 15, 2008 at 5:13 PM, Jorge Chamorro <jorge at jorgechamorro.com> wrote:
El 15/09/2008, a las 22:32, Jon Zeppieri escribió:
IOW, could we have instead a ('standalone') 'callee' property (that isn't to be innocently passed on) ?
...as a property of what?
The activation object of the execution context.
Don't even get me started on activation 'objects'...
Mark, didn't you propose eliminating that language from the spec? (Yup. esdiscuss/2008-April/006062) I'm guessing that's not actually happening for ES3.1.
On Sep 15, 2008, at 10:13 PM, Jorge Chamorro wrote:
El 15/09/2008, a las 22:32, Jon Zeppieri escribió:
IOW, could we have instead a ('standalone') 'callee' property (that isn't to be innocently passed on) ?
...as a property of what?
The activation object of the execution context.
I've been there, done that. Long ago, when crazy Smalltalk-ish
reflection of execution state was in vogue, SpiderMonkey grew magic
reflective properties (ugly names, such as caller). The compiler
would make functions enclosing such things always have an activation
object (real implementations must optimize away activation objects
for performance -- there's no other way).
Besides violating Tennent's Correspondence Principle, this was an
evil mess to implement correctly (consider all the optimizations that
had to be undone). It's not worth it, especially if you want just
callee (use a named function expression, or bind an outer lexical
name) or caller (stack inspection should be served by a carefully
designed API on the side -- "mirror" reflection, not an API on
activation objects).
I agree with Mark about callee. Just say no, if we can wean folks off
of it.
Stack backtracing deserves a separate thread.
On Sep 15, 2008, at 10:21 PM, Jon Zeppieri wrote:
Don't even get me started on activation 'objects'...
Nor me.
Mark, didn't you propose eliminating that language from the spec? (Yup. esdiscuss/2008-April 006062.html) I'm guessing that's not actually happening for ES3.1.
May be too big a change for 3.1, on the agenda for Harmony.
As Dave Herman has argued, mapping scopes onto objects is a primal
sin in the ES specs. Recasting where possible via lexical bindings,
putting the global object, with, and eval into penalty boxes, both in
real terms for programmers (e.g. "use lexical scope"), and in the
spec by making the bad cases entail exceptional work, seems more than
worth the trouble.
It's "easy" to keep hacking around with objects, maybe trying to
freeze some bindings, but doing so is a temporary evil at best, or
foolish child's-play at worst (if done out of some misguided uber-
minimalism, to avoid reforming the spec language to use lexical scope
normatively).
Guy Steele was editor during much of the ES1 period, and he helped
greatly, but compatibility concerns and time-to-"market" prevented us
from taking his advice on this point. I wish we had, though.
El 15/09/2008, a las 23:23, Brendan Eich escribió:
On Sep 15, 2008, at 10:13 PM, Jorge Chamorro wrote:
El 15/09/2008, a las 22:32, Jon Zeppieri escribió:
IOW, could we have instead a ('standalone') 'callee' property (that isn't to be innocently passed on) ?
...as a property of what?
The activation object of the execution context.
I've been there, done that. Long ago, when crazy Smalltalk-ish
reflection of execution state was in vogue, SpiderMonkey grew magic
reflective properties (ugly names, such as caller). The compiler
would make functions enclosing such things always have an activation
object (real implementations must optimize away activation objects
for performance -- there's no other way).Besides violating Tennent's Correspondence Principle, this was an
evil mess to implement correctly (consider all the optimizations
that had to be undone). It's not worth it, especially if you want
just callee (use a named function expression, or bind an outer
lexical name) or caller (stack inspection should be served by a
carefully designed API on the side -- "mirror" reflection, not an
API on activation objects).I agree with Mark about callee. Just say no, if we can wean folks
off of it.Stack backtracing deserves a separate thread.
/be
Ok ok, never mind. After all if I don't want to name an unnamed
(recursive) f() I can still write something like :
(foo= function () { this.call(this) }).call(foo);
:-)
but (function () { callee() })() looked much better...
Thanks,
Brendan -
On Sep 15, 2008, at 3:23 PM, Brendan Eich wrote:
I agree with Mark about callee. Just say no, if we can wean folks off of it.
Easier said than done. Not impossible (well, nothing's impossible),
but its very problematic.
There's a number of use cases given earlier on this thread. Here's
another one:
myElem.addEventListener('click', function () { doSomething();
this.removeEventListener('click', arguments.callee, false)}, false);
Could I do an assignment of the handler? Sure, but syntactically
suboptimal, IMHO.
Having said that, I was under the impression that ES-3.1 ('strict' or
not) changes were going to be purely additive and that we were going
to be waiting until ES-Harmony to start the more wrenching changes to
the language (modulo out-and-out bugs in the ES3 spec from '99 - I
would expect those to be 'fixed' in 3.1). Am I wrong here?
Stack backtracing deserves a separate thread.
Agreed - although I would like to term this 'stack access', denoting
more than just 'backtracing' capability :-). Also, this information
should be available in any context, not just in an exception handler.
Of course, I realize that I'm on the other end of the spectrum from
the minimalists here :-).
On Sep 15, 2008, at 3:23 PM, Brendan Eich wrote:
I agree with Mark about callee. Just say no, if we can wean folks off of it.
2008/9/16 William Edney <bedney at technicalpursuit.com>:
Easier said than done. Not impossible (well, nothing's impossible), but its very problematic.
There's a number of use cases given earlier on this thread. Here's another one:
myElem.addEventListener('click', function () { doSomething(); this.removeEventListener('click', arguments.callee, false)}, false);
Could I do an assignment of the handler? Sure, but syntactically suboptimal, IMHO.
Once JScript has fixed the function name scope bug, you could use
myElem.addEventListener(
'click',
function f(){
doSomething();
this.removeEventListener(
'click',
f,
false);},
false);
Stack backtracing deserves a separate thread. Agreed - although I would like to term this 'stack access', denoting more than just 'backtracing' capability :-). Also, this information should be available in any context, not just in an exception handler. Of course, I realize that I'm on the other end of the spectrum from the minimalists here :-).
There's security concerns here. You don't want to make locals accessible from scopes that are not lexically enclosed, not even if those locals are arguments to the function. You don't want to make inner functions visible or ToString-able just because they are on the call stack. You don't want to break the scope privacy, in other words.
I'm inclined to believe that the best you can do with regard to the call stack would be to give each function a unique one-way identification system and function calls a unique identifier as well (so recursive calls would have the same function IDs but different call IDs). Allow an API for the entire call stack to be exposed in terms of a list of pairs of these two IDs. Then you can compare the function ID to the ID of any lexically known function (or provide an API for looking up a function object in only the current scope chain by function ID), The call ID shouldn't provide any lookup facility since we don't want to make activation frames first class objects. But I can see that an API for traversing the call stack based on these two IDs together should be useful. The reason for the function ID instead function object should be clear - some of those functions may not be lexically visible in the current scope, so shouldn't be exposed. Calling functions from outside your own closest enclosing scope should not be an escape mechanism for inner function objects.
That's not a strongly held opinion, however - reflection is bloody useful in some situations, particularly for debugging.
On Sep 16, 2008, at 12:21 AM, liorean wrote:
2008/9/16 William Edney <bedney at technicalpursuit.com>:
Could I do an assignment of the handler? Sure, but syntactically suboptimal, IMHO.
Once JScript has fixed the function name scope bug, you could use
myElem.addEventListener( 'click', function f(){ doSomething(); this.removeEventListener( 'click', f, false);}, false);
Or to work around the JScript bug:
(function () {
function f(){
doSomething();
this.removeEventListener('click', f, false);
}
myElem.addEventListener('click', f, false);
})();
Lots of workarounds, none free, but still possibly tolerable or even
not measurable in performance and memory costs.
In case anyone missed it,
Ignore the type parameter and annotations, to see the essential
proposal.
On Sun, Sep 14, 2008 at 12:30 PM, Mark S. Miller <erights at google.com> wrote:
On Sun, Sep 14, 2008 at 11:09 AM, liorean <liorean at gmail.com> wrote:
El 13/09/2008, a las 2:08, Brendan Eich escribió:
I hold no brief for callee.
2008/9/14 Jorge Chamorro <jorge at jorgechamorro.com>:
Where can I learn what's wrong with arguments.callee, what's the reason why you all want to get rid of it... ? TIA
The arguments object itself is often passed in order for function F to give function G access to the argument list F with which was called. This seemingly innocent operation should not also inadvertently provide G with the ability to call F. F might otherwise be encapsulated within some larger abstraction.
I don't see why that is a problem. A defensive copy is enough, no? [].slice.call(arguments);
After ES3.1, I think I like the idea of providing stack trace information in a standardized way on Error objects. Anyone care to make a proposal?
Mozilla has a string value for error.stack. This value is only added to an actual Error instance, not any other object. The stack property is added when it is thrown.
Firefox: throwIt(o): "no stack" throwIt(new Error): "stack"
var o = {name: "Custom", message : "My message."}; function throwIt(it) { try { throw it; } catch(ex) { return ex.stack ? "stack" : "no stack"; } }
Instead, it would be more useful for all thrown objects to get a stack property, unless the object already had one.
Test runners should provide a stack to make finding bugs easier. Therefore, a custom error should also get a stack property, unless it already has one (a test framework might be saving a stack, then rethrowing).
It might also be useful to have the Error constructor accept an Error (for rethrowing).
Error (error) If the argument error is an object, the newly constructed object has a message property of the Error's message, and a stack property of the error's stack (if it is not undefined).
If an error is rethrown, it won't get a new stack property.
Thoughts?
Garrett
On Wed, Sep 17, 2008 at 3:07 AM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:
Instead, it would be more useful for all thrown objects to get a stack property, unless the object already had one.
This has been proposed before in Mozilla, but I and others objected because we don't believe that throwing an object should mutate it, and because it would cause primitives to be boxed by the property set (or have more just-so ad hoc logic that is hard to reason about).
I would be more amenable to have {Syntax,}Error.throwObject(obj) provide an Error with stack, and the provided obj as .data or some such.
Mike
On Wed, Sep 17, 2008 at 7:37 AM, Mike Shaver <mike.shaver at gmail.com> wrote:
On Wed, Sep 17, 2008 at 3:07 AM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:
Instead, it would be more useful for all thrown objects to get a stack property, unless the object already had one.
This has been proposed before in Mozilla, but I and others objected because we don't believe that throwing an object should mutate it, and because it would cause primitives to be boxed by the property set (or have more just-so ad hoc logic that is hard to reason about).
While I appreciate the stack property, I disagree on that detail.
If a native object is thrown, and that object is not an Error, then the either the developer threw it using throw or a Host object threw it. Right?
If a thrown native object did not already have a stack, what is the harm in adding one?
Now, if the thrown object already had a stack property then it might be best to just leave that alone and not replace the value. A thrown Host object should have a stack property, but doesn't have to (a DOMException object, for example). calling [CanPut], prior to adding a stack would allow implementations to have the option to add a helpful stack property.
throw Error("msg");
- resulting in a stack, or the hypothetically more efficient:-
throw "msg"
Throwing a Host object:
function makeNode(tag) { try { document.createElement(tag); } catch(ex) { alert(ex.stack); } }
// Should raise DOMException. makeNode(':/');
Thoughts?
I can also write up the pseudo-code algorithm steps for review.
I would be more amenable to have {Syntax,}Error.throwObject(obj) provide an Error with stack, and the provided obj as .data or some such.
A throwError method. Hmm. How would that work? I guess if the method were generic, the properties |type| and |message| would not be changed. Right?
I think |throw| would be easier to spot in source code than |Error.throwObject|. My IDE makes |throw| look blue and bold, and it looks familiar. It would stand out more (at least to me).
Garrett
On Fri, Sep 19, 2008 at 12:26 AM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:
If a thrown native object did not already have a stack, what is the harm in adding one?
What should be done with a sealed object that's thrown?
I'm having trouble reconciling "all thrown objects should have .stack thrust upon them, so that all thrown objects expose the site of the error" with "but it's OK if that .stack property is really something else".
Having |throw "msg"| provide an exception which is not a primitive string is a straight-out break of the language's semantics; I don't think it's feasible at all. (Mozilla code throws naked numbers all over the place, and I suspect would break quite spectacularly if it got an Error object instead.)
Mike
On Fri, Sep 19, 2008 at 6:18 AM, Mike Shaver <mike.shaver at gmail.com> wrote:
On Fri, Sep 19, 2008 at 12:26 AM, Garrett Smith <dhtmlkitchen at gmail.com> wrote:
If a thrown native object did not already have a stack, what is the harm in adding one?
What should be done with a sealed object that's thrown?
In EcmaScript, there is [[CanPut]].
- call [[CanPut]] on THROWN with argument "stack"
- if Result(5) is false, go to step 11. ...
- Return (throw, THROWN, empty).
I'm having trouble reconciling "all thrown objects should have .stack thrust upon them, so that all thrown objects expose the site of the error" with "but it's OK if that .stack property is really something else".
I do not understand what you mean. What is the "something else"? If the stack property exists, it would be a string. What else could it be?
Having |throw "msg"| provide an exception which is not a primitive string is a straight-out break of the language's semantics; I don't think it's feasible at all. (Mozilla code throws naked numbers all over the place, and I suspect would break quite spectacularly if it got an Error object instead.)
I see.
Thank you,
Garrett
On 2008-09-15, at 17:23EDT, Brendan Eich wrote:
Stack backtracing deserves a separate thread.
Indeed. I would not care about arguments.callee if I could have a
backtrace... (You'd be amused by the annotation our compiler inserts
just to be able to show our users a backtrace.)
On 2008-09-19, at 09:18EDT, Mike Shaver wrote:
On Fri, Sep 19, 2008 at 12:26 AM, Garrett Smith <dhtmlkitchen at gmail.com
wrote: If a thrown native object did not already have a stack, what is the harm in adding one?
What should be done with a sealed object that's thrown?
Rather than throwing a whole stack, I'd rather see a mechanism to let
handlers run in the error context. I.e., HANDLER
On Tue, Sep 9, 2008 at 9:21 AM, Mark S. Miller <erights at google.com> wrote:
If we were making the jump from ES3 directly to ES-Harmony, we would probably prohibit the magic 'arguments' object in ES-Harmony strict functions, since its functionality is better served by optional and rest parameters. However, ES3.1 won't have optional and rest parameters, so 'arguments' will remain as the only way to do var-args functions in ES3.1-strict code. Given that ES-H-strict is upwards compatible from ES3.1-strict, this prevents us from prohibiting 'arguments' in ES-H-strict. The most we can do (which we should do) is prohibit 'arguments' in strict ES-H functions which use optional and rest parameters. (This is upwards compatible, since these features are new.) However, ES-H-strict must still allow 'arguments' in other code. If strict 'arguments' is to be better behaved and upwards compatible, we must make the critical fixes in ES3.1-strict.
Currently, the ES3.1 draft spec says that strict 'arguments' has no callee property. Should it instead specify an initial callee property that throws if accessed? This would give better diagnostics from migrating old code to strict. OTOH, making it absent is more consistent with the normal JavaScript feature-testing style. (In a separate note, I'll explain why we want the otherwise-unspecified Function.caller, Function.arguments, and arguments.caller to throw for strict functions.)
Strict 'arguments' should not be joined to its function's parameters. In order to get better diagnostics for migrating old code to strict, should 'arguments' also be frozen?
Should strict 'arguments' be an array? I know we've over this before and that strong reasons were presented why we couldn't do this. But on the ES3.1 phone call this morning no one could remember or regenerate those reasons. Anyone? Pointers to previous threads are a fine answer. Thanks.