Re: My ECMAScript 7 wishlist
The only reason I've used `__noSuchProperty__` name in that example was to indeed remind `__noSuchMethod__` and others anti-name-clashing old patterns we all know but basically I was trying to explain that composing instead of extending is a win for these kind of magic behaviors. It is possible to use a Proxy without needing to go up to Object.prototype: ```js // before creating instances of MyClass // after MyClass definition ... Object.setPrototypeOf( MyClass.prototype, withNoSuchPropertyProxy( MyClass.prototype ) ); ``` but what if we'd like to add more than one magic functionality ? I also agree with Tab that is not so trivial to add is kind of functionality the right way. The old way ( I am not rehashing here, just explaining ) would have made everything easy for developers: ```js // easy ES3 shenanigans anti JIT MyClass.prototype.__noSuchMethod__ = function (id, args) { ... }; // fast path via Symbols as ES6 likes them ... MyClass.prototype[Symbol.noSuchProperty]= function (id) { ... }; ``` Although using all these Symbols to simulate what traits would do in other languages does not feel right. About TC39 simplifying dev life, well ... isn't that the purpose of most ES6 new features and sugar ? So yes, there's some good intent and job going on here but few times I've also read something like: it's hard for engines but there are APIs that let you do that on JS side ... now, imagine how hard and potentially wrong or slow would be in developers hands instead ... As summary, I don't have a solution or a proposal for noSuchProperty and I honestly don't feel the need for it (because Proxy) but I'd like to see some discussion (in another thread maybe) about lightweight traits that are also in Nicolas list (last point of his initial email) Best Regards On Fri, Sep 26, 2014 at 1:38 AM, Brendan Eich bren...@mozilla.org wrote: On Sep 25, 2014, at 7:56 PM, Boris Zbarsky bzbar...@mit.edu wrote: SpiderMonkey used to support __noSuchMethod__, I believe. I implemented __noSuchMethod__ long ago, for the TIBET folks (Smalltalk style JS framework; they wanted a doesNotUnderstand analogue). Please note well the difference between __noSuchMethod__ and anything like __noSuchProperty__. Even ignoring JITs, back in the interpreter only days, I could justify nSM because it was called on a slow path, when the only outcome without it was a guaranteed obj.foo is not callable or worse, obj.foo is not defined error. IOW nSM only kicked in when evaluating obj.foo(args) Not just obj.foo Any nSP of the kind we seem to be discussing would need to fail fast, on evaluation of the dot expression. That is a fast path. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Tab Atkins Jr. wrote: I don't understand how you inferred from Andrea's post that this wish-fulfillment __noSuchProperty__ magic property does not have to handle superclass delegation.. I did not infer that from Andrea's post as his position -- rather the reverse, because he said I also think Proxy already gives us a way ..., to wit the code I showed earlier. Hence my confusion about what was being proposed that differed. At minimum it needs to handle delegating to the object's own prototype (it would be a pretty poor NSP if it couldn't handle methodMissing use-cases as well), and I don't think there's a reasonable case to stop at just one level up; doing so would make this very fragile to refactoring your hierarchy, as methods show up as missing or not depending on where they end up in the class hierarchy. Methinks you protest too much. Just put it below Object.prototype and get on with life. That was the idea, anyway. Yes, what you propose is more flexible. Also more costly. Good luck selling implementors! I hope Andreas will comment now. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Fri, Sep 26, 2014 at 10:29 AM, Brendan Eich bren...@mozilla.org wrote: Tab Atkins Jr. wrote: I don't understand how you inferred from Andrea's post that this wish-fulfillment __noSuchProperty__ magic property does not have to handle superclass delegation.. I did not infer that from Andrea's post as his position -- rather the reverse, because he said I also think Proxy already gives us a way ..., to wit the code I showed earlier. Hence my confusion about what was being proposed that differed. At minimum it needs to handle delegating to the object's own prototype (it would be a pretty poor NSP if it couldn't handle methodMissing use-cases as well), and I don't think there's a reasonable case to stop at just one level up; doing so would make this very fragile to refactoring your hierarchy, as methods show up as missing or not depending on where they end up in the class hierarchy. Methinks you protest too much. Just put it below Object.prototype and get on with life. That was the idea, anyway. Yes, what you propose is more flexible. Also more costly. Good luck selling implementors! I hope Andreas will comment now. Out of curiosity, wouldn't Object.observe require implementors to add precisely this kind of hook into the vm anyway? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Dean Landolt wrote: Out of curiosity, wouldn't Object.observe require implementors to add precisely this kind of hook into the vm anyway? No, but O.o has its own costs. See http://lists.w3.org/Archives/Public/public-script-coord/2014JulSep/0204.html /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Fri, Sep 26, 2014 at 5:01 PM, Jason Orendorff jason.orendo...@gmail.com wrote: On Thu, Sep 25, 2014 at 6:56 PM, Boris Zbarsky bzbar...@mit.edu wrote: SpiderMonkey used to support __noSuchMethod__, I believe. It's still there. js var anObject = { __noSuchMethod__() { return what; } }; js anObject.saysWhat(); what https://bugzilla.mozilla.org/show_bug.cgi?id=683218 is the bug on getting it removed, in case anyone is interested. -- https://annevankesteren.nl/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Brendan Eich wrote: Dean Landolt wrote: Out of curiosity, wouldn't Object.observe require implementors to add precisely this kind of hook into the vm anyway? No, but O.o has its own costs. See http://lists.w3.org/Archives/Public/public-script-coord/2014JulSep/0204.html To say a bit more, as Andreas wrote at the link above, O.o has multifarious costs todo with mutation and notification. An unstratified and *general purpose* __noSuchProperty__ trap would impose costs on property sets as well as gets. Why as well as? Because of the override mistake where a prototype non-writable data property prevents assignment of a shadowing property; and where (symmetric to the data property case) a prototype accessor's setter must be called. If nSP is defined to do other than throw, e.g., to reify a property on demand from some peer property-space, then it must be called for both set and get in the missing case. You could define nSP (maybe someone will, it's still tedious to guess here :-/) to apply only to get, but assuming it would work as its name and interface suggest -- for more than merely throwing an error when called -- I'm proceeding as if it must be called (if present) for missing in-not-own property on set as well as get. The costs should accrue only if some object on the object at hand or along its prototype chain has nSP set on it, but that doesn't help much for implementors. Splitting out prototype chain walking for all gets and sets imposes high cost of code duplication and specialization in modern engines. So-called inline caches must be forked to handle both cases, if you want good performance for objects below the one with nSP. Andreas can say more, but this general problem of pervasive costs and complications due to unstratified traps is exactly the reason we put metaprogramming APIs on Proxies, mostly (legacy accessors, good old o[x] for computed name x, and a few others aside). (BTW, complaining about complexity of Proxy and Reflect is off topic, a move-the-goalposts attempt that makes me grumpy. Library code hides the details. The issue we're trying to get to is user-facing functionality, not implementation complexity inside the black box the user faces.) When I say TC39 wanted to let the ecosystem handle this it was first and foremost about not rushing library design by committee, or even by champions, into a spec, when the the greater number of developers could do something better, search multiple paths in the design space, cooperate and compete, and meet the demand. But there was also a desire not to jam more unstratified traps into objects, with their optimized hot paths. That's why proxies were added. If they can't meet the main use case that Nicholas had in mind, we should find out the hard way, from real code, not just from advance speculations. If the main use case is to emulate (for one's own object-based abstractions) Python and other languages that do not allow obj.typo to pass without runtime error, then a library to add a proxy just below Object.prototype and above one's prototypal (constructor = class) hierarchy might be enough. It won't satisfy all possible use-cases, but that's a positive if it hits the main target and spares us unstratified traps hitting hot paths. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Dean Landolt wrote: http://lists.w3.org/Archives/Public/public-script-coord/2014JulSep/0204.html Sure, O.o isn't free, and I get that using @noSuchProperty would likely result in all kinds of deoptimization. But of all the costs listed in that thread, I'm not seeing any mention of the cost of intercepting changes for notification queueing. You may have missed these points, then: Finally, just to clear up some myths, observation is nothing close to free inside V8 either: - Observing an object slows down all its mutations severely, easily by 10x-100x, depending on the case. - It invalidates a range of routine optimisations, especially for bulk mutations like on arrays. Essentially, observing an object forces every potentially impure operation on it onto the slowest path. - Observation introduces additional type polymorphism, which can cause even unrelated optimisations to fall off the cliff. Most of these costs are inherent to the mechanism, and there probably isn't much of a chance that they can be optimised by more than constant factors. /Andreas I'm assuming this has to be part of any O.o implementation (IIUC the only alternative would be polling observed objects every turn). More importantly that must already paid for all observed objects. You made the comment Good luck selling implementors, and I was just wondering aloud whether they've already been sold on doing the heavy lifting for the sake of O.o. See my longer followup. A general-purpose (as its name implies) nSP would hit lookup (get as well as set) paths, not just mutation (set). But it seems Andreas's just to clear up some myths words were missed, even considering only mutation (not notification). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Brendan Eich wrote: A general-purpose (as its name implies) nSP would hit lookup (get as well as set) paths, not just mutation (set). But it seems Andreas's just to clear up some myths words were missed, even considering only mutation (not notification). From private correspondence with Dean, it seems the cost of O.o (bad enough that the web platform cannot use it all over) may be assumed to be no better and not much worse than the cost of nSP. This isn't the case, nSP is strictly costlier and more complicated than O.o. In JS, `set` applies after prototype chain walking (to find the setter, or the non-writable data property [in which case error], or to decide to make an own property on the directly referenced object). Call the proto walk step `lookup`. `get` is more common than `set` but both require `lookup`, with differences. O.o hits `set` but nSP hits `lookup` so affects both reading and writing. A narrow throw-only nSP, say __throwOnNoSuchProperty__ with boolean value, would hit only the `get`-driven `lookup` paths. Still costlier in dynamic frequency and static code complexity than O.o, but much more targeted and (I think) optimizable. Need Andreas Rossberg and others to weigh in. The pay only if you use nSP or O.o argument has limits. O.o got into Harmony on that basis, but then the myth that it was cheap enough to use all over the web platform grew -- bad myth, Andreas busted it. A general nSP risks similar myth or hype-cycle and won't be optimized enough to stick, any more than O.o will, per Andreas's final Most of these costs are inherent to the mechanism, and there probably isn't much of a chance that they can be optimised by more than constant factors. This implementation complexity and perf cost issue is not just about implementor burden. It changes naive user-(myth)understood economics and adds attractive nuisance risk. But just considering VM costs (code and runtime), it's a big deal. A Proxy on a prototype chain, in contrast, modularizes the hit. It deoptimizes without multiplying `lookup` paths by two. The cost of host objects and proxies on prototypes has already been sunk, since you could make such chains even in 18 years ago. I don't like it when implementor vs. user trade-offs fight in what seems like a zero-sum game. I'm Captain Kirk, I don't believe in the no-win scenario. (How'd that end? Ignore the retread/remake with role reversal!) But in this case both implementors and users are facing physics. If the root post in this thread wants only an error for obj.typo, we shouldn't be chasing nSP, which is unstratified and over-general. We should try the proxy-in-library-clothing path and if it is too painful, consider some targeted and optimizable fix. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Thu, Sep 25, 2014 at 5:38 PM, Brendan Eich bren...@mozilla.org wrote: On Sep 25, 2014, at 7:56 PM, Boris Zbarsky bzbar...@mit.edu wrote: SpiderMonkey used to support __noSuchMethod__, I believe. I implemented __noSuchMethod__ long ago, for the TIBET folks (Smalltalk style JS framework; they wanted a doesNotUnderstand analogue). Please note well the difference between __noSuchMethod__ and anything like __noSuchProperty__. Even ignoring JITs, back in the interpreter only days, I could justify nSM because it was called on a slow path, when the only outcome without it was a guaranteed obj.foo is not callable or worse, obj.foo is not defined error. IOW nSM only kicked in when evaluating obj.foo(args) Not just obj.foo Any nSP of the kind we seem to be discussing would need to fail fast, on evaluation of the dot expression. That is a fast path. I, personally, have only ever used Python and PHP's nSP functionality to implement methods. Most of the fancy uses I see for it in, say, Ruby (like the cool dynamic query methods on ORMs), are also methods. There are certainly uses for this kind of functionality for non-method properties, but I suspect just going with noSuchMethod would satisfy most use-cases. I'd certainly be happier with that. ^_^ ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Tab Atkins Jr. wrote: Any nSP of the kind we seem to be discussing would need to fail fast, on evaluation of the dot expression. That is a fast path. I, personally, have only ever used Python and PHP's nSP functionality to implement methods. Most of the fancy uses I see for it in, say, Ruby (like the cool dynamic query methods on ORMs), are also methods. There are certainly uses for this kind of functionality for non-method properties, but I suspect just going with noSuchMethod would satisfy most use-cases. I'd certainly be happier with that. ^_^ __noSuchMethod__ is not what Nicholas sketched on his wishlist, though. There are two valid use-cases/proposals: 1. Smalltalk doesNotUnderstand (but Smalltalk makes everything a method selected by a message, no get vs. invoke [get+call] distinction), which led to SpiderMonkey's __noSuchMethod__. This is an unstratified trap to handle obj.foo() but not obj.foo -- in JS, whether it handles `do { let f = obj.foo; f(); }` is a good question. 2. Nicholas's request for a way to make obj.typo an error. I wish I had a nickel for every time someone new to JS asked me for this since 1995 (or perhaps 1998 when try-catch was done). This is all about failing fast on typo'ed or otherwise missing property references, gets as well as get+calls. These are two distinct things. You should start a new thread if you want (1). But we've been over it several times already: https://www.google.com/search?q=site%3Aesdiscuss.org+noSuchMethod What new helium do you have this time to get __noSuchMethod__ airborne? The Proxy-based library solution from Tom Van Cutsem at http://esdiscuss.org/topic/nosuchmethod-and-direct-proxies is sucking heavier gasses out of the air around you :-P. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Wed, Sep 24, 2014 at 3:28 PM, Brendan Eich bren...@mozilla.org wrote: Tab Atkins Jr. wrote: Using subclassing to bung in some arbitrary trait is really terrible. :/ It requires either adding it up high in your class hierarchy, or having to write a custom NoSuchPropertyClass which extends your superclass, so you can then extend it. Ok, let's not hand-wave mixin syntax, though (Andrea hacks __proto__). What API do you prefer? I'm partial to magic-named functions, but that's probably my experience speaking, rather than a more thoughtful opinion. Symbol-named magic functions would be better, since we have those and most other languages don't. Andrea wasn't even hacking __proto__ - they're just adding it to the class prototype, so newly constructed objects'll have it. I'm rather surprised that the group actually considered that sort of code to be appropriate - it's known that it doesn't compose well with other traits-as-superclasses or normal subclassing. What code was not appropriate? TC39 balked at standardizing library API, and good for them. You want better API, design it! The use of a Proxy superclass to implement the functionality, which you demonstrated in an earlier email. Or did you want `sealed class` or other such syntax, and I misunderstood? Nah, using superclasses in general is the bad thing here; it doesn't compose well without multi-inheritance, which JS likely isn't going to do. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Thu, Sep 25, 2014 at 12:50 PM, Brendan Eich bren...@mozilla.org wrote: Tab Atkins Jr. wrote: If you try to add it in a targeted way in the middle of your hierarchy, it requires either __proto__ hacking, something like: class superclass {...} ... x = makeNSPProxy() x.__proto__ = superclass() class subclass extends x {...} (In such code, you need x to be a function with .prototype that is the proxy, as just up-thread at https://mail.mozilla.org/pipermail/es-discuss/2014-September/039565.html but no big deal.) Yes, the proxy's get handler must deal with superclasses other than Object, if that's required. The requirements aren't clear (we're probably assuming something that doesn't match what developers want in full), but that's the point: TC39 is not the library designer droid you're looking for. On the other hand, a magic property can be placed exactly where you want it, without any difficulty. No one made a coherent proposal for the magic property. It was not clear from Andrea's latest post, where he even allows I also think Proxy already gives us a way to work around the `__noSuchProperty__` issue that this wish-fulfillment __noSuchProperty__ magic property does not have to handle superclass delegation. So please define the magic property fully before recommending it as better than the Proxy-based library approach. How would it work? The engine calls the function value of this magic property only when the object on which it is set has no such *own* property? If so, then the handler function must not throw if some superclass contains the wanted name. Just as the Proxy get handler I showed does for Object.prototype. If not, then does the magic property trigger only when the object on which it is set *and every object on its prototype chain* has no such property? That is a bit nasty on the JS engine optimized lookup side, but perhaps implementors should work harder so users don't have to. Is that what you're proposing? Then we should summon optimizing engine hackers. I've cc'ed one :-P. Yes, that is what I'm proposing. If lookup fails completely (reaches Object.prototype without finding the named property), it then does a second lookup for the magic NSP property, and if it finds it, executes it with the property name, returning the return value as the result of the original lookup. I don't understand how you inferred from Andrea's post that this wish-fulfillment __noSuchProperty__ magic property does not have to handle superclass delegation.. At minimum it needs to handle delegating to the object's own prototype (it would be a pretty poor NSP if it couldn't handle methodMissing use-cases as well), and I don't think there's a reasonable case to stop at just one level up; doing so would make this very fragile to refactoring your hierarchy, as methods show up as missing or not depending on where they end up in the class hierarchy. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On 9/25/14, 4:31 PM, Tab Atkins Jr. wrote: Yes, that is what I'm proposing. If lookup fails completely (reaches Object.prototype without finding the named property), it then does a second lookup for the magic NSP property, and if it finds it, executes it with the property name, returning the return value as the result of the original lookup. SpiderMonkey used to support __noSuchMethod__, I believe. It was removed because it caused problems for the JIT (in addition to being non-standard, etc, etc). Might be worth looking into what those were, exactly, before we consider standardizing something like this. -Boris ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Sep 25, 2014, at 7:56 PM, Boris Zbarsky bzbar...@mit.edu wrote: SpiderMonkey used to support __noSuchMethod__, I believe. I implemented __noSuchMethod__ long ago, for the TIBET folks (Smalltalk style JS framework; they wanted a doesNotUnderstand analogue). Please note well the difference between __noSuchMethod__ and anything like __noSuchProperty__. Even ignoring JITs, back in the interpreter only days, I could justify nSM because it was called on a slow path, when the only outcome without it was a guaranteed obj.foo is not callable or worse, obj.foo is not defined error. IOW nSM only kicked in when evaluating obj.foo(args) Not just obj.foo Any nSP of the kind we seem to be discussing would need to fail fast, on evaluation of the dot expression. That is a fast path. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Brendan Eich wrote: Tab Atkins Jr. wrote: This works great as a general principle, but honestly tons of languages have already forged this path. It's pretty straightforward, I think. I made the case for building it in sooner, but TC39 wanted less sooner based on library usage. There was a minority position (as voiced; could be majority) that argued JS is different, object detection plus optional fields means this is the wrong direction. Not trying to be controversial ;-). Would you use ES6 classes and have the ones you wrote all extend NoSuchPropertyClass? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Wed, Sep 24, 2014 at 2:56 PM, Brendan Eich bren...@mozilla.org wrote: Brendan Eich wrote: Tab Atkins Jr. wrote: This works great as a general principle, but honestly tons of languages have already forged this path. It's pretty straightforward, I think. I made the case for building it in sooner, but TC39 wanted less sooner based on library usage. There was a minority position (as voiced; could be majority) that argued JS is different, object detection plus optional fields means this is the wrong direction. Not trying to be controversial ;-). Would you use ES6 classes and have the ones you wrote all extend NoSuchPropertyClass? Using subclassing to bung in some arbitrary trait is really terrible. :/ It requires either adding it up high in your class hierarchy, or having to write a custom NoSuchPropertyClass which extends your superclass, so you can then extend it. I'm rather surprised that the group actually considered that sort of code to be appropriate - it's known that it doesn't compose well with other traits-as-superclasses or normal subclassing. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Agreed with Tab, in terms of composing inherited functionality it's hard to beat prototypal and the ease of `Class.prototype.__noSuchProperty__ = function(){ ... };` traitish approach. I also think Proxy already gives us a way to work around the `__noSuchProperty__` issue On Wed, Sep 24, 2014 at 11:17 PM, Tab Atkins Jr. jackalm...@gmail.com wrote: On Wed, Sep 24, 2014 at 2:56 PM, Brendan Eich bren...@mozilla.org wrote: Brendan Eich wrote: Tab Atkins Jr. wrote: This works great as a general principle, but honestly tons of languages have already forged this path. It's pretty straightforward, I think. I made the case for building it in sooner, but TC39 wanted less sooner based on library usage. There was a minority position (as voiced; could be majority) that argued JS is different, object detection plus optional fields means this is the wrong direction. Not trying to be controversial ;-). Would you use ES6 classes and have the ones you wrote all extend NoSuchPropertyClass? Using subclassing to bung in some arbitrary trait is really terrible. :/ It requires either adding it up high in your class hierarchy, or having to write a custom NoSuchPropertyClass which extends your superclass, so you can then extend it. I'm rather surprised that the group actually considered that sort of code to be appropriate - it's known that it doesn't compose well with other traits-as-superclasses or normal subclassing. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Tab Atkins Jr. wrote: Using subclassing to bung in some arbitrary trait is really terrible. :/ It requires either adding it up high in your class hierarchy, or having to write a custom NoSuchPropertyClass which extends your superclass, so you can then extend it. Ok, let's not hand-wave mixin syntax, though (Andrea hacks __proto__). What API do you prefer? I'm rather surprised that the group actually considered that sort of code to be appropriate - it's known that it doesn't compose well with other traits-as-superclasses or normal subclassing. What code was not appropriate? TC39 balked at standardizing library API, and good for them. You want better API, design it! Or did you want `sealed class` or other such syntax, and I misunderstood? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Hi Nicholas, sorry for the tardy reply. I did propose NoSuchProperty to Ecma TC39 today. To recap: // Library code starts here. const NoSuchProperty = Proxy(Object.prototype, { // uncomment for nasty Firefox bug workaround: // has: function(target, name) { return true; }, get: function(target, name, receiver) { if (name in Object.prototype) { return Reflect.get(Object.prototype, name, receiver); } throw new TypeError(name + is not a defined property); } }); function NoSuchPropertyClass() {} NoSuchPropertyClass.prototype = NoSuchProperty; // End library code. // Your client code starts here. class MySaferClass extends NoSuchPropertyClass { ... } The library code is self-hosted based on ES6 Proxies and Reflect. The committee reaction was to let this be put in popular libraries, in forms to be polished based on actual developer experience, and then we can standardize once there is a clear winner and strong adoption. Hope this is survivable. I argued we should shortcut to reduce the burden on the ecosystem but (as I've argued many times) TC39 believes we are least capable compared to the wider ecosystem (github, etc.) in designing, user-testing, polishing, and finalizing APIs. We can do final polish and formal specification, for sure. Y'all should do the hard part, not because we are lazy but because you are many, closer to your problem domains and use-cases, and collectively wiser about the details. /be Brendan Eich wrote: Just FTR, I'll put a ratioanlized ES7 proposal on the agenda for the July TC39 meeting, as per https://twitter.com/BrendanEich/status/475067783282057216 Thanks to Nicholas for the suggestion! /be Brendan Eich wrote: Nicholas C. Zakas wrote: It can be done with Proxy, but that kind of sucks because I always need to go through the extra step of creating the Proxy for each object and passing around the Proxy instead. To me, this is similar to setting a property to be readonly in strict mode, except its writeonly (or rather, write-first). What about the code I showed, which shows a singleton being spliced high on many objects' prototype chains to handle the missing property throw? js var NoSuchProperty = Proxy({}, { has: function(target, name) { return true; }, get: function(target, name, receiver) { if (name in Object.prototype) { return Reflect.get(Object.prototype, name, receiver); } throw new TypeError(name + is not a defined property); } }); js var obj = Object.create(NoSuchProperty) js obj.foo = 42 42 js obj.foo 42 js obj.bar /tmp/p.js:7:4 TypeError: bar is not a defined property You could avoid Object.create by assigning to a Constructor.prototype, or hacking with __proto__, of course. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Tue, Sep 23, 2014 at 2:34 PM, Brendan Eich bren...@mozilla.org wrote: Hi Nicholas, sorry for the tardy reply. I did propose NoSuchProperty to Ecma TC39 today. To recap: // Library code starts here. const NoSuchProperty = Proxy(Object.prototype, { // uncomment for nasty Firefox bug workaround: // has: function(target, name) { return true; }, get: function(target, name, receiver) { if (name in Object.prototype) { return Reflect.get(Object.prototype, name, receiver); } throw new TypeError(name + is not a defined property); } }); function NoSuchPropertyClass() {} NoSuchPropertyClass.prototype = NoSuchProperty; // End library code. // Your client code starts here. class MySaferClass extends NoSuchPropertyClass { ... } The library code is self-hosted based on ES6 Proxies and Reflect. The committee reaction was to let this be put in popular libraries, in forms to be polished based on actual developer experience, and then we can standardize once there is a clear winner and strong adoption. Hope this is survivable. I argued we should shortcut to reduce the burden on the ecosystem but (as I've argued many times) TC39 believes we are least capable compared to the wider ecosystem (github, etc.) in designing, user-testing, polishing, and finalizing APIs. We can do final polish and formal specification, for sure. Y'all should do the hard part, not because we are lazy but because you are many, closer to your problem domains and use-cases, and collectively wiser about the details. This works great as a general principle, but honestly tons of languages have already forged this path. It's pretty straightforward, I think. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Tab Atkins Jr. wrote: This works great as a general principle, but honestly tons of languages have already forged this path. It's pretty straightforward, I think. I made the case for building it in sooner, but TC39 wanted less sooner based on library usage. There was a minority position (as voiced; could be majority) that argued JS is different, object detection plus optional fields means this is the wrong direction. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Andrea Giammarchi wrote: In such case the only concern would be why `Object.prototype` is considered but not inherited properties too. Because NoSuchProperty is meant to be inserted just before Object.prototype, avoiding that loop. What's more, the loop is unnecessary: var NoSuchProperty = Proxy({}, { get: function(target, name, receiver) { while (target = Object.getPrototypeOf(target)) { if (name in target) { return Reflect.get(target, name, receiver); } } throw new TypeError(name + is not a defined property); } }); If NoSuchProperty is inserted just before Object.prototype on a chain of ordinary objects, its get handler won't be invoked until no such property is indeed found along the path from the original target to NoSuchProperty. Therefore all iterations but the last (where target is Object.prototype) are redundant. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Because NoSuchProperty is meant to be inserted just before Object.prototype I miss this constrain, wouldn't have bothered with the loop otherwise. Regards On Sun, Jun 15, 2014 at 11:27 AM, Brendan Eich bren...@mozilla.org wrote: Andrea Giammarchi wrote: In such case the only concern would be why `Object.prototype` is considered but not inherited properties too. Because NoSuchProperty is meant to be inserted just before Object.prototype, avoiding that loop. What's more, the loop is unnecessary: var NoSuchProperty = Proxy({}, { get: function(target, name, receiver) { while (target = Object.getPrototypeOf(target)) { if (name in target) { return Reflect.get(target, name, receiver); } } throw new TypeError(name + is not a defined property); } }); If NoSuchProperty is inserted just before Object.prototype on a chain of ordinary objects, its get handler won't be invoked until no such property is indeed found along the path from the original target to NoSuchProperty. Therefore all iterations but the last (where target is Object.prototype) are redundant. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
that `has:()=true` breaks with features detections that are meant to be less obtrusive than getters ... i.e. `'geolocation' in navigator` or `'innerHTML' in genericNode` and all others that are not supposed to pass through a potentially expensive getter to retrieve a feature detection purpose info. Long story short that NoSuchProperty is obtrusive in an upside-down way ... I personally would not use that and would not expect that anywhere The 'has' handler is required to work around a Firefox limitation. A compliant ES6 implementation only needs the 'get' handler. Related bug reports: https://bugzilla.mozilla.org/show_bug.cgi?id=914314 https://bugzilla.mozilla.org/show_bug.cgi?id=1009199 ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Interesting bug, thanks for the info. In such case the only concern would be why `Object.prototype` is considered but not inherited properties too. If inheritance should be considered, I'd rather go like this: ``` var NoSuchProperty = Proxy({}, { get: function(target, name, receiver) { while (target = Object.getPrototypeOf(target)) { if (name in target) { return Reflect.get(target, name, receiver); } } throw new TypeError(name + is not a defined property); } }); ``` Best Regards On Sat, Jun 7, 2014 at 1:42 AM, André Bargull andre.barg...@udo.edu wrote: that `has:()=true` breaks with features detections that are meant to be less obtrusive than getters ... i.e. `'geolocation' in navigator` or `'innerHTML' in genericNode` and all others that are not supposed to pass through a potentially expensive getter to retrieve a feature detection purpose info. Long story short that NoSuchProperty is obtrusive in an upside-down way ... I personally would not use that and would not expect that anywhere The 'has' handler is required to work around a Firefox limitation. A compliant ES6 implementation only needs the 'get' handler. Related bug reports: https://bugzilla.mozilla.org/show_bug.cgi?id=914314 https://bugzilla.mozilla.org/show_bug.cgi?id=1009199 ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Le 06/06/2014 01:08, Rick Waldron a écrit : On Thu, Jun 5, 2014 at 6:42 PM, Nicholas C. Zakas standa...@nczconsulting.com mailto:standa...@nczconsulting.com wrote: * `Object.deepPreventExtensions()`, `Object.deepSeal()`, `Object.deepFreeze()` - deep versions of `Object.preventExtensions()`, et al. Does deep mean that a Map instance's [[MapData]] is frozen if deepFreeze is called on a ? eg. what happens here: var m = Object.deepFreeze(new Map()); m.set(1, 1); I think the intention behind Object.freeze was to make objects immutable (at a shallow level), so maybe the semantics of Map.prototype.set (and all modifying operations, of Mapco) should be changed to read the [[IsExtensible]] and throw if false is returned. Given Maps are already in the wild, this decision might need to be taken quickly. or should an Object.makeImmutable be introduced? (it would be freeze + make all internal [[*Data]] objects immutable) * `Object.preventUndeclaredGet()` - change an object's behavior to throw an error if you try to read from a property that doesn't exist (instead of returning `undefine`). (I already know that Nicholas and I disagree on the topic, but sharing for debating). This can be achieved with Proxy right, or is that too cumbersome? Code-readability-wise, wrapping in a proxy is as cumbersome as a call to Object.preventUndeclaredGet I guess. This sort of concerns are only development-time concerns and I believe the runtime shouldn't be bothered with these (I'm aware it already is in various web). For instance, the TypeScript compiler is capable today of catching this error. Given that we have free, cross-platform and fairly easy to use tools, do we need assistance from the runtime? David [1] https://twitter.com/passy/status/469127322072014849 ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Fri, Jun 6, 2014 at 5:44 AM, David Bruant bruan...@gmail.com wrote: Le 06/06/2014 01:08, Rick Waldron a écrit : On Thu, Jun 5, 2014 at 6:42 PM, Nicholas C. Zakas standa...@nczconsulting.com wrote: * `Object.deepPreventExtensions()`, `Object.deepSeal()`, `Object.deepFreeze()` - deep versions of `Object.preventExtensions()`, et al. Does deep mean that a Map instance's [[MapData]] is frozen if deepFreeze is called on a ? eg. what happens here: var m = Object.deepFreeze(new Map()); m.set(1, 1); I think the intention behind Object.freeze was to make objects immutable (at a shallow level), so maybe the semantics of Map.prototype.set (and all modifying operations, of Mapco) should be changed to read the [[IsExtensible]] and throw if false is returned. Given Maps are already in the wild, this decision might need to be taken quickly. The main intention of Object.freeze is to make objects tamper proof. To ensure that their API surface is only according to the provider, not to the clients, and thereby prevent client A from corrupting the API seen by client B. This is why Object.freeze has no effect on non-configurable accessor properties -- their writable-property-like behavior is fully in control of the abstraction provider. Of course, it does also cause some further immutability beyond that initial purpose, and so there become valid purposes too. The API surface of an array consists of data properties, so freezing an array turns it into a shallowly immutable array. Typed Arrays refuse to be frozen as of ES6. Post ES6, perhaps we will enable them to be frozen, resulting in their indexed properties being immutable, since these are presented as data properties. Were we allow these to be frozen, they would also become un-neuterable, which means they would be passed over postMessage by sharing of the underlying data, rather than by ownership transfer of that data, which is exactly what you want once the data is immutable. By contrast, a Map's state is more like the private instance variable state of a closure or a post-ES6 class. Object.freeze of a Map should not alter the mutability of this state for the same reason it does not alter the state captured by a closure or a future class instance. or should an Object.makeImmutable be introduced? (it would be freeze + make all internal [[*Data]] objects immutable) We do need something like that. But it's a bit tricky. A client of an object should not be able to attack it by preemptively deep-freezing it against its wishes. * `Object.preventUndeclaredGet()` - change an object's behavior to throw an error if you try to read from a property that doesn't exist (instead of returning `undefine`). (I already know that Nicholas and I disagree on the topic, but sharing for debating). Once again, perhaps it is something that the provider of an object could determine, but it shouldn't be something up to an object's clients. But like you observe below, we can already do this with a proxy, so let's not introduce a second mechanism without a compelling reason. And the proxy solution wraps the initial object, rather than changing its behavior in place, so it does not enable one client to corrupt the behavior seen by other clients. This can be achieved with Proxy right, or is that too cumbersome? Code-readability-wise, wrapping in a proxy is as cumbersome as a call to Object.preventUndeclaredGet I guess. This sort of concerns are only development-time concerns and I believe the runtime shouldn't be bothered with these (I'm aware it already is in various web). For instance, the TypeScript compiler is capable today of catching this error. Given that we have free, cross-platform and fairly easy to use tools, do we need assistance from the runtime? Yes. Object.freeze is a runtime production protection mechanism, because attacks that are only prevented during development don't matter very much ;). David [1] https://twitter.com/passy/status/469127322072014849 ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Le 06/06/2014 15:57, Mark S. Miller a écrit : By contrast, a Map's state is more like the private instance variable state of a closure or a post-ES6 class. The capabilities to arbitrarily modify Maps (set/delete on all keys, with any values) will be expected by any ES6-compliant code to be globally available, so a Map's state cannot reasonably be considered private. This differs from the state of a closure where its access is strictly moderated by the public API giving access to it and to the fact that this API is not provided globally (unlike Map.prototype). Object.freeze of a Map should not alter the mutability of this state for the same reason it does not alter the state captured by a closure or a future class instance. I'd argue the Map state is very much like regular objects (for which you can't deny [[Set]], [[Delete]], etc.), not closure's state. In an ES6 world, denying access to the global Map.prototype.* would break legitimate code, so that's not really an option confiners like Caja could provide. or should an Object.makeImmutable be introduced? (it would be freeze + make all internal [[*Data]] objects immutable) We do need something like that. But it's a bit tricky. A client of an object should not be able to attack it by preemptively deep-freezing it against its wishes. I don't see the difference with shallow-freezing? It's currently not possible to defend against shallow-freezing (it will be possible via wrapping in a proxy). This can be achieved with Proxy right, or is that too cumbersome? Code-readability-wise, wrapping in a proxy is as cumbersome as a call to Object.preventUndeclaredGet I guess. This sort of concerns are only development-time concerns and I believe the runtime shouldn't be bothered with these (I'm aware it already is in various web). For instance, the TypeScript compiler is capable today of catching this error. Given that we have free, cross-platform and fairly easy to use tools, do we need assistance from the runtime? Yes. Object.freeze is a runtime production protection mechanism, because attacks that are only prevented during development don't matter very much ;). Just to clarify, I agree that Object.freeze was necessary in ES5 (have we had proxies, it might have been harder to justify?), because there was no good alternative to protect an object against the parties it was shared with. But the concern Nicholas raises doesn't seem to have this property. Reading a property that doesn't exist doesn't carry a security risk, does it? Object.preventUndeclaredGet doesn't really protect against anything like ES5 methods did. David ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Fri, Jun 6, 2014 at 7:37 AM, David Bruant bruan...@gmail.com wrote: Le 06/06/2014 15:57, Mark S. Miller a écrit : By contrast, a Map's state is more like the private instance variable state of a closure or a post-ES6 class. The capabilities to arbitrarily modify Maps (set/delete on all keys, with any values) will be expected by any ES6-compliant code to be globally available, so a Map's state cannot reasonably be considered private. This differs from the state of a closure where its access is strictly moderated by the public API giving access to it and to the fact that this API is not provided globally (unlike Map.prototype). That's a good point, but doesn't really address what I'm trying to say. The map's state is manipulable through its API surface, rather than being the properties which are its API surface. In the abstract perhaps this is a subtle distinction, but it is the distinction Object.freeze is built on. Consider if you wrote in JavaScript a Map abstraction of your own that held the data structures of your map implementation in private instance variables. Object.freeze would clearly not touch your map implementation. For builtins, internal properties serve the role that private instance variables will in class instances. This does bring up an interesting issue though. Even if you wanted your map to respond to Object.freeze by making this internal implementation be no longer mutable, you can't, unless the Map is a proxy, because no other JS object can sense when it is being frozen. More on this in another email coming soon. Object.freeze of a Map should not alter the mutability of this state for the same reason it does not alter the state captured by a closure or a future class instance. I'd argue the Map state is very much like regular objects (for which you can't deny [[Set]], [[Delete]], etc.), not closure's state. In an ES6 world, denying access to the global Map.prototype.* would break legitimate code, so that's not really an option confiners like Caja could provide. Why would we want to deny access to Map.prototype.* ? If there were a Map among the primordials, for example, if Map.prototype itself were a Map, then, in combination with the primordial existence of Map.prototype.set, we'd have a global communications channel. (Indeed, SES repairs Date.prototype when Data.prototype is a Date, and likewise for WeakMap https://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/repairES5.js#2806 https://bugzilla.mozilla.org/show_bug.cgi?id=861219.) or should an Object.makeImmutable be introduced? (it would be freeze + make all internal [[*Data]] objects immutable) We do need something like that. But it's a bit tricky. A client of an object should not be able to attack it by preemptively deep-freezing it against its wishes. I don't see the difference with shallow-freezing? It's currently not possible to defend against shallow-freezing (it will be possible via wrapping in a proxy). Therefore a defensible API surface in JS cannot contain as part of its contract that it presents data properties that remain writable. Except as you point out, for proxies which refuse to be frozen. This restriction on the possibilities for defensible API surfaces is an unfortunate consequence of the unpleasant choices we had for securing ES5, as you point out below. This can be achieved with Proxy right, or is that too cumbersome? Code-readability-wise, wrapping in a proxy is as cumbersome as a call to Object.preventUndeclaredGet I guess. This sort of concerns are only development-time concerns and I believe the runtime shouldn't be bothered with these (I'm aware it already is in various web). For instance, the TypeScript compiler is capable today of catching this error. Given that we have free, cross-platform and fairly easy to use tools, do we need assistance from the runtime? Yes. Object.freeze is a runtime production protection mechanism, because attacks that are only prevented during development don't matter very much ;). Just to clarify, I agree that Object.freeze was necessary in ES5 (have we had proxies, it might have been harder to justify?), because there was no good alternative to protect an object against the parties it was shared with. Exactly. But even with proxies, proxies are way way too heavy to be the only form of defensible object. ES6 classes also give us a place to stand to start to talk about the difference between provider and client. Hopefully ES7 will support defensible classes that make defensible instances. But because of the historical path these decisions took, I expect that this class-based defensiveness will only be sugar for a pattern of freezing anyway, so it won't make a fundamental difference. But the concern Nicholas raises doesn't seem to have this property. Reading a property that doesn't exist doesn't carry a security risk, does it? Object.preventUndeclaredGet doesn't
Re: My ECMAScript 7 wishlist
Couldn't preventUndeclaredGet() be implemented with proxies? It actually sounds like an extremely useful feature for development builds of libraries and applications. Typos are very very common, and often difficult to look over while debugging. On the other hand, it would break a lot of existing code if you try to pass it as an object to a library; you'd have to declare every possible value it might check (which isn't necessarily bad). Most of the time, it's just an options object, or an object it'll iterate over the keys of. Using it on arrays would also reduce off-by-1 errors (though I don't see them often in JS). On Fri, Jun 6, 2014 at 7:37 AM, David Bruant bruan...@gmail.com wrote: Le 06/06/2014 15:57, Mark S. Miller a écrit : By contrast, a Map's state is more like the private instance variable state of a closure or a post-ES6 class. The capabilities to arbitrarily modify Maps (set/delete on all keys, with any values) will be expected by any ES6-compliant code to be globally available, so a Map's state cannot reasonably be considered private. This differs from the state of a closure where its access is strictly moderated by the public API giving access to it and to the fact that this API is not provided globally (unlike Map.prototype). Object.freeze of a Map should not alter the mutability of this state for the same reason it does not alter the state captured by a closure or a future class instance. I'd argue the Map state is very much like regular objects (for which you can't deny [[Set]], [[Delete]], etc.), not closure's state. In an ES6 world, denying access to the global Map.prototype.* would break legitimate code, so that's not really an option confiners like Caja could provide. or should an Object.makeImmutable be introduced? (it would be freeze + make all internal [[*Data]] objects immutable) We do need something like that. But it's a bit tricky. A client of an object should not be able to attack it by preemptively deep-freezing it against its wishes. I don't see the difference with shallow-freezing? It's currently not possible to defend against shallow-freezing (it will be possible via wrapping in a proxy). This can be achieved with Proxy right, or is that too cumbersome? Code-readability-wise, wrapping in a proxy is as cumbersome as a call to Object.preventUndeclaredGet I guess. This sort of concerns are only development-time concerns and I believe the runtime shouldn't be bothered with these (I'm aware it already is in various web). For instance, the TypeScript compiler is capable today of catching this error. Given that we have free, cross-platform and fairly easy to use tools, do we need assistance from the runtime? Yes. Object.freeze is a runtime production protection mechanism, because attacks that are only prevented during development don't matter very much ;). Just to clarify, I agree that Object.freeze was necessary in ES5 (have we had proxies, it might have been harder to justify?), because there was no good alternative to protect an object against the parties it was shared with. But the concern Nicholas raises doesn't seem to have this property. Reading a property that doesn't exist doesn't carry a security risk, does it? Object.preventUndeclaredGet doesn't really protect against anything like ES5 methods did. David ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Le 06/06/2014 17:47, Frankie Bagnardi a écrit : Couldn't preventUndeclaredGet() be implemented with proxies? Yes it can. Doing it left as an exercise to the reader... Wait... Don't bother, Nicholas did it :-) http://www.nczonline.net/blog/2014/04/22/creating-defensive-objects-with-es6-proxies/ It actually sounds like an extremely useful feature for development builds of libraries and applications. Typos are very very common, and often difficult to look over while debugging. On the other hand, it would break a lot of existing code if you try to pass it as an object to a library; you'd have to declare every possible value it might check (which isn't necessarily bad). Most of the time, it's just an options object, or an object it'll iterate over the keys of. Using it on arrays would also reduce off-by-1 errors (though I don't see them often in JS). Ever since I've started using forEach/map/filter/reduce, I haven't had an off-by-one error on arrays. Highly recommanded! (I think I've heard Crockford making the same recommandation in a recent talk, but I cannot find the link) David On Fri, Jun 6, 2014 at 7:37 AM, David Bruant bruan...@gmail.com mailto:bruan...@gmail.com wrote: Le 06/06/2014 15:57, Mark S. Miller a écrit : By contrast, a Map's state is more like the private instance variable state of a closure or a post-ES6 class. The capabilities to arbitrarily modify Maps (set/delete on all keys, with any values) will be expected by any ES6-compliant code to be globally available, so a Map's state cannot reasonably be considered private. This differs from the state of a closure where its access is strictly moderated by the public API giving access to it and to the fact that this API is not provided globally (unlike Map.prototype). Object.freeze of a Map should not alter the mutability of this state for the same reason it does not alter the state captured by a closure or a future class instance. I'd argue the Map state is very much like regular objects (for which you can't deny [[Set]], [[Delete]], etc.), not closure's state. In an ES6 world, denying access to the global Map.prototype.* would break legitimate code, so that's not really an option confiners like Caja could provide. or should an Object.makeImmutable be introduced? (it would be freeze + make all internal [[*Data]] objects immutable) We do need something like that. But it's a bit tricky. A client of an object should not be able to attack it by preemptively deep-freezing it against its wishes. I don't see the difference with shallow-freezing? It's currently not possible to defend against shallow-freezing (it will be possible via wrapping in a proxy). This can be achieved with Proxy right, or is that too cumbersome? Code-readability-wise, wrapping in a proxy is as cumbersome as a call to Object.preventUndeclaredGet I guess. This sort of concerns are only development-time concerns and I believe the runtime shouldn't be bothered with these (I'm aware it already is in various web). For instance, the TypeScript compiler is capable today of catching this error. Given that we have free, cross-platform and fairly easy to use tools, do we need assistance from the runtime? Yes. Object.freeze is a runtime production protection mechanism, because attacks that are only prevented during development don't matter very much ;). Just to clarify, I agree that Object.freeze was necessary in ES5 (have we had proxies, it might have been harder to justify?), because there was no good alternative to protect an object against the parties it was shared with. But the concern Nicholas raises doesn't seem to have this property. Reading a property that doesn't exist doesn't carry a security risk, does it? Object.preventUndeclaredGet doesn't really protect against anything like ES5 methods did. David ___ es-discuss mailing list es-discuss@mozilla.org mailto:es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On 6/5/2014 4:08 PM, Rick Waldron wrote: * `Object.deepPreventExtensions()`, `Object.deepSeal()`, `Object.deepFreeze()` - deep versions of `Object.preventExtensions()`, et al. Does deep mean that a Map instance's [[MapData]] is frozen if deepFreeze is called on a ? eg. what happens here: var m = Object.deepFreeze(new Map()); m.set(1, 1); In your blog it mentions the silent failure in non-strict mode, I suspect that would still have to apply to these additions for semantic consistency. I wouldn't expect this to apply to [[MapData]] since those are not enumerable own properties of the map instance. * `Object.preventUndeclaredGet()` - change an object's behavior to throw an error if you try to read from a property that doesn't exist (instead of returning `undefine`). This can be achieved with Proxy right, or is that too cumbersome? It can be done with Proxy, but that kind of sucks because I always need to go through the extra step of creating the Proxy for each object and passing around the Proxy instead. To me, this is similar to setting a property to be readonly in strict mode, except its writeonly (or rather, write-first). -- ___ Nicholas C. Zakas http://www.nczonline.net ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On 6/6/2014 8:38 AM, Mark S. Miller wrote: But the concern Nicholas raises doesn't seem to have this property. Reading a property that doesn't exist doesn't carry a security risk, does it? Object.preventUndeclaredGet doesn't really protect against anything like ES5 methods did. That's true, but misses the point I was trying to make. For normal ES objects, it is already part of their API contract with their clients that the clients can do feature testing to detect the presence or absence of a method. The most common way to do such feature testing is to get the property and see if it is falsy. (Variations include, testing for undefined, testing for undefined or null, and testing if its typeof is function.) It's fine if the provider of an abstraction does not wish to support this pattern. But it is not ok for one client of an object which does support it to prevent that object's other clients from successfully using feature detection. Sorry I was sleeping while most of this conversation was happening. :) I understand the point about feature detection, it would suck if some random code did Object.preventUndeclaredGet() on an object you own and were using feature detection on. I still wish for some way to do this other than through proxies, but I agree that it would be nice for just the object provider to be able to set this behavior. -- ___ Nicholas C. Zakas http://www.nczonline.net ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Le 06/06/2014 18:16, Nicholas C. Zakas a écrit : On 6/6/2014 8:38 AM, Mark S. Miller wrote: But the concern Nicholas raises doesn't seem to have this property. Reading a property that doesn't exist doesn't carry a security risk, does it? Object.preventUndeclaredGet doesn't really protect against anything like ES5 methods did. That's true, but misses the point I was trying to make. For normal ES objects, it is already part of their API contract with their clients that the clients can do feature testing to detect the presence or absence of a method. The most common way to do such feature testing is to get the property and see if it is falsy. (Variations include, testing for undefined, testing for undefined or null, and testing if its typeof is function.) It's fine if the provider of an abstraction does not wish to support this pattern. But it is not ok for one client of an object which does support it to prevent that object's other clients from successfully using feature detection. Sorry I was sleeping while most of this conversation was happening. :) I understand the point about feature detection, it would suck if some random code did Object.preventUndeclaredGet() on an object you own and were using feature detection on. I still wish for some way to do this other than through proxies, but I agree that it would be nice for just the object provider to be able to set this behavior. It is possible via an API following the same pattern than revocable proxies: {object, toggle} = makeUndeclGetThrowObject() toggle(); // now the object throws when there is a [[Get]] on an undef property Keep the toggle function locally so only trusted parties access it, share the object as you wish. Admittedly more cumbersome than your solution or proxies :-p David ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Fri, Jun 6, 2014 at 8:38 AM, Mark S. Miller erig...@google.com wrote: The most common way to do such feature testing is to get the property and see if it is falsy. this is why for years many of us have been advocating the usage of `'name' in object` instead of passing through getters ... but I can see the problem when it comes to retrieve, let's say `requestAnimationFrame`, passing through all prefixes ... About that, if I can add one wish for ES7, I really wish that if there's anything that works with a prefix, an `experimentalPrefix` property is exposed somewhere so that we can stop guessing which one would work for the current engine and simplify features detections too. Best Regards ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Nicholas C. Zakas wrote: It can be done with Proxy, but that kind of sucks because I always need to go through the extra step of creating the Proxy for each object and passing around the Proxy instead. To me, this is similar to setting a property to be readonly in strict mode, except its writeonly (or rather, write-first). What about the code I showed, which shows a singleton being spliced high on many objects' prototype chains to handle the missing property throw? js var NoSuchProperty = Proxy({}, { has: function(target, name) { return true; }, get: function(target, name, receiver) { if (name in Object.prototype) { return Reflect.get(Object.prototype, name, receiver); } throw new TypeError(name + is not a defined property); } }); js var obj = Object.create(NoSuchProperty) js obj.foo = 42 42 js obj.foo 42 js obj.bar /tmp/p.js:7:4 TypeError: bar is not a defined property You could avoid Object.create by assigning to a Constructor.prototype, or hacking with __proto__, of course. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Just FTR, I'll put a ratioanlized ES7 proposal on the agenda for the July TC39 meeting, as per https://twitter.com/BrendanEich/status/475067783282057216 Thanks to Nicholas for the suggestion! /be Brendan Eich wrote: Nicholas C. Zakas wrote: It can be done with Proxy, but that kind of sucks because I always need to go through the extra step of creating the Proxy for each object and passing around the Proxy instead. To me, this is similar to setting a property to be readonly in strict mode, except its writeonly (or rather, write-first). What about the code I showed, which shows a singleton being spliced high on many objects' prototype chains to handle the missing property throw? js var NoSuchProperty = Proxy({}, { has: function(target, name) { return true; }, get: function(target, name, receiver) { if (name in Object.prototype) { return Reflect.get(Object.prototype, name, receiver); } throw new TypeError(name + is not a defined property); } }); js var obj = Object.create(NoSuchProperty) js obj.foo = 42 42 js obj.foo 42 js obj.bar /tmp/p.js:7:4 TypeError: bar is not a defined property You could avoid Object.create by assigning to a Constructor.prototype, or hacking with __proto__, of course. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
that `has:()=true` breaks with features detections that are meant to be less obtrusive than getters ... i.e. `'geolocation' in navigator` or `'innerHTML' in genericNode` and all others that are not supposed to pass through a potentially expensive getter to retrieve a feature detection purpose info. Long story short that NoSuchProperty is obtrusive in an upside-down way ... I personally would not use that and would not expect that anywhere ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
My ECMAScript 7 wishlist
I wrote this blog post about some of the pain points I'm dealing with and dreams of how ES7 might be able to address them: http://www.nczonline.net/blog/2014/06/03/my-ecmascript-7-wishlist/ A short overview in lieu of posting the whole article here: * `Array.prototype.first()`, `Array.prototype.last()` - return the first and last items, respectively. * `Array.prototype.isEmpty()` - return true when empty (would also be nice on strings, maps, etc.). * `Function.empty` - a standard empty function that can be used when you just want an empty function (IMHO, it indicates intent much better than other options toda). * Custom descriptor attributes - David mentioned this likely will never happen, which makes me sad. Maybe the decorators proposal solves this use case. * `Object.deepPreventExtensions()`, `Object.deepSeal()`, `Object.deepFreeze()` - deep versions of `Object.preventExtensions()`, et al. * `Object.preventUndeclaredGet()` - change an object's behavior to throw an error if you try to read from a property that doesn't exist (instead of returning `undefine`). * Lightweight traits - simple syntax sugar for object literals and classes to facilitate mixins. Further rationale and explanation is in the post. The last three, in particular, scratch particular itches I currently have. **Note:** Please don't take these as formal proposals. If any of the ideas seems worthwhile, I'm happy to discuss further and/or put together an actual proposal. Thanks. -- ___ Nicholas C. Zakas http://www.nczonline.net ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Thu, Jun 5, 2014 at 6:42 PM, Nicholas C. Zakas standa...@nczconsulting.com wrote: I wrote this blog post about some of the pain points I'm dealing with and dreams of how ES7 might be able to address them: http://www.nczonline.net/blog/2014/06/03/my-ecmascript-7-wishlist/ A short overview in lieu of posting the whole article here: * `Array.prototype.first()`, `Array.prototype.last()` - return the first and last items, respectively. Check out Till's response to the previous thread about first and last: https://mail.mozilla.org/pipermail/es-discuss/2014-May/037158.html * `Array.prototype.isEmpty()` - return true when empty (would also be nice on strings, maps, etc.). +1 * `Function.empty` - a standard empty function that can be used when you just want an empty function (IMHO, it indicates intent much better than other options toda). +1 * Custom descriptor attributes - David mentioned this likely will never happen, which makes me sad. Maybe the decorators proposal solves this use case. The Proxy security issue: http://esdiscuss.org/topic/object-getownpropertydescriptor-can-return-just-about-anything I had hoped that a black list of writable, configurable, enumerable properties would suffice: http://esdiscuss.org/topic/object-getownpropertydescriptor-can-return-just-about-anything#content-14 but I there is a discussion around some form of custom meta properties. * `Object.deepPreventExtensions()`, `Object.deepSeal()`, `Object.deepFreeze()` - deep versions of `Object.preventExtensions()`, et al. Does deep mean that a Map instance's [[MapData]] is frozen if deepFreeze is called on a ? eg. what happens here: var m = Object.deepFreeze(new Map()); m.set(1, 1); In your blog it mentions the silent failure in non-strict mode, I suspect that would still have to apply to these additions for semantic consistency. * `Object.preventUndeclaredGet()` - change an object's behavior to throw an error if you try to read from a property that doesn't exist (instead of returning `undefine`). This can be achieved with Proxy right, or is that too cumbersome? * Lightweight traits - simple syntax sugar for object literals and classes to facilitate mixins. + 1 Rick ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
* `Function.empty` - a standard empty function that can be used when you just want an empty function (IMHO, it indicates intent much better than other options toda). Ironically, that’s what Function.prototype is. But using that object in that capacity is beyond confusing. I’d prefer a different name such as “noop” or “doNothing”; “empty” doesn’t feel right in the context of something executable. Also, I don’t find using an empty arrow function too bad (to me, it looks quite intention-revealing): ```js someAsyncMethod(() = {}); ``` -- Dr. Axel Rauschmayer a...@rauschma.de rauschma.de ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
Rick Waldron wrote: * `Object.preventUndeclaredGet()` - change an object's behavior to throw an error if you try to read from a property that doesn't exist (instead of returning `undefine`). This can be achieved with Proxy right, or is that too cumbersome? js var NoSuchProperty = Proxy({}, { has: function(target, name) { return true; }, get: function(target, name, receiver) { if (name in Object.prototype) { return Object.prototype[name]; } throw new TypeError(name + is not a defined property); } }); js var obj = Object.create(NoSuchProperty) js obj.foo = 42 42 js obj.foo 42 js obj.bar /tmp/p.js:7:4 TypeError: bar is not a defined property /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Jun 5, 2014, at 5:52 PM, Brendan Eich bren...@mozilla.org wrote: Rick Waldron wrote: * `Object.preventUndeclaredGet()` - change an object's behavior to throw an error if you try to read from a property that doesn't exist (instead of returning `undefine`). This can be achieved with Proxy right, or is that too cumbersome? js var NoSuchProperty = Proxy({}, { has: function(target, name) { return true; }, get: function(target, name, receiver) { if (name in Object.prototype) { return Object.prototype[name]; } throw new TypeError(name + is not a defined property); } need to make sure accessor methods use the right this value: get: function (target, name, receiver) { if (name in Object.prototype) return Reflect.get(Object.prototype, name, receiver); throw new TypeError(name + “ is not a defined property”); } }); ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
LOL - as if O.p has getters! Thanks, this is more general in case of insanity. /be On Jun 5, 2014, at 6:09 PM, Allen Wirfs-Brock al...@wirfs-brock.com wrote: On Jun 5, 2014, at 5:52 PM, Brendan Eich bren...@mozilla.org wrote: Rick Waldron wrote: * `Object.preventUndeclaredGet()` - change an object's behavior to throw an error if you try to read from a property that doesn't exist (instead of returning `undefine`). This can be achieved with Proxy right, or is that too cumbersome? js var NoSuchProperty = Proxy({}, { has: function(target, name) { return true; }, get: function(target, name, receiver) { if (name in Object.prototype) { return Object.prototype[name]; } throw new TypeError(name + is not a defined property); } need to make sure accessor methods use the right this value: get: function (target, name, receiver) { if (name in Object.prototype) return Reflect.get(Object.prototype, name, receiver); throw new TypeError(name + “ is not a defined property”); } }); ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: My ECMAScript 7 wishlist
On Jun 5, 2014, at 6:25 PM, Brendan Eich bren...@mozilla.org wrote: LOL - as if O.p has getters! Thanks, this is more general in case of insanity. It probably always a good idea to use Reflect.* calls inside of proxy handlers. I’m seen variants of this mistake in many handlers snippets that are floating around. Need to encourage best practices. Allen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss