On Jul 29, 2013, at 5:11 PM, David Bruant wrote:

> Le 29/07/2013 20:41, Allen Wirfs-Brock a écrit :
>> The legacy [[Class]] internal property conflated these two concepts.  
>> Sometimes it was used for to ensure that a built-in method was operating 
>> upon an instance that actually had the internal state or conformed to other 
>> implementation level invariants needed by the method. Other times, [[Class]] 
>> was tested  for basic external behavioral classification purposes that don't 
>> really care at all about implementation level object invariants.
>> 
>> In most cases, the ES6 spec. language such as  "if O is an exotic X object" 
>> or "does X have a [[XXX]] internal data property" works fine as a direct 
>> replacement of an ES5  [[Class]] test because there are a one-to-one 
>> correspond between a ES6 built-in that is represented by specific kind of 
>> exotic object or that has a specific internal data property and with a ES5 
>> built-in with the corresponding [[Class]] value.
> Can all the [[Class]] replacement tests be passed by proxies in a way or 
> another so that a proxy can impersonate a given "class" of exotic object? 
> (most likely by having one of these objects as target)

Nope.  We would have had the same problems if we had kept [[Class]]. In ES<=5.1 
[[Class]] is used to conflate situational specific testing for a number of 
independent characteristics of objects.  Some of those characteristics can not 
be transparently prloxied (for example this object reference directly refers a 
special object representation with some specific implementation dependent 
representation of some private state 0)

> 
> Also, I fail to understand the difference between "if O is an exotic X 
> object" and "if O.[[Class]] === X".

There really isn't much.  But getting rid of [[Class]] enables us to separate 
the previously conflated characteristics.

> 
>>> If proxies for arrays do not pass such tests, some built-ins behave in 
>>> unexpected ways.
>> What's expected?  Just because a proxy has an exotic array object as its 
>> target doesn't mean that is functions as an exotic array.
> This suggests that the opposite could be true, that is that a proxy with any 
> target might impersonates an array as long as it passes some tests. I wonder 
> how much of a good idea this is.

One of the goals for ES6 proxies was to enable self-hosting of built-ins.  In 
theory, there is no reason that a proxy couldn't be use buy an implementation 
to implement its "exotic array objects".  However, because the spec. explicitly 
distinguishes  "exotic array objects" from other built-in exotic object and 
from general proxies, such a host implementation would still have to have a way 
to identify the proxy-implemented exotic arrays as such and for test for them 
in every context where the spec. says an "exotic array object" is required. 
That branding test would be that implementations way of implementing "is O is 
an exotic Array object".

> 
> The (granted implicit) model of "a proxy for exotic object X is seen by all 
> algorithms as an exotic object X" feels simpler even if it means that a proxy 
> might not act as an internal algorithm expects.

memory safety hazard.  Every place that checks for "is O an exotic X object" 
would have to have a subsequent "is O a Proxy whose target is an exotic X" and 
take different code paths.  If you screw it up, you may have memory safety 
issues. 

> In any case, regardless of how many tests a proxy passes, it has always a way 
>  to wrongly behave after having passed the test.

The key things are hard dependencies between native code built-in methods that 
expect specific fixed object layouts and the actual instances of those objects.

> 
>>> Here's the list of relevant built-ins, based on searching the ES6 spec for 
>>> "is an exotic Array object":
>>> * Object.prototype.toString
>> As currently spec'ed. toString will invoke the HasProperty(@@toStringTag) of 
>> the proxy and if the result is true invoke the Get trap to retrieve the 
>> value used to construct the toString result. All of the new built-ins have 
>> @@toStringTag methods.  I considered adding them for the built--ins that 
>> carry over from ES5.  Then, if the @@toStringTag of Array.prototype is 
>> "Array" then Object.prototype.toString(new Proxy([ ], { })) should return 
>> "[Object Array]".
> Excellent.
> It also means that any proxy that customizes its @@toStringTag could have 
> O.p.toString return "[object Array]", right?

right, but I forgot about step 20.e of Object.prototype.toString in the current 
draft.  This tries to prevent using @@toStringTag to return any of the ES<6 era 
[[Class]] tags.  There is past controversy about that step. Some think such 
aliased results should be allows (eg, the use case I described just above). 
Other think that it should throw in this case rather than the current hack in 
the spec.   There is also an argument that permitting something other than a 
true exotic array (for example) to return "[object Array]]" could be a breaking 
change if such an object is encountered by some legacy code.
> 
>> However, this would be a breaking change for existing code explicitly wires 
>> their prototype chain to inherit from Array.prototype.
> Saving existing code from proxies is a dead end in my opinion.

This has nothing to do with Proxies.

Consider:

var x = [__proto__: Array.prototype].

In ES<=5.1
   Object.prototype.toString(x)  //returns "[object Object]"

If in ES6, Array.prototype has an built-in @@toStringTag property whose value 
is "Array" then: 
   Object.prototype.toString(x)  //returns "[object Array]"

> 
>>> * Array.isArray
>> We've discussed the meaning of this in the past and have agreed that it 
>> should be considered a test for exotic array-ness and in particular the 
>> length invariant. A Proxy whose target is an exotic array may or may not 
>> qualify.
> I don't really understand what you're testing here to checking the length 
> invariant.

It's an implementation internal test.  It is what it means to be "an exotic 
array object". See 8.4.2 of the current ES6 draft for the complete 
specification of what it means to be an "exotic array object"

> Can a proxy be written to pass this test?
no, not with portable ES code.  The logic is within Array.isArray, not within 
the object it is applied to.

> Can a proxy be written so that the value of this test changes over time?
no

> 
>>> * JSON.stringify
>> There are two distinct use cases of [[Class]]==Array in the ES5 spec of this 
>> function.  Both are currently in the ES6 spec. as  exotic array tests.  The 
>> first use case is to see if the "replaceer" argument is an white-list array. 
>>  This could be special cased via a @@isJSONArray that would work through a 
>> proxy, but  I dubious that the additional complexity is justified.
> Maybe a test on whether the value is iterable? (so that replacer can also be 
> a Set, etc.)

That would be an extension to the current JSON.stringify functionality.  Not 
implausible, but nobody has proposed any such changes for ES6.



> 
>>> It's worth noting that I hit upon these issues because users of my 
>>> harmony-reflect shim, which are using direct proxies today in ES5, have 
>>> reported them (see [1],[2]). This adds some evidence that users expect the 
>>> above built-ins to behave transparently w.r.t. proxies for their use cases. 
>>> My library patches some of these built-ins to recognize my own emulated 
>>> proxies, but this is just an ad hoc solution. ES6 users will obviously not 
>>> be able to do this.
>> They may expect this, but I don't see what generalizations we can make.  
>> Whether a proxy over a built-in is behaviorally substitutable for the 
>> built-in completely dependent upon the the definition of the specific proxy.
> Again, this seems to suggest that a proxy could pretend to be a Date to one 
> algorithm, an Array to another and a RegExp to another. I'm not sure what 
> good comes out of that.

What do you mean to "be a Date".  The ES6 spec. defines Dateness very 
specifically.  It is an ordinary object (ie, not a Proxy) that has a 
[[DateValue]] internal data property that can be recognized as such by an 
implementation.  The only way the ES6 spec. provides for creating such an 
object is via the Date[[@@create]] method defined in 15.9.3.5 of the current 
draft.  This method is inherited by subclass constructors so  instances of 
subclasses of the Date constructor, by default, also will pass the Dateness 
test.

However, the this is not a very interesting test.  It is only used to make sure 
that built-in Date.prototype methods that assume that they have direct access 
to a [[DateValue]] slot actually are operating upon such an object. 

Application level JS code really should not be doing such pseudo-nominal type 
testing.  At the application level, JS operates best using dynamic behavioral 
types.  If an object has the methods specified for Date instances and those 
methods return the expected results, why do you case whether or not you are 
dealing with an instance of the original built-in Date or an instance of 
somebodies alternative Date implementation.

Similarly, the ES6 spec. is careful to make sure that an object that is an 
instance of an enhanced or alternative regular expression engine can be used 
anyplace a built-in RegExp object was previously required.

Allen




> 
> David
> 

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to