On Jan 20, 2015, at 11:38 AM, Jordan Harband wrote:
> "X is bad" is, quite frankly, mostly irrelevant here. When X is bad in JS,
> then TC39 seems to have gone with primarily one of two choices: use strict
> mode to remove it, or, provide a better option Y so that developers *want* to
> move away from X and start using Y. In this case, the value of type checking
> doesn't matter - people do it, and so it must be considered.
>
> There are two primary usages of Object.prototype.toString in my eyes:
> - constructs like `Object.prototype.toString.call(new String('foo')) ===
> Object.prototype.toString.call('foo')` (or using that to throw an explicit
> error "don't use object forms of primitives") which ensures that people using
> my code won't fall into the very common footgun (thanks, Java) of using boxed
> primitives. (The same issue will apply to Symbols in ES6, but at least I can
> do `typeof Symbol.prototype.toString.call(value) === 'symbol'`)
> - Testing of values from other realms (namely iframes).
> `Object.prototype.toString.call(value) === '[object Array]'` is a remarkably
> consistently written return value of an `Array.isArray` polyfill, since it
> works no matter how somebody has monkeyed with any global Array object.
>
> Certainly it is trivial to construct a malicious array, for example, and pass
> that around breaking all sorts of things. The goal, in my opinion, of
> Object.prototype.toString checking is *not* security - it's avoiding common
> developer hazards. In other words, I want my code to fail fast when the
> developer unintentionally passes me the wrong thing - something that I think
> we can agree happens often.
>
> 1) configurability of @@toStringTag
> It appears that TC39 considers it important to not break existing JS code
> with spec changes. Thus, `Object.prototype.toString.call(foo)`, for any ES5
> value "foo", must always and forever return the same value that it returned
> in ES5 - otherwise, existing code may follow different code paths in ES5
> versus ES6, which is a hazard. This leads me to the belief that @@toStringTag
> values on ES5 builtins should never be changeable.
>
> Some have replied to this, "if you don't run first, all bets are off - freeze
> it if you want it". Fair point! If you don't run first and keep a reference
> to Object.prototype.toString, you're screwed anyways. However, in ES5, if I
> *do* run first, I have 100% opt-in protection against somebody breaking
> things I care about. Essentially all polyfilled `Array.isArray` code on the
> web could break if it is possible to redefine
> `Array.prototype[@@toStringTag]` (in any realm, not just the one I start in),
> which is currently possible by default in ES6.
>
> I believe that all built-in objects, in all realms, should have a
> nonconfigurable @@toStringTag for this reason.
>
> 2) Since people *are* doing this type checking, if I make an object that
> defines its @@toStringTag value to return "Array", I will break code that
> does this. (Hence the protections in
> https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
> I've filed https://bugs.ecmascript.org/show_bug.cgi?id=3506 to discuss the
> missing values (namely Math, JSON, and Object, and Null, and Undefined have
> been mentioned as well).
>
> Currently, the spec handles this by including a whitelist of values, and
> specifying that any value not in this list receives a "~" prefix. (The value
> of the prefix itself is irrelevant, let's please not bikeshed it).
>
> My proposal is that rather than maintain a whitelist, and have that added
> complexity, that ES6 specifies that any user-defined @@toStringTag value will
> *always* and unconditionally have a prefix applied.
>
> --------------------
>
>
> I believe that either of these proposals by themselves will be a win - but
> with both together, "nominal type checking" / branding code will continue to
> work, and there will be no hazards or footguns by default.
>
> For those who dislike this kind of code, I challenge you and the committee to
> finalize and publish a better approach for answering the question "does this
> behave like an array" or "does this behave like a map" that work cross-realm
> (besides exhaustive duck-typing and/or feature detection), rather than
> attempting to simply oppress what many consider to be a valid approach, and a
> functional and existing one.
there are two things going on with @@toStringTag and ES6 O.p.toString
1) @@toStringTag is primarily intended as an oopen-ended extension point that
allows JS class (and other abstraction) to parameterize the descriptive string
produced by O.p.toString. This parameterization is desirable (as an
alternative or supplement to over-riding toString) in support of (usually
debugging) use cases where O.p.toString called as a reliable means to get a
displayable "[ ]" description of an object.
For that purpose, we don't really care whether an object's @@toStringTag is
stable or even whether it spoofs one of the legacy tag values. It's just a hook
for plugging into that extension point for that purpose.
2) [[Class]] is gone because it did not provide an extensible way to do brand
checking yet it was (indirectly through O.p.toString) being used in that way.
O.p.toString never provided a way to do brand checking on values defined using
JS definable constructors. When and if we decide that we can no longer live
without such an extensible branding mechanism we will define one, with its one
mechanism and interface. We wouldn't misuse O.p.toString for that purpose.
If developers started to use @@toStringTag as a brand that they access using
O.p.toString (and we start making accommodations in support of that usage) we'd
be worse off WRT branding then we are with ES5.
I would sooner see @@toStringTag go completely way than see it start to be used
as a brand. If that is how we really think it will go down, then I say we
should screw the convenience that we were trying to offer with #1 above and
completely revert to what is essential ES5 behavior. In therms of the current
spec. that means anything allocated by a ES defined constructor would answer
"[object Object]".
As I said in a tweet yesterday: Our dilemma: some things that made sense in the
past & people are used to, don't make future sense
Using, O.p.toString is one of those things. If improving the "produce a debug
string" behavior is going to reenforce a O.p.toString usage that doesn't make
sense for the future (or even for ES6), then we probably shouldn't improve
O.p.toString.
(But one remaining problem, if we lost @@toStringTag then there isn't any
particularly good way to explain the behavior of it when applied to DOM
objects.)
allen
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss