ES4 draft: Object
We (Google) find setting the [[DontEnum]] flag with propertyIsEnumerable(name, flag) to be quite ugly. I can't find any references to why the setter was tagged along onto the getter in this way? Is there a reason why we don't want setPropertyIsEnumerable(name, flag) instead of overloading the getter?
2008/3/5 Lars Hansen <lhansen at adobe.com>:
On Mar 5, 2008, at 5:04 PM, Erik Arvidsson wrote:
We (Google) find setting the [[DontEnum]] flag with propertyIsEnumerable(name, flag) to be quite ugly. I can't find any references to why the setter was tagged along onto the getter in this way? Is there a reason why we don't want setPropertyIsEnumerable(name, flag) instead of overloading the getter?
Adding any properties to Object.prototype at this point is difficult.
You may break object-detection tests. Is it worth the risk?
-----Original Message----- From: Erik Arvidsson [mailto:erik.arvidsson at gmail.com] Sent: 5. mars 2008 17:04 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: ES4 draft: Object
We (Google) find setting the [[DontEnum]] flag with propertyIsEnumerable(name, flag) to be quite ugly.
Can't argue with that.
I can't find any references to why the setter was tagged along onto the getter in this way? Is there a reason why we don't want setPropertyIsEnumerable(name, flag) instead of overloading the getter?
It was felt, and I think it is still felt, that it is dangerous to introduce new names on the Object prototype, because the risk of clashes with code on the web is too great. As the problem that is solved by the overloading is really worth solving, the ugliness of the solution seemed to be acceptable.
I'm not sure it is such a big issue since the new method would be [[DontEnum]] but I'm willing to let this rest.
The reason why I don't think it is that a big risk is that a lot of people still use Array instead of Object for "associative arrays" and I don't think adding the array extras introduced any problems. Brendan, did you get any bug reports on Spidermonkey when array extras was added?
On Mar 5, 2008, at 5:28 PM, Erik Arvidsson wrote:
I'm not sure it is such a big issue since the new method would be [[DontEnum]] but I'm willing to let this rest.
Object detection does not care about enumerability.
The reason why I don't think it is that a big risk is that a lot of people still use Array instead of Object for "associative arrays" and I don't think adding the array extras introduced any problems.
Arrays are bad for "associative arrays" for many reasons, but
probably their abusers don't care!
Brendan, did you get any bug reports on Spidermonkey when array extras was added?
No, but consider that in our DOM at least, the global object,
document, etc. etc. all delegate to Object.prototype -- not to
Array.prototype.
argued strongly on IRC today for something that does not
overload set with get -- this seems to be the heart of the "ugly"
objection, although there are other things to dislike about both the
form and function of propertyIsEnumerable (it does not consider
prototypes, as for-in does; its name is overlong and it violates
standard naming conventions for "is" method).
To avoid injecting a public name into Object.prototype, we could put
a new "setPropertyIsEnumerable" (yechh) name in the ES4
namespace. Then there's no breaking change. We do this already, of
course, but not for property attribute fiddling.
An alternative we've discussed, which Ian also brought up: add a
keyword for declaring DontEnum properties. It turns out the full
combination of property attributes is not interesting. We have const
for DontDelete+ReadOnly (ReadOnly without DontDelete guarantees
nothing, is worthless) including in object initialisers:
obj = {const PI: 22/7}; // Ohio legislature once so legislated
We have (I hope it got filed) a ticket asking for DontDelete by
itself via var in object initialisers:
obj = {var x: "mutate me, but you can't delete me!"}
DontEnum is orthogonal but clear by default for binding and assigning
forms in ES3. It is set for "built-ins", generally speaking (some odd
exceptions that I recall in the Error objects -- I was not around
much for ES3, although I did the work in Netscape 4 that fed into it!).
So, rather than add an ugly "dontenum" keyword that would be seldom
used, the group preferred a built-in method. But this may have been a
minor mistake. For those interested, here's the full rationale as I
remember it:
Besides avoiding adding anything with a plain name to
Object.prototype, the appeal (if you can call it that) of extending
propertyIsEnumerable with an optional second argument lies in the
ability to call it with two arguments in an ES3 implementation. That
won't have any effect, but it won't break callin code in ES3
implementations either.
Thus Ajax libraries such as Prototype (if it still does this) could
add calls to turn off enumerability of properties the library sets on
standard prototypes, and then those properties would disappear from
for-in loop enumerations in ES4 implementations.
Mainly we do not expect the ability to turn off enumeration of a
given property to be a frequent programmer task, so we did not want
to add a new method if we could piggy-back on an existing
Object.prototype method. For library authors, we wanted something
that failed soft in ES3. And yeah, we knew it was "ugly", but
propertyIsEnumerable is already ugly already.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Brendan Eich Sent: 5. mars 2008 19:19 To: Erik Arvidsson Cc: es4-discuss at mozilla.org es4-discuss Subject: Re: ES4 draft: Object
To avoid injecting a public name into Object.prototype, we could put a new "setPropertyIsEnumerable" (yechh) name in the ES4 namespace. Then there's no breaking change. We do this already, of course, but not for property attribute fiddling.
Right, it comes up from time to time. Using ES4 here would set a precedent that might not be the best.
An alternative we've discussed, which Ian also brought up: add a keyword for declaring DontEnum properties. It turns out the full combination of property attributes is not interesting. We have const for DontDelete+ReadOnly (ReadOnly without DontDelete guarantees nothing, is worthless) including in object initialisers:
obj = {const PI: 22/7}; // Ohio legislature once so legislated
We have (I hope it got filed) a ticket asking for DontDelete by itself via var in object initialisers:
obj = {var x: "mutate me, but you can't delete me!"}
Not filed in the Trac to my knowledge, but not forgotten.
DontEnum is orthogonal but clear by default for binding and assigning forms in ES3. It is set for "built-ins", generally speaking (some odd exceptions that I recall in the Error objects -- I was not around much for ES3, although I did the work in Netscape 4 that fed into it!).
The text of E262-3 does not reveal anything unusual about the Error objects, as far as I can see.
So, rather than add an ugly "dontenum" keyword that would be seldom used, the group preferred a built-in method. But this may have been a minor mistake. For those interested, here's the full rationale as I remember it:
Besides avoiding adding anything with a plain name to Object.prototype, the appeal (if you can call it that) of extending propertyIsEnumerable with an optional second argument lies in the ability to call it with two arguments in an ES3 implementation. That won't have any effect, but it won't break callin code in ES3 implementations either.
Thus Ajax libraries such as Prototype (if it still does this) could add calls to turn off enumerability of properties the library sets on standard prototypes, and then those properties would disappear from for-in loop enumerations in ES4 implementations.
Mainly we do not expect the ability to turn off enumeration of a given property to be a frequent programmer task, so we did not want to add a new method if we could piggy-back on an existing Object.prototype method. For library authors, we wanted something that failed soft in ES3. And yeah, we knew it was "ugly", but propertyIsEnumerable is already ugly already.
Also, a declarative form is of uncertain value, since the object that receives the new property whose attr must be set, already exists. Thus if there is some sort of new keyword form it needs to be associated with the assignment somehow.
On Wed, Mar 5, 2008 at 7:19 PM, Brendan Eich <brendan at mozilla.org> wrote:
Hixie argued strongly on IRC today for something that does not
So, rather than add an ugly "dontenum" keyword that would be seldom used, the group preferred a built-in method. But this may have been a minor mistake. For those interested, here's the full rationale as I remember it:
obj = {dontenum length: 10} );
vs.
obj = {length: 10}; obj.propertyIsEnumerable("length", false);
Which is more ugly?
Garrett
On Mar 6, 2008, at 5:30 PM, Lars Hansen wrote:
The text of E262-3 does not reveal anything unusual about the Error objects, as far as I can see.
How embarrassing. The original implementation of Error and friends in
SpiderMonkey, but one of the Netscape folks involved with ES3 in TG1
at the time, makes Error.prototype.name and Error.prototype.message
enumerable. Filed as this bug:
On Mar 6, 2008, at 5:57 PM, Garrett Smith wrote:
On Wed, Mar 5, 2008 at 7:19 PM, Brendan Eich <brendan at mozilla.org>
wrote:Hixie argued strongly on IRC today for something that does not
So, rather than add an ugly "dontenum" keyword that would be seldom used, the group preferred a built-in method. But this may have
been a minor mistake. For those interested, here's the full rationale as I remember it:obj = {dontenum length: 10} );
vs.
obj = {length: 10}; obj.propertyIsEnumerable("length", false);
Which is more ugly?
Except the known use-case, setting DontEnum properties on standard
prototypes (as the Prototype Ajax library did and still does AFAIK),
does not use object initialisers.
Even so, "dontenum" as a compound word starting with a contraction
whose apostrophe is elided is no beauty contest winner. But before
arguing over those aesthetics, we need a more compelling use-case.
Remember that fixtures declared in classes are not enumerable, which
is the right default for all the "native" (built-in is better, since
these can be and are increasingly self-hosted) objects' properties.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Brendan Eich Sent: 6. mars 2008 18:04 To: Garrett Smith Cc: es4-discuss at mozilla.org es4-discuss; Erik Arvidsson Subject: Re: ES4 draft: Object
Remember that fixtures declared in classes are not enumerable, which
is the right default for all the "native" (built-in is better, since
these can be and are increasingly self-hosted) objects' properties.
Agreed "native" is misnomer now. I've adopted "predefined" (or "pre-defined" depending on my mood) instead of "built-in" but this is something that will be cleaned up in the editorial process pretty soon.
Garrett Smith wrote:
obj = {dontenum length: 10} ); vs.
obj = {length: 10}; obj.propertyIsEnumerable("length", false);
Which is more ugly?
Garrett
Of course I prefere the first line but these two cases are different. I wonder if a builtin method will not cause more problems instead of avoiding them.
What I mean is what is the expected behaviour in this case?
Object.prototype.propertyIsEnumerable("length", false);
// some stuff ... and then ...
Object.prototype.propertyIsEnumerable("length", true);
I think Object.prototype is the perfect case scenario where this built in method could cause more chaos than ever, while the dontenum in declaration makes sense specially when I write my own object and/or prototoype and I would like that no one will be able to change my not enumerable property. So I guess built in method is error/conflicts/problems prone while the first one is quite safer (and at the same time, I do like to know if others add some Object.prototype, both to avoid conflicts, problems, and errors)
These are only my 5 cents to this interesting discussion.
Kind
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Andrea Giammarchi Sent: 7. mars 2008 00:25 To: Es4-discuss at mozilla.org Subject: Re: ES4 draft: Object
Garrett Smith wrote:
obj = {dontenum length: 10} ); vs.
obj = {length: 10}; obj.propertyIsEnumerable("length", false);
Which is more ugly?
Garrett Of course I prefere the first line but these two cases are different. I wonder if a builtin method will not cause more problems instead of avoiding them.
What I mean is what is the expected behaviour in this case?
Object.prototype.propertyIsEnumerable("length", false); // some stuff ... and then ...
Object.prototype.propertyIsEnumerable("length", true);
Clearly length is meant not to be enumerated between the first call and the second call. We might want to consider pinning down what would happen if an enumeration of an object is going on when the enumerabilitiy of one of its properties is changed. The details of that would likely be compatible with what happens if a property is deleted or added during enumeration, and the meaning of that is already defined in ES3 (unvisited deleted properties are not visited; added properties may or may not be visited).
I think Object.prototype is the perfect case scenario where this built in method could cause more chaos than ever, while the dontenum in declaration makes sense specially when I write my own object and/or prototoype and I would like that no one will be able to change my not enumerable property.
Which is only one of the problems we're trying to solve, though.
So I guess built in method is error/conflicts/problems prone
But it solves a problem that it's important to solve and that is not solved declaratively.
while the first one is quite safer (and at the same time, I do like to know if others add some Object.prototype, both to avoid conflicts, problems, and errors)
These are only my 5 cents to this interesting discussion.
Appreciated.
Brendan Eich wrote:
To avoid injecting a public name into Object.prototype, we could put
a new "setPropertyIsEnumerable" (yechh) name in the ES4
namespace. Then there's no breaking change. We do this already, of
course, but not for property attribute fiddling.
Since enumerability only applies to for-in, how about the iterator namespace? Object.prototype.iterator::setPropertyIsEnumerable(prop, enable). For consistency, you could also have Object.prototype.iterator::isPropertyEnumerable(prop) which delegates to Object.prototype.isPropertyEnumerable(prop).
An alternative we've discussed, which Ian also brought up: add a
keyword for declaring DontEnum properties.
This dontenum modifier can be used in addition to the method for convenience - it would make class declarations cleaner.
But despite that, I think dontenum is inelegant. Its only effect would be on the default enumerator, which is only one iterator, and AFAIK is not special at all. IMO, it isn't worth the bloat. If there was a way to define such modifiers within script, then I think the dontenum modifier would be nice. If such a capability is planned for a future ES edition, then I think dontenum is fine.
-Yuh-Ruey Chen
On Mar 7, 2008, at 1:14 PM, Yuh-Ruey Chen wrote:
Brendan Eich wrote:
To avoid injecting a public name into Object.prototype, we could put a new "setPropertyIsEnumerable" (yechh) name in the ES4 namespace. Then there's no breaking change. We do this already, of course, but not for property attribute fiddling.
Since enumerability only applies to for-in, how about the iterator namespace?
Good suggestion.
Here's something I wrote a while ago (minor edits to update for
context) that attempts to subsume enumerability under namespacing:
If we say enumeration considers only public non-DontEnum properties,
then DontEnum is always true for namespaced props. Mixing DontEnum
magic into the
obj = {var dontDel: 42, const RO_AND_DD: "foo"}
idea is wrong -- these should be public and enumerable as other
properties in object initialisers are, by default. But we are this
close to eliminating an independent DontEnum attribute. The problem
is how to express public + DontEnum.
Suppose we had a predefined, open namespace, say dontenum (surely
not the best name). If you write
obj = {dontenum::cant_see_me: 33}
then
for (i in obj) print(i)
shows nothing. But
obj.cant_see_me
works, because dontenum is always open. No more need for an
orthogonal DontEnum attribute.
Instead of
obj.propertyIsEnumerable('i_want_to_hide', false)
users would just say
obj.dontenum::i_want_to_hide = ...
(typically obj is a prototype object and ... is a function). The
"setter" mode (second boolean arg) of propertyIsEnumerable is ugly
(everyone says so, no one denies). It's arguably hard to use (because
verbose). It's racy for users who have to call it after creating a
property (there's a window in which the property is enumerable -- not
a threading race per se, more low-level bug habitat and needless
worry). It's a burden on implementations that want immutable
attributes for props.
On the down side, you can't just add
obj.dontenum::foo = function () {...}
to ES3 code that runs in ES3-only browsers, as you could with the
propertyIsEnumerable hack. We should in any case design for the long
run.
Note that any namespace would hide a property from enumeration;
dontenum is not magic in that sense, only in that it is predefined
and open by default (again, its name is a strawman).
For the long run, rationalizing dontenum via namespaces seems good to
me.
Object.prototype.iterator::setPropertyIsEnumerable(prop, enable). For consistency, you could also have Object.prototype.iterator::isPropertyEnumerable(prop) which
delegates to Object.prototype.isPropertyEnumerable(prop).
Clever relocation of "is" in the predicate names ;-). We're probably
stuck with propertyIsEnumerable.
An alternative we've discussed, which Ian also brought up: add a keyword for declaring DontEnum properties.
This dontenum modifier can be used in addition to the method for convenience - it would make class declarations cleaner.
True of a namespace already, note.
But despite that, I think dontenum is inelegant. Its only effect would be on the default enumerator, which is only one iterator, and AFAIK is not special at all. IMO, it isn't worth the bloat. If there was a
way to define such modifiers within script, then I think the dontenum
modifier would be nice. If such a capability is planned for a future ES
edition, then I think dontenum is fine.
What do you think of the only-public-properties-are-enumeable idea?
Obvious problem to be solved: public means several things: "no
namespace" (backward compatible) vs. "public in this package or
class". We should discuss this more.
Brendan Eich wrote:
Suppose we had a predefined, open namespace, say dontenum (surely
not the best name). If you writeobj = {dontenum::cant_see_me: 33}
then
for (i in obj) print(i)
shows nothing. But
obj.cant_see_me
works, because dontenum is always open. No more need for an
orthogonal DontEnum attribute.Instead of
obj.propertyIsEnumerable('i_want_to_hide', false)
users would just say
obj.dontenum::i_want_to_hide = ...
Interesting and clever proposal. Some thoughts:
-
It would become harder to change the enumerability of a property (compared to a enumerability method): |obj.prop=obj.dontenum::prop; delete obj.dontenum::prop;| That said, I'm not sure there are many use cases that involving changing enumerability after the prop's enumerability is initially defined.
-
If you add setPropertyIsEnumerable(), how would that interact with this? Would it change the namespace as described above, or just toggle the hidden DontEnum attribute?
-
Users may think this dontenum namespace is magical and yet another thing to keep in mind, when it really just relies on the namespace trick you mentioned. With the introduction of classes and namespaces, the rules of enumerability are already more complex, so this adds to the cognitive load slightly. BTW, if setPropertyIsEnumerable() is added, would it be possible to make fixtures enumerable? Enumerability is orthogonal to static analysis (which fixtures are meant to help with), so I don't see why not.
-
The name sucks :p
Object.prototype.iterator::setPropertyIsEnumerable(prop, enable). For consistency, you could also have Object.prototype.iterator::isPropertyEnumerable(prop) which
delegates to Object.prototype.isPropertyEnumerable(prop).Clever relocation of "is" in the predicate names ;-). We're probably
stuck with propertyIsEnumerable.
Honestly, that wasn't intentional - it just came off my fingers naturally :)
-Yuh-Ruey Chen
On Mar 7, 2008, at 6:30 PM, Yuh-Ruey Chen wrote:
Interesting and clever proposal. Some thoughts:
- It would become harder to change the enumerability of a property (compared to a enumerability method): |obj.prop=obj.dontenum::prop; delete obj.dontenum::prop;| That said, I'm not sure there are many use cases that involving changing enumerability after the prop's enumerability is initially defined.
There are no valid use-cases IMO -- what Prototype et al. want is a
way to create DontEnum properties on (standard or not) prototype
objects ab initio.
- If you add setPropertyIsEnumerable(), how would that interact with this? Would it change the namespace as described above, or just toggle the hidden DontEnum attribute?
I would withdraw that proposed extension (under any API) in favor of
this one.
- Users may think this dontenum namespace is magical and yet another thing to keep in mind, when it really just relies on the namespace
trick you mentioned. With the introduction of classes and namespaces, the rules of enumerability are already more complex, so this adds to the cognitive load slightly.
Slightly, but it takes away the magic DontEnum attribute, formerly
hogged by the specification and magic predefined objects.
BTW, if setPropertyIsEnumerable() is added, would it be possible to make fixtures enumerable? Enumerability is orthogonal to static analysis (which fixtures are meant to help with), so I don't see why not.
This gets to the heart of the public vs. public-in-context-of-class-
or-package issue. We need to sort this out to find out exactly how
orthogonal enumerability is, as well as decide how flexible it should
be.
- The name sucks :p
Indeed. How about 'hidden' or 'nonEnumerable'?
here some thought
why not have a global utility function, maybe in the magic namespace
SetPropertyFlag( reference:Object, name:*, setflag:int, unsetflag:int = 0 ):Boolean
the flag could have this logic
| decimal | bitFlag | ReadOnly | DontDelete | DontEnum | | 0| 000| no| no| no| | 1| 001| no| no| yes| | 2| 010| no| yes| no| | 3| 011| no| yes| yes| | 4| 100| yes| no| no| | 5| 101| yes| no| yes| | 6| 110| yes| yes| no| | 7| 111| yes| yes| yes|
some usages:
someobj = { foo:123, bar:456 };
SetPropertyFlag( someobj, "foo", 0x110 ); SetPropertyFlag( someobj, ["foo","bar"], 0x001 ); //etc.
I personally would like to be able to do that also on the prototype SetPropertyFlag( myAlmostNativeObject.prototype, [ "some", "properties", "to", "protect" ], 0x011 );
On Mar 7, 2008, at 7:27 PM, zwetan wrote:
here some thought
why not have a global utility function, maybe in the magic namespace
SetPropertyFlag( reference:Object, name:*, setflag:int, unsetflag:int = 0 ):Boolean
No, sorry -- DontDelete upholds important integrity properties (like,
you may not replace a setter for window.location). This over-
generalizes in a dangerous and unwanted way.
The goal is to find the minimum amount of mutating meta-programming
sharpness for this "make certain properties non-enumerable" tool. If
it can be done with a one-time namespace qualification step, that wins.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of zwetan Sent: 7. mars 2008 19:28 To: Brendan Eich Cc: es4-discuss at mozilla.org es4-discuss; Yuh-Ruey Chen; Erik Arvidsson Subject: Re: ES4 draft: Object
why not have a global utility function, maybe in the magic namespace
The "magic" namespace (like "informational" and "helper") is a specification artifact, it does not exist in an implementation.
On 2008-03-07, at 22:43 EST, Brendan Eich wrote:
The goal is to find the minimum amount of mutating meta-programming sharpness for this "make certain properties non-enumerable" tool. If it can be done with a one-time namespace qualification step, that
wins.
A few comments:
I am confused. I guess I thought everything was in a namespace, just
that there is a default namespace that things with no explicit
namespace are in. Which makes me wonder how the namespace/not-
enumerable proposal will really work.
I think it is absolutely right that fixtures are not enumerable. And
a bug that things that would have been fixtures if you had them are
enumerable in es3. It seems to me that for-in/enumerability are all
about Maps, maybe now that we have Maps (and classes with fixtures),
we don't have to worry about enumerability in Objects so much.
Finally, for debugging, I would want to be able to find all the
properties of an object, non-enumerable and fixtures included. Will
there be a way to introspect like that?
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of P T Withington Sent: 8. mars 2008 10:10 To: Brendan Eich Cc: zwetan; es4-discuss at mozilla.org es4-discuss; Yuh-Ruey Chen; Erik Arvidsson Subject: Re: ES4 draft: Object
On 2008-03-07, at 22:43 EST, Brendan Eich wrote:
The goal is to find the minimum amount of mutating meta-programming sharpness for this "make certain properties non-enumerable" tool. If it can be done with a one-time namespace qualification step, that wins.
A few comments:
I am confused. I guess I thought everything was in a namespace, just that there is a default namespace that things with no explicit namespace are in. Which makes me wonder how the namespace/not- enumerable proposal will really work.
Some namespaces are "public" and distinguished thereby, properties in these namespaces would be enumerated. One of the public namespaces is the default namespace that things with no explicit namespace are in. (We're still fine-tuning the details of that.)
Finally, for debugging, I would want to be able to find all the properties of an object, non-enumerable and fixtures included. Will there be a way to introspect like that?
Not certain yet. Being able to "just do it" is a security concern if you think namespaces are in part about hiding access to private data. (Debugging facilities in the language are not a good idea if there is any chance at all that some of the code in a program may be hostile. We have to make a choice about that when we write the spec for the reflection mechanism.)
Brendan Eich wrote:
- Users may think this dontenum namespace is magical and yet another thing to keep in mind, when it really just relies on the namespace
trick you mentioned. With the introduction of classes and namespaces, the rules of enumerability are already more complex, so this adds to the cognitive load slightly.Slightly, but it takes away the magic DontEnum attribute, formerly
hogged by the specification and magic predefined objects.
But doesn't DontEnum still have to be there for ES3 objects? How else would you prevent the enumeration of ES3 builtin methods, e.g. Object.prototype.toString()? Or is there some more open namespace magic that I'm unware of?
BTW, if setPropertyIsEnumerable() is added, would it be possible to make fixtures enumerable? Enumerability is orthogonal to static analysis (which fixtures are meant to help with), so I don't see why not.
This gets to the heart of the public vs. public-in-context-of-class- or-package issue. We need to sort this out to find out exactly how
orthogonal enumerability is, as well as decide how flexible it should
be.
Well, I think the only overlap is that public-in-class-context methods (or any method really) default to be non-public in terms of enumerability
- The name sucks :p
Indeed. How about 'hidden' or 'nonEnumerable'?
I'd prefer 'hidden', since 'nonEnumerable' implies more strongly that a prop needs to be in that namespace to not be enumerable, when that's not the case.
Which gets me back to the cognitive load issue. Even with a name like 'hidden', they may think they may have to do some funky syntax like |obj.hidden::other_ns::prop| do hide a prop in another namespace.
While we're on the topic of namespaces, I read in the ES4 overview: "In addition, the names internal, public, protected, and private denote namespace values, but the values depend on the context of the use." As the wiki doesn't talk about this, and the online spec is inaccessible/outdated, can you elaborate on this? I wonder if 'private' could be used somehow for enumerability.
-Yuh-Ruey Chen
On Mar 8, 2008, at 1:16 PM, Yuh-Ruey Chen wrote:
Brendan Eich wrote:
- Users may think this dontenum namespace is magical and yet another thing to keep in mind, when it really just relies on the namespace trick you mentioned. With the introduction of classes and namespaces, the rules of enumerability are already more complex, so this adds to the cognitive load slightly.
Slightly, but it takes away the magic DontEnum attribute, formerly hogged by the specification and magic predefined objects.
But doesn't DontEnum still have to be there for ES3 objects? How else would you prevent the enumeration of ES3 builtin methods, e.g. Object.prototype.toString()? Or is there some more open namespace
magic that I'm unware of?
ES3 code can't detect namespaces, so arguably shouldn't care if we
were to implement DontEnum using an open namespace. But this could be
a problem for mixed ES3 and ES4 scenarios where the ES4 code does
introspect via Name objects and is surprised to find "ES3" objects
with qualified property names.
BTW, if setPropertyIsEnumerable() is added, would it be possible to make fixtures enumerable? Enumerability is orthogonal to static analysis (which fixtures are meant to help
with), so I don't see why not.This gets to the heart of the public vs. public-in-context-of-class- or-package issue. We need to sort this out to find out exactly how orthogonal enumerability is, as well as decide how flexible it should be.
Well, I think the only overlap is that public-in-class-context methods (or any method really) default to be non-public in terms of
enumerability
Is that the right design?
dynamic class C { public var fixture; function C() { this.expando = 42; } } for (var i in new C) intrinsic::print(i);
wouldn't you expect to see "expando" printed? Is it printed because
the default namespace for expando is the public-default one? I
believe it is not, in the current proposal and the RI. Rather, each
class or package gets a nonce-named public namespace, different from
every other such per-class or per-package public. IIRC package trumps
class -- classes within a package share the package's nonce-named
public namespace (Jeff, Lars, please correct me if I'm wrong).
While we're on the topic of namespaces, I read in the ES4 overview:
"In addition, the names internal, public, protected, and private denote namespace values, but the values depend on the context of the use." As the wiki doesn't talk about this, and the online spec is inaccessible/outdated, can you elaborate on this? I wonder if
'private' could be used somehow for enumerability.
This is the entire issue. See above and let me know if that's unclear.
Brendan Eich wrote:
On Mar 8, 2008, at 1:16 PM, Yuh-Ruey Chen wrote:
But doesn't DontEnum still have to be there for ES3 objects? How else would you prevent the enumeration of ES3 builtin methods, e.g. Object.prototype.toString()? Or is there some more open namespace
magic that I'm unware of?ES3 code can't detect namespaces, so arguably shouldn't care if we
were to implement DontEnum using an open namespace. But this could be
a problem for mixed ES3 and ES4 scenarios where the ES4 code does
introspect via Name objects and is surprised to find "ES3" objects
with qualified property names.
I'm talking about how the enumerability (or lack thereof) of Object.prototype.* are determined. Or are prototype methods also not enumerable by default like fixtures?
Let me get this straight. The rules of enumerability are:
- If a property is a fixture, it's not enumerable. BTW, what exactly is a fixture? Does this included ES3-style prototype methods? And would it be possible to init a fixture to be enumerable?
- If a property is not in the public namespace, it's not enumerable.
- Otherwise, it is enumerable.
- No hidden DontEnum attribute.
Are we trying to simplify this to the last three rules using some namespace voodoo to handle the fixture rule? I'm confused.
Well, I think the only overlap is that public-in-class-context methods (or any method really) default to be non-public in terms of
enumerabilityIs that the right design?
dynamic class C { public var fixture; function C() { this.expando = 42; } } for (var i in new C) intrinsic::print(i);
wouldn't you expect to see "expando" printed? Is it printed because
the default namespace for expando is the public-default one? I
believe it is not, in the current proposal and the RI. Rather, each
class or package gets a nonce-named public namespace, different from
every other such per-class or per-package public. IIRC package trumps
class -- classes within a package share the package's nonce-named
public namespace (Jeff, Lars, please correct me if I'm wrong).
I meant non-expando vars and methods defined within the class, whatever the terminology is used (I see 'fixture' thrown around everywhere), are not enumerable. So yes, expando should be printed. And the RI does print it, so I'm not sure what you're talking about. Are you saying that each property is defined in a "class-specific public" namespace, and therefore does not get enumerated? I would say that expando properties should be defined in the "global public" namespace, so that they would be enumerated.
-Yuh-Ruey Chen
On Mar 9, 2008, at 3:01 PM, Yuh-Ruey Chen wrote:
Brendan Eich wrote:
ES3 code can't detect namespaces, so arguably shouldn't care if we were to implement DontEnum using an open namespace. But this could be a problem for mixed ES3 and ES4 scenarios where the ES4 code does introspect via Name objects and is surprised to find "ES3" objects with qualified property names.
I'm talking about how the enumerability (or lack thereof) of Object.prototype.* are determined. Or are prototype methods also not enumerable by default like fixtures?
My reply was not meant to be an answer, but a question: should we
drop this dontenum-namespace idea because it will break ES3+ES4
mixtures where the ES4 code reflects on a property's namespace
qualifier by using a Name object?
Let me get this straight. The rules of enumerability are:
- If a property is a fixture, it's not enumerable.
I never said any such thing. My hope was to avoid special cases and
use only a predefined, opened-by-definition namespace.
BTW, what exactly is a fixture?
That's a good question, and it seems the answer is not in the wiki.
Trying the trac:
bugs.ecmascript.org/ticket/233
which references the overview document.
In ES3 terms, a fixture is a property with the DontDelete attribute set.
But there's more to it than that, for reasons expanded on in #233 to
do with namespace lookup and early binding. And there is another case
that Lars pointed out recently to do with dynamic classes, but I'll
avoid trying to summarize it except to say that a fixture is a fixed
property that can't be deleted or shadowed.
Does this included ES3-style prototype methods? And would it be possible to init a fixture to be enumerable?
In the sketch I mailed, you didn't see any mention of fixtures --
IIRC you asked about them in reply. Just to explain what my proposal
was aiming to do: It seems to me that if we have to add a magic bit
back into the skinny is-it-enumerable decision tree, we may as well
keep DontEnum.
- If a property is not in the public namespace, it's not enumerable.
Right, I said that ;-).
- Otherwise, it is enumerable.
- No hidden DontEnum attribute.
4 is more of an aside than a step, but yeah, that's it :-).
Are we trying to simplify this to the last three rules using some namespace voodoo to handle the fixture rule? I'm confused.
My proposal had no mention of fixture "voodoo" -- you cast that
spell ;-).
Notice that most DontEnum (almost all) properties defined by ES3 are
not fixtures because they are not DontDelete. IIRC only a few such as
standard prototype's constructor properties are DontDelete.
Is that the right design?
dynamic class C { public var fixture; function C() { this.expando = 42; } } for (var i in new C) intrinsic::print(i);
wouldn't you expect to see "expando" printed? Is it printed because the default namespace for expando is the public-default one? I believe it is not, in the current proposal and the RI. Rather, each class or package gets a nonce-named public namespace, different from every other such per-class or per-package public. IIRC package trumps class -- classes within a package share the package's nonce-named public namespace (Jeff, Lars, please correct me if I'm wrong).
I meant non-expando vars and methods defined within the class,
whatever the terminology is used (I see 'fixture' thrown around everywhere
(Not in my proposal)
), are not enumerable. So yes, expando should be printed.
From your numbered steps above, step 3 must have been reached -- so
expando is in the public-public (null qualifier in Name object
reflection) namespace. Not in the class-C-public namespace. My
question is whether that is the right design choice.
And the RI does print it, so I'm not sure what you're talking about.
The RI is (a) not normative (nothing apart from ES3+fixes is yet),
(b) based on a DontEnum attribute model, not on my proposal. Not sure
why you thought I was talking about it. I was making a new proposal.
Are you saying that each property is defined in a "class-specific public" namespace, and therefore does not get enumerated? I would say that expando properties should be defined in the "global public" namespace, so that they would be enumerated.
Ok, I like that. I'm not arguing a side, just showing the choice.
There may be other cases, though, where a class-public property name
might want to be enumerable. In such a case the step 2 you wrote
would need to elaborate its "public" test. But again, my proposal was
about enumerability, independent of fixture-ness.
Draft 2 of the spec for the Object class. Changelog near the beginning.
On 10/03/2008, Lars Hansen <lhansen at adobe.com> wrote:
Draft 2 of the spec for the Object class. Changelog near the beginning.
The draft HTML seems a little broken. There's … in it early on, later these appear raw in the source (which displays as an empty square in Opera and IE8).
And near the end of the document you have "</p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></div>"
Slightly broken conversion tool?
Brendan Eich wrote:
On Mar 9, 2008, at 3:01 PM, Yuh-Ruey Chen wrote:
Brendan Eich wrote:
ES3 code can't detect namespaces, so arguably shouldn't care if we were to implement DontEnum using an open namespace. But this could be a problem for mixed ES3 and ES4 scenarios where the ES4 code does introspect via Name objects and is surprised to find "ES3" objects with qualified property names.
I'm talking about how the enumerability (or lack thereof) of Object.prototype.* are determined. Or are prototype methods also not enumerable by default like fixtures?
My reply was not meant to be an answer, but a question: should we
drop this dontenum-namespace idea because it will break ES3+ES4
mixtures where the ES4 code reflects on a property's namespace
qualifier by using a Name object?
Does this included ES3-style prototype methods? And would it be possible to init a fixture to be enumerable?
In the sketch I mailed, you didn't see any mention of fixtures --
IIRC you asked about them in reply. Just to explain what my proposal
was aiming to do: It seems to me that if we have to add a magic bit
back into the skinny is-it-enumerable decision tree, we may as well
keep DontEnum.
And the RI does print it, so I'm not sure what you're talking about.
The RI is (a) not normative (nothing apart from ES3+fixes is yet),
(b) based on a DontEnum attribute model, not on my proposal. Not sure
why you thought I was talking about it. I was making a new proposal.
Ok, that cleared things up a bit. I didn't realize you were making a proposal and asking questions based off it :) I thought you were mentioning how the RI was implemented today at the moment.
Can you elaborate on cases where "ES4 code does introspect via Name objects and is surprised to find "ES3" objects with qualified property names."? I can't think of any.
BTW, what exactly is a fixture?
That's a good question, and it seems the answer is not in the wiki.
Trying the trac:bugs.ecmascript.org/ticket/233
which references the overview document.
In ES3 terms, a fixture is a property with the DontDelete attribute set.
But there's more to it than that, for reasons expanded on in #233 to
do with namespace lookup and early binding. And there is another case
that Lars pointed out recently to do with dynamic classes, but I'll
avoid trying to summarize it except to say that a fixture is a fixed
property that can't be deleted or shadowed.
Thanks, that's a good summary, though some of the commentary in the ticket was confusing (like lth's example - was x.toString decided to be ambiguous?).
Is that the right design?
dynamic class C { public var fixture; function C() { this.expando = 42; } } for (var i in new C) intrinsic::print(i);
wouldn't you expect to see "expando" printed? Is it printed because the default namespace for expando is the public-default one? I believe it is not, in the current proposal and the RI. Rather, each class or package gets a nonce-named public namespace, different from every other such per-class or per-package public. IIRC package trumps class -- classes within a package share the package's nonce-named public namespace (Jeff, Lars, please correct me if I'm wrong).
I meant non-expando vars and methods defined within the class,
whatever the terminology is used (I see 'fixture' thrown around everywhere), are not enumerable. So yes, expando should be printed.From your numbered steps above, step 3 must have been reached -- so
expando is in the public-public (null qualifier in Name object
reflection) namespace. Not in the class-C-public namespace. My
question is whether that is the right design choice.
Are they any particular disadvantages that you can think of? Maybe another ES4 designer can chime in here.
BTW, a bit off topic: if a class is defined in a namespace, are the properties of that class also defined in that namespace (instead of just public/class-public)? Namespaces aren't clearly specified in the wiki or the overview, the tickets are unorganized, and the RI tends to barf when it comes to them. Ugh, I feel Apple's pain when they complain that they don't know where to start...
Are you saying that each property is defined in a "class-specific public" namespace, and therefore does not get enumerated? I would say that expando properties should be defined in the "global public" namespace, so that they would be enumerated.
Ok, I like that. I'm not arguing a side, just showing the choice.
There may be other cases, though, where a class-public property name
might want to be enumerable. In such a case the step 2 you wrote
would need to elaborate its "public" test. But again, my proposal was
about enumerability, independent of fixture-ness./be
Yeah, I asked whether it was possible to make a class-public property enumerable. And if so, I really would not want to do something silly like |public public var x|.
-Yuh-Ruey Chen
On 10/03/2008, Lars Hansen <lhansen at adobe.com> wrote:
Draft 2 of the spec for the Object class. Changelog near the beginning.
<intrinsic::toString ( )>
The intrinsic toString method returns the concatenation of "[", "object", the class name of the object, and "]".
There should probably be a whitepace between "object" and the class
name, as is the case in the implementation.
~~<intrinsic::valueOf ( )>~~
If the object is the result of calling the Object constructor with a
host object (Host objects), it is implementation-defined whether
valueOf returns its this value or another value such as the host
object originally passed to the constructor.
"(Host objects)" looks a bit weird like that. Should probably be either "(see Host objects)" analogously to how you later use "(see HasProperty-defn)" or reference style, "[Host objects]"
<intrinsic::isPrototypeOf ( value )>
The function magic::getPrototype extracts the [[Prototype]] property from the object. See magic:getPrototype.
And here a reference to documentation elsewhere. Probably should try
to be consistent about how those look. I assume these are going to be
links in the finished spec?
intrinsic function propertyIsEnumerable(name: EnumerableId, flag: (boolean|undefined) = undefined): boolean
I too find the second parameter here abhorrent. Please find another way to solve it (Brendan's namespace idea maybe) or remove this feature altogether.
How does property lookup deal with properties indexed by numbers that are not int or uint? toEnumerableId converts them to strings, but the Vector proposal indicated that vectors don't.
Waldemar
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of liorean Sent: 10. mars 2008 17:52 To: es4-discuss at mozilla.org Subject: Re: ES4 draft: Object
On 10/03/2008, Lars Hansen <lhansen at adobe.com> wrote:
Draft 2 of the spec for the Object class. Changelog near the beginning.
<intrinsic::toString ( )>The intrinsic toString method returns the concatenation of "[", "object", the class name of the object, and "]".There should probably be a whitepace between "object" and the class name, as is the case in the implementation.
There should. Corrected.
<intrinsic::valueOf ( )>If the object is the result of calling the Object constructor with a host object (Host objects), it is implementation-defined whether valueOf returns its this value or another value such as the host object originally passed to the constructor."(Host objects)" looks a bit weird like that. Should probably be either "(see Host objects)" analogously to how you later use "(see HasProperty-defn)" or reference style, "[Host objects]"
Corrected.
<intrinsic::isPrototypeOf ( value )>The function magic::getPrototype extracts the [[Prototype]] property from the object. See magic:getPrototype.And here a reference to documentation elsewhere. Probably should try to be consistent about how those look. I assume these are going to be links in the finished spec?
The primary format of the final spec is word and/or pdf, so I don't know about links there. I hope that we can release an HTML version too, and obviously it would be good if there could be good hyperlinking there. I would like to promise all of that but I am not going to do it. However, there will at least be proper references to the sections that define the magic functions.
In general the specification of the magic functions is somewhat unstructured at present. I appreciate this reminder about that :-)
-----Original Message----- From: Waldemar Horwat [mailto:waldemar at google.com] Sent: 10. mars 2008 18:50 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: ES4 draft: Object
intrinsic function propertyIsEnumerable(name: EnumerableId, flag: (boolean|undefined) = undefined): boolean
I too find the second parameter here abhorrent. Please find another way to solve it (Brendan's namespace idea maybe) or remove this feature altogether.
The feature was approved by the WG and solves a practical problem. If another way to solve this practical problem is proposed (in a more structured form than in the ongoing discussion) and finds favor with the WG, then fine -- of course we can replace it. Until then, this feature stays as it is until the WG can be convinced that it needs to be removed. Personally I think that "it is ugly/abhorrent" is a weak basis on which to remove the current feature.
How does property lookup deal with properties indexed by numbers that are not int or uint? toEnumerableId converts them to strings, but the Vector proposal indicated that vectors don't.
The intrinsic::hasOwnProperty and intrinsic::propertyIsEnumerable methods on Object require EnumerableId parameters, and no conversion happens automatically when they are called (there should be no automatic conversion to union types, even from a number to a number in the type). So programs trying to call the intrinsic methods on doubles (say) will receive errors. This is true independent of Vector.
The prototype methods inherited from Object perform conversions explicitly, but can be overridden in Vector to prevent the conversion. Ergo the hasOwnProperty and propertyIsEnumerable methods on Vector should be able to handle arbitrary names, and should return "false" for all property names that are numbers not in the uint range -- if that's what we think is the desired behavior.
That said, you raise a good point and I don't think the story is fully coherent yet. Certainly we don't have a working prototype of this.
(And yet another point is that I expect that the int and uint types are not going to be in the final language and that some of the machinery in Object will have to be rethought as a consequence of that change.)
As far as I can see this is not a problem in the file I sent out, nor in the one I received from the reflector.
What mailer are you using?
Anyone else see this?
On 11/03/2008, Lars Hansen <lhansen at adobe.com> wrote:
As far as I can see this is not a problem in the file I sent out, nor in the one I received from the reflector.
What mailer are you using?
Gmail's web interface. And checking, it appears only using the View link, not the Download link. So it appears to be GMail's fault.
Lars Hansen wrote:
The feature was approved by the WG and solves a practical problem. If another way to solve this practical problem is proposed (in a more structured form than in the ongoing discussion) and finds favor with the WG, then fine -- of course we can replace it. Until then, this feature stays as it is until the WG can be convinced that it needs to be removed. Personally I think that "it is ugly/abhorrent" is a weak basis on which to remove the current feature.
We are the WG. Are you saying that substantive discussions of your proposals are not welcome? Not sure what the point of participating is if that's the case.
I'm dealing with a serious insurrection of folks who believe that the ES4 working group has a bad attitude, based on Brendan's public comments and responses to issues like this one. They're quite visible.
Waldemar
-----Original Message----- From: Waldemar Horwat [mailto:waldemar at google.com] Sent: 10. mars 2008 19:40 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: ES4 draft: Object
Lars Hansen wrote:
The feature was approved by the WG and solves a practical problem. If another way to solve this practical problem is proposed (in a more structured form than in the ongoing discussion) and finds favor with the WG, then fine -- of course we can replace it. Until then, this feature stays as it is until the WG can be convinced that it needs to be removed. Personally I think that "it is ugly/abhorrent" is a weak basis on which to remove the current feature.
We are the WG. Are you saying that substantive discussions of your proposals are not welcome? Not sure what the point of participating is if that's the case.
Sorry, I didn't realize that "I find it abhorrent" qualified as substantive discussion. My fault. Won't happen again.
I'm dealing with a serious insurrection of folks who believe that the ES4 working group has a bad attitude, based on Brendan's public comments and responses to issues like this one. They're quite visible.
Debate is only good. I merely pointed out the obvious thing, namely that until there is an alternative proposal written up to deal with this issue, the current design stands unless the WG, as a group, decides to just get rid of it (leaving the problem it was designed to solve solution-less).
I like the idea of making non-public-namespaced properties be not-enumerable and getting rid of DontEnum. We've talked loosely about it for a while. But it's remained loose talk, it has never made it to the stage where it is a coherent proposal.
On Mon, Mar 10, 2008 at 11:11 PM, Jeff Dyer <jodyer at adobe.com> wrote:
it would be helpful to follow up with possible solutions or at least insight into what makes it abhorrent (your word).
FWIW, I also did not grasp the force of the objection, and would like to understand better.
No doubt the WG has attitude. There are strong personalities involved and we have had our share of knocks. Sometimes that shows.
It can probably show less if everyone makes an effort. The WG has most of the implementors one could hope for, so this opportunity shouldn't be wasted.
On 3/10/08 5:40 PM, Waldemar Horwat wrote:
Lars Hansen wrote:
The feature was approved by the WG and solves a practical problem. If another way to solve this practical problem is proposed (in a more structured form than in the ongoing discussion) and finds favor with the WG, then fine -- of course we can replace it. Until then, this feature stays as it is until the WG can be convinced that it needs to be removed. Personally I think that "it is ugly/abhorrent" is a weak basis on which to remove the current feature.
We are the WG.
Yes indeed.
Are you saying that substantive discussions of your proposals are not welcome?
Not speaking for Lars, of course, but your feedback in this case seemed to lack substance. Maybe it was in joking (I thought so at first), but it would be helpful to follow up with possible solutions or at least insight into what makes it abhorrent (your word).
Not sure what the point of participating is if that's the case.
It's absolutely not the case.
I'm dealing with a serious insurrection of folks who believe that the ES4 working group has a bad attitude, based on Brendan's public comments and responses to issues like this one. They're quite visible.
No doubt the WG has attitude. There are strong personalities involved and we have had our share of knocks. Sometimes that shows. You of all people should understand why. But please do share more about the insurrection you are dealing with. Maybe then we can be more sensitive.
On Mon, 10 Mar 2008, Waldemar Horwat wrote:
intrinsic function propertyIsEnumerable(name: EnumerableId, flag: (boolean|undefined) = undefined): boolean
I too find the second parameter here abhorrent. Please find another way to solve it (Brendan's namespace idea maybe) or remove this feature altogether.
I believe what Waldemar is saying is that the method has a name that implies that it is a getter, but that the proposal has it working as a setter. This has a number of disadvantages. Primarily, it is unintuitive for authors. This makes code maintenance significantly more complicated, as readers of code tend to assume that getters cannot have side effects. This leads directly to bugs.
We should design ES4 in a way that someone who is experienced with other programming languages, but who has never learnt ES4, can by and large correctly guess what any arbitrary ES4 code is doing. In this particular case, we have failed to achieve that.
On Mar 10, 2008, at 7:01 PM, Lars Hansen wrote:
We are the WG. Are you saying that substantive discussions of your proposals are not welcome? Not sure what the point of participating is if that's the case.
Sorry, I didn't realize that "I find it abhorrent" qualified as substantive discussion. My fault. Won't happen again.
The optional second argument to make propertyIsEnumerable a setter has
some practical problems:
- It violates the very strong norm that getter and setter functions
are separate and have their own different arguments. It will make the
feature harder to use and code using it harder to understand, to some
extent. - It makes it impossible to feature test for the ability to disable
enumerability, compared to a separate setter.
Against the argument that it is too risky compatibility-wise to add a
new method to the object prototype (apparently the only reason things
were done), I propose that it is overblown. Mozilla has defined new
methods and properties on all objects. We have copied some in Safari
and seen no web compatibility issues, I assume Mozilla has not had any
as well. Specifically, I am thinking of defineSetter,
defineGetter, lookupSetter, lookupGetter, and proto.
Has any study been done on how many sites currently make use of the
property names of a likely setter for propertyIsEnumerable?
I'm dealing with a serious insurrection of folks who believe that the ES4 working group has a bad attitude, based on Brendan's public comments and responses to issues like this one. They're quite visible.
Debate is only good. I merely pointed out the obvious thing, namely that until there is an alternative proposal written up to deal with this issue, the current design stands unless the WG, as a group, decides to just get rid of it (leaving the problem it was designed to solve solution-less).
Surely reviewing this spec is the appropriate time to revisit this
issue. I'd like to propose the following three alternatives to the
current proposal:
- Remove the feature entirely from ES4 (as part of the "judicious
feature cuts" process) until a more appropriate syntax is found - Replace two-argument form of propertyIsEnumerable with
setPropertyIsEnumerable - Replace two-argument form of propertyIsEnumerable with
setPropertyIsEnumerable
I think each of these options is so trivial as to not require a
lengthy write-up.
What is the process for the WG deciding whether to make one of these
changes, or something else?
I like the idea of making non-public-namespaced properties be not-enumerable and getting rid of DontEnum. We've talked loosely about it for a while. But it's remained loose talk, it has never made it to the stage where it is a coherent proposal.
I don't like syntax-based alternatives since they cannot be made to
degrade gracefully in ES3 UAs.
, Maciej
On Mar 10, 2008, at 11:14 PM, Maciej Stachowiak wrote:
The optional second argument to make propertyIsEnumerable a setter has some practical problems:
- It violates the very strong norm that getter and setter functions are separate and have their own different arguments. It will make the feature harder to use and code using it harder to understand, to some extent.
- It makes it impossible to feature test for the ability to disable enumerability, compared to a separate setter.
Against the argument that it is too risky compatibility-wise to add a new method to the object prototype (apparently the only reason things were done), I propose that it is overblown. Mozilla has defined new methods and properties on all objects. We have copied some in Safari and seen no web compatibility issues, I assume Mozilla has not had any as well. Specifically, I am thinking of defineSetter, defineGetter, lookupSetter, lookupGetter, and proto.
Has any study been done on how many sites currently make use of the property names of a likely setter for propertyIsEnumerable?
I forgot to mention, making two-argument propertyIsEnumerable have
setter semantics can be a tiny compatibility risk too, if any code
accidentally calls it with two args and does not expect it to act as a
setter in this case. Do we have any way to quantify the relative
compatibility risk of the current design vs. a separate setter?
, Maciej
On Mon, Mar 10, 2008 at 11:14 PM, Maciej Stachowiak <mjs at apple.com> wrote:
[...] I'd like to propose the following three alternatives to the current proposal:
- Remove the feature entirely from ES4 (as part of the "judicious feature cuts" process) until a more appropriate syntax is found
- Replace two-argument form of propertyIsEnumerable with setPropertyIsEnumerable
- Replace two-argument form of propertyIsEnumerable with setPropertyIsEnumerable
So long as setPropertyIsEnumerable is a method of Object.prototype, it raises the otherwise pointless question of the meaning of overriding it. At the last ES3.1 face-to-face, we agreed instead on the following static method on Object, as recorded at es3.1:targeted_additions_to_array_string_object_date:
Object.dontEnum(object, memberName)
The own property memberName of object is marked to be excluded for the for in enumeration.
If a new object is made that delegates to object, the new object may create and change its own memberName property, and that property will be enumerable.
That page also lists static methods on Object to set the attributes DontDelete and ReadOnly. The proposed static Object.fix(obj) is inspired by proposed ES4's notion of a fixture -- a non-dynamic object. Such static methods allow most of the restrictions associated with ES4's classes to be expressed without new syntax or major new concepts.
On Mar 10, 2008, at 11:35 PM, Mark S. Miller wrote:
On Mon, Mar 10, 2008 at 11:14 PM, Maciej Stachowiak <mjs at apple.com>
wrote:[...] I'd like to propose the following three alternatives to the current proposal:
- Remove the feature entirely from ES4 (as part of the "judicious feature cuts" process) until a more appropriate syntax is found
- Replace two-argument form of propertyIsEnumerable with setPropertyIsEnumerable
- Replace two-argument form of propertyIsEnumerable with setPropertyIsEnumerable
So long as setPropertyIsEnumerable is a method of Object.prototype, it raises the otherwise pointless question of the meaning of overriding it.
I don't see how it raises any special questions - it's not called
internally by the implementation or anything.
At the last ES3.1 face-to-face, we agreed instead on the following static method on Object, as recorded at <es3.1:targeted_additions_to_array_string_object_date
:
That proposal seems to depart markedly from the past plan that ES 3.1
is to be a subset of ES4. Has that plan been abandoned? ES3.1
organizers, what's up?
(If you'd like to propose this design for ES4 as well then the basic
approach seems sound, in that it avoids whatever risk there is to
polluting the namespace of all objects without being conceptually
confusing. Also the names lean too much towards terse instead of
descriptive for such obscure operations. For instance
Object.readOnly(o, p) would read much better as something like
Object.makePropertyReadOnly(o, p).)
, Maciej
I think that there is general agreement that
- Object.prototype should not be polluted
- Namespaces to add functionality are difficult to handle
- Changing a getter to a setter by adding a new parameter is not a good idea.
We will have this discussion over and over again as we add properties and methods to Object and other classes.
What if we considered a more ES3-like approach?
Let's bite the bullet and add one element to Object.prototype. This element serves as a container for all future extensions. So, instead of
myObject.propertyIsEnumerable ("myProp", true);
use
myObject.NameToBeDefined.setPropertyEnumerable ("myProp", true);
This way, we reserve a "namespace", which in fact is an object, that can take all of our current and future extensions without risking to Break The Web.
Yes, I know that "this" inside setPropertyEnumerable() would usually not refer to myObject, but to the NameToBeDefined object. But since the language owns that object, it could be declared final, and then, we could add the changed behavior of "this" to the language implementation without having to do anything else but mention that behavior in the spec.
What do you think?
Michael
Yuh-Ruey Chen <maian330 at gmail.com> wrote:
Which gets me back to the cognitive load issue. Even with a name like 'hidden', they may think they may have to do some funky syntax like |obj.hidden::other_ns::prop| do hide a prop in another namespace.
This was confusing me... How would you define a property to have a qualified name and be in the dontenum namespace at the same time? Or are namespaced properties non-enumerable anyway? (That would feel wrong to me..)
namespace dontenum_ns = dontenum other_ns; obl.dontenum_ns::prop = 1;
?
The problem here would be the property would always be accessible to all scopes, since only one of the namespaces needs to be open. ES4 doesn't currently have a way to express AND/OR namespace combinations.
Peter
On Mar 11, 2008, at 3:11 AM, Michael Daumling wrote:
I think that there is general agreement that
- Object.prototype should not be polluted
- Namespaces to add functionality are difficult to handle
- Changing a getter to a setter by adding a new parameter is not a
good idea.We will have this discussion over and over again as we add properties and methods to Object and other classes.
What if we considered a more ES3-like approach?
Let's bite the bullet and add one element to Object.prototype. This element serves as a container for all future extensions. So, instead
ofmyObject.propertyIsEnumerable ("myProp", true);
use
myObject.NameToBeDefined.setPropertyEnumerable ("myProp", true);
This way, we reserve a "namespace", which in fact is an object, that
can take all of our current and future extensions without risking to Break The Web.
This is not unreasonable, but if adding to Object.prototype is truly
terrifying then I think the approach Mark Miller linked to, of static
methods on the Object constructor, is cleaner:
Object.setPropertyEnumerable(myObject, "myProp", true);
It's less object-oriented but free of any pollution of the
Object.prototype namespace.
It might even be better to make this transition one-way, since it
shouldn't be allowed to make built-in properties that are DontEnum
enumerable:
Object.makePropertyNonEnumerable(myObject, "myProp");
, Maciej
On 11/03/2008, Maciej Stachowiak <mjs at apple.com> wrote:
This is not unreasonable, but if adding to Object.prototype is truly terrifying then I think the approach Mark Miller linked to, of static methods on the Object constructor, is cleaner: /snip/ It might even be better to make this transition one-way, since it shouldn't be allowed to make built-in properties that are DontEnum enumerable:
Could as well make it a global function then. Just give it an obscure preventEnumeration(obj, propName) signature or something.
I would very much like to see a declarative keyword for use with global, class and namespace variable and function declarations and in object literals. There's really not much reason to require a function call for a declarative feature if there's a cheap and much nicer way to do it in ES4, as long as you have a solution for ES3.
(Kinda like how there's a specific slice syntax in ES4, but the slice functionality itself is ES3 compatible.)
- Remove the feature entirely from ES4 (as part of the "judicious feature cuts" process) until a more appropriate syntax is found
Setting dontEnum is immensely valuable as a framework developer. I realize that is not a very technical argument, but controlling dontEnum is of more value than many of the other features in ES4, and would certainly hope it is not omitted.
So long as setPropertyIsEnumerable is a method of Object.prototype, it raises the otherwise pointless question of the meaning of overriding it. At the last ES3.1 face-to-face, we agreed instead on the following static method on Object, as recorded at es3.1:targeted_additions_to_array_string_object_date:
Object.dontEnum(object, memberName)
I realize I wasn't there (at the ES3.1 F2F, only called in), but why was this discussed for ES3.1? I thought the page you are referencing was simply items that hadn't been cleaned up yet, since it clearly does not subset ES4 (as I noted in my comments on that page). If we desire that this be a part of ES3.1, it should be brought up as a proposal for ES4, so that ES3.1 can safe subset it, rather than creating new divergent features for ES3.1. Frankly, I like the idea of using "Object.dontEnum(object, memberName)", that sounds great to me, but it should be an ES4 proposal first and should not be considered for inclusion in ES3.1 unless and until accepted by ES4. BTW, Another possible syntax could be: Object.setAttribute(object, memberName,attributeFlag) which could be used to set the dontEnum, readOnly, and dontDelete flags and probably more closely follows the internal structure of implementations as well. Thanks, Kris
Peter Hall wrote:
Yuh-Ruey Chen <maian330 at gmail.com> wrote:
Which gets me back to the cognitive load issue. Even with a name like 'hidden', they may think they may have to do some funky syntax like |obj.hidden::other_ns::prop| do hide a prop in another namespace.
This was confusing me... How would you define a property to have a qualified name and be in the dontenum namespace at the same time? Or are namespaced properties non-enumerable anyway? (That would feel wrong to me..)
The latter. It is weird, I agree. But the enumerator can easily be customized, so I want the default enumerator be as non-magical as possible - and therefore, as simple as possible. In fact, I'm a bit bothered that propertyIsEnumerable is a method of Object yet can be rendered useless when changing the enumerator - along this line of reasoning, one would think that such a specific method shouldn't be a method of Object at all, but rather the enumerator itself.
ES4 doesn't currently have a way to express AND/OR namespace combinations.
Yeah, I vaguely remember some discussion concerning multiple namespaces per property a long time ago (maybe a year ago). It was rejected probably due to complexity.
-Yuh-Ruey Chen
The latter. It is weird, I agree. But the enumerator can easily be customized, so I want the default enumerator be as non-magical as possible - and therefore, as simple as possible. In fact, I'm a bit bothered that propertyIsEnumerable is a method of Object yet can be rendered useless when changing the enumerator - along this line of reasoning, one would think that such a specific method shouldn't be a method of Object at all, but rather the enumerator itself.
So how would you go about enumerating the properties of an arbitrary dynamic object, whose properties could be in namespaces?
Yeah, I vaguely remember some discussion concerning multiple namespaces per property a long time ago (maybe a year ago). It was rejected probably due to complexity.
Not being able to combine them effectively makes many of the initial proposal's use-cases useless, because two use-cases may arise simultaneously when integrating two codebases, and be irreparably conflicting.
Peter
On Mar 10, 2008, at 6:43 PM, Yuh-Ruey Chen wrote:
Ok, that cleared things up a bit. I didn't realize you were making a proposal and asking questions based off it :)
This is my fault for being unclear. I wrote:
Here's something I wrote a while ago (minor edits to update for context) that attempts to subsume enumerability under namespacing:
and made a proposal. The "attempts to subsume" bit was not as clear
as it could have been that I was making a novel proposal. Sorry about
that.
Can you elaborate on cases where "ES4 code does introspect via Name objects and is surprised to find "ES3" objects with qualified property names."? I can't think of any.
I can't think of one right now, but the thought comes from the
proposal, where you can reflect on names of types, at least. True,
you can't iterate over all properties of an object, e.g., the expando
example. But type names can be namespaced, and the Name object is
used to reflect these.
If we end up making all namespaced properties non-enumerable, then we
may want a property iterator that returns the names of all properties
in a given object. Even if this is not in the standard, it's likely
to be an extension. Or it could be added in the future.
The question of whether ES3+4 mixtures might not want enumerable ES3
properties to appear to be in a predefined-in-ES4 namespace seems to
me worth considering, at the limit as a future-proofing step.
In ES3 terms, a fixture is a property with the DontDelete
attribute set.But there's more to it than that, for reasons expanded on in #233 to do with namespace lookup and early binding. And there is another case that Lars pointed out recently to do with dynamic classes, but I'll avoid trying to summarize it except to say that a fixture is a fixed property that can't be deleted or shadowed.
Thanks, that's a good summary, though some of the commentary in the ticket was confusing (like lth's example - was x.toString decided
to be ambiguous?).
No, the ticket is open and it needs more work. I wonder, though,
whether Lars's example does not show how 'use namespace intrinsic' in
that lexical scope (block) simply makes that open namespace take
precedence. Since there is no explicitly opened public namespace in
that scope, x.toString there resolves to x.intrinsic::toString.
The RI is buggy, it does not make const property initialisers
DontDelete:
var x = { const toString: 37 } { use namespace intrinsic x.toString }
[function Function] x.toString
37
x.toString = 42
42
x.toString
37
delete x.toString
true
x.toString [function Function]
I filed bugs.ecmascript.org/ticket/372 on this RI bug.
We should work on this ticket more in the near term, I think.
On Mar 11, 2008, at 1:14 AM, Maciej Stachowiak wrote:
The optional second argument to make propertyIsEnumerable a setter has some practical problems:
- It violates the very strong norm that getter and setter functions are separate and have their own different arguments. It will make the feature harder to use and code using it harder to understand, to some extent.
- It makes it impossible to feature test for the ability to disable enumerability, compared to a separate setter.
I agree with these points.
Against the argument that it is too risky compatibility-wise to add a new method to the object prototype (apparently the only reason things were done),
No, that was not the only reason. The other reason (it may be a bad
reason, but I pointed it out in an earlier message to make it
apparent): so calls passing two arguments would not cause ES3
implementations to fail as would "undetected" calls to a new method.
But I agree that the numbered points you make are sufficient to
reconsider the two-argument proposal that was accepted a while go.
Just for the es4-discuss list's information, I want to provide some
historical context. the working group at that time had many large and
small issues to grapple with, so we did not dedicate too much time to
the "Prototype Ajax library" problem (wanting to set DontEnum on user-
defined properties). We were not so much in favor of the de minimus
second argument idea that was quickly settled on, as we were opposed
to adding another method with a plain name to Object.prototype. And
even that wasn't a strong objection, in light of namespaces (more
below on that).
This was at the time very much "small potatoes", which I for one am
happy to revisit (I've already sketched a namespace-based
alternative). It should not be used to impeach the entirely of the
group's work.
I propose that it is overblown. Mozilla has defined new methods and properties on all objects.
In 1998 or 1999, we added "meta" methods and properties,
distinguished (after Python) by leading and trailing double underscores.
We haven't tried adding a plainly named property to Object.prototype
in quite a while.
We have copied some in Safari and seen no web compatibility issues, I assume Mozilla has not had any as well. Specifically, I am thinking of defineSetter, defineGetter, lookupSetter, lookupGetter, and proto.
These are all metaprogramming hooks, but so arguably is
setPropertyIsEnumerable -- so I agree with your reasoning here.
Has any study been done on how many sites currently make use of the property names of a likely setter for propertyIsEnumerable?
We have not crawled the web looking for plain-named methods, but we
have spidered for other patterns we proposed to change incompatibly,
and the results are not decisive. For example, looking for attempts
to rebind standard constructors found only a dead MSN .js file
rebinding Error, but when we tried in Firefox 3 beta 2 to lock down
the global bindings for Date, etc., we found many sites and web apps
breaking. This is a different case, of course, but my point stands:
searching the web is informative, but not decisive.
Debate is only good. I merely pointed out the obvious thing, namely that until there is an alternative proposal written up to deal with this issue, the current design stands unless the WG, as a group, decides to just get rid of it (leaving the problem it was designed to solve solution-less).
Surely reviewing this spec is the appropriate time to revisit this issue.
Yes, and this list is the right place. It's pretty clear Lars agrees,
and nothing he wrote cited above contradicts it. So let's discuss more.
I'd like to propose the following three alternatives to the current proposal:
- Remove the feature entirely from ES4 (as part of the "judicious feature cuts" process) until a more appropriate syntax is found
- Replace two-argument form of propertyIsEnumerable with setPropertyIsEnumerable
- Replace two-argument form of propertyIsEnumerable with setPropertyIsEnumerable
I think each of these options is so trivial as to not require a lengthy write-up.
What is the process for the WG deciding whether to make one of these changes, or something else?
Arguing here in es4-discuss might be enough, if the argument makes
progress and the participants reach a consensus.
I like the idea of making non-public-namespaced properties be not-enumerable and getting rid of DontEnum. We've talked loosely about it for a while. But it's remained loose talk, it has never made it to the stage where it is a coherent proposal.
I don't like syntax-based alternatives since they cannot be made to degrade gracefully in ES3 UAs.
I think that's a good point too, since as noted above it was one of
the two reasons for the lame-duck two-argument proposal we've accepted.
Brendan Eich wrote:
If we end up making all namespaced properties non-enumerable, then we
may want a property iterator that returns the names of all properties
in a given object. Even if this is not in the standard, it's likely
to be an extension. Or it could be added in the future.
Absolutely. In fact, I thought there was already an iterator that yields all properties regardless of enumerability, just that this iterator would not be the default iterator for objects. If it's not in ES4, it should definitely be there because it's essential for those that want to customize enumerability.
The question of whether ES3+4 mixtures might not want enumerable ES3
properties to appear to be in a predefined-in-ES4 namespace seems to
me worth considering, at the limit as a future-proofing step.
What do you mean by ES3+4 mixture - how would you tell that one part is ES3 and another part ES4, or how a program is pure ES4?
-Yuh-Ruey Chen
Peter Hall wrote:
The latter. It is weird, I agree. But the enumerator can easily be customized, so I want the default enumerator be as non-magical as possible - and therefore, as simple as possible. In fact, I'm a bit bothered that propertyIsEnumerable is a method of Object yet can be rendered useless when changing the enumerator - along this line of reasoning, one would think that such a specific method shouldn't be a method of Object at all, but rather the enumerator itself.
So how would you go about enumerating the properties of an arbitrary dynamic object, whose properties could be in namespaces?
I thought there was an iterator that would enumerate all properties regardless of enumerability (including those in other namespaces), but apparently not. There should be such an iterator though.
Yeah, I vaguely remember some discussion concerning multiple namespaces per property a long time ago (maybe a year ago). It was rejected probably due to complexity.
Not being able to combine them effectively makes many of the initial proposal's use-cases useless, because two use-cases may arise simultaneously when integrating two codebases, and be irreparably conflicting.
Peter
What "initial proposal" are you talking about?
Yuh-Ruey Chen wrote:
Brendan Eich wrote:
If we end up making all namespaced properties non-enumerable, then we
may want a property iterator that returns the names of all properties
in a given object. Even if this is not in the standard, it's likely
to be an extension. Or it could be added in the future.Absolutely. In fact, I thought there was already an iterator that yields all properties regardless of enumerability, just that this iterator would not be the default iterator for objects. If it's not in ES4, it should definitely be there because it's essential for those that want to customize enumerability.
As was discussed before, the language cannot have such an iterator. Were this iterator to exist, an attacker could use it to discover hidden properties of objects, get access to private and other restricted namespaces, alter private state, and break through abstraction barriers.
Waldemar
FWIW, AS3, which doesn't have iterators, enumerates only the expando properties of an object. This was in part because we didn't have (qualified) name objects that could be enumerated, and in part because of it being complicated to determine the set of names that could be safely exposed (e.g. all the names in open namespaces, all the public names regardless of their current visibility, etc). IOW, we punted.
Draft 3 of the spec for the Object class. Changelog near the beginning. (Notably this version includes draft code for createProperty.)
That sore thumb propertyIsEnumerable.
propertyIsEnumerable, as a setter, sets the DontEnum flag for the object's own property. A value of 'false' makes the prop not show up in - for in. propertyIsEnumerable, as a getter, gets the value of the negation of the DontEnum flag, and does not check the prototype chain. Has nothing to do with whether or not the object shows up in - for in.
The addition adds a non-orthogonal aspect, apparently to add some needed functionality.
This unituitive behavior is especially problematic to amateur, or unskilled developers. The following code example from google demonstrates this to be true:
doctype.googlecode.com/svn/trunk/goog/base.js
if (Object.prototype.propertyIsEnumerable) { /**
- Safe way to test whether a property is enumarable. It allows testing
- for enumarable on objects where 'propertyIsEnumerable' is overridden or
- does not exist (like DOM nodes in IE).
- @param {Object} object The object to test if the property is enumerable.
- @param {string} propName The property name to check for.
- @return {boolean} True if the property is enumarable.
- @private / goog.propertyIsEnumerable_ = function(object, propName) { return Object.prototype.propertyIsEnumerable.call(object, propName); }; } else { /*
- Safe way to test whether a property is enumarable. It allows testing
- for enumarable on objects where 'propertyIsEnumerable' is overridden or
- does not exist (like DOM nodes in IE).
- @param {Object} object The object to test if the property is enumerable.
- @param {string} propName The property name to check for.
- @return {boolean} True if the property is enumarable.
- @private */ goog.propertyIsEnumerable_ = function(object, propName) {
// KJS in Safari 2 is not ECMAScript compatible and lacks crucial methods
// such as propertyIsEnumerable. We therefore use a workaround.
// Does anyone know a more efficient work around?
/ BROKEN: this approach checks the prototype chain with - if(propName in object) // (gsmith) if (propName in object) { for (var key in object) { if (key == propName) { return true; } } } return false; }; }
Because we can see that the author appears to have made the assumption that propertyIsEnumerable would check the prototype chain. This is a perfectly natural assumption.
What is more confusing is that with the new proposal:
function X(){} X.prototype = { p : true; };
var x = new x; x.propertyIsEnumerable('p'); // false, p is in the [[Prototype]] for(var prop in x) alert(prop); // alerts 'p'
Garrett
2008/3/17 Lars Hansen <lhansen at adobe.com>:
On 2008-05-15, at 18:48 EDT, Garrett Smith wrote:
That sore thumb propertyIsEnumerable.
propertyIsEnumerable, as a setter, sets the DontEnum flag for the object's own property. A value of 'false' makes the prop not show up in - for in. propertyIsEnumerable, as a getter, gets the value of the negation of the DontEnum flag, and does not check the prototype chain. Has nothing to do with whether or not the object shows up in - for in.
The addition adds a non-orthogonal aspect, apparently to add some needed functionality.
This unituitive behavior is especially problematic to amateur, or unskilled developers. The following code example from google demonstrates this to be true:
[...]
Is a virtual property (one defined by a getter/setter pair) enumerable?
Is Object.prototype.propertyIsEnumerable required to be generic? Can
it be applied to a non-Object (a native DOM object)?
OpenLaszlo struggles with this sore thumb too. I hadn't even
considered that someone might replace Object methods. Check out
objectOwnProperties
, the last function in (LzDebug)[svn.openlaszlo.org/openlaszlo/trunk/WEB-INF/lps/lfc/debugger/LzDebug.lzs
].
This is the draft for the Object class. It's very similar to the Object object in ES3, the only addition (to my knowledge) is the extra parameter to propertyIsEnumerable. And of course the specification formalism is new.
Please comment.