On Jul 29, 2013, at 9:06 AM, Tom Van Cutsem wrote: > Hi, > > Originally, direct proxies were specified such that a direct proxy would > inherit the [[Class]] of the target object it wraps. The intent was for e.g. > proxies-for-arrays to be recognized properly as arrays. > > In ES6 the [[Class]] property is gone and the current draft instead uses > wording such as "if O is an exotic Array object..." > > My understanding is that a proxy for an array would not pass such a test, > unless we explicitly state otherwise.
yes that's correct > > Allen, could you clarify your intent? What you are running into is very closely related to vthe private state access issues that led us to define DelegatingHandler/ForwardHandler, etc. The difference here is that you are concerned about external behavioral branding rather than internal implementation branding of the sort that occurs for private state access. 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. However, a proxy on such a built-in does not pass such a test. A Proxy object whose target is an Array is an exotic proxy object, not an exotic array object. It would be unsafe to allow such a proxy to be recognized an an exotic array regardless of whether it was done via the ES6 spec. language or via [[Class]]==Array if we still had it. A proxy for an array and an array is not likely to have the same implementation shape. A method that tests for an exotic array object might should be able to assume, for example, that its implementation depended encoding of the "length" property is present. Disaster would follow if the method tried to operate upon a proxy instance instead of an actual exotic array instance. The general solution to this problem is to decouple the shape branding/testing from the behavior testing. This is already done a number of places in the ES6 spec to support subclassing flexibility. For example, a number of the ES<=5 String method such as "replace" have different behavior depending upon whether an argument is a RegExp or a string. In the ES6 spec. the [[Class]]==RegExp test is replaced by an HasProperty test for @@isRegExp and and a form of double dispatching. This permits subclasses of RegExp (or even alternative regular expression engines that don't inherit from the built-in RegExp) to be used in that argument position. It may also be transparent to proxying (depending upon the handler). In general, an case by case analysis needs to be done for each of the situations where [[Class]] is used in ES5 for behavioral checking to se if and how an alternative needs to be provided for ES6. > > 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. > > 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]". However, this would be a breaking change for existing code explicitly wires their prototype chain to inherit from Array.prototype. > * 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. We could add a @@isArray tag, but I'm not convinced that it is really needed. > * Array.prototype.concat As currently spec'ed the [[Class]] test has been replaced with an exotic array test on the this value (supporting creating subclass instances) and a IsConcatSpreadable abstract operation (defined in terms of @@isConcatSpreadable) falling back to an exotic array test) used to decide whether to spread argument values. To make concat spread a proxied array you would have to explicitly define the @@isConcatSpreadable property of the target object. To maintain legacy computability with objects inheriting from Array.prototype we can't just have include @@isConcatSpreadable on Array.prototype. There may be other teaks we could do. But we talked about concat at last weeks meeting WRT Typed Arrays and concluded that concat is pretty bogus and it may be a turd that is not worth polishing. > * 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. The other use case is when deciding whether to use { } or [ ] notation to encode the object. The exotic array test is the right one for ES5 compatibility. The appropriate JSON encoding of a Proxy that has an exotic array target is going to be application dependent. JSON.parse already has an extensibility hook (toJSON) that presumably should be sufficient to deal with such objects. > * JSON.parse I believe that this [[Class]]==Array (now is exotic array) test is only applied to objects created by evaluating the JSON text as if it was an object literal. Such objects will never be proxies. > * ArrayBuffer.isView yup. The use cases for isView aren't all that clear to me. It could be expressed a @@isView test if it has important use cases. > > If we don't make proxies-for-arrays work transparently, we get results such > as: > > var p = new Proxy( [1,2] , {} ); Yes, and the only reason Array.prototype methods will work is because they have all been carefully defined to be generic and not depend upon any internal array state. The methods of most other built-ins will fail if you create an object in this manner because it defaults to delegating rather than forwarding. > > Object.prototype.toString.call(p) // "[object Object]", expected "[object > Array]" > Array.isArray(p) // false, expected true > [0].concat(p) // [0,p], expected [0,1,2] > JSON.stringify(p) // "{\"0\":1,\"1\":2}", expected "[1,2]" > > Note that none of these built-ins actually relies upon some structural array > invariant. Passing in a proxy that has properties such as "length", "0", etc. > works equally well. see above... > > > 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. I think this is just something that anybody who uses Proxy needs to be aware of. Allen _______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss