Read access to [[Class]]?
[[Class]] begone in ES6.
Also you are making an ambiguous API. If we add class syntax and someone writes class string {}, which "string" is string?
The "class" trope should not be mixed with "type", IMHO.
On Fri, Jan 27, 2012 at 12:08 PM, Brendan Eich <brendan at mozilla.org> wrote:
[[Class]] begone in ES6.
Assuming the public facing API has no intention of breaking back compatibility, what internal property will be read to determine the return value of Object.prototype.toString (15.2.4.2)?
Rick Waldron wrote:
On Fri, Jan 27, 2012 at 12:08 PM, Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:
[[Class]] begone in ES6.
Assuming the public facing API has no intention of breaking back compatibility, what internal property will be read to determine the return value of Object.prototype.toString (15.2.4.2)?
[[NativeBrand]] if my memory does not fail me; but it only works for builtins, other will all return [[object object]]. Look at the newest proposal, allenwb has posted a link a week or two ago (I can't find it, I probably deleted the mail with it already).
On Jan 27, 2012, at 9:59 AM, Rick Waldron wrote:
On Fri, Jan 27, 2012 at 12:08 PM, Brendan Eich <brendan at mozilla.org> wrote: [[Class]] begone in ES6.
Assuming the public facing API has no intention of breaking back compatibility, what internal property will be read to determine the return value of Object.prototype.toString (15.2.4.2)?
The current ES6 draft defines its like this:
15.2.4.2 Object.prototype.toString ( )
When the toString method is called, the following steps are taken:
If the this value is undefined, return "[object Undefined]".
If the this value is null, return "[object Null]".
Let O be the result of calling ToObject passing the this value as the argument.
If O has a [[NativeBrand]] internal property, let tag be the corresponding value from the Table 23.
Else, let tag be the string value "Object".
Return the String value that is the result of concatenating the three Strings "[object ", tag, and "]".
Table 23 — Tags for Classified Native Objects
[[NativeBrand]] Value tag Value NativeFunction "Function" NativeArray "Array" StringWrapper "String" BooleanWrapper "Boolean" NumberWrapper "Number" NativeMath "Math" NativeDate "Date" NativeRegExp "RegExp" NativeError "Error" NativeJSON "JSON" NativeArguments "Arguments"
Basically, there is a fixed set of hardwired (to the spec.) object kinds that toString knows about. There is no implication that all objects must carry around an additional string value that is used to parameterize toString. If you want to extend an implementation with new built-in object kinds that toString recognizes then you must also extend your implementation of toString.
[[Class]] has been treated by some as a mandatory implementation level extension point for adding new internal types, but it wasn't.
Note that I have considered extending the above definition of toString with an explicit ES level extension point based using private names:
If O has as well known private name property (think of it as: privateToStringTag) then tag is set to ToString of the value of that property. This would happen between steps 4 and 5.
Thanks to both of you, I wasn't aware that this was already documented in an ES6 draft - my apologies.
I noticed in the ES6 draft it states that host objects do not have
the [[NativeBrand]] internal property.
I think "do not have" should be reworded to "are not required to have".
This would allow existing inferences like
{}.toString.call(window.opera) == '[object Opera]'
.
Is the window.opera object in fact a host object? From the documentation at www.howtocreate.co.uk/operaStuff/operaObject.html, it seems the
only magic behavior is in its methods, not in the object itself. Is there something magical about the behavior of this object itself?
Note that just because the object itself is non-standard and provided by the host does not make it a host object.
On Jan 27, 2012, at 10:15 PM, John-David Dalton wrote:
I noticed in the ES6 draft it states that host objects do not have the [[NativeBrand]] internal property. I think "do not have" should be reworded to "are not required to have". This would allow existing inferences like
{}.toString.call(window.opera) == '[object Opera]'
.
I probably need to be more explicit about host objects and add another step just before step 5
If O is a host object, let tag be an implementation defined string value. The value may not be "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String".
Also, the definition of Host Object needs to be tighten up to make it clear that an object is only a host object if it has some "magical" behavior.
Finally, window.opera is really just a normal object it could use my proposed private name based mechanism to set its toString tag.
On Jan 28, 2012, at 8:21 AM, Allen Wirfs-Brock wrote:
On Jan 27, 2012, at 10:15 PM, John-David Dalton wrote:
I noticed in the ES6 draft it states that host objects do not have the [[NativeBrand]] internal property. I think "do not have" should be reworded to "are not required to have". This would allow existing inferences like
{}.toString.call(window.opera) == '[object Opera]'
.I probably need to be more explicit about host objects and add another step just before step 5
If O is a host object, let tag be an implementation defined string value. The value may not be "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String".
Also, the definition of Host Object needs to be tighten up to make it clear that an object is only a host object if it has some "magical" behavior.
Yes, it's an old subtle terminological topic which is required to be mentioned in the spec, I think it would be good.
In fact, if an object is completely conforming with the spec, it cannot be distinguished from native objects -- regardless in which language it's written (in JS itself or in a lower language).
Moreover, many "host" objects are implemented in JS itself -- e.g. some standard libraries as in Node.js. I called them inefficiently as "native-host" objects.
But from this viewpoint -- is this algorithm step is correct? I.e. window.opera being as a native-host can nevertheless have as its [[NativeBrand]] values of "Array", "Object", etc?
And a host object then becomes as "an object provided by a host environment and has semantic differentiations from this specification".
(the only thing it's needed to consider is not to make it even more confused or complicated).
Dmitry
@MarkM
Is there something magical about the behavior of this object itself?
For kicks the "magical" bit would be its awesome pre ES6 internal [[Class]] value. Can we leave "magical" out of a spec convo?
Note that just because the object itself is non-standard and provided by the host does not make it a host object.
es5.github.com/#x4.3.8 A host object is an "object supplied by the host environment to complete the execution environment of ECMAScript."
Seems like the opera
object falls into that description. I mean it
could be a "built-in" if supplied by the ECMAScript environment but
that's beside the point. I really just want the wiggle room that ES5.x
provides for [[Class]]/flags/tags/brands.
@Allen
I probably need to be more explicit about host objects and add another step just before step 5
If O is a host object, let tag be an implementation defined string value. The value may not be "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String".
+1 for less complex solution and a continuation of the pre-ES6 wording.
Also, the definition of Host Object needs to be tighten up to make it clear that an object is only a host object if it has some "magical" behavior.
-1 magical.
Finally, window.opera is really just a normal object it could use my proposed private name based mechanism to set its toString tag.
-1 for the more complex option.
The current ES6 draft defines its like this:
15.2.4.2 Object.prototype.toString ( )
When the toString method is called, the following steps are taken:
If the this value is undefined, return "[object Undefined]". If the this value is null, return "[object Null]". Let O be the result of calling ToObject passing the this value as the argument. If O has a [[NativeBrand]] internal property, let tag be the corresponding value from the Table 23. Else, let tag be the string value "Object". Return the String value that is the result of concatenating the three Strings "[object ", tag, and "]". Table 23 — Tags for Classified Native Objects
[[NativeBrand]] Value tag Value NativeFunction "Function" NativeArray "Array" StringWrapper "String" BooleanWrapper "Boolean" NumberWrapper "Number" NativeMath "Math" NativeDate "Date" NativeRegExp "RegExp" NativeError "Error" NativeJSON "JSON" NativeArguments "Arguments"
Not sure that it makes sense (backward compatibility...), but one could handle primitive booleans, numbers, and strings differently:
Currently: $ Object.prototype.toString.call(true) '[object Boolean]'
Alternative: $ Object.prototype.toString.call(true) '[primitive boolean]'
On Jan 28, 2012, at 11:33 AM, John-David Dalton wrote:
@Allen
Also, the definition of Host Object needs to be tighten up to make it clear that an object is only a host object if it has some "magical" behavior.
-1 magical.
the spec. won't actually say "magical", but the important distinction that the spec. needs to make concerns objects that have special behaviors (ie, different from the "native object" behaviors defined within the specification). It really doesn't matter to the spec if such objects are supplied by the "engine" implementation or a host environment or some "foreign object" interface. The only thing that is important is that they in some way deviate from the definition of native objects. Similar, if an object is not observably different in its behavior from the specification for a native object then it does not matter how it was defined.
In the case of ({}}.toString.call(window.opera) the fact that it returns a value that is not in the specification is enough "magic" to qualify window.opera as such a non-native object.
Finally, window.opera is really just a normal object it could use my proposed private name based mechanism to set its toString tag.
-1 for the more complex option.
We want to be able to define things that were historically implemented as "host objects" using pure ECMAScript code. One things such objects have done is to extend the range of values produced by Object.prototype.toString. So, we need a pure ECMAScript way to accomplish that.
@Allen
We want to be able to define things that were historically implemented as "host objects" using pure ECMAScript code. One things such objects have done is to extend the range of values produced by Object.prototype.toString. So, we need a pure ECMAScript way to accomplish that.
You could simplify it by making NativeArray = "Array"
and
NativeRegExp = "RegExp"
and so on. Then you could drop the "Tags"
table and extending the range of Object#toString is as easy as adding
a custom [[NativeBrand]] property value.
Axel Rauschmayer wrote:
Alternative: $ Object.prototype.toString.call(true) '[primitive boolean]'
No gratuitous runtime incompatibility, it will only break compat and punish early browser impelmentations. We've been over this. Please resist the urge to "clean things up".
On Jan 28, 2012, at 4:07 PM, John-David Dalton wrote:
@Allen
We want to be able to define things that were historically implemented as "host objects" using pure ECMAScript code. One things such objects have done is to extend the range of values produced by Object.prototype.toString. So, we need a pure ECMAScript way to accomplish that.
You could simplify it by making
NativeArray = "Array"
andNativeRegExp = "RegExp"
and so on. Then you could drop the "Tags" table and extending the range of Object#toString is as easy as adding a custom [[NativeBrand]] property value.
and name the internal property "[[Class]]". But then, how would you implement it in pure ES code?
Also noite that "[[Class]]" doesn't actually exist as string valued per object state in many implementations. That is one of the problems with the current spec, it creates the impression that it such state actually exists.
@Allen
and name the internal property "[[Class]]". But then, how would you implement it in pure ES code?
You can name the internal property whatever you want [[Class]] / [[NativeBrand]] / [[Whatevz]]. It's internal so less important to me what the name is.
I don't think setting [[NativeBrand]] or having a mechanism for returning custom Object#toString values should be allowed outside of the ES implementation internals (ignoring overwriting the method with a dev defined method).
This is one of the reasons why {}.toString.call(window.opera)
is
such a strong inference because it's harder to fake w/o mucking with
the Object.prototype.
Also note that "[[Class]]" doesn't actually exist as string valued per object state in many implementations. That is one of the problems with the current spec, it creates the impression that it such state actually exists.
Sounds like a per implementation issue/detail. I'm sure there are lots of internal corners cut in every implementation.
John-David Dalton wrote:
Also note that "[[Class]]" doesn't actually exist as string valued per object state in many implementations. That is one of the problems with the current spec, it creates the impression that it such state actually exists.
Sounds like a per implementation issue/detail. I'm sure there are lots of internal corners cut in every implementation.
No, we do not want normative spec language requiring a per-object private-named property that determines the Object.prototype.toString.call(x).slice(8,-1) result.
Doing this naively will both impose unacceptable overhead and allow type-confusion attacks.
I sympathize with your goal of avoiding two cases, native and host, one of which enumerates cases, the other underspecified -- with the private-name override applying only to the non-enumerated cases. We should try to unify machinery where we can. Not at the price of per-instance "class-name".
@/be
No, we do not want normative spec language requiring a per-object private-named property that determines the Object.prototype.toString.call(x).slice(8,-1) result.
Doing this naively will both impose unacceptable overhead and allow type-confusion attacks.
I'm talking about adding back the wording that ES5.x currently has allowing host objects to set their own internal [[Class]] (or whatever it's new incarnation is) value. Most objects will still have an internal [[NativeBrand]] property of some kind associated with them so I don't see how this is an overhead concern.
Not at the price of per-instance "class-name".
The current ES6 draft handles this by allowing objects to not have a
[[NativeBrand]] property specified as Object#toString
will simply
return "[object Object]".
On Sat, Jan 28, 2012 at 7:11 PM, Brendan Eich <brendan at mozilla.org> wrote:
Axel Rauschmayer wrote:
Alternative: $ Object.prototype.toString.**call(true) '[primitive boolean]'
No gratuitous runtime incompatibility, it will only break compat and punish early browser impelmentations. We've been over this.
+1.
Please resist the urge to "clean things up".
Hi Brendan, I think I agree with what I think you're trying to say. But I do not agree with what you said. In going from ES3 to ES5, we've cleaned up many things. In going from ES5 to ES6, we are again striving to clean up some things, such as completion reform. But for every thing we successfully clean up, there may be 1000 we would have liked to clean up but can't. Such cleanups are the riskiest part of what we do; and we have learned, IMO, the appropriate level of conservatism. Sometimes we overshoot and sometimes we undershoot. But when we succeed, it is some of our most important accomplishments. I hope we never lose this urge.
John-David Dalton wrote:
Doing this naively will both impose unacceptable overheadand allow
type-confusion attacks.
I'm talking about adding back the wording that ES5.x currently has allowing host objects to set their own internal [[Class]] (or whatever it's new incarnation is) value. Most objects will still have an internal [[NativeBrand]] property of some kind associated with them so I don't see how this is an overhead concern.
It's not a problem if [[NativeBrand]] cannot be shadowed or own- vs. in-tested. Then it's just like [[Class]], only narrower.
Not at the price of per-instance "class-name".
The current ES6 draft handles this by allowing objects tonot have a [[NativeBrand]] property
This too may be ok if not observable. But you wrote:
You could simplify it by making NativeArray = "Array"
and
NativeRegExp = "RegExp"
and so on. Then you could drop the "Tags"
table and extending the range of Object#toString is as easy as adding
a custom [[NativeBrand]] property value.
and Allen then replied:
But then, how would you implement it in pure ES code?
That's desirable for a self-hosted DOM, but the use of a private name means that we have two cases: private name not "in" the object, so use an internal "class test" equivalent in observable outcome via O.p.toString to ES1-5; private name "in" the object, use its value (ToString'ed).
I don't see how to simplify further without using the private name for both cases, in which case we have overhead (less for "in" than "own", we could bind the private name in the appropriate prototype object) and, more significant: the type-confusion problem due to shadowing.
When I wrote:
Please resist the urge to "clean things up".
I did not mean resist at all costs. Just resist the low-reward/high-compat-break-risk temptations. HTH,
@/be
This too may be ok if not observable. But you wrote:
You could simplify it by making
NativeArray = "Array"
andNativeRegExp = "RegExp"
and so on. Then you could drop the "Tags" table and extending the range of Object#toString is as easy as adding a custom [[NativeBrand]] property value.and Allen then replied:
But then, how would you implement it in pure ES code?
Sorry for the confusion. I wasn't suggesting exposing this stuff. I think the [[NativeBrand]] property should be internal.
@MarkM
A host object is an "object supplied by the host environment to complete the execution environment of ECMAScript."
Sigh. This came up last time as well, and as participants in the spec writing process our only excuse was "That text isn't normative". That doesn't really excuse it from being so wrong that it leads people into endless confusion. If ES6 does keep the current terminology, we must at least fix or remove this confusing text.
JavaScript was my first language so I don't have the pollution of Java-isms to confuse me. I can only go by what's spec'ed as I lack the inside info ;)
Native object: es5.github.com/#x4.3.6 Built-in object: es5.github.com/#x4.3.7 Host object: es5.github.com/#x4.3.8
I should stress that the technical category of the object is less
important to me.
I really just wanted to +1 allowing objects like window.opera
to
continue to have {}.toString.call(...)
results like [object Opera]
under ES6.
I would still love to get read access to the [[Class]] property in ES.next.
Proposal: A function getTypeName().
Axel