Alternative proposal to privateName.public
Here is a code snippet using the current direct proxy proposal and the private name proposals:
var name = new Name(); var target = {};
var p = new Proxy(target, {/* all default "transparent forwarding" traps */});
target[name] = 1; p[name] = 2;
// target[name] === 1 // target[name.toString()] === 2
2 properties are added with code that used the exact same unforgeable private name twice. I'm not sure to what extent this result is intended (since the private name design predates the switch to the direct proxies design), nor whether it's a good or a bad idea, but this discovery was a bit surprising.
I don't know whether my or Herby's alternative proposals are the end response, but I think the current interaction with proxies and private names needs to be reconsidered.
David
Le 17/12/2011 14:24, David Bruant a écrit :
Le 17/12/2011 14:24, David Bruant a écrit :
(...)
Proposal
What about a 'trapped' boolean in the private name constructor?
It would work this way:
var n = new Name(false); // don't trap when used is a proxy var p = new Proxy({}, maliciousHandler); p[n] = 21; // the set trap is NOT called! var v = p[n]; // the get trap is NOT called and v === 21
Basically, when the private name is created with the 'trapped' boolean to false, the proxy becomes oblivious to being called with these private names.
There has been some other proposals suggesting ways to bypass the proxy to work directly on the target. Since I have been brief, my proposal could be interepreted as such and it was not my intention. So here are additional code snippets to further explain my proposal.
var n = new Name(false); // untrapped name var t = {};
var p = new Proxy(t, maliciousHandler);
p[n] = 21; // the malicious set trap is NOT called! var v = p[n]; // the malicious get trap is NOT called and v === 21
Object.getOwnPropertyDescriptor(t, n); // undefined
In this proposal, in the case of untrapped names, only the proxy identity is used internally. No trap is called and the target remains untouched. There is neither implicit nor explicit forwarding to the target. If the code in possession of both a reference to the private name and a suspicious object does not want the suspicious object to have to do anything with the name, it can define the private name as untrapped and the proxy will be oblivious to the private name.
This choice is made in order to make the private name owners responsible for what they do with the private name, choose who they want the name to be shared with.
At first, I shared Andreas's concern about introducing a flag in feature X that only seems to affect a superficially unrelated feature Y.
However, having skimmed the private names page, I stumbled upon a section that seems to want to introduce precisely such a flag for different but related purposes: < harmony:private_name_objects#possible_visibility_flag
Having also just read about the different use cases of "private" names versus just "unique" names, it would make a lot of sense to me if we would separate these two (either via a flag or via different constructors):
- private names: invisible to for..in, Object.getOwnPropertyNames, and even proxies
- unique names: fully visible to for..in, Object.getOwnPropertyNames, and proxies
By tying the flag to use cases of private names (are you interested in the name's privacy or its uniqueness?), it makes more sense to include it in the API.
Cheers, Tom
2011/12/20 David Bruant <bruant.d at gmail.com>
Le 22/12/2011 13:20, Tom Van Cutsem a écrit :
At first, I shared Andreas's concern about introducing a flag in feature X that only seems to affect a superficially unrelated feature Y.
However, having skimmed the private names page, I stumbled upon a section that seems to want to introduce precisely such a flag for different but related purposes: harmony:private_name_objects#possible_visibility_flag
Having also just read about the different use cases of "private" names versus just "unique" names,
Can you give a link to or describe these uses cases? It will make easier (at least to me) understanding your proposal and compare it with other proposals.
Thanks,
2011/12/22 David Bruant <bruant.d at gmail.com>
Le 22/12/2011 13:20, Tom Van Cutsem a écrit :
Having also just read about the different use cases of "private" names versus just "unique" names, Can you give a link to or describe these uses cases? It will make easier (at least to me) understanding your proposal and compare it with other proposals.
I was just referring to the text on the private names proposal page, in particular the section "Possible visibility flag" mentioning modular monkey-patching.
The use case of modular monkey-patching is driven by the need for a unique property name, not necessarily a private property name. The goal here is to avoid name clashes.
Le 22/12/2011 17:15, Tom Van Cutsem a écrit :
2011/12/22 David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>>
Le 22/12/2011 13:20, Tom Van Cutsem a écrit : > Having also just read about the different use cases of "private" names > versus just "unique" names, Can you give a link to or describe these uses cases? It will make easier (at least to me) understanding your proposal and compare it with other proposals.
I was just referring to the text on the private names proposal page, in particular the section "Possible visibility flag" mentioning modular monkey-patching.
The use case of modular monkey-patching is driven by the need for a unique property name, not necessarily a private property name. The goal here is to avoid name clashes.
That was the part I was failing to understand, thanks for the explanation.
I don't find anything to say to your proposal. I understand the differences with what I proposed, but can't find argument to choose one or the other. I think the part I was finding the most annoying in the current proposal was the forced conversion in the trap (and being forced to convert back while in the handler) and you proposal gets rid of it too.
Le 22/12/2011 13:20, Tom Van Cutsem a écrit :
(...)
Having also just read about the different use cases of "private" names versus just "unique" names, it would make a lot of sense to me if we would separate these two (either via a flag or via different constructors):
- private names: invisible to for..in, Object.getOwnPropertyNames, and even proxies
- unique names: fully visible to for..in, Object.getOwnPropertyNames, and proxies
By tying the flag to use cases of private names (are you interested in the name's privacy or its uniqueness?), it makes more sense to include it in the API.
I've given more thought to this idea. For a second, let's imagine we have 2 independent flags: "visible" (or "reflectable") and "trapped". It would make 4 cases:
- visible & trapped
- invisible & trapped
- visible & untrapped
- invisible & untrapped
Case 3 would be a bit of a weird beast: the property should in theory be visible/reflectable, but cannot be trapped in the proxy. This is a rather inconsistent case.
My original proposal, was to choose between 2 or 4. Tom's proposal is to choose between 1 and 4. Another alternative would be to have the choice between 1, 2 and 4 (which would require something else than one or two booleans).
I have a slight preference for Tom's proposal as it makes even clearer that the trapped name can also be found through reflection (with Object.getOwnPropertyName) and should be handled with care.
2011/12/22 Tom Van Cutsem <tomvc.be at gmail.com>:
At first, I shared Andreas's concern about introducing a flag in feature X that only seems to affect a superficially unrelated feature Y.
However, having skimmed the private names page, I stumbled upon a section that seems to want to introduce precisely such a flag for different but related purposes: harmony:private_name_objects#possible_visibility_flag
Having also just read about the different use cases of "private" names versus just "unique" names, it would make a lot of sense to me if we would separate these two (either via a flag or via different constructors):
- private names: invisible to for..in, Object.getOwnPropertyNames, and even proxies
- unique names: fully visible to for..in, Object.getOwnPropertyNames, and proxies
I don't see how you need anything new in the language to support unique names.
var newUniqueName = (function() { var counter = 0; return function () { return "uniquename" + counter++; }; })();
var MyClass = (function() { var private1 = newUniqueName(); var private2 = newUniqueName(); return function() { this.my_public_variable = 0; this[private1] = 1; this[private2] = 2; } })();
var my_instance = new MyClass();
I'm a big fan of building what we need out of what we have rather than adding more and more to the language. Benefits include:
- Keeps VM complexity down
- Available now on all browsers
- If it turns out to be dumb we are not stuck with it for all eternity
Le 26/12/2011 15:56, Erik Corry a écrit :
2011/12/22 Tom Van Cutsem <tomvc.be at gmail.com>:
At first, I shared Andreas's concern about introducing a flag in feature X that only seems to affect a superficially unrelated feature Y.
However, having skimmed the private names page, I stumbled upon a section that seems to want to introduce precisely such a flag for different but related purposes: harmony:private_name_objects#possible_visibility_flag
Having also just read about the different use cases of "private" names versus just "unique" names, it would make a lot of sense to me if we would separate these two (either via a flag or via different constructors):
- private names: invisible to for..in, Object.getOwnPropertyNames, and even proxies
- unique names: fully visible to for..in, Object.getOwnPropertyNames, and proxies
I don't see how you need anything new in the language to support unique names.
var newUniqueName = (function() { var counter = 0; return function () { return "uniquename" + counter++; }; })();
I think that in the proposal, the definition of "unique" is "unique across the program". And this can't be achieved in JavaScript since no program can know, at a given time, which names are used and which are not. It also cannot know which names will be generated (this last part is undecidable anyway).
If it was considered to introduce a module that contained a property name in order to access ES5.1 [[Class]], this name could not be a string since it could conflict with other names used on this object. However, a unique name could be used.
import className from "@class";
var o = {'class': 1, 'className':2, '[[Class]]':3}; var a = o <| [];
o[className]; // "Object" a[className]; // "Array"
Despite my effort to make a collision in o definition, I can safely retrieve the internal [[Class]] of an object since the unique name stored in the className variable is guaranteed to be unique.
This will also allow implementors to experiment whatever they want without polluting objects, nor doing weird string-based "property-like non-properties" like 'proto', 'noSuchMethod'.
I don't know for [[class]], but iterators [1] already propose such a thing.
David
2011/12/26 David Bruant <bruant.d at gmail.com>:
Le 26/12/2011 15:56, Erik Corry a écrit :
2011/12/22 Tom Van Cutsem <tomvc.be at gmail.com>:
At first, I shared Andreas's concern about introducing a flag in feature X that only seems to affect a superficially unrelated feature Y.
However, having skimmed the private names page, I stumbled upon a section that seems to want to introduce precisely such a flag for different but related purposes: harmony:private_name_objects#possible_visibility_flag
Having also just read about the different use cases of "private" names versus just "unique" names, it would make a lot of sense to me if we would separate these two (either via a flag or via different constructors):
- private names: invisible to for..in, Object.getOwnPropertyNames, and even proxies
- unique names: fully visible to for..in, Object.getOwnPropertyNames, and proxies
I don't see how you need anything new in the language to support unique names.
var newUniqueName = (function() { var counter = 0; return function () { return "uniquename" + counter++; }; })(); I think that in the proposal, the definition of "unique" is "unique across the program". And this can't be achieved in JavaScript since no program can know, at a given time, which names are used and which are not. It also cannot know which names will be generated (this last part is undecidable anyway).
This can be fixed by convention. As long as there is only one uniqueName function then the names it makes will be unique. To ensure there is only one it can be installed like so:
if (!Object.newUniqueName) Object.newUniqueName = (... // See above.
The uniqueName string above can be replaced with something like i_have_read_and_abide_by_the_unique_name_convention
If that seems onerous to you consider whether it is really harder than modifying the VM and then waiting 10 years for your change to be universally available.
Le 26/12/2011 16:37, Erik Corry a écrit :
2011/12/26 David Bruant <bruant.d at gmail.com>:
Le 26/12/2011 15:56, Erik Corry a écrit :
I don't see how you need anything new in the language to support unique names.
var newUniqueName = (function() { var counter = 0; return function () { return "uniquename" + counter++; }; })(); I think that in the proposal, the definition of "unique" is "unique across the program". And this can't be achieved in JavaScript since no program can know, at a given time, which names are used and which are not. It also cannot know which names will be generated (this last part is undecidable anyway). This can be fixed by convention. As long as there is only one uniqueName function then the names it makes will be unique. To ensure there is only one it can be installed like so:
if (!Object.newUniqueName) Object.newUniqueName = (... // See above.
The uniqueName string above can be replaced with something like i_have_read_and_abide_by_the_unique_name_convention
I does not prevent conflicts. Only the likelyhood of conflicts since the name can be forged by other means than output of your function. In some applications it will be fine enough. In some others it won't.
If that seems onerous to you consider whether it is really harder than modifying the VM and then waiting 10 years for your change to be universally available.
I get your point, but 10 years is still better than never. Hopefully, we'll still be alive to see it in browsers ;-)
Though, since you work on V8 and that node.js updates regularly and aligns with new V8 versions, it's likely that people will be able to use unique names long before 10 years after you ship the change.
On a side note, I have seen a talk where someone mentionned that the "use the same JavaScript in server and client" was partially bullshit, because Node has support for some ES5 features that legacy web browsers can't even emuate.
2011/12/26 David Bruant <bruant.d at gmail.com>:
Le 26/12/2011 16:37, Erik Corry a écrit :
2011/12/26 David Bruant <bruant.d at gmail.com>:
Le 26/12/2011 15:56, Erik Corry a écrit :
I don't see how you need anything new in the language to support unique names.
var newUniqueName = (function() { var counter = 0; return function () { return "uniquename" + counter++; }; })(); I think that in the proposal, the definition of "unique" is "unique across the program". And this can't be achieved in JavaScript since no program can know, at a given time, which names are used and which are not. It also cannot know which names will be generated (this last part is undecidable anyway). This can be fixed by convention. As long as there is only one uniqueName function then the names it makes will be unique. To ensure there is only one it can be installed like so:
if (!Object.newUniqueName) Object.newUniqueName = (... // See above.
The uniqueName string above can be replaced with something like i_have_read_and_abide_by_the_unique_name_convention I does not prevent conflicts. Only the likelyhood of conflicts since the name can be forged by other means than output of your function. In some applications it will be fine enough. In some others it won't.
You stated that the unique names are "fully visible to for..in, Object.getOwnPropertyNames, and proxies". At that point there is no sense in worrying about forged unique names. Anyone can get hold of the unique name just by looking at an object that uses it. Making the unique names unforgeable doesn't help you if the original is there for the picking.
If that seems onerous to you consider whether it is really harder than modifying the VM and then waiting 10 years for your change to be universally available. I get your point, but 10 years is still better than never. Hopefully, we'll still be alive to see it in browsers ;-)
Though, since you work on V8 and that node.js updates regularly and aligns with new V8 versions, it's likely that people will be able to use unique names long before 10 years after you ship the change.
This is true, but it still makes sense to be conservative in what is added to the language vs. what can be done as an npm module.
On a side note, I have seen a talk where someone mentionned that the "use the same JavaScript in server and client" was partially bullshit, because Node has support for some ES5 features that legacy web browsers can't even emuate.
It's also partly bullshit because most of the time you want to write very different code on the server and the client. For one thing the trust model is completely different and that has a huge effect on all the code. For another the modules infrastructure that you have on node is (for good reasons, I suspect) completely different to the infrastructure available on the client, which makes a difference to the way you structure your program.
Never the less, there is a subset of JS that can be used on both the server and a given subset of supported clients, which is about the most you can expect.
Le 26/12/2011 17:27, Erik Corry a écrit :
2011/12/26 David Bruant <bruant.d at gmail.com>:
Le 26/12/2011 16:37, Erik Corry a écrit :
2011/12/26 David Bruant <bruant.d at gmail.com>:
Le 26/12/2011 15:56, Erik Corry a écrit :
I don't see how you need anything new in the language to support unique names.
var newUniqueName = (function() { var counter = 0; return function () { return "uniquename" + counter++; }; })(); I think that in the proposal, the definition of "unique" is "unique across the program". And this can't be achieved in JavaScript since no program can know, at a given time, which names are used and which are not. It also cannot know which names will be generated (this last part is undecidable anyway). This can be fixed by convention. As long as there is only one uniqueName function then the names it makes will be unique. To ensure there is only one it can be installed like so:
if (!Object.newUniqueName) Object.newUniqueName = (... // See above.
The uniqueName string above can be replaced with something like i_have_read_and_abide_by_the_unique_name_convention I does not prevent conflicts. Only the likelyhood of conflicts since the name can be forged by other means than output of your function. In some applications it will be fine enough. In some others it won't. You stated that the unique names are "fully visible to for..in, Object.getOwnPropertyNames, and proxies". At that point there is no sense in worrying about forged unique names. Anyone can get hold of the unique name just by looking at an object that uses it. Making the unique names unforgeable doesn't help you if the original is there for the picking.
I agree that unique names have a limited use when it comes to security, but the unforgeability property prevents conflicts. That's the use case that has been found for them in the private name proposal [1]: "This would be useful for e.g. modular monkey-patching."
David
Le 26/12/2011 14:40, David Bruant a écrit :
(...) I've given more thought to this idea. For a second, let's imagine we have 2 independent flags: "visible" (or "reflectable") and "trapped". It would make 4 cases:
- visible & trapped
- invisible & trapped
- visible & untrapped
- invisible & untrapped
Regarding unforgeable names as part of standard libraries (like for [[class]] or 'iterator'), it seems that for these two examples, these are things that all objects already "have", so it would make sense that all objects have a value for it (at least for [[Class]]).
However, if these unique property names were visible, it could break backward compatibility:
var o = {a:1}; Object.getOwnPropertyNames(o);
If property names for iterator or [[Class]] were visible, then the above snippets would have a different behavior in ES5 and ES6. So, standard unforgeable names, if they refer to something objects all have should be invisible.
Moreover, these standard names have no reason to be hidden from proxies (one more argument against having a systematic private -> public
translation when trapping). So it seems they should be trapped.
So it seems to me that these standard private names should be invisible and trapped (case 2 from above).
On Mon, Dec 26, 2011 at 7:54 AM, David Bruant <bruant.d at gmail.com> wrote:
On a side note, I have seen a talk where someone mentionned that the "use the same JavaScript in server and client" was partially bullshit, because Node has support for some ES5 features that legacy web browsers can't even emu[l]ate.
A pointer would be appreciated, thanks. Caja has a rather complete emulation of ES5 that runs in legacy browsers going back to IE6. The features that we're missing -- synchronous "eval" and the "Function" constructor -- are not features I would expect many Node programs to use. What other features are we missing?
You can try out our emulation online at < caja.appspot.com/trycaja/index.html> and caja.appspot.com.
Oh, and we prevent access to all property names that end in "__" (double underbar), which again is probably not what that speaker had in mind.
On Mon, Dec 26, 2011 at 8:27 AM, Erik Corry <erik.corry at gmail.com> wrote:
On a side note, I have seen a talk where someone mentionned that the "use the same JavaScript in server and client" was partially bullshit, because Node has support for some ES5 features that legacy web browsers can't even emuate.
It's also partly bullshit because most of the time you want to write very different code on the server and the client. For one thing the trust model is completely different and that has a huge effect on all the code.
Hi Erik, I'm wondering what you have in mind here. Could you please expand? Thanks.
For another the modules infrastructure that you have on node is (for good reasons, I suspect) completely different to the infrastructure available on the client, which makes a difference to the way you structure your program.
AMD (Asynchronous Module Definition) as supported on the client by require.js and curl.js is catching on for precisely that reason. There is no shortage of adapters between AMD and CommonJS modules, so AMD modules are also quite usable on the server.
Never the less, there is a subset of JS that can be used on both the server and a given subset of supported clients, which is about the most you can expect.
Which subset do you have in mind? Our ES5 emulation works on all ES3 clients on which we've tested, going back to IE6.
Le 26/12/2011 19:40, Mark S. Miller a écrit :
On Mon, Dec 26, 2011 at 7:54 AM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:
On a side note, I have seen a talk where someone mentionned that the "use the same JavaScript in server and client" was partially bullshit, because Node has support for some ES5 features that legacy web browsers can't even emu[l]ate.
A pointer would be appreciated, thanks. Caja has a rather complete emulation of ES5 that runs in legacy browsers going back to IE6. The features that we're missing -- synchronous "eval" and the "Function" constructor -- are not features I would expect many Node programs to use. What other features are we missing?
You can try out our emulation online at caja.appspot.com/trycaja/index.html and caja.appspot.com.
Since ES3 is Turing-complete, it's always possible to write an ES5.1 interpreter and run any ES5.1 code (as used in node) in an ES3-capable browser. I don't think neither Caja nor my extreme idea have been considered by the speaker.
I think that the concern that was raised was about not having the features natively. For instance, Object.defineProperty is not available and unless using rewritting like in Caja (or more radical solutions), it's not possible to emulate it in the browser.
In practice, IE6/7/8 already suffer from a performance penalty and adding the overhead of runtime checks made by Caja are probably not worth the cost in most web applications. That is certainly the reason why people will prefer to either "dumb down" their code to ES3 if they care about sharing or maintain the few sharable parts independently rather than using a rewritter.
Do you have benchmarks on the overhead between "naive code" and Caja generated code, specifically on IE6/7/8?
For another the modules infrastructure that you have on node is (for good reasons, I suspect) completely different to the infrastructure available on the client, which makes a difference to the way you structure your program.
I’ve heard that claim many times, but I have yet to see compelling evidence [1]. I find the competing module standards Common JS Modules and Asynchronous Module Definitions (AMDs) to be very similar. Some argue that AMDs are too complicated, but I don’t think they could be any simpler (in their basic version). Having two competing module standards hurts the JavaScript ecosystem, but with ECMAScript.next modules that problem will hopefully soon be gone.
AMD (Asynchronous Module Definition) as supported on the client by require.js and curl.js is catching on for precisely that reason. There is no shortage of adapters between AMD and CommonJS modules, so AMD modules are also quite usable on the server.
The adapters are all a bit cumbersome to use. IMHO, the best option is still boilerplate (to conditionally turn an AMD into a Node.js module): ({ define: typeof define === "function" ? define : function(A,F) { module.exports = F.apply(null, A.map(require)) } }). define([ "./module1", "./module2" ], function (module1, module2) { return ... } );
On Mon, Dec 26, 2011 at 11:08 AM, David Bruant <bruant.d at gmail.com> wrote:
Le 26/12/2011 19:40, Mark S. Miller a écrit :
[...]
A pointer would be appreciated, thanks. Caja has a rather complete emulation of ES5 that runs in legacy browsers going back to IE6. [...]
You can try out our emulation online at < caja.appspot.com/trycaja/index.html> and caja.appspot.com.
Since ES3 is Turing-complete, it's always possible to write an ES5.1 interpreter and run any ES5.1 code (as used in node) in an ES3-capable browser. I don't think neither Caja nor my extreme idea have been considered by the speaker.
Likely. My point was to avoid the same oversight being made by those who hear and repeat the speaker's message ;).
The Caja translation is much more direct than a general emulation of one Turing complete language on another. It maps ES5 concepts onto ES3 concepts very directly, enabling the emulated ES5 world to interoperate pleasantly with the host ES3 world. For example, leaving aside property names ending with "__" (double underscore), if "x.foo" succeeds in both guest and host language, it generally means the same thing. The directness of the mapping also helps the efficiency of the layering.
I think that the concern that was raised was about not having the features natively. For instance, Object.defineProperty is not available and unless using rewritting like in Caja (or more radical solutions), it's not possible to emulate it in the browser.
In practice, IE6/7/8 already suffer from a performance penalty and adding the overhead of runtime checks made by Caja are probably not worth the cost in most web applications. That is certainly the reason why people will prefer to either "dumb down" their code to ES3 if they care about sharing or maintain the few sharable parts independently rather than using a rewrit[]er.
It depends on your goals. More and more web applications are starting to write off IE6 as no longer supported. Depending on your goals, it may be better to have your modern web app limp along correctly on legacy platforms than to not work correctly or at all on them.
Do you have benchmarks on the overhead between "naive code" and Caja generated code, specifically on IE6/7/8?
I no longer have easy access to a machine running IE6. As I recall, the slower the JS engine, the less relative overhead added by the Caja translation (which btw is a nice tradeoff since we expect to switch modern browsers over to translation-free SES). If memory serves, the relative slowdown on Rhino was similar to the relative slowdown on IE6. Though if someone could double check this, I'd be grateful.
The Rhino benchmark comparisons are at < code.google.com/p/google-caja/wiki/Performance>. The "cajita" and "valija" numbers are no longer relevant. Please check only the various "original" and "es53" checkboxes. "es53" is short for "EcmaScript 5 over (as emulated on) EcmaScript 3". The most relevant results are the macro-benchmark runtimes, for which we get
testRichards: 2.1x slowdown testEarleyBoyer: No significant slowdown
As always, take all benchmarks with a pound of salt.
Le 26/12/2011 22:28, Mark S. Miller a écrit :
On Mon, Dec 26, 2011 at 11:08 AM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:
Le 26/12/2011 19:40, Mark S. Miller a écrit :
[...]
A pointer would be appreciated, thanks. Caja has a rather complete emulation of ES5 that runs in legacy browsers going back to IE6. [...] You can try out our emulation online at <http://caja.appspot.com/trycaja/index.html> and <http://caja.appspot.com/>.
Since ES3 is Turing-complete, it's always possible to write an ES5.1 interpreter and run any ES5.1 code (as used in node) in an ES3-capable browser. I don't think neither Caja nor my extreme idea have been considered by the speaker.
Likely. My point was to avoid the same oversight being made by those who hear and repeat the speaker's message ;).
And it was a good idea since I fell into the trap myself ;-)
Here is a proposal related to the discussion we've had in the other thread. Gist: gist.github.com/1490167
Introduction
We've seen in the thread "Are Private name and Weak Map the same feature? and the Assoc API" that .public was here to prevent proxies from having access to a private name. This constructs results in a bigger efforts to use private names in coordination with proxies (need to access the public part and a map to retrieve the private name with the remaining danger of sharing the map with the wrong entities).
Sam Tobin-Hochstadt gave an example of a simple program we may not expect to leak a private name but would if private names passed through proxies and the program was used with a malicious proxy. I answered that we should consider changing the way we write program and I admit that I am myself a bit annoyed by this statement.
So here is a proposal where I think I can make both parties (those who don't want to write complicated programs to protect their private names and those who don't want to write complicated programs to make private names and proxies work together) happy.
Proposal
What about a 'trapped' boolean in the private name constructor?
It would work this way:
var n = new Name(false); // don't trap when used is a proxy var p = new Proxy({}, maliciousHandler); p[n] = 21; // the set trap is NOT called! var v = p[n]; // the get trap is NOT called and v === 21
Basically, when the private name is created with the 'trapped' boolean to false, the proxy becomes oblivious to being called with these private names.
var n = new Name(true); // trap when used on a proxy var p = new Proxy({}, { get:function(target, name, receiver){ console.log(name === n); return target[name]; }, set:function(target, name, val, receiver){ console.log(name === n); target[name] = val; } }); p[n] = 21; // the set trap is called and logs 'true' var v = p[n]; // the get trap is called, logs 'true' and v === 21
The name passed to the traps is the actual private name.
In the case exposed by Sam, using an untrapped name will do the trick, in my cases, using trapped names will do the trick as well. If someone wants some proxies (and by "proxies" I mean "untrusted third party") to have access to a secret and some not, then, i think it's fair to ask this person to have several secrets and dictionary and arbitrary complicated constructs to achieve her goal.
If as Sam suggested, most cases don't need proxies to have access to an information, let the default value of 'trapped' be 'false'.
I expect the following response: "but then the proxy does not reify the operation made on untrapped private names". It is true and it is true with the current proposal as well. Currently, maybe that the proxy traps something but it's using an altered information (which I'm not sure can really be considered as a true reification). At the end of the day, I don't think there is a feature loss in not calling a function or calling it with a virtually useless information. What I'm proposing is to make more explicit (a boolean in the private name constructor) whether or not we want a proxy to do something useful with a name.
Is it a good compromise?