On Fri, May 24, 2013 at 7:02 AM, Tom Van Cutsem <tomvc...@gmail.com> wrote: > As I mentioned upstream, I don't think extending the Proxy API with hooks > for accessing the private state of every possible built-in is going to fly. > > Here's a constructive counter-proposal: > > The difficulty of your use case is that you ideally would want to use > inheritance to inherit all of Map's utility methods, but you are forced to > use composition (i.e. wrapping) to avoid users applying the superclass > methods directly onto your Map subclass. > > You could actually combine both techniques (inheritance and composition) to > achieve the best of both worlds: > > // use inheritance to inherit Map's utilities > class StringMap extends Map { > get(key) { > return super(String(key)); > }, > set(key, value) { > return super(String(key), String(value)); > }, > // similar for has, delete > } > > // use composition via explicit forwarding to achieve protection against > tampering with the StringMap instance directly: > function createProtectedStringMap() { > var wrappedMap = new StringMap(); > return new Proxy(wrappedMap, { > invoke: function(wrappedMap, name, args, thisBinding) { > return wrappedMap[name](...args); > } > }); > } > > var m = createProtectedStringMap(); > m.set(k,v) // wrappedMap.set(k,v) > m.get(k) // wrappedMap.get(k) > m.keys() // wrappedMap.keys() > > Map.prototype.set.call(m,k,v) // error: m is not a Map > // even: > StringMap.prototype.set.call(m,k,v) // error: m is not a Map > > It's not strictly necessary to use a Proxy here, since you could easily > manually implement a forwarder object that defines all of the methods in the > Map API. Using a Proxy has the advantage that if the Map API is extended > later, the protected StringMap will support the extended API. Note though, > that if in some later edition, the Map object suddenly defines a new method > that allows alternative ways of getting/setting keys in the Map, the > StringMap could be corrupted. You can't have it both ways: either you > restrict the set of operations on your StringMap, which increases integrity, > or you inherit an open-ended set of operations, which increases > forward-compatibility. > > If you're going to spec the protected string map, think of it as an exotic > object (aka host object) whose [[invoke]] operation explicitly forwards the > request to a wrapped StringMap. > > (Note: the "invoke" trap used above was only just added to the Proxy API at > this week's TC39 meeting, but you could implement this abstraction today by > having the "get" trap returning a bound method)
What does the invoke trap do? It looks like it somehow covers the "p.foo()" case? Can it handle "var f = p.foo.bind(p); f();" as well? Just pointing me to docs would be sufficient, if they exist yet. So, this is pretty clever. It gives me most of what I want, which is better than any suggestion so far. The only bit I don't like is covered by your statement "You can't have it both ways: either you restrict the set of operations on your StringMap, which increases integrity, or you inherit an open-ended set of operations, which increases forward-compatibility.". In most languages, I *can* have it both ways, because maps are parametric by their very nature; the only difference between a string->string map and an object->object map is the type declaration. I simply don't understand why Javascript's Map apparently makes this impossible, forcing all Maps to be any->any, and offering only hacks (admittedly clever ones) that partially work if you want a restricted type. I mean, if I was a bad actor and just *specified* that the object was a Map subclass but all attempts to set a value coerced both the key and the value to strings, that would be it. Simple and easy to understand, and implementation wouldn't be hard either - it would be a trivial change on the c++ side. It's just here on the good-actor side that it's hard, because Maps hop through proxies with their internal [[MapData]] property. It's just so simple and easy to do exactly what I want with an object map, where setting "style.vars[obj1] = obj2;" coerces both objects to strings and that's that. I don't understand why it's supposed to be so difficult for Maps, such that if I want the best solution I have to open myself up to corruption. :/ ~TJ _______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss