David Bruant (2013-07-30T02:04:52.000Z)
Le 30/07/2013 03:09, Allen Wirfs-Brock a écrit :
> 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)
By "this object reference", are you talking about the "this" keyword? 
Even then, I wouldn't be sure to understand. Which part of ES5.1 are you 
referring to?
I believe a code snippet to explain would make things easier to understand.

>> 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.
When you test for "if O is an exotic array object", what characteristic 
are you testing for, then?
This still feels like branding to me.

>>>> 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".
Self-hosted code is privileged and may have some power not described by 
the ES spec (and as far as I know, that's actually the case both for 
SpiderMonkey and V8 self-hosted code). So self-hosted code can 
distinguish an actual array from a proxy, that's not an issue.
Non-privileged code being able to distinguish an array and a proxy for 
an array is more worrisome.

>> 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.
That's an implementation hazard; not sure what your point is here.
Note that the design of proxies (can only be new objects, are limited to 
an immutable {target, handler} data structure, stratified API, etc.) go 
a long way in preventing memory safety issues. It sounds reasonable to 
think that implementors will do the necessary rest of work to prevent 
these issues in already implemented algorithms.
Actually, if the stratification is respected and there is no free 
bypass, there is no reason to 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.
I don't think you're answering my point.
All tests that methods can pass are pre-conditions.
If a proxy can pass the test, it can fake it and then behave mistakenly.
If a proxy can't pass the test, then user-land code can distinguish an 
object from its target.
Something has to be given up. And giving up on indistinguishability 
would defeat the purpose of proxies (and the expectation as feedback on 
Tom's library suggests).

>>> 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]"
oh ok. Sorry about that, I had completely misinterpreted your point here.

>>>> * 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.
If some code contains an Array.isArray test, it will behave differently 
if running against a membrane or normal objects. I don't think that's 
desirable.
For membranes to be transparent, a proxy must be able to behave the 
*exact* same way its target would. Clearly if the logic of some built-in 
is within the built-in not within the object it is applied to, this is 
plain impossible.


>>>> 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".
I wrote "pretend to be a Date". That's at least to the algorithms 
testing, no matter how they do it.

> 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.
Same problem than above about membrane transparency.

Aside, but related: instead of passing a target (an already built 
object), it might be an interesting idea to have a Proxy constructor 
that takes an @@create so that the target passes the @@create-related tests.

David
domenic at domenicdenicola.com (2013-08-04T22:41:21.122Z)
Le 30/07/2013 03:09, Allen Wirfs-Brock a écrit :

> 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)

By "this object reference", are you talking about the "this" keyword? 
Even then, I wouldn't be sure to understand. Which part of ES5.1 are you 
referring to?
I believe a code snippet to explain would make things easier to understand.

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

When you test for "if O is an exotic array object", what characteristic 
are you testing for, then?
This still feels like branding to me.

> 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".

Self-hosted code is privileged and may have some power not described by 
the ES spec (and as far as I know, that's actually the case both for 
SpiderMonkey and V8 self-hosted code). So self-hosted code can 
distinguish an actual array from a proxy, that's not an issue.
Non-privileged code being able to distinguish an array and a proxy for 
an array is more worrisome.

> 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.

That's an implementation hazard; not sure what your point is here.
Note that the design of proxies (can only be new objects, are limited to 
an immutable {target, handler} data structure, stratified API, etc.) go 
a long way in preventing memory safety issues. It sounds reasonable to 
think that implementors will do the necessary rest of work to prevent 
these issues in already implemented algorithms.
Actually, if the stratification is respected and there is no free 
bypass, there is no reason to have memory safety issues.

> 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.

I don't think you're answering my point.
All tests that methods can pass are pre-conditions.
If a proxy can pass the test, it can fake it and then behave mistakenly.
If a proxy can't pass the test, then user-land code can distinguish an 
object from its target.
Something has to be given up. And giving up on indistinguishability 
would defeat the purpose of proxies (and the expectation as feedback on 
Tom's library suggests).

> This has nothing to do with Proxies.
>
> Consider:

oh ok. Sorry about that, I had completely misinterpreted your point here.

> no, not with portable ES code.  The logic is within Array.isArray, not within the object it is applied to.

If some code contains an Array.isArray test, it will behave differently 
if running against a membrane or normal objects. I don't think that's 
desirable.
For membranes to be transparent, a proxy must be able to behave the 
*exact* same way its target would. Clearly if the logic of some built-in 
is within the built-in not within the object it is applied to, this is 
plain impossible.


> What do you mean to "be a Date".

I wrote "pretend to be a Date". That's at least to the algorithms 
testing, no matter how they do it.

> 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.

Same problem than above about membrane transparency.

Aside, but related: instead of passing a target (an already built 
object), it might be an interesting idea to have a Proxy constructor 
that takes an @@create so that the target passes the @@create-related tests.