On Jan 14, 2009, at 2:54 PM, Mark S. Miller wrote:
So let's imagine that we do nothing to "fix" arguments. How does
magic unfixed arguments play with the rest of the spec? Some
questions:
1) Where is the semantic state that distinguishes magic arguments
from other array-like non-arrays that inherit from Array.prototype?
There needs to be such semantic state, to account for the
acceptability of arguments as the second argument of
Function.prototype.apply, to whit:
(From 15.3.4.3 of 22dec08 draft)
> Otherwise, if argArray is neither an array nor an arguments object
(see 10.3.2),
> a TypeError exception is thrown. If argArray is either an array or
an arguments
> object, [...]
Btw, that reference to 10.3.2 above looks like it should reference
10.5 Arguments Object, which says
> The arguments object is created by calling the abstract operation
> CreateArgumentsObject with arguments [...]
There is no separate definition of CreateArgumentsObject. Rather, it
seems its definition is the remaining text in 10.5. The spec here
for non-strict arguments gives it a [[Class]] of "Object" and a
[[Prototype]] of the original Array.prototype. It also,
mysteriously, gives in a [[Constructor]] of the Object constructor.
But there is no internal [[Constructor]] property!
These seem like easily fixed 3.1 bugs. File 'em?
So, leaving aside the probably mistaken [[Constructor]] property,
.constructor must be what's meant.
js> function f(){return arguments.constructor}
js> f()
function Object() {
[native code]
}
I'm not any more fond of this than you, but it is what it is.
Function.prototype.apply has no semantic state to inspect in order
to carry out its spec.
When in doubt, use brute force. ES3 already did. Quoting from its spec
for Function.prototype.apply:
15.3.4.3 Function.prototype.apply (thisArg, argArray)
The apply method takes two arguments, thisArg and argArray, and
performs a function call using the [[Call]] property of the object. If
the object does not have a [[Call]] property, a TypeError exception is
thrown.
If thisArg is null or undefined, the called function is passed the
global object as the this value. Otherwise, the called function is
passed ToObject(thisArg) as the this value.
If argArray is null or undefined, the called function is passed no
arguments. Otherwise, if argArray is neither an array nor an arguments
object (see section 10.1.8), a TypeError exception is thrown. If
argArray is either an array or an arguments object, the function is
passed the (ToUint32(argArray.length)) arguments argArray[0],
argArray[1], …, argArray[ToUint32(argArray.length)–1].
The length property of the apply method is 2.
2) What happens when you freeze an arguments object?
In the Kona draft (and still in the 22dec08 draft), the joining of
non-strict arguments with the parameter variables is specified by
synthesizing getter/setter functions. At the Kona meeting, Waldemar
(I think) pointed out 1) that the way this was specified had
inadvertent non-hygienic lexical variable capture problems, and 2)
given the new attribute revelation APIs, such explanatory getters
and setters could be reified, requiring us to specify whether
they're frozen, when they're ===, etc.
Yeah, Waldemar and I both pointed out (IIRC I did this first :-P) that
this overspecifies arguments.
Rather than fixing these issues, at Kona we decided instead not to
specify them in terms of getters and setters. But then, what does
Object.getOwnPropertyDescriptor(arguments, '3') return? If it claims
arguments[3] is a writable data property, then one should be able to
freeze it.
Yes.
Once frozen, does this prevent writing the corresponding parameter
variable?
No, not by assigning to the parameter name.
Or does it sever the joining of arguments[3] and that parameter
variable?
Yes.
If instead it is specified with a non-configurable getter/setter
pair, then freezing does not affect it.
But that overspecifies.
3) What happens when you write to an object that inherits from an
arguments object?
No setters (overspecifies), so the same thing as today:
js> function f(){}
js> f.prototype = (function(){return arguments})()
[object Object]
js> o = new f
[object Object]
js> o[0] = 42
42
js> o[0]
42
js> function g(){}
js> g.prototype = (function(){return arguments})(1,2,3)
[object Object]
js> p = new g
[object Object]
js> p[0]
1
js> p[1]
2
js> p[2]
3
js> p[0]=4
4
js> p[0]
4
js> seal(p.__proto__)
js> p[0]=5
5
js> p[0]
5
js> g.prototype[0]=6
typein:19: Error: g.prototype[0] is read-only
(seal is a SpiderMonkey js shell function that calls an API to freeze
the object in 3.1 terms.)
Given
var sub = Object.create(arguments);
sub[3] = foo;
does this assignment modify the parameter variable joined to
arguments[3]?
No, no setter in the prototype that is called instead of shadowed.
Instead, shadowing as happens in ES1-3.
Once again, if we think of arguments[3] as a data property, then the
answer should be no. We've instead created a new own '3' property on
sub and have not affected its parent. But if we think of it as an
accessor property, then this assignment should invoke the inherited
setter.
Accessor overspecifies. Leave it underspecified and avoid malinvesting
in arguments polishing!
Whatever we do with strict arguments, we need to answer all three of
these questions for non-strict arguments anyway.
I tend to think the answers should be the same, although as noted in a
previous message I'm open to the idea of freezing strict arguments.
Until we settle the semantics of non-strict arguments, it's
premature to try to determine whether strict arguments can live with
that semantics.
Hope the above helps,
/be
_______________________________________________
Es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss