Security Demands Simplicity (was: Private Slots)

# Mark S. Miller (12 years ago)

First, I agree with David that security is important, both for security per se and for modularity in general. ES5 improved the language when it fixed the accidental leaks between the world of objects and scope-chain objects, like thrown functions. ES5/strict improved the language when it made functions really encapsulated, poisoned caller and arguments, and repaired the remaining violations of static scoping. ES6 modules and CommonJS/Node modules were born and remain encapsulated. I have not heard anyone suggesting any of these would be improved by introducing pliers for opening them against their will (except in a debugger, which is a different issue). Tons of code have been written in ES3. We still made the world a better place when we plugged its leaks.

I also agree with David that unique symbols are not an encapsulation mechanism, but rather, merely a mechanism to avoid namespace collisions.

As for Java's reflective breakage of "private", www.nbcnews.com/technology/technolog/us-warns-java-software-security-concerns-escalate-1B7938755.

However, after the "Private Slots" thread, I spent a sleepless night chewing on getting rid of private symbols. I now think we should. Going back to my earlier

On Wed, Jan 16, 2013 at 12:37 PM, Mark S. Miller <erights at google.com> wrote:

My position on private symbols.

My position on classes is and has always been that classes are worth introducing into the language only if they give us, or can be used with, an affordable means for true object encapsulation. Assuming Allen is right about what actual implementors will do (which I find plausible) then WeakMaps are not that means.

I still have this position on classes. But I no longer buy that pessimistic conclusion about WeakMaps. Consider how WeakMaps would be used by the expansion of classes-with-private. Just 'cause it's on the top of my head, below I use the old representation of one WeakMap per class providing access to a record of all the private state. For the same reason, I'll use the encapsulation of the Purse example without any of the numeric checks.

class Purse { constructor(private balance) { getBalance() { return balance; } makePurse() { return Purse(0); } deposit(amount, srcPurse) { private(srcPurse).balance -= amount; balance += amount; } }

expansion

let Purse = (function() { let amp = WeakMap(); function Purse(balance) { amp.set(this, Object.seal({ get balance() { return balance; }, set balance(newBalance) { balance = newBalance; } })); } Purse.prototype = { getBalance: function() { return balance; }, makePurse: function() { return Purse(0); }, deposit: function(amount, srcPurse) { amp.get(srcPurse).balance -= amount; } } return Purse; })();

Ignore the issues about whether we should use one WeakMap per class and a private record as above, or a WeakMap per private field name declaration.

Ignore the use of accessors so that private field names track variables. If we had instead stored the state in the private field and compiled getBalance to use the field, that doesn't affect the important issue.


Notice that the lifetime of amp cannot be shorter than the lifetimes of the Point instances, since the instances retain amp, and these instances are the only objects ever stored as keys in amp.


For this usage common pattern, the lifetime of the keys of amp cannot outlive the lifetime of amp.

Thus, no matter what the spec says, normatively or not, about expected storage costs, competitive pressures will drive JS engine implementors to make this efficient. The way to make this efficient is by the technique previously discussed -- hang the private state off of the object, not the weakmap. Use the weakmap only as an unforgeable token for naming and accessing this state. If we get rid of private symbols, we should expect this pattern of usage of WeakMap to have the same cost that private symbols would have had. We can help implementors achieve that by having this expansion call instead "WeakMap(USE_KEY_LIFETIME_HINT)" or whatever it is called. Then implementations would not have to recognize the pattern by other means.

Complexity is the enemy of security. We already have four encapsulation mechanisms in ES6 in addition to private symbols:

  1. functions encapsulating lexical variables
  2. WeakMaps
  3. proxies encapsulating handlers and targets
  4. modules encapsulating what they don't export.

With enough spec and engineering effort, any of these could be grown into a means for providing efficient class/object encapsulation. Of them, I think #2 is most plausible. Even is we find #2 does not work out, we should think about growing one of the other candidates as an alternative to private symbols. Security demands simplicity, and semantic simplicity is more important than implementation simplicity.

# Andreas Rossberg (12 years ago)

On 17 January 2013 18:00, Mark S. Miller <erights at google.com> wrote:

I still have this position on classes. But I no longer buy that pessimistic conclusion about WeakMaps. Consider how WeakMaps would be used by the expansion of classes-with-private. Just 'cause it's on the top of my head, below I use the old representation of one WeakMap per class providing access to a record of all the private state. For the same reason, I'll use the encapsulation of the Purse example without any of the numeric checks.

class Purse { constructor(private balance) { getBalance() { return balance; } makePurse() { return Purse(0); } deposit(amount, srcPurse) { private(srcPurse).balance -= amount; balance += amount; } }

Hm, I'm afraid I don't fully understand that example. There seems to be a missing closing brace for the constructor, and I don't know what the free occurrences of 'balance' are referring to. Also, the second line of the deposit function seems to be missing in the expansion.

# Mark S. Miller (12 years ago)

On Thu, Jan 17, 2013 at 9:13 AM, Andreas Rossberg <rossberg at google.com> wrote:

Hm, I'm afraid I don't fully understand that example. There seems to be a missing closing brace for the constructor, and I don't know what the free occurrences of 'balance' are referring to. Also, the second line of the deposit function seems to be missing in the expansion.

You're right. I also forgot a "new". Corrected I think:

class Purse { constructor(private balance) {} getBalance() { return balance; } makePurse() { return new Purse(0); } deposit(amount, srcPurse) { private(srcPurse).balance -= amount; balance += amount; } }

expansion

let Purse = (function() { let amp = WeakMap(); function Purse(balance) { amp.set(this, Object.seal({ get balance() { return balance; }, set balance(newBalance) { balance = newBalance; } })); } Purse.prototype = { getBalance: function() { return balance; }, makePurse: function() { return new Purse(0); }, deposit: function(amount, srcPurse) { amp.get(srcPurse).balance -= amount; balance += amount; } } return Purse; })();

Please let me know if anything else remains mysterious or looks wrong.

# Mark S. Miller (12 years ago)

Still not right. I screwed up the scoping of the "balance" variable. Another correction coming soon.

# Mark S. Miller (12 years ago)

(As usual, trying to write code in a hurry actually cost more time :(. Sorry to have spent every else's time as well :(. )

class Purse { constructor(private balance) {} getBalance() { return balance; } makePurse() { return new Purse(0); } deposit(amount, srcPurse) { private(srcPurse).balance -= amount; balance += amount; } }

expansion

let Purse = (function() { let amp = WeakMap(); function Purse(balance) { amp.set(this, Object.seal({ balance: balance })); } Purse.prototype = { getBalance: function() { return amp.get(this).balance; }, makePurse: function() { return new Purse(0); }, deposit: function(amount, srcPurse) { amp.get(srcPurse).balance -= amount; amp.get(this).balance += amount; } } return Purse; })();

When I said previously

"Ignore the use of accessors so that private field names track variables. If we had instead stored the state in the private field and compiled getBalance to use the field, that doesn't affect the important issue."

it turns out only the second representation works, as above.

In any case, the "ignore" statement remains. This is all besides the point I'm really trying to make. I hope my code is now corrected enough for the real point to be clear. The Ephemeron gc technique contributes nothing to the ability to reclaim space for such code because of the relative lifetimes of the map and its keys.

# Axel Rauschmayer (12 years ago)

Can we separate of concerns?

  1. I love symbols.

  2. I also love the idea that you can mark properties as private, as invisible to the outside. Currently I prefix my properties with _ to do so, but it’s an ugly namespace pollution.

  3. But, for me and probably most applications, the privacy does not have to be bullet proof. For the rare cases where we need. bullet-proofing, weak maps might be enough.

  4. Enumerability still seems like something that only exists because for for-in. Unfortunately, it can’t take up role #2, because so many things are already non-enumerable that are clearly public.

Does that mean that the current ES6 model could be simplified? For example: another property attribute visible, making properties invisible. Not sure where the invisibility matters: at an IDE level, when you do an expansion (e.g. type myobj.). Or should it also be hidden from all current iteration operations (Object.getOwnPropertyNames() etc.)? In either case, there would still be a way of iterating over all properties, making copying etc. straightforward.

# Brandon Benvie (12 years ago)

Well keep in mind that Symbols of any type already require using a new function, getOwnKeys (or whatever the name is) so they are already essentially "super non-enumerable". They are reflectable if someone really wants to see them, but they are never reflected in Object.keys, Object.getOwnPropertyNames, or for...in enumeration.

# Axel Rauschmayer (12 years ago)

Right. That could work in favor of this approach.

# Herby Vojčík (12 years ago)

Mark S. Miller wrote:

I also agree with David that unique symbols are not an encapsulation mechanism, but rather, merely a mechanism to avoid namespace collisions.

However, after the "Private Slots" thread, I spent a sleepless night chewing on getting rid of private symbols. I now think we should. Going back to my earlier

On Wed, Jan 16, 2013 at 12:37 PM, Mark S. Miller<erights at google.com> wrote:

My position on private symbols.

My position on classes is and has always been that classes are worth introducing into the language only if they give us, or can be used with, an affordable means for true object encapsulation. Assuming Allen is right about what actual implementors will do (which I find plausible) then WeakMaps are not that means.

I still have this position on classes. But I no longer buy that pessimistic conclusion about WeakMaps. Consider how WeakMaps would be used by the expansion of classes-with-private. Just 'cause it's on the top of my head, below I use the old representation of one WeakMap per class providing access to a record of all the private state. For the same reason, I'll use the encapsulation of the Purse example without any of the numeric checks.

class Purse { ... an example w/o and w/ external WeakMap

Thus, no matter what the spec says, normatively or not, about expected storage costs, competitive pressures will drive JS engine implementors to make this efficient. The way to make this efficient is by the technique previously discussed -- hang the private state off of the object, not the weakmap. Use the weakmap only as an unforgeable token for naming and accessing this state. If we get rid of private symbols, we should expect this pattern of usage of WeakMap to have the same cost that private symbols would have had. We can help implementors achieve that by having this expansion call instead "WeakMap(USE_KEY_LIFETIME_HINT)" or whatever it is called. Then implementations would not have to recognize the pattern by other means.

Complexity is the enemy of security. We already have four encapsulation mechanisms in ES6 in addition to private symbols:

  1. functions encapsulating lexical variables
  2. WeakMaps
  3. proxies encapsulating handlers and targets
  4. modules encapsulating what they don't export.

With enough spec and engineering effort, any of these could be grown into a means for providing efficient class/object encapsulation. Of them, I think #2 is most plausible. Even is we find #2 does not work out, we should think about growing one of the other candidates as an alternative to private symbols. Security demands simplicity, and semantic simplicity is more important than implementation simplicity.

This leads me to the question: Are symbols needed at all (even unique ones)? They could be implemented using WeakMap (or any other "external encapsulator) as well. The only difference is, they would be known and reflectanle.

Thus (including my idea in "Private symbols as WeakMap sugar"), if we could write

obj[prop]

and mean

[isExternalEncapsulator] ? prop.get(obj) : legacy obj[prop]

then WeakMaps will simply have @@isExternalEncapsulator return true.

The reflectability is the only other matter to be solved. In case of symbols, API like gOPN and Object.keys() was not touched, and other API must have been added to include symbols.

So, thinking about it, if there were no symbols at all but there were (more generic) encapsulators, one can simply add APIs to return visible encapsulators, the same as it return symbols now (the encapsulators need to know if they are reflectable or not, of course).

I think there may be less confusion because encapsulators would clearly be different beasts than properties; symbols tried to be as similar as possible but even then they need different API.

-- Cheers, --MarkM

Herby

P.S.: Now that I think about it, encapsulators need to be weak, so they do not keep an instance living; therefore probably only WeakMaps are legible to be one.

So alternative proposal is to allow obj[wm] and to allow wm to set itself and reflectable/nonreflectable when used in obj[wm]. Thus, wms can replace symbols altogether.

P.P.S.: Or not (reacting to "only WeakMaps are legible"). It can be done other way: all the external encapsulator are weak by default (in a sense, their pointer to instance is not treated as strong). Probably just a crazy thought experiment... but in that way, one need no WeakMap, just a Map used as external encapsulator to have weak Map ;-)

# David Bruant (12 years ago)

Le 17/01/2013 18:00, Mark S. Miller a écrit :

However, after the "Private Slots" thread, I spent a sleepless night chewing on getting rid of private symbols.

Happens to me so often :-)

I now think we should. Going back to my earlier

On Wed, Jan 16, 2013 at 12:37 PM, Mark S. Miller <erights at google.com> wrote:

My position on private symbols.

My position on classes is and has always been that classes are worth introducing into the language only if they give us, or can be used with, an affordable means for true object encapsulation. Assuming Allen is right about what actual implementors will do (which I find plausible) then WeakMaps are not that means. I still have this position on classes. But I no longer buy that pessimistic conclusion about WeakMaps. Consider how WeakMaps would be used by the expansion of classes-with-private. Just 'cause it's on the top of my head, below I use the old representation of one WeakMap per class providing access to a record of all the private state. For the same reason, I'll use the encapsulation of the Purse example without any of the numeric checks.

class Purse { constructor(private balance) { getBalance() { return balance; } makePurse() { return Purse(0); } deposit(amount, srcPurse) { private(srcPurse).balance -= amount; balance += amount; } }

expansion

I don't understand the need to expand. This is the class syntax. It creates a constructor. Instances of this constructor have some behavior and characteristics described by the class body. If the class body allows to define something private, implementors will implement that and work very hard on performance. I don't think implementors will expand before compiling and using the class syntax as some sort of macro (implementors are free to tell I'm wrong here). Assuming I'm right, what it expands to exactly does not matter much, whether it's private name or weakmaps or whatever else.

Now you're only talking about privacy in the context of classes in this post. I understand this as assuming private symbols would be only use in class or class-like contexts (after referred as "this assumption" or "the assumption"). If that's your assumption, then I agree that if classes have a "private" syntax that is efficiently compiled, private symbols can be dropped. It has to be noted that if private symbols are dropped, the set of private properties is sealed once and for all in the class body. That is, it's not possible to add or remove a private property after initialization. (However it remains possible to privately attach information to an object with a weakmap kept private)

If "private symbols will be only used in class or class-like contexts" is your assumption, let's talk about it. Historically, JavaScript objects have been used in both in OO contexts and as maps. This confusion led to some bugs (involving proto for instance) As I have suggested in another recent post, most of the time, objects in OO contexts are pretty stable: they are created with a given set of properties which doesn't change. This stability could even be declared statically (that's what the class syntax does actually). However, for the map use case, it makes sense to add and remove properties at runtime.

ES6 adds actual maps, so objects (especially the ones instanciated after class-born constructors) could be legitimately considered as reserved for OO contexts. That's a reasoning I agree with since I can't think of cases besides classes where private symbols would be used and for which weakmaps wouldn't be better.

So I guess I agree with the removal of private symbols given classes have a syntax for private properties and no other relevant use case for private symbols is found.

# Mark S. Miller (12 years ago)

On Thu, Jan 17, 2013 at 10:02 AM, David Bruant <bruant.d at gmail.com> wrote:

Le 17/01/2013 18:00, Mark S. Miller a écrit :

However, after the "Private Slots" thread, I spent a sleepless night chewing on getting rid of private symbols.

Happens to me so often :-)

I now think we should. Going back to my earlier

On Wed, Jan 16, 2013 at 12:37 PM, Mark S. Miller <erights at google.com> wrote:

My position on private symbols.

My position on classes is and has always been that classes are worth introducing into the language only if they give us, or can be used with, an affordable means for true object encapsulation. Assuming Allen is right about what actual implementors will do (which I find plausible) then WeakMaps are not that means.

I still have this position on classes. But I no longer buy that pessimistic conclusion about WeakMaps. Consider how WeakMaps would be used by the expansion of classes-with-private. Just 'cause it's on the top of my head, below I use the old representation of one WeakMap per class providing access to a record of all the private state. For the same reason, I'll use the encapsulation of the Purse example without any of the numeric checks.

class Purse { constructor(private balance) { getBalance() { return balance; } makePurse() { return Purse(0); } deposit(amount, srcPurse) { private(srcPurse).balance -= amount; balance += amount; } }

expansion

I don't understand the need to expand. This is the class syntax. It creates a constructor. Instances of this constructor have some behavior and characteristics described by the class body. If the class body allows to define something private, implementors will implement that and work very hard on performance. I don't think implementors will expand before compiling and using the class syntax as some sort of macro (implementors are free to tell I'm wrong here). Assuming I'm right, what it expands to exactly does not matter much, whether it's private name or weakmaps or whatever else.

Regarding what I actually said, you're right. I am being unbelievable confusing this morning. After that sleepless night, I should probably wait till I'm rested before posting again. Foolishly perhaps, I'll try to answer anyway.

  1. It remains useful to define the semantics of classes by expansion. Then one can reason about classes by reasoning about the expansion. This is especially helpful for automated tools. The semantics had then better be faithful to the explanatory expansion.

  2. Until ES7 provides private, there will be transpilers that provide actual privacy by other means.

  3. Most important, and most unsaid by my previous email: In ES6, since there's no built-in class/object privacy mechanism, ES6 programmers in general and ES6 class programmers specifically have to achieve real privacy by other means -- by engaging in some pattern using other constructs. Previous conversations assumed that these patterns would be expressed in terms of private symbols. Absent private symbols, the pattern shown by my expansion above, as painful as it is, is what will be expressed manually. By the time ES7 rolls out, there will be a lot of that code.

# Mark Miller (12 years ago)

I just looked back at the example code in "Distributed Electronic Rights in JavaScript". It uses WeakMaps in two places: Fig1 line2 and Fig3 line2. Both of these express objects using the objects-as-closures pattern, not using classes. It's clearly affordable here since there are only 3 and 2 methods respectively. In both of these, the use of WeakMap has the lifetime properties I point out -- the weakmap's keys can never outlive the weakmap. The pattern, as you see there, expressed manually without any classes, is syntactically quite pleasant. Implementations that use Ephemeron gc techniques to implement these weakmaps will be outperformed by implementations that simply hang the private value state off of the key object. These manual uses of WeakMap would use the hint if such a hint existed.

# David Bruant (12 years ago)

Le 17/01/2013 19:30, Mark S. Miller a écrit :

On Thu, Jan 17, 2013 at 10:02 AM, David Bruant <bruant.d at gmail.com> wrote:

Le 17/01/2013 18:00, Mark S. Miller a écrit :

I still have this position on classes. But I no longer buy that pessimistic conclusion about WeakMaps. Consider how WeakMaps would be used by the expansion of classes-with-private. Just 'cause it's on the top of my head, below I use the old representation of one WeakMap per class providing access to a record of all the private state. For the same reason, I'll use the encapsulation of the Purse example without any of the numeric checks.

class Purse { constructor(private balance) { getBalance() { return balance; } makePurse() { return Purse(0); } deposit(amount, srcPurse) { private(srcPurse).balance -= amount; balance += amount; } }

expansion I don't understand the need to expand. This is the class syntax. It creates a constructor. Instances of this constructor have some behavior and characteristics described by the class body. If the class body allows to define something private, implementors will implement that and work very hard on performance. I don't think implementors will expand before compiling and using the class syntax as some sort of macro (implementors are free to tell I'm wrong here). Assuming I'm right, what it expands to exactly does not matter much, whether it's private name or weakmaps or whatever else. Regarding what I actually said, you're right. I am being unbelievable confusing this morning. After that sleepless night, I should probably wait till I'm rested before posting again. Foolishly perhaps, I'll try to answer anyway.

  1. It remains useful to define the semantics of classes by expansion. Then one can reason about classes by reasoning about the expansion. This is especially helpful for automated tools. The semantics had then better be faithful to the explanatory expansion.

  2. Until ES7 provides private

I may have missed something. In the harmony proposal on the wiki [1] (is the wiki down? I'm looking at google's cache), I see that syntax for private properties is in the proposal and nothing suggests it's postpone until ES7. I see in recent notes that the @-syntax isn't fully agreed upon, but that's where I'm at. Is there no private syntax at all for classes in ES6?

there will be transpilers that provide actual privacy by other means.

What syntax and semantics will these transpilers use if TC39 doesn't reach an agreement?

  1. Most important, and most unsaid by my previous email: In ES6, since there's no built-in class/object privacy mechanism, ES6 programmers in general and ES6 class programmers specifically have to achieve real privacy by other means -- by engaging in some pattern using other constructs. Previous conversations assumed that these patterns would be expressed in terms of private symbols. Absent private symbols, the pattern shown by my expansion above, as painful as it is, is what will be expressed manually. By the time ES7 rolls out, there will be a lot of that code.

People who care will use a transpiler (and I personally don't care if the generated code is slightly slower than what it could be natively or ugly). People who don't care about privacy will go on with their lives writing JavaScript as they do in ES5. That's an ephemeral problem in my opinion and isn't the "most important" point. Am I underestimating it?

David

[1] harmony:classes

# Kevin Smith (12 years ago)

The Ephemeron gc technique contributes nothing to the ability to reclaim space for such code because of the relative lifetimes of the map and its keys.

Yes - my line-of-thought as well. If I may riff on your code a bit (ymmv):

class Purse {

    // Move private field declaration out here for more scopy-ness
    private balance;

    // Allow private without arg as shorthand for "private(this)"
    constructor(balance = 0) { private.balance = balance; }

    // Everything else the same
    getBalance() { return balance; }
    makePurse() { return new Purse; }
    deposit(amount, srcPurse) {
        private(srcPurse).balance -= amount;
        balance += amount;
    }
}

The expansion is exactly the same, except that the private record is sealed before the constructor body starts executing. Reconceptualizing private instance state in terms of a separate object stored in a weakmap - interesting!

# Mark S. Miller (12 years ago)

On Thu, Jan 17, 2013 at 1:13 PM, David Bruant <bruant.d at gmail.com> wrote:

Le 17/01/2013 19:30, Mark S. Miller a écrit :

  1. Until ES7 provides private

I may have missed something. In the harmony proposal on the wiki [1] (is the wiki down? I'm looking at google's cache),

I don't know, but seems down for me too. But the classes accepted for ES6 are "maximally minimal" and contain no field declarations of any kind.

I see that syntax for private properties is in the proposal and nothing suggests it's postpone until ES7. I see in recent notes that the @-syntax isn't fully agreed upon, but that's where I'm at. Is there no private syntax at all for classes in ES6?

Correct.

there will be transpilers that provide actual privacy by other means.

What syntax and semantics will these transpilers use if TC39 doesn't reach an agreement?

  1. Most important, and most unsaid by my previous email: In ES6, since there's no built-in class/object privacy mechanism, ES6 programmers in general and ES6 class programmers specifically have to achieve real privacy by other means -- by engaging in some pattern using other constructs. Previous conversations assumed that these patterns would be expressed in terms of private symbols. Absent private symbols, the pattern shown by my expansion above, as painful as it is, is what will be expressed manually. By the time ES7 rolls out, there will be a lot of that code.

People who care will use a transpiler (and I personally don't care if the generated code is slightly slower than what it could be natively or ugly). People who don't care about privacy will go on with their lives writing JavaScript as they do in ES5. That's an ephemeral problem in my opinion and isn't the "most important" point. Am I underestimating it?

I believe you are. For abstractions with small numbers of methods and no interesting inheritance, I find the objects-as-closures pattern + weakmaps for class-private instance variables, as in our recent paper, to be more pleasant than the likely ES7 class syntax anyway; and much simpler to understand. And it's way more pleasant than the maximin classes accepted for ES6. We will see a lot of code manually using WeakMaps in this way.

# Brandon Benvie (12 years ago)

November's meeting had re-instituted computed property names to compensate for the dropping @name support to allow for symbols to be used as method names. AFAICT there's currently no actual way to create a private Symbol in any active proposals for ES6, however (this was only covered as part of the @names proposal). Currently Symbol only accepts one parameter, and that's the string label/name for it.

# Mark S. Miller (12 years ago)

On Thu, Jan 17, 2013 at 1:50 PM, Mark S. Miller <erights at google.com> wrote:

On Thu, Jan 17, 2013 at 1:13 PM, David Bruant <bruant.d at gmail.com> wrote:

Le 17/01/2013 19:30, Mark S. Miller a écrit :

  1. Until ES7 provides private

I may have missed something. In the harmony proposal on the wiki [1] (is the wiki down? I'm looking at google's cache),

I don't know, but seems down for me too. But the classes accepted for ES6 are "maximally minimal" and contain no field declarations of any kind.

Back up. Maximin is at strawman:maximally_minimal_classes

# Kevin Smith (12 years ago)

It seems as if this approach to private class members also allows us to describe private methods in a convenient way. Private methods can be attached to the prototype of the private field object, thus avoiding per-instance allocation. Of course, the correct "this" value has to be used in the expansion when calling the private method, but this approach appears to be compatible with mixins (whereas private symbols are not).

gist.github.com/4561871

Thoughts?

# Russell Leggett (12 years ago)

On Thu, Jan 17, 2013 at 9:40 PM, Kevin Smith <khs4473 at gmail.com> wrote:

It seems as if this approach to private class members also allows us to describe private methods in a convenient way. Private methods can be attached to the prototype of the private field object, thus avoiding per-instance allocation. Of course, the correct "this" value has to be used in the expansion when calling the private method, but this approach appears to be compatible with mixins (whereas private symbols are not).

gist.github.com/4561871

Thoughts?

I've been on the fence with the debate, but I'll admit that this proposal is really winning me over. I don't know if it's too late to get this syntax in, but it would be a major win for me. I like this a lot more than private symbols. It actually feels a lot like deconstructing an abstract data type or pattern matching a case class in scala. There is something nice about keeping all that extra stuff off the actual object. I'm not sure if I'd still like it as much without the syntax, but maybe I would now.

I have do have a few questions on this, though:

  1. How does this interact with proxies? I'm guessing that a proxy will not map correctly and therefore make it inaccessible. Personally, I'm ok with that.

  2. Can I use private as a normal variable? Can it be bound to a new variable, passed as an argument, or returned from a function? constructor(balance = 0) { let p = private; p.checkNum(amount); p.balance = balance; } I would understand not going this route, but it definitely restricts access very tightly where private member or manually using a weakmap would allow it. Perhaps that edge case can just be handled by falling back to manually using a weakmap. I admit its a pretty narrow case, and perhaps is best to not be easy to do. If it were to be allowed to be passed around, the shorthand accessor syntax wouldn't work unless you captured the "this" as part of the assignment. Definitely possible, though.

  3. Can private() be used to add arbitrary object to the map even if they don't belong to the class? let obj = {}; private(obj).balance = 10; I'm guessing not, as it translates to a get right now, but curious if this was a consideration.

# Domenic Denicola (12 years ago)

If we’re making up new syntax, I think this would be much nicer if “private.x” were spelled “this. at x” and “private(x)” were spelled “x.@”

Also, I don’t see why constructors need to use the “private.x” syntax whereas other methods get to use the free variable?

With these in mind I give the following fork: gist.github.com/4562796

From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Kevin Smith Sent: Thursday, January 17, 2013 21:40 To: Mark S. Miller Cc: Brendan Eich; es-discuss Subject: Re: Security Demands Simplicity (was: Private Slots)

It seems as if this approach to private class members also allows us to describe private methods in a convenient way. Private methods can be attached to the prototype of the private field object, thus avoiding per-instance allocation. Of course, the correct "this" value has to be used in the expansion when calling the private method, but this approach appears to be compatible with mixins (whereas private symbols are not).

gist.github.com/4561871

Thoughts?

# Kevin Smith (12 years ago)

Also, I don’t see why constructors need to use the “private.x” syntax whereas other methods get to use the free variable?

They wouldn't. I was just trying to show the different ways to do the same thing.



With these in mind I give the following fork: gist.github.com/4562796

Cool variation! We still might want to use @ for (unique) symbols though... (?) Also, "private" makes it clear we are dealing with a separate (although intimately related) object.

# Brendan Eich (12 years ago)

Domenic Denicola wrote:

If we’re making up new syntax, I think this would be much nicer if “private.x” were spelled “this. at x” and “private(x)” were spelled “x.@”

+1 and this is not a minor point.

Also, I don’t see why constructors need to use the “private.x” syntax whereas other methods get to use the free variable?

+2 -- Same here, only moreso!

This thread goes over ground well-trod in 2011:

esdiscuss/2011-July/015787

and others -- search for "private data record" 2011 es-discuss site:mail.mozilla.org.

With these in mind I give the following fork: gist.github.com/4562796

Kevin, could you do an alterna-gist as Domenic proposes? Of course there's much more at stake than syntax, but it would help.

# Russell Leggett (12 years ago)

I've been stewing on this some more, and I realized something potentially very interesting. If we use weakmaps in the manner specified by Kevin (with Mark's help), I think we've very nearly added nominal typing to classes. Assuming this privacy behavior was tied to classes, and assuming only members of the class could be added to the weakmap, and assuming that all members of the class belonged to the weakmap, the weakmap would quite literally be the set of all members of that class (type). If there was an easy way of asking the weakmap if an object belonged to the it, it would the same as asking if the object was an instance of that class in a very strong, nominal way. I guess I think this could be a plus, because it allows for nominal types to be tied to classes, and yet do it in a very javascripty feeling way.

Thoughts?

# David Bruant (12 years ago)

Le 18/01/2013 06:47, Russell Leggett a écrit :

On Thu, Jan 17, 2013 at 9:40 PM, Kevin Smith <khs4473 at gmail.com <mailto:khs4473 at gmail.com>> wrote:

It seems as if this approach to private class members also allows
us to describe private methods in a convenient way.  Private
methods can be attached to the _prototype_ of the private field
object, thus avoiding per-instance allocation.  Of course, the
correct "this" value has to be used in the expansion when calling
the private method, but this approach appears to be compatible
with mixins (whereas private symbols are not).

https://gist.github.com/4561871

Thoughts?

I've been on the fence with the debate, but I'll admit that this proposal is really winning me over. I don't know if it's too late to get this syntax in, but it would be a major win for me.

syntax details aside (especially with Domenic improvements), I agree.

I like this a lot more than private symbols.

Assuming the only valid use case for private symbols is classes, I agree

It actually feels a lot like deconstructing an abstract data type or pattern matching a case class in scala. There is something nice about keeping all that extra stuff off the actual object. I'm not sure if I'd still like it as much without the syntax, but maybe I would now.

I have do have a few questions on this, though:

  1. How does this interact with proxies? I'm guessing that a proxy will not map correctly and therefore make it inaccessible. Personally, I'm ok with that.

It does not interact that well because of the this-binding. Classes have been long expected, so will be used, so proxies have to work well with them.

 class Purse {
     private balance;

     constructor(balance = 0) {
         private.balance = balance;
     }

      getBalance() { return balance; }
 }

 var p = new Purse(6);
 var pp = new Proxy(p, {});
 console.log(pp.getBalance());

We should be able to expect p and pp to act the same in all circumstances. With Kevin's expansion, pp.getBalance() throws a TypeError, because "amp.get(this)" is not an object. Proxy authors can work around that by returning functions bound to the target, but this is not free obviously (create as much bound functions as there are of (instance, method) pair).

The interaction between private syntax and proxies has the following components:

  1. Do the proxy and the target act the same regarding private properties? The answer is yes with whitelisted private symbols, no with naive weakmap-expanded private syntax (I explain non-naive below) I think the answer should be yes here.

  2. Does the proxy has a word to say about the access to the private property? It's yes with private symbols (different trap if whitelisted or not). The answer is yes for weakmap-expanded private syntax, but with some explanation. The proxy can't know about the private property, however, every access to something private has to be performed through something public (public accessor or method). Indeed, the class encapsulate access to private state (this isn't true of private symbols in general). Since the proxy can cut communication of public accesses, it can also cut accesses to private properties. I think that this additional simplicity weighs in favor of the removal of private symbols.

(I had a third point in mind and I forgot it...)

Above I've talked about naive weakmap-expanded private syntax. A less naive version could "override" the Proxy constructor to know about proxy -> target associations. Access to private state wouldn't start with the

naive "amp.get(this)", but rather with "amp.get(proxyToFinalTarget(this) || this)". ("finalTarget" in case the target of the proxy is itself a proxy). For native syntax, we can imagine this setup exists before any code runs. The transpiler story is a bit less clear to me. I see different ways of writing class transpilers with different properties:

  1. make classes incompatible with proxies
  2. share (aka "leak") the proxyToFinalTarget function to transpiled classes
  3. bring the full transpiler in the runtime so that classes can work with proxies (only the transpiler has access to proxyToFinalTarget) I think different users in different contexts all have a solution to their need.
  1. Can I use private as a normal variable? Can it be bound to a new variable, passed as an argument, or returned from a function? constructor(balance = 0) { let p = private; p.checkNum(amount); p.balance = balance; } I would understand not going this route, but it definitely restricts access very tightly where private member or manually using a weakmap would allow it. Perhaps that edge case can just be handled by falling back to manually using a weakmap.

Mark's point about private symbol being mostly useful in the context of class convinced me that pretty much every other context can work with WeakMaps.

No opinion on your third point.

# Kevin Smith (12 years ago)

Kevin, could you do an alterna-gist as Domenic proposes? Of course there's much more at stake than syntax, but it would help.

I've updated my gist to clarify Domenic's second point, but I'll let him maintain his fork for the "@" syntax variation.

# Kevin Smith (12 years ago)

I've been stewing on this some more, and I realized something potentially

very interesting. If we use weakmaps in the manner specified by Kevin (with Mark's help),

Um, that should be "Mark (with Kevin's itty-bitty help)". : )

# Kevin Smith (12 years ago)

The interaction between private syntax and proxies has the following components:

  1. Do the proxy and the target act the same regarding private properties? The answer is yes with whitelisted private symbols, no with naive weakmap-expanded private syntax (I explain non-naive below)

What happens if the private symbol is not on the whitelist? Does the private symbol get/set operation get forwarded to the target, or does it fail? (Sorry for the remedial question.)

# David Bruant (12 years ago)

Le 19/01/2013 16:30, Kevin Smith a écrit :

The interaction between private syntax and proxies has the
following components:
1) Do the proxy and the target act the same regarding private
properties?
The answer is yes with whitelisted private symbols, no with naive
weakmap-expanded private syntax (I explain non-naive below)

What happens if the private symbol is not on the whitelist? Does the private symbol get/set operation get forwarded to the target, or does it fail?

It calls the unknownPrivateSymbol trap. If the trap throws, the operation fails. In all other cases (no trap or trap which doesn't throw), it's forwarded. I made the assumption that private syntax result in whitelisted symbols and that's actually a non-trivial assumption... hmm... it's actually a false assumption. Somehow, the private symbols generated from private syntax would need to be exposed by the class or something to be added to the whitelist set when a proxy wants to transparently wrap class instances. This would force to violate the class encapsulation. This means that for proxies to work with class instances, private syntax has to expand to something else than private symbols. WeakMap being first choice obviously and the necessity of private symbols become more and more questionable.

(Sorry for the remedial question.)

No worries. Sorry for being too quick in my explanations ;-)

# Kevin Smith (12 years ago)

It calls the unknownPrivateSymbol trap. If the trap throws, the operation fails. In all other cases (no trap or trap which doesn't throw), it's forwarded.

And can frozen objects be proxy targets, or no? More generally, can a proxy effectively override the behavior of a non-configurable, non-writable property on the target?

var target = Object.create(null, { foo: { writable: false,

configurable: false, value: 1 } });

var proxy = new Proxy(target, {
  get(obj, name) { return name === "foo" ? 0 : obj[name]; }
});

console.log(target.foo); // 1
console.log(proxy.foo); // 0

This this allowed?

# David Bruant (12 years ago)

Le 19/01/2013 19:04, Kevin Smith a écrit :

It calls the unknownPrivateSymbol trap. If the trap throws, the
operation fails. In all other cases (no trap or trap which doesn't
throw), it's forwarded.

And can frozen objects be proxy targets, or no?

Yes they can.

More generally, can a proxy effectively override the behavior of a non-configurable, non-writable property on the target?

var target = Object.create(null, { foo: { writable: false, 

configurable: false, value: 1 } });

var proxy = new Proxy(target, {
  get(obj, name) { return name === "foo" ? 0 : obj[name]; }
});

console.log(target.foo); // 1
console.log(proxy.foo); // 0

This this allowed?

It is not. At the exit of the get trap, the JS engine checks whether invariants should be enforced for the given property on the target. In your case, the runtime sees that the target has a non-configurable non-writable property called 'foo' with 1 as value. When you try to return 0, it will throw a TypeError because of invariant violation. You can read about invariants at harmony:direct_proxies#invariant_enforcement

# Brendan Eich (12 years ago)

Something was nagging me from the 2011-era debate over private symbols vs. weak maps, and that's the ability of a weak map to implement soft fields on frozen objects. Symbols (private or not) can't do that.

Not sure this matters in the current proxy-focused thread, but I thought I would point it out explicitly.

# Allen Wirfs-Brock (12 years ago)

On Jan 19, 2013, at 11:39 AM, Brendan Eich wrote:

Something was nagging me from the 2011-era debate over private symbols vs. weak maps, and that's the ability of a weak map to implement soft fields on frozen objects. Symbols (private or not) can't do that.

Not sure this matters in the current proxy-focused thread, but I thought I would point it out explicitly.

It seems like at the root of this question revolves around what you mean by a "frozen object".

In ES5 there is no such concept. There really is only the Object.freeze/Object.isFrozen functions. These are defined operationally in terms of specific constructs that exist in ES5. There really is no guidance in those definitions as to how they should be extended when when new constructs are added to the language.

Also, the effect obtained by applying Object.freeze to an object isn't a fundamental characteristic of an object. The exact same effect can be achieve by a combination of other calls and Object.isFreeze will still report true.

Who's to say that a symbol keyed object can't be added to an object after Object.freeze has been applied to it. Before we can say that we have to define the semantics of symbol keyed objects and possibly extend Object.freeze/isFrozen to accommodate those new semantics. What's the guidance for doing so? What abstract concept of "frozen" do we to apply to the semantics of new language features?

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

Who's to say that a symbol keyed object

You must mean "property" not "object".

can't be added to an object after Object.freeze has been applied to it. Before we can say that we have to define the semantics of symbol keyed objects and possibly extend Object.freeze/isFrozen to accommodate those new semantics. What's the guidance for doing so? What abstract concept of "frozen" do we to apply to the semantics of new language features?

This is not an abstract question. Implementors may want Object.preventExtensions to put an object in a state such that optimizations can rely on it never growing new properties. Frozen objects could even be promoted by a copying GC into read-only memory, with the OS virtualized hardware MMU providing greater integrity.

More to the point, programmers may wish a frozen object o having properties p and q, and null or frozen prototype chain of known properties, never to grow property r. Whereas using weakmaps explicitly, of course one can call r.set(o, v) to set r's value to v.

If we are talking about private syntax only in classes, this may not matter. Either private symbols or weak maps suffice. But proxies can tell, and prototype-based inheritance works for private symbols but not weak maps.

So even if we allow a frozen object to grow new private-symbol-keyed properties not in that object at the time it was frozen (or let's say the time Object.preventExtensions or its internal form was invoked), private symbols and weak maps are distinguishable. If they weren't, we surely would have no need for one or the other.

# Mark S. Miller (12 years ago)

On Sat, Jan 19, 2013 at 5:15 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Jan 19, 2013, at 11:39 AM, Brendan Eich wrote:

Something was nagging me from the 2011-era debate over private symbols vs. weak maps, and that's the ability of a weak map to implement soft fields on frozen objects. Symbols (private or not) can't do that.

Not sure this matters in the current proxy-focused thread, but I thought I would point it out explicitly.

It seems like at the root of this question revolves around what you mean by a "frozen object".

In ES5 there is no such concept. There really is only the Object.freeze/Object.isFrozen functions. These are defined operationally in terms of specific constructs that exist in ES5. There really is no guidance in those definitions as to how they should be extended when when new constructs are added to the language.

Also, the effect obtained by applying Object.freeze to an object isn't a fundamental characteristic of an object. The exact same effect can be achieve by a combination of other calls and Object.isFreeze will still report true.

Who's to say that a symbol keyed object can't be added to an object after Object.freeze has been applied to it.

Me. We've been over this. With the semantics you suggest, sharing an immutable object and an immutable Symbol opens a communications channel. That is why the private symbol freeze exemption exempted private-symbol-named properties from the non-writability and non-configurability of freeze, but only for properties that were already there. Private symbols must not be exempt from non-extensibility, or you get an unpluggable channel.

WeakMaps don't have this problem because you can only add a key to a mutable weakmap. We can account for the mutablity in terms of explicitly mutable state. An immutable weakmap, easily achieved by a trivial wrapping of a weakmap, allows lookup but no further additions, thereby not opening up a communications channel. And weakmaps don't need magic exemptions, partial or otherwise.

Before we can say that we have to define the semantics of symbol keyed objects and possibly extend Object.freeze/isFrozen to accommodate those new semantics. What's the guidance for doing so? What abstract concept of "frozen" do we to apply to the semantics of new language features?

Let's start with support for ocap reasoning. Or even info flow reasoning for that matter, which is sufficient for the above issues.

# Allen Wirfs-Brock (12 years ago)

On Jan 19, 2013, at 6:02 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

Who's to say that a symbol keyed object

You must mean "property" not "object".

of couse

can't be added to an object after Object.freeze has been applied to it. Before we can say that we have to define the semantics of symbol keyed objects and possibly extend Object.freeze/isFrozen to accommodate those new semantics. What's the guidance for doing so? What abstract concept of "frozen" do we to apply to the semantics of new language features?

This is not an abstract question. Implementors may want Object.preventExtensions to put an object in a state such that optimizations can rely on it never growing new properties.

Then are we talking about what I would probably call non-extensible objects. You are saying you want the "shape" of the object to be fixed.

Frozen objects could even be promoted by a copying GC into read-only memory, with the OS virtualized hardware MMU providing greater integrity.

But here you are talking about some broader from of immutability that presumably includes non-property per instance state (eg, "internal data properties") and any sort of "private" per instance state that the language provides. Either of those go beyond what ES5 Object.preventExtensions or even Object.freeze provides.

More to the point, programmers may wish a frozen object o having properties p and q, and null or frozen prototype chain of known properties, never to grow property r. Whereas using weakmaps explicitly, of course one can call r.set(o, v) to set r's value to v.

Right, they may. My point we is that we have to decide on all the characteristics of a "frozen" object before we can answer all of these questions.

I like the fact that you used "r" as a name above because certainly a WeakMap can be used to define a relationship involving an immutable object o. But we shouldn't be creating confusion by pretending that those relationships are the samething as a property of o or anyway part of o. Such a relationship is an independent entity that exists separately from o and can be defined independently of o.

If we are talking about private syntax only in classes, this may not matter. Either private symbols or weak maps suffice.

I think a very important characteristic of classes is that they really are just sugar and don't have any special magic. We should try to maintain that characteristic.

But proxies can tell, and prototype-based inheritance works for private symbols but not weak maps.

So even if we allow a frozen object to grow new private-symbol-keyed properties not in that object at the time it was frozen (or let's say the time Object.preventExtensions or its internal form was invoked), private symbols and weak maps are distinguishable. If they weren't, we surely would have no need for one or the other.

Well, then we are probably debating about how much we agree with each other. Weakmaps and private Symbols are totally different things. Just because some problems can be solved with either one doesn't make them equivalent.

But, that said, you certainly could use Weakmaps to define a style of relationship that supports inheritance like mention above. Just create a subclass of Weakmap like:

class WeakMapWithKeyInheritance extends Weakmap { has(key) { if (super.has(key)) return true; let proto = Object.getPrototypeOf(key); If (proto === null) return false; return this.has(proto); } get(key) { if (super.has(key)) return super.get(key); let proto = Object.getPrototypeOf(key); If (proto === null) return false; return this.get(proto); } }

# Allen Wirfs-Brock (12 years ago)

On Jan 19, 2013, at 6:36 PM, Mark S. Miller wrote:

On Sat, Jan 19, 2013 at 5:15 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Jan 19, 2013, at 11:39 AM, Brendan Eich wrote:

Something was nagging me from the 2011-era debate over private symbols vs. weak maps, and that's the ability of a weak map to implement soft fields on frozen objects. Symbols (private or not) can't do that.

Not sure this matters in the current proxy-focused thread, but I thought I would point it out explicitly.

It seems like at the root of this question revolves around what you mean by a "frozen object".

In ES5 there is no such concept. There really is only the Object.freeze/Object.isFrozen functions. These are defined operationally in terms of specific constructs that exist in ES5. There really is no guidance in those definitions as to how they should be extended when when new constructs are added to the language.

Also, the effect obtained by applying Object.freeze to an object isn't a fundamental characteristic of an object. The exact same effect can be achieve by a combination of other calls and Object.isFreeze will still report true.

Who's to say that a symbol keyed object can't be added to an object after Object.freeze has been applied to it.

Me. We've been over this. With the semantics you suggest, sharing an immutable object and an immutable Symbol opens a communications channel. That is why the private symbol freeze exemption exempted private-symbol-named properties from the non-writability and non-configurability of freeze, but only for properties that were already there. Private symbols must not be exempt from non-extensibility, or you get an unpluggable channel.

Just to be clear, I'm not arguing for any specific semantics. I'm just saying we can't compare alternative solutions to a problem until we have defined all the requirements. Brendan, in his initial reply to my comment, suggested some possible requirements that ES5 Object.freeze or Object.preventExtensions probably don't meet even if we ignore the existence of symbols.

WeakMaps don't have this problem because you can only add a key to a mutable weakmap. We can account for the mutablity in terms of explicitly mutable state. An immutable weakmap, easily achieved by a trivial wrapping of a weakmap, allows lookup but no further additions, thereby not opening up a communications channel. And weakmaps don't need magic exemptions, partial or otherwise.

There is no "magic" involved with private symbols, it is just a small matter of specifying how they interact with existing reflection operations. In contrast, if you want relationships formed using Weakmap to appear as if they were object properties then you are asking for an illusion that takes real magic.

I'll partially repeat myself from my reply to Brendan. Just because you have two mechanisms that can solve the same program doesn't mean that they are equivalent or that one the mechanism is necessarily redundant. Weakmaps and symbol keyed properties (private or not) are very different mechanism. They each have a variety of uses and differing implementation and performance characteristics. It really is a distraction to focus exclusively on a single use case that might be supported by two different mechanism and use it to argue that only one of the mechanism is needed.

Before we can say that we have to define the semantics of symbol keyed objects and possibly extend Object.freeze/isFrozen to accommodate those new semantics. What's the guidance for doing so? What abstract concept of "frozen" do we to apply to the semantics of new language features?

Let's start with support for ocap reasoning. Or even info flow reasoning for that matter, which is sufficient for the above issues.

OK, those requires are?

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

On Jan 19, 2013, at 6:02 PM, Brendan Eich wrote:

Frozen objects could even be promoted by a copying GC into read-only memory, with the OS virtualized hardware MMU providing greater integrity.

But here you are talking about some broader from of immutability that presumably includes non-property per instance state (eg, "internal data properties") and any sort of "private" per instance state that the language provides. Either of those go beyond what ES5 Object.preventExtensions or even Object.freeze provides.

No, I'm talking about plain old Object instances that are frozen and live long enough.

More to the point, programmers may wish a frozen object o having properties p and q, and null or frozen prototype chain of known properties, never to grow property r. Whereas using weakmaps explicitly, of course one can call r.set(o, v) to set r's value to v.

Right, they may. My point we is that we have to decide on all the characteristics of a "frozen" object before we can answer all of these questions.

I like the fact that you used "r" as a name above because certainly a WeakMap can be used to define a relationship involving an immutable object o. But we shouldn't be creating confusion by pretending that those relationships are the samething as a property of o or anyway part of o.

Quite agree.

Such a relationship is an independent entity that exists separately from o and can be defined independently of o.

If we are talking about private syntax only in classes, this may not matter. Either private symbols or weak maps suffice.

I think a very important characteristic of classes is that they really are just sugar and don't have any special magic. We should try to maintain that characteristic.

That seems to be agreed upon by everyone (anyone dissent?).

But proxies can tell, and prototype-based inheritance works for private symbols but not weak maps.

So even if we allow a frozen object to grow new private-symbol-keyed properties not in that object at the time it was frozen (or let's say the time Object.preventExtensions or its internal form was invoked), private symbols and weak maps are distinguishable. If they weren't, we surely would have no need for one or the other.

Well, then we are probably debating about how much we agree with each other. Weakmaps and private Symbols are totally different things. Just because some problems can be solved with either one doesn't make them equivalent.

Agreed, but the live proposal here is to use weakmaps for class private instance methods/variables, and not have private symbols.

But, that said, you certainly could use Weakmaps to define a style of relationship that supports inheritance like mention above. Just create a subclass of Weakmap like:

class WeakMapWithKeyInheritance extends Weakmap { has(key) { if (super.has(key)) return true; let proto = Object.getPrototypeOf(key); If (proto === null) return false; return this.has(proto); } get(key) { if (super.has(key)) return super.get(key); let proto = Object.getPrototypeOf(key); If (proto === null) return false; return this.get(proto); } }

Right, but could you have "classes as sugar" including private syntax as David proposed (based on weak maps) that can access prototype-delegated properties? (I guess they would be 'protected' in that case.)

# Kevin Smith (12 years ago)

It is not. At the exit of the get trap, the JS engine checks whether invariants should be enforced for the given property on the target. In your case, the runtime sees that the target has a non-configurable non-writable property called 'foo' with 1 as value. When you try to return 0, it will throw a TypeError because of invariant violation. You can read about invariants at harmony:direct_proxies#invariant_enforcement

Excellent - thanks for the link! One more: What is the reason for not providing an API for unwrapping a proxy (e.g. your proxyToFinalTarget)?

# Allen Wirfs-Brock (12 years ago)

On Jan 19, 2013, at 8:39 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

...

I like the fact that you used "r" as a name above because certainly a WeakMap can be used to define a relationship involving an immutable object o. But we shouldn't be creating confusion by pretending that those relationships are the samething as a property of o or anyway part of o.

Quite agree.

... Well, then we are probably debating about how much we agree with each other. Weakmaps and private Symbols are totally different things. Just because some problems can be solved with either one doesn't make them equivalent.

Agreed, but the live proposal here is to use weakmaps for class private instance methods/variables, and not have private symbols.

Then we should be close to resolving that if we agree that WeakMap are primarily useful for represent external relationships between objects. The encapsulated state of an object isn't this sort of external relationship and presumably "private state" is the most intimate form of encapsulated state. It would be very misleading to both ES programmer and implementors if what syntactically appears to be private state is specified to have the semantics of an external relationship.

But, that said, you certainly could use Weakmaps to define a style of relationship that supports inheritance like mention above. Just create a subclass of Weakmap like:

class WeakMapWithKeyInheritance extends Weakmap { has(key) { if (super.has(key)) return true; let proto = Object.getPrototypeOf(key); If (proto === null) return false; return this.has(proto); } get(key) { if (super.has(key)) return super.get(key); let proto = Object.getPrototypeOf(key); If (proto === null) return false; return this.get(proto); } }

Right, but could you have "classes as sugar" including private syntax as David proposed (based on weak maps) that can access prototype-delegated properties? (I guess they would be 'protected' in that case.)

Yes, its is closer to what "protected" means in some languages and we may want to add syntax at some point to make it easier to defined private/protected properties. But it would be a very misleading and unexpected if that syntax was just sugar for WeakMap operations. Nobody defines private state of a class with the expectation that it has the implementation cost of maintaining an external relationship.

Private Symbols and Weakmaps are both useful mechanism that are naturally optimized for different use cases. We wouldn't be making the world simpler by requiring that Weakmaps be used for all those use cases.

I tend to be in the same camp as those who say that they only need non-private Symbols. But I know that there are significant segments of the developer community who what stronger encapsulation than is offered by reflectable unique symbols. In particular, I don't see a good way to do strong branding (which the DOM folks demand) using only non-private Symbols. I don't see regular WeakMaps as an viable solution to that problem because of the GC impact. We could try to introduce a separate per instance mechanism for branding, but then we would have yet another orthogonal feature to integrate (including with Proxies) . Private symbols, so for, seems like smallest incremental extension that is a practical solution for the branding use case.

# David Bruant (12 years ago)

Le 20/01/2013 05:27, Allen Wirfs-Brock a écrit :

Weakmaps and symbol keyed properties (private or not) are very different mechanism. They each have a variety of uses and differing implementation and performance characteristics. It really is a distraction to focus exclusively on a single use case that might be supported by two different mechanism and use it to argue that only one of the mechanism is needed.

What are the different uses of private symbols? More specifically: what are the remaining explicit uses of private symbols in a language with private syntax in classes?

I think symbols were introduced given experience in ES5 and assuming being an improvement on top of ES5 as we know it. On top of pure ES5, private symbols make a lot of sense. In ES5+class-with-private-syntax, I'm much more skeptical.

Also, private syntax as private symbol makes the proxy story complicated [1], because the class or its instances need to publicize private symbols so that proxies can add them to their whitelist when wrapping class instances. I don't think leaking abstraction is a valid option, so it means that only 2 out of the 3 following can be kept in the language:

  1. proxy-wrapping class instances (without leaking abstractions)
  2. private syntax in class
  3. private symbols We can probably predict in advance that JavaScript authors will largely not want to give up on 2) (even if it comes only in ES7). Should 1) or
  4. be given up? Unless relevant use cases different than class-like usages are provided, 3) can disappear in my opinion.

David

[1] esdiscuss/2013-January/028285

# David Bruant (12 years ago)

Le 20/01/2013 06:43, Allen Wirfs-Brock a écrit :

On Jan 19, 2013, at 8:39 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

...

I like the fact that you used "r" as a name above because certainly a WeakMap can be used to define a relationship involving an immutable object o. But we shouldn't be creating confusion by pretending that those relationships are the samething as a property of o or anyway part of o. Quite agree.

... Well, then we are probably debating about how much we agree with each other. Weakmaps and private Symbols are totally different things. Just because some problems can be solved with either one doesn't make them equivalent. Agreed, but the live proposal here is to use weakmaps for class private instance methods/variables, and not have private symbols. Then we should be close to resolving that if we agree that WeakMap are primarily useful for represent external relationships between objects. The encapsulated state of an object isn't this sort of external relationship and presumably "private state" is the most intimate form of encapsulated state. It would be very misleading to both ES programmer and implementors if what syntactically appears to be private state is specified to have the semantics of an external relationship.

I disagree with Brendan when he says "to use weakmaps for class private instance methods/variables"... well... it depends on what "use" means: The spec is allowed to /use/ anything it needs to make the class private syntax work. If the spec says that private properties are like properties but aren't enumerated in Object.gOPN calls, fine. If the spec says that private properties require a lookup to some object -> value

map, fine (but misleading, I agree).

A different problem is what the private syntax is decided to expand to, which we can refer to as the "transpiler problem". Transpilers are limited to /use/ what's available in the language. While the spec can decide to invent new property types which aren't enumerated with Object.gOPN, transpilers don't have this possibility.

In my opinion, the most important topic which hasn't been addressed yet and which defines the boundaries of the 2 above points is the exact semantics of private syntax. I'm careful to not say "private properties", because it would imply that these things have the same characteristics than what we know as public properties. It's pretty clear from existing examples that we want to be able to attach values based on names, that's a given.

A couple of questions regarding class private syntax (once again, I'm only talking about the syntax, not private symbols):

  1. Do we want ES5 property semantics (property descriptors, etc.)? => Because of the class encapsulation, I don't think enumerable,

configurable, writable are needed. Public methods can guard any invariants they need. => No opinion yet on private getters/setters. Maybe they can be useful.

  1. Do we want "private inheritance" (suggested by Brendan below) => No opinion yet.

But, that said, you certainly could use Weakmaps to define a style of relationship that supports inheritance like mention above. Just create a subclass of Weakmap like:

class WeakMapWithKeyInheritance extends Weakmap { has(key) { if (super.has(key)) return true; let proto = Object.getPrototypeOf(key); If (proto === null) return false; return this.has(proto); } get(key) { if (super.has(key)) return super.get(key); let proto = Object.getPrototypeOf(key); If (proto === null) return false; return this.get(proto); } } Right, but could you have "classes as sugar" including private syntax as David proposed (based on weak maps) that can access prototype-delegated properties? (I guess they would be 'protected' in that case.) Yes, its is closer to what "protected" means in some languages and we may want to add syntax at some point to make it easier to defined private/protected properties. But it would be a very misleading and unexpected if that syntax was just sugar for WeakMap operations. Nobody defines private state of a class with the expectation that it has the implementation cost of maintaining an external relationship.

Once again, the spec (well... you in that case :-) ) will do whatever is necessary to make the feature understandable by spec readers. Transpilers will work with whatever is in the language. If they only have weakmaps, they'll use that.

I tend to be in the same camp as those who say that they only need non-private Symbols. But I know that there are significant segments of the developer community who what stronger encapsulation than is offered by reflectable unique symbols. In particular, I don't see a good way to do strong branding (which the DOM folks demand) using only non-private Symbols. I don't see regular WeakMaps as an viable solution to that problem because of the GC impact. We could try to introduce a separate per instance mechanism for branding, but then we would have yet another orthogonal feature to integrate (including with Proxies) . Private symbols, so for, seems like smallest incremental extension that is a practical solution for the branding use case.

Does the branding need to be dynamic? Could the problem be solved by class private syntax?

 class Branded{
     private brand;
     constructor(brand){
         this. at brand = brand;
     }
     getBrand(){
         return this. at brand;
     }
 }

 class Node{
     constructor(brand){
         Branded.call(this, 'Node');
     }
 }

 var n = new Node();
 console.log(Branded.prototype.getBrand.call(n));

Could this work? Once again, spec and implementations are free to see class private syntax as object own properties regardless of what we use (weakmaps?) to reason about the class syntax or to transpile it to.

# David Bruant (12 years ago)

Le 20/01/2013 06:36, Kevin Smith a écrit :

It is not. At the exit of the get trap, the JS engine checks
whether invariants should be enforced for the given property on
the target. In your case, the runtime sees that the target has a
non-configurable non-writable property called 'foo' with 1 as
value. When you try to return 0, it will throw a TypeError because
of invariant violation. You can read about invariants at
http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies#invariant_enforcement

Excellent - thanks for the link! One more: What is the reason for not providing an API for unwrapping a proxy (e.g. your proxyToFinalTarget)?

If anyone can unwrap proxies, then the security benefits they provide are virtually non-existent akin to the Reflect API in Java. It's possible to implement such a function yourself and expose it for some of your proxies, but it should be an opt-in, not something available by default to everyone in the language.

# Kevin Smith (12 years ago)

Got it. And one more (thanks for bearing with me): what affect does throwing an error have within a trap?

var target = {
  foo: function() { this.bar; console.log("after bar"); },
  bar: 0
};

var proxy = new Proxy(target, {
  get: function(obj, name) {
    if (name === "bar") throw new Error("boom");
    return obj[name];
  }
});

proxy.foo();

Does the stack unwind as with normal error handling? Does "after bar" get logged? (FF18 answers yes to the first and no to the second question.)

# Juan Ignacio Dopazo (12 years ago)

2013/1/17 Mark S. Miller <erights at google.com>

By the time ES7 rolls out, there will be a lot of that code.

I very much agree and I think we may see that sooner. I wrote a gist ( gist.github.com/2901426) using WeakMaps for privates about 7 months ago, before this discussion. I expect to see code like that in the wild once WeakMaps get to Node without a flag.

I think the nicest thing about specifying privates with WeakMaps is that using it in a transpiler would be seamless. There wouldn't be any functionality lost. And that would be specially good since browsers are implementing new APIs before new syntax. That means we could get privates sooner!

Juan

# Allen Wirfs-Brock (12 years ago)

On Jan 20, 2013, at 3:04 AM, David Bruant wrote:

Le 20/01/2013 05:27, Allen Wirfs-Brock a écrit :

Weakmaps and symbol keyed properties (private or not) are very different mechanism. They each have a variety of uses and differing implementation and performance characteristics. It really is a distraction to focus exclusively on a single use case that might be supported by two different mechanism and use it to argue that only one of the mechanism is needed. What are the different uses of private symbols?

Branding is the important one.

Secure private communications between "friend" objects is another possible one.

More specifically: what are the remaining explicit uses of private symbols in a language with private syntax in classes?

But we don't have such a language. There are no harmonious proposals for such a syntax. Getting classes into ES6 via the max-min class proposal was possible because we agreed not to debate that issue.

We we were going to talk about adding support for addition semantics to classes we should start at the level of the object model. Not with syntax. ES6 class syntax is good because it is defined in terms of the object model and more basic operations that are defined in the language. You don't have to use class syntax to define a class equivalent. So, if we are going to introduce private object state the first step is to define how it works at the object model level. IMO and for reasons I've already presented, saying that private state is just relationships defined via WeakMaps is a non-starter.

I think symbols were introduced given experience in ES5 and assuming being an improvement on top of ES5 as we know it. On top of pure ES5, private symbols make a lot of sense. In ES5+class-with-private-syntax, I'm much more skeptical.

Actually experience with a number of languages. But like I said, there is no ES5+class-with-private-syntax so I don't see how that is a useful argument.

Also, private syntax as private symbol makes the proxy story complicated [1], because the class or its instances need to publicize private symbols so that proxies can add them to their whitelist when wrapping class instances. I don't think leaking abstraction is a valid option, so it means that only 2 out of the 3 following can be kept in the language:

I don't have a problem at all with making the proxy story more complicated. Proxys are an expert feature designed for some specific use cases. they are probably an attractive nuisance. I would advise most JS programmer that if they are going down the road of using a Proxy, they are probably making a mistake. In that light, placing the extra complexity within the Proxy story seems just fine.

  1. proxy-wrapping class instances (without leaking abstractions)
  2. private syntax in class
  3. private symbols We can probably predict in advance that JavaScript authors will largely not want to give up on 2) (even if it comes only in ES7). Should 1) or 3) be given up? Unless relevant use cases different than class-like usages are provided, 3) can disappear in my opinion.

If the above was the only choice (and I don't think it is) I would probably pick #1 to eliminate.

# Brendan Eich (12 years ago)

David Bruant wrote:

I disagree with Brendan when he says "to use weakmaps for class private instance methods/variables"... well... it depends on what "use" means: The spec is allowed to /use/ anything it needs to make the class private syntax work. If the spec says that private properties are like properties but aren't enumerated in Object.gOPN calls, fine. If the spec says that private properties require a lookup to some object -> value map, fine (but misleading, I agree).

I don't think we disagree.

If there were no observable differences at all between weak maps and private symbols, we would have only one. Since there are, and since you propose to map private-in-class syntax onto weak maps in part on account of these differences (viz, proxying and whitelist population without privacy-leaks), here we are.

Perhaps I should have written "utilize"? Bleah.

# Brendan Eich (12 years ago)

David Bruant wrote:

Once again, the spec (well... you in that case :-) ) will do whatever is necessary to make the feature understandable by spec readers. Transpilers will work with whatever is in the language. If they only have weakmaps, they'll use that.

See harmony:harmony, in particular

Means
  1. Minimize the additional semantic state needed beyond ES5.
  2. Provide syntactic conveniences for: 1. good abstraction patterns; 2. high integrity patterns; 3. defined by desugaring into kernel semantics.

We aim to unify the "spec problem" and the "transpiler problem" where possible. Not always, not necessarily by spec-by-desugaring. But we don't want magic in the spec that leaves transpilers falling into the tarpit, if we can avoid it.

# David Bruant (12 years ago)

Le 20/01/2013 18:59, Brendan Eich a écrit :

David Bruant wrote:

I disagree with Brendan when he says "to use weakmaps for class private instance methods/variables"... well... it depends on what "use" means: The spec is allowed to /use/ anything it needs to make the class private syntax work. If the spec says that private properties are like properties but aren't enumerated in Object.gOPN calls, fine. If the spec says that private properties require a lookup to some object -> value map, fine (but misleading, I agree).

I don't think we disagree.

If there were no observable differences at all between weak maps and private symbols, we would have only one. Since there are, and since you propose to map private-in-class syntax onto weak maps in part on account of these differences (viz, proxying and whitelist population without privacy-leaks), here we are.

I don't understand, this paragraph went too quickly. I understand that you're saying that the proposal I defend (which includes getting rid of private symbols) relies on the differences between private symbols and weakmaps and hence both are necessary? Am I fully misunderstanding?

# David Bruant (12 years ago)

Le 20/01/2013 18:54, Allen Wirfs-Brock a écrit :

On Jan 20, 2013, at 3:04 AM, David Bruant wrote:

Le 20/01/2013 05:27, Allen Wirfs-Brock a écrit :

Weakmaps and symbol keyed properties (private or not) are very different mechanism. They each have a variety of uses and differing implementation and performance characteristics. It really is a distraction to focus exclusively on a single use case that might be supported by two different mechanism and use it to argue that only one of the mechanism is needed. What are the different uses of private symbols? Branding is the important one.

Secure private communications between "friend" objects is another possible one.

Secure private communication between friend objects can be achieved simply and efficiently with functions or weakmaps already. Why is there a need for an additional construct for that use case?

More specifically: what are the remaining explicit uses of private symbols in a language with private syntax in classes? But we don't have such a language. There are no harmonious proposals for such a syntax. Getting classes into ES6 via the max-min class proposal was possible because we agreed not to debate that issue.

Will we ever? If the answer is yes, my point stands. TypeScript has private syntax in class. People want it. The ES6 cut-off was too short for TC39 to settle on private in class, but the pressure is here so it's going to happen sooner or later (Mark was saying ES7).

We we were going to talk about adding support for addition semantics to classes we should start at the level of the object model. Not with syntax. ES6 class syntax is good because it is defined in terms of the object model and more basic operations that are defined in the language. You don't have to use class syntax to define a class equivalent. So, if we are going to introduce private object state the first step is to define how it works at the object model level.

Does defining how it works at the object model level commits the language to expose a runtime construct allowing to use the lowest-level feature directly? I think it does not. You think it does, hence private symbols:

  • For which benefits?
  • One downside is that it brings along more complexity to the language... well... to proxies at least. I understand and somewhat agree with your argument that complexifying proxies isn't a problem, but at least, there should be a benefit. Also, given that private-in-classes aren't in ES6, why would it be necessary to add private symbols in ES6? Until there is a feature that demands to be desugared as private symbols (I don't think there is, but you probably know better, so tell me if so), it might be wise to put them on hold.

IMO and for reasons I've already presented, saying that private state is just relationships defined via WeakMaps is a non-starter.

I disagree with your arguments on WeakMaps, because choosing to desugar the syntax to WeakMaps doesn't commit neither the implementation nor the spec to actually use weakmaps to spec or to implement the feature.

I think symbols were introduced given experience in ES5 and assuming being an improvement on top of ES5 as we know it. On top of pure ES5, private symbols make a lot of sense. In ES5+class-with-private-syntax, I'm much more skeptical. Actually experience with a number of languages. But like I said, there is no ES5+class-with-private-syntax so I don't see how that is a useful argument.

There will be.

Also, private syntax as private symbol makes the proxy story complicated [1], because the class or its instances need to publicize private symbols so that proxies can add them to their whitelist when wrapping class instances. I don't think leaking abstraction is a valid option, so it means that only 2 out of the 3 following can be kept in the language: I don't have a problem at all with making the proxy story more complicated. "complicated" was an expression. Either proxies don't work with class instances, making them vastly pointless or classes need to publicize their private symbols (or maybe expose something like "myClass.acceptProxy" which is marginally better), thus ruining their own encapsulation.

Proxys are an expert feature designed for some specific use cases. they are probably an attractive nuisance. I would advise most JS programmer that if they are going down the road of using a Proxy, they are probably making a mistake. In that light, placing the extra complexity within the Proxy story seems just fine.

To the point of making proxies useless when interacting with class instances or ruining class encapsulation?

  1. proxy-wrapping class instances (without leaking abstractions)
  2. private syntax in class
  3. private symbols We can probably predict in advance that JavaScript authors will largely not want to give up on 2) (even if it comes only in ES7). Should 1) or 3) be given up? Unless relevant use cases different than class-like usages are provided, 3) can disappear in my opinion. If the above was the only choice (and I don't think it is)

Which other choices do you think are available?

Davud

# David Bruant (12 years ago)

Le 20/01/2013 19:01, Brendan Eich a écrit :

David Bruant wrote:

Once again, the spec (well... you in that case :-) ) will do whatever is necessary to make the feature understandable by spec readers. Transpilers will work with whatever is in the language. If they only have weakmaps, they'll use that.

See harmony:harmony, in particular

Means

  1. Minimize the additional semantic state needed beyond ES5.
  2. Provide syntactic conveniences for: 1. good abstraction patterns; 2. high integrity patterns; 3. defined by desugaring into kernel semantics.

We aim to unify the "spec problem" and the "transpiler problem" where possible.

I think the goal of providing syntactic conveniences defined by desugaring into kernel semantics is a valuable goal. It's good to reason about this semantics and it's good for transpilers. I however disagree that it should be taken to the letter as Allen seems to take it. Specifically, I disagree with the idea that all kernal semantics should be exposed as runtime constructs. They should only if they prove to be useful for some other purpose.

Not always, not necessarily by spec-by-desugaring. But we don't want magic in the spec that leaves transpilers falling into the tarpit, if we can avoid it.

Semantics of private-class syntax hasn't been agreed on, so it's hard to say if transpilers would have a hard time with the eventually-agreed semantics, but Mark's desugaring with WeakMaps could work, couldn't it?

# Brendan Eich (12 years ago)

David Bruant wrote:

Le 20/01/2013 18:59, Brendan Eich a écrit :

David Bruant wrote:

I disagree with Brendan when he says "to use weakmaps for class private instance methods/variables"... well... it depends on what "use" means: The spec is allowed to /use/ anything it needs to make the class private syntax work. If the spec says that private properties are like properties but aren't enumerated in Object.gOPN calls, fine. If the spec says that private properties require a lookup to some object -> value map, fine (but misleading, I agree).

I don't think we disagree.

If there were no observable differences at all between weak maps and private symbols, we would have only one. Since there are, and since you propose to map private-in-class syntax onto weak maps in part on account of these differences (viz, proxying and whitelist population without privacy-leaks), here we are. I don't understand, this paragraph went too quickly. I understand that you're saying that the proposal I defend (which includes getting rid of private symbols) relies on the differences between private symbols and weakmaps and hence both are necessary? Am I fully misunderstanding?

I think you are misreading, and we're going off track. You did not agree with my use of "use". I'm not sure why, but my second reply about wanting to minimize kernel semantics in the spec, and thereby support desugaring/trans-compilation, may shed light on the deeper issue (if there is one).

In none of this did I try to make a case for private symbols and weak maps. That case has been made but I'm not going to rehash it, and your argument predicated on private-in-class for weak maps is a fine argument to make (assuming the predicate condition, which is not true for ES6

# Brendan Eich (12 years ago)

David Bruant wrote:

Le 20/01/2013 19:01, Brendan Eich a écrit :

David Bruant wrote:

Once again, the spec (well... you in that case :-) ) will do whatever is necessary to make the feature understandable by spec readers. Transpilers will work with whatever is in the language. If they only have weakmaps, they'll use that.

See harmony:harmony, in particular

Means

  1. Minimize the additional semantic state needed beyond ES5.
  2. Provide syntactic conveniences for: 1. good abstraction patterns; 2. high integrity patterns; 3. defined by desugaring into kernel semantics.

We aim to unify the "spec problem" and the "transpiler problem" where possible. I think the goal of providing syntactic conveniences defined by desugaring into kernel semantics is a valuable goal. It's good to reason about this semantics and it's good for transpilers. I however disagree that it should be taken to the letter as Allen seems to take it. Specifically, I disagree with the idea that all kernal semantics should be exposed as runtime constructs. They should only if they prove to be useful for some other purpose.

Hence my "where possible".

Note the Reference internal type among others.

Please do try to avoid casting positions as absolute when they explicitly are not.

Not always, not necessarily by spec-by-desugaring. But we don't want magic in the spec that leaves transpilers falling into the tarpit, if we can avoid it. Semantics of private-class syntax hasn't been agreed on, so it's hard to say if transpilers would have a hard time with the eventually-agreed semantics, but Mark's desugaring with WeakMaps could work, couldn't it?

Do we want protected? Do we have an issue (Kevin is getting at this) with proxies observing that private-in-class is based on something other than properties named by private symbols?

If proxies can observe that, yes, you are right: we may still use internal spec types to specify private-in-class. But we should also consider using weak maps and supporting transpilers, and committing to any observables.

Doing so closes the door to private symbols in the future, in my view.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

We we were going to talk about adding support for addition semantics to classes we should start at the level of the object model. Not with syntax. ES6 class syntax is good because it is defined in terms of the object model and more basic operations that are defined in the language. You don't have to use class syntax to define a class equivalent. So, if we are going to introduce private object state the first step is to define how it works at the object model level.

Cutting there to dodge the controversy over private-in-class based on weak maps, to divide and conquer in the meta-discussion with David.

Here I agree with you, and we have talked in the past of the perils of "Scenario Solving" by adding just-so syntax with novel semantics, which is not decomposed into orthogonal primitives. You saw that in a past life (VB5, cough) and it made messes, even though the intentions (serving developer problem-scenarios) were good.

I think TC39 as a whole agrees.

But this doesn't settle the private-in-class-on-weak-map hash, IMHO.

# Allen Wirfs-Brock (12 years ago)

On Jan 20, 2013, at 11:12 AM, David Bruant wrote:

Le 20/01/2013 18:54, Allen Wirfs-Brock a écrit :

On Jan 20, 2013, at 3:04 AM, David Bruant wrote:

Le 20/01/2013 05:27, Allen Wirfs-Brock a écrit :

Weakmaps and symbol keyed properties (private or not) are very different mechanism. They each have a variety of uses and differing implementation and performance characteristics. It really is a distraction to focus exclusively on a single use case that might be supported by two different mechanism and use it to argue that only one of the mechanism is needed. What are the different uses of private symbols? Branding is the important one.

Secure private communications between "friend" objects is another possible one. Secure private communication between friend objects can be achieved simply and efficiently with functions or weakmaps already. Why is there a need for an additional construct for that use case?

You didn't address branding.

Also, here is another use case. Self hosting the ES built-in library, which makes extensive use of private internal state.

I don't think you can make valid statements about simplicity and efficiencies without working through more specific use cases. I have a fundamental disagreement with arguments that start with the premise that WeakMaps and private Symbol keyed properties have comparable performance profiles.

More specifically: what are the remaining explicit uses of private symbols in a language with private syntax in classes? But we don't have such a language. There are no harmonious proposals for such a syntax. Getting classes into ES6 via the max-min class proposal was possible because we agreed not to debate that issue. Will we ever? If the answer is yes, my point stands. TypeScript has private syntax in class. People want it. The ES6 cut-off was too short for TC39 to settle on private in class, but the pressure is here so it's going to happen sooner or later (Mark was saying ES7).

The problem wasn't that the ES6 cut-off was too short to get private into class. The problem was that we couldn't agree upon a meaning (or syntax) for private in classes and it was preventing the inclusion of any class syntax. I don't know how long it will take to resolve this. Never is always a possibility.

TypeScript private is not what you want. The are a purely compile-time construct and don't provide any strong runtime encapsulation.

We we were going to talk about adding support for addition semantics to classes we should start at the level of the object model. Not with syntax. ES6 class syntax is good because it is defined in terms of the object model and more basic operations that are defined in the language. You don't have to use class syntax to define a class equivalent. So, if we are going to introduce private object state the first step is to define how it works at the object model level. Does defining how it works at the object model level commits the language to expose a runtime construct allowing to use the lowest-level feature directly?

Hopefully. I think an important characteristic of the current class definition syntax is that it conceptually desugars to more primitive features of the object model that are exposed to the language. If you don't like the class syntax or want to extend it you can use functional definitional forms or write a transpiler that produce identical runtime structures to what is produced by class declarations.

If class definitions exploit capabilities that are only available to them, then we would loose this characteristic.

I think it does not. You think it does, hence private symbols:

  • For which benefits?
  • One downside is that it brings along more complexity to the language... well... to proxies at least. I understand and somewhat agree with your argument that complexifying proxies isn't a problem, but at least, there should be a benefit. Also, given that private-in-classes aren't in ES6, why would it be necessary to add private symbols in ES6? Until there is a feature that demands to be desugared as private symbols (I don't think there is, but you probably know better, so tell me if so), it might be wise to put them on hold.

Branding of a large population of highly volatile object instances. The need to do this is completely orthogonal with classes. Arguably private symbols adds no complexity to the actual syntactic ES language, but only to the standard library (including the Proxy handler API).

IMO and for reasons I've already presented, saying that private state is just relationships defined via WeakMaps is a non-starter. I disagree with your arguments on WeakMaps, because choosing to desugar the syntax to WeakMaps doesn't commit neither the implementation nor the spec to actually use weakmaps to spec or to implement the feature.

How something is specified is certainly an indication of design intent. All object properties could be specified in terms of WeakMap induced relationships among otherwise stateless object identities. Why don't we do that? Presumably because there is a design intent is that an object is most often a manifestation of a strongly coupled set of data that should be managed as unit. To specify it as a loosely couple set of externally imposed relationships would be sending a completely different message to both language implementors and users.

A table/relationship based language may be a find thing (I think there already is a widely used one) but it isn't a particularly good way to describe an object based language.

Probably more significantly, your "this is just the spec" argument depends upon class level syntax that is specified using the same specification mechanisms. In other the syntax only works in the context of class declarations. For it to work equivalently for non-class syntax based uses (and still be interoperable with actual syntactic class declarations) actual WeakMap objects would have to be exposed.

I think symbols were introduced given experience in ES5 and assuming being an improvement on top of ES5 as we know it. On top of pure ES5, private symbols make a lot of sense. In ES5+class-with-private-syntax, I'm much more skeptical. Actually experience with a number of languages. But like I said, there is no ES5+class-with-private-syntax so I don't see how that is a useful argument. There will be.

Arguable, and certainly the timeframe isn't short. I don't really want to wait 5-10 years before addressing the branding use case.

Also, private syntax as private symbol makes the proxy story complicated [1], because the class or its instances need to publicize private symbols so that proxies can add them to their whitelist when wrapping class instances. I don't think leaking abstraction is a valid option, so it means that only 2 out of the 3 following can be kept in the language: I don't have a problem at all with making the proxy story more complicated. "complicated" was an expression. Either proxies don't work with class instances, making them vastly pointless or classes need to publicize their private symbols (or maybe expose something like "myClass.acceptProxy" which is marginally better), thus ruining their own encapsulation.

Actually this whole discussion makes me question the validity of the current Proxy design rather than that of private Symbol. I may be on the road towards getting on the NotificationProxy train.

Class instances are just objects so classes shouldn't be part of this discussion. In particularly, the built-ins are examples of objects that have private per instance state which also presumably needs to work with Proxies if we expect to be using Proxies to wrap arbitrary objects. I'm particularly sensitive to build-in related issues because I've been spending lots of time in the built-in spec. making sure they are subclassable. We also have a lot more built-ins in ES6 so special case handling of each one is starting to be problematic.

It seems to me that the real problem isn't just with private symbols, its about what a method can assume about its this value.

Consider:

var d = new Date(); var p = Proxy(d, {});

p.getSeconds();

This is going to result in a call to getSeconds with p as its this value.

Now, getSeconds is currently specified to only work with objects that it can identify as real date instances. The first thing that getSeconds does is a brand check on its this value. That brand check is specified as checking that the this object has a [[DateValue]] internal data property. Later it will use the value of that internal data property as the timeValue that the seconds is extracted from. Now p, as a proxy instance, doesn't have such an internal data property but instead has other internal data properties such as [[Target]] so, without special case magic, the internal access to p's [[DateValue]] will fail and the method will throw a TypeError.

The magic, that presumably has to be applied to all such internal data property accesses, is that when an internal data property is accessed off of a proxy that access must be applied to the target object rather than the proxy object. In that case the [[DateValue]] on p would become a [[DateValue]] access on d which would work. If Date is self hosted and a private Symbol was used to represent [[DateValue]] then we would want things to work the same way.

This suggests a possible generalized solution to the Proxy/private symbol exposure problem:

The [[Get]] and [[Set]] (and probably some others) internal methods of a proxy never call the corresponding trap when the property key is a private Symbol. Instead, they trace the [[Target]] chain of the proxy until a non-proxy object is reached (call this the "ultimate target"). It then invokes the ultimate target's [[Gett]]/[[Set]] using that same private Symbol key. The result of that operation is then returned as the value of the original [[Get]]/[[Set]].

The "private" state access is applied to the correct object and there is no exposure of the private symbol!

Proxys are an expert feature designed for some specific use cases. they are probably an attractive nuisance. I would advise most JS programmer that if they are going down the road of using a Proxy, they are probably making a mistake. In that light, placing the extra complexity within the Proxy story seems just fine. To the point of making proxies useless when interacting with class instances or ruining class encapsulation?

  1. proxy-wrapping class instances (without leaking abstractions)
  2. private syntax in class
  3. private symbols We can probably predict in advance that JavaScript authors will largely not want to give up on 2) (even if it comes only in ES7). Should 1) or 3) be given up? Unless relevant use cases different than class-like usages are provided, 3) can disappear in my opinion. If the above was the only choice (and I don't think it is) Which other choices do you think are available?

for example, the above.

# Brendan Eich (12 years ago)

Skipping contentious stuff (some of which, e.g. "classes as sugar", I agree with -- and so does TC39) to get to this paragraph:

Allen Wirfs-Brock wrote:

The [[Get]] and [[Set]] (and probably some others) internal methods of a proxy never call the corresponding trap when the property key is a private Symbol. Instead, they trace the [[Target]] chain of the proxy until a non-proxy object is reached (call this the "ultimate target"). It then invokes the ultimate target's [[Gett]]/[[Set]] using that same private Symbol key. The result of that operation is then returned as the value of the original [[Get]]/[[Set]].

The "private" state access is applied to the correct object and there is no exposure of the private symbol!

If I want to proxy for a date instance I might rather have @@DateValue available and passed as a name to get and set traps, so I can keep my own time number, and not have to delegate to a Date prototype that has the right milliseconds since the Epoch.

Perhaps you're in favor of that in addition? But if so, how would one proxy such that the @@DateValue symbol was the name parameter to get and set traps?

# Allen Wirfs-Brock (12 years ago)

On Jan 20, 2013, at 2:27 PM, Brendan Eich wrote:

Skipping contentious stuff (some of which, e.g. "classes as sugar", I agree with -- and so does TC39) to get to this paragraph:

Allen Wirfs-Brock wrote:

The [[Get]] and [[Set]] (and probably some others) internal methods of a proxy never call the corresponding trap when the property key is a private Symbol. Instead, they trace the [[Target]] chain of the proxy until a non-proxy object is reached (call this the "ultimate target"). It then invokes the ultimate target's [[Gett]]/[[Set]] using that same private Symbol key. The result of that operation is then returned as the value of the original [[Get]]/[[Set]].

The "private" state access is applied to the correct object and there is no exposure of the private symbol!

If I want to proxy for a date instance I might rather have @@DateValue available and passed as a name to get and set traps, so I can keep my own time number, and not have to delegate to a Date prototype that has the right milliseconds since the Epoch.

For the above I'm thinking of situations where an external party (a membrane?) is wrapping an object that wasn't specifically designed to be proxied. That presumably is the use case that people are primarily concerned about in the recent discussions. As far as I can tell, without special accommodations for all the built-ins' internal state they just don't work if proxied in that manner because the "wrong" this value is passed to all of the methods. Same as for previous proposal for censoring private symbols.

I actually don't see why you would ever need to use a Proxy to implement a ES compatible data abstraction. There is nothing about that abstractions (unlike Array, for example) that requires changing the MOP level behavior manifested by its instances.

Mostly what we are dealing with, WRT Date, is balancing legacy compatibility issues with subclassability issues. The legacy spec for all the Date prototype methods says they throw if they are applied to a this value that does not have the internal state of a Date instance. Among other things, this allows implementations to use custom c structs for Date instances and implement access to the timeValue as a impl level brand check followed by a direct slot access. The ES6 specification of these methods is intentionally vague (the timeValue is in the [[DaveValue]] "internal data property" (which could be implemented using a private Symbol keyed property) rather than explicitly specified as a private Symbol keyed property. This (along with the @@create method) allows subclass to have the same private state layout as the superclass and allows it to be recognized by the methods.

Because of this design, the Date prototype methods are only usable on objects that are allocated using Date.@@create (subclasses achieve this via class-side inheritance). It would arguably be a better OO design to have all those methods access the timeValue via a specified @@DateValue method call. Then they could be install on any object that supplies (possibly via inheritance) such a method. But that design seems to make it harder to use an optimization object struct and possibly to perfectly duplicate the legacy failure behavior.

Perhaps you're in favor of that in addition? But if so, how would one proxy such that the @@DateValue symbol was the name parameter to get and set traps?

I'm not sure if you are getting at something other than what I've described above. If a @@DateValue private symbol is actually used as the implementation of [[DateValue]] then actuality that would happen.

Were you thinking about something different?

# Brandon Benvie (12 years ago)

On Sunday, January 20, 2013, Allen Wirfs-Brock wrote:

I'm not sure if you are getting at something other than what I've described above. If a @@DateValue private symbol is actually used as the implementation of [[DateValue]] then actuality that would happen.

I explored implementing [[DateValue]], [[NumberValue]], etc. as symbols e.g. @@DateValue, but this causes observably different behavior from what is specified with a.) inheritance, and b.) interaction with proxies.

Object.create(new Date).getDate(); // works but is specified to not work

new Proxy(new Date, {}).getDate(); // works but is specified to not work

Since Proxy is new in ES6 this of course can still be fixed, but to make the first case work would be a (probably benign) backward incompatible change and isn't going to work cross-engine unless the spec is changed to explicitly allow/require it.

# Brandon Benvie (12 years ago)

Err I take it back. They can be implemented in terms of private symbols, but every access has to be guarded with a hasOwn check which would be observable to a Proxy.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

Perhaps you're in favor of that in addition? But if so, how would one proxy such that the @@DateValue symbol was the name parameter to get and set traps?

I'm not sure if you are getting at something other than what I've described above. If a @@DateValue private symbol is actually used as the implementation of [[DateValue]] then actuality that would happen.

Were you thinking about something different?

See Brandon's followups. Proxies can tell if an object is fielding direct vs. delegated accesses.

# Allen Wirfs-Brock (12 years ago)

On Jan 20, 2013, at 4:42 PM, Brandon Benvie wrote:

On Sunday, January 20, 2013, Allen Wirfs-Brock wrote: I'm not sure if you are getting at something other than what I've described above. If a @@DateValue private symbol is actually used as the implementation of [[DateValue]] then actuality that would happen.

I explored implementing [[DateValue]], [[NumberValue]], etc. as symbols e.g. @@DateValue, but this causes observably different behavior from what is specified with a.) inheritance, and b.) interaction with proxies.

I spend a lot of time working of Date last week, so you don't have the latests.

In the case of inheritance at least, if it has observably different behavior it hasn't been implemented correctly or the spec. is still buggy.

This is what my working draft currently says:

Unless explicitly stated otherwise, the methods of the Date prototype object defined below are not generic and the this value passed to them must be an object that has a [[DateValue]] internal data property that has been initialized to a time value.

The abstract operation thisTimeValue(value) performs the following steps:

If Type(value) is Object and value has a [[DateValue]] internal data property, then
Let n be the Number that is the value of value’s [[NumberData]] internal data property.
If n is not undefined, then return n.
Throw a TypeError exception.

In following descriptions of functions that are properties of the Date prototype object, the phrase “this Date object” refers to the object that is the this value for the invocation of the function. The phrase “this time value” within the specification of a method refers to the result returned by calling the abstract operation thisTimeValue with the this value of the method invocation passed as the argument.

Object.create(new Date).getDate(); // works but is specified to not work

If you are using a private Symbol for [[DateValue]] then the "has a [[DateValue]] internal data property" check in the above code needs to do a [[HasOwnProperty]] check rather than a [[HasProperty]]

new Proxy(new Date, {}).getDate(); // works but is specified to not work

Not sure why this works in you implementation. Can you explain.

Since Proxy is new in ES6 this of course can still be fixed, but to make the first case work would be a (probably benign) backward incompatible change and isn't going to work cross-engine unless the spec is changed to explicitly allow/require it.

Right, everything is still in flux which is why it is great that you are trying these things.

# Allen Wirfs-Brock (12 years ago)

On Jan 20, 2013, at 4:46 PM, Brandon Benvie wrote:

Err I take it back. They can be implemented in terms of private symbols, but every access has to be guarded with a hasOwn check which would be observable to a Proxy.

Well, proxy observability is the topic of discussion. they wouldn't be observable via the proposal I floated in an earlier message.

# Allen Wirfs-Brock (12 years ago)

On Jan 20, 2013, at 4:53 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

Perhaps you're in favor of that in addition? But if so, how would one proxy such that the @@DateValue symbol was the name parameter to get and set traps?

I'm not sure if you are getting at something other than what I've described above. If a @@DateValue private symbol is actually used as the implementation of [[DateValue]] then actuality that would happen.

Were you thinking about something different?

See Brandon's followups. Proxies can tell if an object is fielding direct vs. delegated accesses.

IO'm probably being dense, but I don't seen the issue. This sounds like further support for the idea of direct forwarding of private symbol based internal methods to the ultimate target without any observability by the Proxy handlers.

If the concern is abstractions that are directly implemented using Proxy (rather than things like membrane wrappers) then the implementation of the abstraction knows about this behavior and can structure its target object to handle its private Symbol property requests.

# David Bruant (12 years ago)

Le 20/01/2013 23:05, Allen Wirfs-Brock a écrit :

Hopefully. I think an important characteristic of the current class definition syntax is that it conceptually desugars to more primitive features of the object model that are exposed to the language. If you don't like the class syntax or want to extend it you can use functional definitional forms or write a transpiler that produce identical runtime structures to what is produced by class declarations.

If class definitions exploit capabilities that are only available to them, then we would loose this characteristic.

True.

On Jan 20, 2013, at 11:12 AM, David Bruant wrote:

"complicated" was an expression. Either proxies don't work with class instances, making them vastly pointless or classes need to publicize their private symbols (or maybe expose something like "myClass.acceptProxy" which is marginally better), thus ruining their own encapsulation. Actually this whole discussion makes me question the validity of the current Proxy design rather than that of private Symbol. I may be on the road towards getting on the NotificationProxy train.

If there is time to make that big of a change, Mark's idea of action proxies could be considered too. I've only expressed reluctance on the list, because it allows to do weird things when badly used, but for all use cases I've had, it would be fine. Tom expressed reluctance regarding the cost of action proxies, but I'm not entirely sure it's founded. Although Notification and action proxies are good to get rid of the invariants cost, I'm not entirely sure they can help to reduce the complexity when it comes to private symbols.

(...)

This suggests a possible generalized solution to the Proxy/private symbol exposure problem:

The [[Get]] and [[Set]] (and probably some others) internal methods of a proxy never call the corresponding trap when the property key is a private Symbol. Instead, they trace the [[Target]] chain of the proxy until a non-proxy object is reached (call this the "ultimate target"). It then invokes the ultimate target's [[Gett]]/[[Set]] using that same private Symbol key. The result of that operation is then returned as the value of the original [[Get]]/[[Set]].

The "private" state access is applied to the correct object and there is no exposure of the private symbol!

It can work for built-in private state (and could work for private class syntax too), but not for user-generated or obtained private symbols: Let's say 2 untrusted parties are in 2 membranes. They share a private symbol and each has access to a proxy wrapping a common target. With the private symbols semantics you're describing, these 2 untrusted parties have an unmediated communication channel. An unmediated communication channel defeats the purpose of having put the 2 untrusted parties in membranes in the first place. The semantics of user-generated or obtained symbols has to go through proxy mediation because of this use case, hence the whitelist and the unknownPrivateSymbol trap in the current proxy design.

# David Bruant (12 years ago)

Le 20/01/2013 21:53, Brendan Eich a écrit :

Allen Wirfs-Brock wrote:

We we were going to talk about adding support for addition semantics to classes we should start at the level of the object model. Not with syntax. ES6 class syntax is good because it is defined in terms of the object model and more basic operations that are defined in the language. You don't have to use class syntax to define a class equivalent. So, if we are going to introduce private object state the first step is to define how it works at the object model level.

Cutting there to dodge the controversy over private-in-class based on weak maps, to divide and conquer in the meta-discussion with David.

Here I agree with you, and we have talked in the past of the perils of "Scenario Solving" by adding just-so syntax with novel semantics, which is not decomposed into orthogonal primitives. You saw that in a past life (VB5, cough) and it made messes, even though the intentions (serving developer problem-scenarios) were good.

I'm too new to the programming language world, so I don't have this context, but I'm curious. Do you have links or further explanations on this topic?

Thanks,

# Allen Wirfs-Brock (12 years ago)

On Jan 20, 2013, at 5:42 PM, David Bruant wrote:

Le 20/01/2013 23:05, Allen Wirfs-Brock a écrit :

On Jan 20, 2013, at 11:12 AM, David Bruant wrote:

"complicated" was an expression. Either proxies don't work with class instances, making them vastly pointless or classes need to publicize their private symbols (or maybe expose something like "myClass.acceptProxy" which is marginally better), thus ruining their own encapsulation. Actually this whole discussion makes me question the validity of the current Proxy design rather than that of private Symbol. I may be on the road towards getting on the NotificationProxy train. If there is time to make that big of a change, Mark's idea of action proxies could be considered too. I've only expressed reluctance on the list, because it allows to do weird things when badly used, but for all use cases I've had, it would be fine. Tom expressed reluctance regarding the cost of action proxies, but I'm not entirely sure it's founded. Although Notification and action proxies are good to get rid of the invariants cost, I'm not entirely sure they can help to reduce the complexity when it comes to private symbols.

(...)

This suggests a possible generalized solution to the Proxy/private symbol exposure problem:

The [[Get]] and [[Set]] (and probably some others) internal methods of a proxy never call the corresponding trap when the property key is a private Symbol. Instead, they trace the [[Target]] chain of the proxy until a non-proxy object is reached (call this the "ultimate target"). It then invokes the ultimate target's [[Gett]]/[[Set]] using that same private Symbol key. The result of that operation is then returned as the value of the original [[Get]]/[[Set]].

The "private" state access is applied to the correct object and there is no exposure of the private symbol! It can work for built-in private state (and could work for private class syntax too), but not for user-generated or obtained private symbols: Let's say 2 untrusted parties are in 2 membranes. They share a private symbol and each has access to a proxy wrapping a common target. With the private symbols semantics you're describing, these 2 untrusted parties have an unmediated communication channel.

How did they originally come to share the private symbol? Don't they have to have some common point of origin with visibility of the symbol or have both been provided the private symbol by some third party. In either case couldn't they have closure captured references to each other and use those references for direct communicate?

An unmediated communication channel defeats the purpose of having put the 2 untrusted parties in membranes in the first place. The semantics of user-generated or obtained symbols has to go through proxy mediation because of this use case, hence the whitelist and the unknownPrivateSymbol trap in the current proxy design.

This really makes me start to question even more the viability of Proxy based membranes (direct proxies, at least) as an isolation mechanism. Independent of private Symbols, it isn't clear that it is a practical approach.

Also, I think some of the issues discussed in the thread esdiscuss/2012-December/027246 have bearing on this discussion. It is probably worth taking a second look.

# Brendan Eich (12 years ago)

David Bruant wrote:

Le 20/01/2013 21:53, Brendan Eich a écrit :

Allen Wirfs-Brock wrote:

We we were going to talk about adding support for addition semantics to classes we should start at the level of the object model. Not with syntax. ES6 class syntax is good because it is defined in terms of the object model and more basic operations that are defined in the language. You don't have to use class syntax to define a class equivalent. So, if we are going to introduce private object state the first step is to define how it works at the object model level.

Cutting there to dodge the controversy over private-in-class based on weak maps, to divide and conquer in the meta-discussion with David.

Here I agree with you, and we have talked in the past of the perils of "Scenario Solving" by adding just-so syntax with novel semantics, which is not decomposed into orthogonal primitives. You saw that in a past life (VB5, cough) and it made messes, even though the intentions (serving developer problem-scenarios) were good. I'm too new to the programming language world, so I don't have this context, but I'm curious. Do you have links or further explanations on this topic?

Take the Scheme Report intro paragraph, for example:

"Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary. Scheme demonstrates that a very small number of rules for forming expressions, with no restrictions on how they are composed, suffice to form a practical and efficient programming language that is flexible enough to support most of the major programming paradigms in use today. "

Contrast with a language such as VB in the context of MS COM:

www.roblocher.com/whitepapers/oletypes.aspx

I recall Allen mentioning mutable value types (!), multiple array and date types, and a rising tide of non-compositional types and features.

If we make class-with-private-syntax bespoke and custom, we raise the risk of creating some spec-internal new semantics (extending the ES5 kernel). Besides the loss of ability to mix-and-match functions and objects using APIs, as Allen noted, all else equal the odds of loss of compositionality go up.

You're right to stress, as Tom and Mark have from the start, that private symbols must not pierce membranes (or else a side channel hazard exists). That risk exists with bespoke (internal-weakmap-based) private-in-class syntax and semantics too. There's no free lunch. Yes, weak maps inside the spec avoid this hazard, but they could leak through the spec abstraction -- and even if they don't, the lack of property-in-object-named-by-private-symbol leaks.

We should stick to the minimize-kernel-semantics plan, and take the Scheme Report's intro to heart.

# Brandon Benvie (12 years ago)

This looks to address the issues. The reason Proxoes would work (it doesn't currently because I opted to switch back to using a regular internal property because of the issue I mentioned) is because getting the date value changes from "get internal property value" for which Proxies have no specified forwarding method, to "forward [[Get]] of @@DateValue" which obviously they have the mechanism to accomplish.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

This really makes me start to question even more the viability of Proxy based membranes (direct proxies, at least) as an isolation mechanism. Independent of private Symbols, it isn't clear that it is a practical approach.

Firefox relies on proxies based on the original spec. They are "viable" -- we don't have any soundness problems, and we bugfix validity problems until zarro boogs.

Of course, we have not yet implemented symbols, private or public.

Seriously, why are you doubting proxies? Please give more of an analytical argument. Yes, unknownPrivateSymbol as a "throw or do nothing" trap seems hacky. We may find a better way. But that's not a problem with proxies or membranes so much as private symbols in conjunction with the MOP and the integrity properties we want to enforce.

# Brandon Benvie (12 years ago)

Going to the title of this thread, it's my view that private symbols should just auto-forward to the ultimate target no matter what, and that this "trap opt-out" on a per property basis should be counted as a feature instead of a limitation. Making any class/construct automatically unproxyable upon its first use of a private symbol, as someone said earlier, fatally wounds the value of proxies. But the more complex solutions are hacky, as Brendan put it, at best and flimsy/fragile in all likelihood, The only other viable option is to remove private symbols entirely or to simply expose them to proxy traps, essentially making them slightly-less-visible-but-not-entirely-private symbols.

# Brendan Eich (12 years ago)

Brandon Benvie wrote:

Going to the title of this thread, it's my view that private symbols should just auto-forward to the ultimate target no matter what,

Doesn't this allow private symbols to pierce membranes? Or do you mean that each trap would have to check a whitelist and throw on miss.

and that this "trap opt-out" on a per property basis should be counted as a feature instead of a limitation.

I don't know what you mean by this clause.

# Brandon Benvie (12 years ago)

On Sun, Jan 20, 2013 at 10:41 PM, Brendan Eich <brendan at mozilla.com> wrote:

Brandon Benvie wrote:

Going to the title of this thread, it's my view that private symbols should just auto-forward to the ultimate target no matter what,

Doesn't this allow private symbols to pierce membranes? Or do you mean that each trap would have to check a whitelist and throw on miss.

Somebody argued a while back, which convinced me, was that if the two parties already have access to the same private symbol, then they've already got a communication channel most likely. I would argue further that a private symbol's primary use case is for holding sensitive internal state that absolutely shouldn't be mucked with in most cases. If you wanted it to be potentially mucked with, you'd make it a normal symbol.

and that this "trap opt-out" on a per property basis should be counted as

a feature instead of a limitation.

I don't know what you mean by this clause.

What I meant was that it'd be a useful feature to have a method of selectively making properties non-proxied. For example, say Date was implemented using the @@DateValue private symbol. As the implementor of Date, I'd have no problem with Date instances being proxied, but I'd want to continue having guarantees about just that one property. I'd want guarantees that it will not throw unexpectedly and leave my object (the actual proxy target) in a broken state. I think it'd be really useful to have this guarantee as a feature of private symbols (with the other benefit that it drastically simplifies the proxy/private.symbol story).

# Brandon Benvie (12 years ago)

Er to clarify, I didn't mean non-proxied. I meant directly forwarded to the target with no possibility of a malfunction or interception by any proxy traps. I want the property forwarded unconditionally, so it has the same guarantees that a regular [[Get]] does (except in the case of revokable proxies which will have to throw when the target is gone, but this still implies that the ultimate target object can't be harmed by the proxy improperly handling the forward/throwing).

# David Bruant (12 years ago)

Le 21/01/2013 03:35, Allen Wirfs-Brock a écrit :

On Jan 20, 2013, at 5:42 PM, David Bruant wrote:

Le 20/01/2013 23:05, Allen Wirfs-Brock a écrit :

On Jan 20, 2013, at 11:12 AM, David Bruant wrote:

"complicated" was an expression. Either proxies don't work with class instances, making them vastly pointless or classes need to publicize their private symbols (or maybe expose something like "myClass.acceptProxy" which is marginally better), thus ruining their own encapsulation. Actually this whole discussion makes me question the validity of the current Proxy design rather than that of private Symbol. I may be on the road towards getting on the NotificationProxy train. If there is time to make that big of a change, Mark's idea of action proxies could be considered too. I've only expressed reluctance on the list, because it allows to do weird things when badly used, but for all use cases I've had, it would be fine. Tom expressed reluctance regarding the cost of action proxies, but I'm not entirely sure it's founded. Although Notification and action proxies are good to get rid of the invariants cost, I'm not entirely sure they can help to reduce the complexity when it comes to private symbols.

(...)

This suggests a possible generalized solution to the Proxy/private symbol exposure problem:

The [[Get]] and [[Set]] (and probably some others) internal methods of a proxy never call the corresponding trap when the property key is a private Symbol. Instead, they trace the [[Target]] chain of the proxy until a non-proxy object is reached (call this the "ultimate target"). It then invokes the ultimate target's [[Gett]]/[[Set]] using that same private Symbol key. The result of that operation is then returned as the value of the original [[Get]]/[[Set]].

The "private" state access is applied to the correct object and there is no exposure of the private symbol! It can work for built-in private state (and could work for private class syntax too), but not for user-generated or obtained private symbols: Let's say 2 untrusted parties are in 2 membranes. They share a private symbol and each has access to a proxy wrapping a common target. With the private symbols semantics you're describing, these 2 untrusted parties have an unmediated communication channel.

How did they originally come to share the private symbol? Don't they have to have some common point of origin with visibility of the symbol or have both been provided the private symbol by some third party.

No, they have shared the symbol through mediated communication. As Tom has argued multiple times, while symbols are "objects", they should be considered as primitive values that can't be wrapped by proxies. So the 2 parties, while using the mediated communication came to share a private symbol. They didn't need a third party for that.

In either case couldn't they have closure captured references to each other and use those references for direct communicate?

No, you created each of this context separately and you are the only entity with access to both and for whatever good reason of yours, you initially make them share a single object through which their mediated communication start. A setup like one that Mark describes much better than I do [1]. For instance, you're a webpage, each untrusted party is a widget and you have an event mechanism through which you allow widgets to communicate for some time and for whatever good reason.

An unmediated communication channel defeats the purpose of having put the 2 untrusted parties in membranes in the first place. The semantics of user-generated or obtained symbols has to go through proxy mediation because of this use case, hence the whitelist and the unknownPrivateSymbol trap in the current proxy design.

This really makes me start to question even more the viability of Proxy based membranes (direct proxies, at least) as an isolation mechanism. Independent of private Symbols, it isn't clear that it is a practical approach.

I wonder how you're coming to such a question. It is a practical approach assuming proxies can properly mediate communication.

Also, I think some of the issues discussed in the thread esdiscuss/2012-December/027246 have bearing on this discussion. It is probably worth taking a second look.

I think too that this thread revealed unclear stratification properties in built-in algorithms, but I'm not following how relates to this discussion.

David

[1] www.youtube.com/watch?v=w9hHHvhZ_HY&feature=player_detailpage#t=2574s

# Tom Van Cutsem (12 years ago)

2013/1/20 Kevin Smith <khs4473 at gmail.com>

It is not. At the exit of the get trap, the JS engine checks whether

invariants should be enforced for the given property on the target. In your case, the runtime sees that the target has a non-configurable non-writable property called 'foo' with 1 as value. When you try to return 0, it will throw a TypeError because of invariant violation. You can read about invariants at harmony:direct_proxies#invariant_enforcement

Excellent - thanks for the link!

I wrote an introductory article a while back explaining precisely the interaction between frozen objects and proxies, and the invariant checks. Easier to digest than the draft spec on the wiki: soft.vub.ac.be/~tvcutsem/invokedynamic/frozen

# Tom Van Cutsem (12 years ago)

2013/1/20 Allen Wirfs-Brock <allen at wirfs-brock.com>

I don't have a problem at all with making the proxy story more complicated. Proxys are an expert feature designed for some specific use cases. they are probably an attractive nuisance. I would advise most JS programmer that if they are going down the road of using a Proxy, they are probably making a mistake. In that light, placing the extra complexity within the Proxy story seems just fine.

While in the specific case of proxies & private symbols, I think it is fine if proxies take an extra complexity hit, I'd like to push back a little here regarding the more general point.

People often complain that proxies are complicated. Well here's the deal: proxies are only as complicated as the ES object model requires them to be. The more features we add to the ES object model, the more complexity we face in proxies. It's awkward to blame proxies for that extra complexity. Proxies are just a (power-)tool that aim to let the ES programmer emulate all aspects of ES objects in Javascript itself. If Javascript objects become more complex, they obviously also become more complex to emulate.

It also gives a false sense of simplicity to think that pushing off extra complexity into proxies actually sweeps all the complexity under the rug for the non-expert programmers. Extra complexity in proxies also implies extra complexity for any other kind of exotic/host object. Point-in-case: how will host objects interact with private symbols? It isn't entirely obvious. Does a WindowProxy forward them, or does each WindowProxy have its own set? These issues reappear outside of Proxies proper.

# Tom Van Cutsem (12 years ago)

2013/1/21 Brandon Benvie <brandon at brandonbenvie.com>

On Sun, Jan 20, 2013 at 10:41 PM, Brendan Eich <brendan at mozilla.com>wrote:

Brandon Benvie wrote:

Going to the title of this thread, it's my view that private symbols should just auto-forward to the ultimate target no matter what,

Doesn't this allow private symbols to pierce membranes? Or do you mean that each trap would have to check a whitelist and throw on miss.

Somebody argued a while back, which convinced me, was that if the two parties already have access to the same private symbol, then they've already got a communication channel most likely. I would argue further that a private symbol's primary use case is for holding sensitive internal state that absolutely shouldn't be mucked with in most cases. If you wanted it to be potentially mucked with, you'd make it a normal symbol.

As David mentioned already, I have previously argued that symbols should pass through a membrane unwrapped (like primitive values) because the only useful property of a symbol is its identity, and wrapping an identity with a fresh identity is pointless.

Given that assumption ("symbols pierce membranes"), Allen and Brandon's proposal of having private symbol access auto-unwrap proxies is a non-starter, as David already explained (it would open up a communications channel).

So let's revisit the assumption: if a membrane does intercept and wrap symbols, then two previously isolated pieces of code can never come to share the same private symbol. In this case, Allen and Brandon's proposal might actually work out, in which case we avoid proxy vs private symbol complexity, and wrapping class instances with private state just works.

If membranes use WeakMaps internally to ensure that they always generate unique wrappers for their objects, then wrapping private symbols might just work (because passing the same private symbol back and forth across the membrane will preserve the symbol's identity).

The remaining assumption is that there are no built-in, well-known private symbols in the global environment by default. I.e. all "well known" symbols such as @@iterator should be unique symbols. Otherwise it's trivial for two isolated scripts to get access to the same private symbol, and pierce the membrane that separates them.

# Andreas Rossberg (12 years ago)

Amen.

/Andreas

On 21 Jan 2013 17:23, "Tom Van Cutsem" <tomvc.be at gmail.com> wrote:

2013/1/20 Allen Wirfs-Brock <allen at wirfs-brock.com>

I don't have a problem at all with making the proxy story more

complicated. Proxys are an expert feature designed for some specific use cases. they are probably an attractive nuisance. I would advise most JS programmer that if they are going down the road of using a Proxy, they are probably making a mistake. In that light, placing the extra complexity within the Proxy story seems just fine.

While in the specific case of proxies & private symbols, I think it is

fine if proxies take an extra complexity hit, I'd like to push back a little here regarding the more general point.

People often complain that proxies are complicated. Well here's the deal:

proxies are only as complicated as the ES object model requires them to be. The more features we add to the ES object model, the more complexity we face in proxies. It's awkward to blame proxies for that extra complexity. Proxies are just a (power-)tool that aim to let the ES programmer emulate all aspects of ES objects in Javascript itself. If Javascript objects become more complex, they obviously also become more complex to emulate.

It also gives a false sense of simplicity to think that pushing off extra

complexity into proxies actually sweeps all the complexity under the rug for the non-expert programmers. Extra complexity in proxies also implies extra complexity for any other kind of exotic/host object. Point-in-case: how will host objects interact with private symbols? It isn't entirely obvious. Does a WindowProxy forward them, or does each WindowProxy have its own set? These issues reappear outside of Proxies proper.

# Andreas Rossberg (12 years ago)

On 21 Jan 2013 17:33, "Tom Van Cutsem" <tomvc.be at gmail.com> wrote:

So let's revisit the assumption: if a membrane does intercept and wrap

symbols, then two previously isolated pieces of code can never come to share the same private symbol.

I'm not sure I understand what "wrapping a symbol" would actually mean. Are you implying that proxies that target a symbol should actually be usable as a symbol?

# David Bruant (12 years ago)

Le 21/01/2013 19:20, Andreas Rossberg a écrit :

On 21 Jan 2013 17:33, "Tom Van Cutsem" <tomvc.be at gmail.com <mailto:tomvc.be at gmail.com>> wrote:

So let's revisit the assumption: if a membrane does intercept and wrap symbols, then two previously isolated pieces of code can never come to share the same private symbol.

I'm not sure I understand what "wrapping a symbol" would actually mean. Are you implying that proxies that target a symbol should actually be usable as a symbol?

Maybe that's what Tom meant, but in the case he described, creating a corresponding new private symbol would work just fine without having to create a proxy around a private symbol.

# Brendan Eich (12 years ago)

Andreas Rossberg wrote:

On 21 Jan 2013 17:33, "Tom Van Cutsem" <tomvc.be at gmail.com <mailto:tomvc.be at gmail.com>> wrote:

So let's revisit the assumption: if a membrane does intercept and wrap symbols, then two previously isolated pieces of code can never come to share the same private symbol.

I'm not sure I understand what "wrapping a symbol" would actually mean. Are you implying that proxies that target a symbol should actually be usable as a symbol?

I think Tom meant wrapping as in membrane-wrapping, just as any object going through a membrane implemented by a proxy would be wrapped. I.e., symbols are not primitive values, they are objects. So membrane-wrapping a symbol means making a new symbol that can be mapped back to the original when flowing back across the membrane.

We had the old .public idea in play to do something like this. A proxy would never get access to a private symbol, only its "public key". If it had been endowed with a map of such back to the private identities, then it could unwrap. Otherwise, no privacy leak.

It seems even simpler to make symbols a distinct class of object (special case for property naming) and otherwise let them be membrane-wrapped. (Let's defer typeof result value.)

# Allen Wirfs-Brock (12 years ago)

On Jan 21, 2013, at 12:04 AM, David Bruant wrote:

Le 21/01/2013 03:35, Allen Wirfs-Brock a écrit :

Also, I think some of the issues discussed in the thread esdiscuss/2012-December/027246 have bearing on this discussion. It is probably worth taking a second look. I think too that this thread revealed unclear stratification properties in built-in algorithms, but I'm not following how relates to this discussion.

In particular, look at the above threading starting here esdiscuss/2012-December/027277 . For internal method calls, the default behavior of Proxy is to forward to the target. But for ES level method calls through the Proxy, delegation is used. This can leads to inconsistent behavior between direct use of a target object and proxied use of that target, even if the proxy has no handler behavior.

A root problem is that on method invocations through a proxy methods of the target object are invoked with the proxy, rather than the target, as the this value. This means that any assumption the methods have about valid this values are broken. For example, if a built-in method requires a specific implementation level record structure, that method will fail unless it explicitly deals with the possibility it might be indirected through multiple proxies.

This is why we have the private symbol proxy issues we have been discussing. Such a method, instead of directly accessing private symbol properties on on the instance it is operating upon is forced to re-delegate each access back through the proxy.

This also creates problems is you were using WeakMaps to represent encapsulated state:

import $$create ... ; //get the @@create symbol let fooPrivate = new WeakMap; export class Foo { doSomething)_ { let privateState = fooPrivate.get(this)
if (privateState == undefined) throw Error("invalid foo object"); /* do something using the private state */ } } Object.mixin({ [$$create] () { let obj = super$$create; //allocate an ordinary instance fooPrivate.set(obj, {}); //set up a private slot for the new object return obj } });

let f = new Foo(); let pf = new Proxy(f, {});

f.doSomething(); //works fine pf.doSomething(); //throws because pf is passed as the this value to doSomething and pf does not have an entry in fooPrivate.

# Kevin Smith (12 years ago)

A root problem is that on method invocations through a proxy methods of

the target object are invoked with the proxy, rather than the target, as the this value. This means that any assumption the methods have about valid this values are broken.

I believe this is correct. It is a separate proxy issue not directly related to WeakMap/private symbol debate.

# Brendan Eich (12 years ago)

Kevin Smith wrote:

A root  problem is that on method invocations through a proxy
methods of the target object are invoked with the proxy, rather
than the target, as the this value.  This means that any
assumption the methods have about valid this values are broken.

I believe this is correct. It is a separate proxy issue not directly related to WeakMap/private symbol debate.

That's right. It is not identital too, or completely overlapping with, the private member access that comes up with the other proposals -- not all such accesses involve |this|.

Note also that in the browser JS embedding, it is crucial that |this| bind to the WindowProxy, not the Window (the object at the top of the scope chain). FWIW, and we do hope to keep ES6 Proxy and WindowProxy aligned so that implementations could use the former (via internal C++ APIs at first, no doubt) to implement the latter.

# Tom Van Cutsem (12 years ago)

2013/1/21 Brendan Eich <brendan at mozilla.com>

Andreas Rossberg wrote:

On 21 Jan 2013 17:33, "Tom Van Cutsem" <tomvc.be at gmail.com <mailto: tomvc.be at gmail.com>> wrote:

So let's revisit the assumption: if a membrane does intercept and wrap symbols, then two previously isolated pieces of code can never come to share the same private symbol.

I'm not sure I understand what "wrapping a symbol" would actually mean. Are you implying that proxies that target a symbol should actually be usable as a symbol?

I think Tom meant wrapping as in membrane-wrapping, just as any object going through a membrane implemented by a proxy would be wrapped. I.e., symbols are not primitive values, they are objects. So membrane-wrapping a symbol means making a new symbol that can be mapped back to the original when flowing back across the membrane.

We had the old .public idea in play to do something like this. A proxy would never get access to a private symbol, only its "public key". If it had been endowed with a map of such back to the private identities, then it could unwrap. Otherwise, no privacy leak.

It seems even simpler to make symbols a distinct class of object (special case for property naming) and otherwise let them be membrane-wrapped. (Let's defer typeof result value.)

What I meant is that the membrane allocates a new private symbol per private symbol it intercepts, and keeps a mapping between the two. I didn't mean to imply creating a proxy for a symbol.

# Tom Van Cutsem (12 years ago)

2013/1/21 Kevin Smith <khs4473 at gmail.com>

A root problem is that on method invocations through a proxy methods of

the target object are invoked with the proxy, rather than the target, as the this value. This means that any assumption the methods have about valid this values are broken.

I believe this is correct. It is a separate proxy issue not directly related to WeakMap/private symbol debate.

Indeed. But in Javascript, methods shouldn't (in general) make any assumptions about their |this| values. The |this| value can be any random object. It's just the zeroth parameter to a function. That's why in traits.js (which was designed for high-integrity abstractions) we decided to .bind() all methods so the |this| value could be relied upon.

Let's talk through Allen and Brandon's suggestion of auto-unwrapping private symbol access on proxies. If a membrane can intercept all exchanged private symbols I think this could be made to work.

# Nathan Wall (12 years ago)

Object.create(new Date).getDate(); // works but is specified to not work

I understand why this doesn't work, but why shouldn't it work? I'm curious why the language doesn't allow for this... ? It seems reasonable at first glance.

Nathan

# Allen Wirfs-Brock (12 years ago)

On Jan 21, 2013, at 4:44 PM, Nathan Wall wrote:

Object.create(new Date).getDate(); // works but is specified to not work

I understand why this doesn't work, but why shouldn't it work? I'm curious why the language doesn't allow for this... ? It seems reasonable at first glance.

Because, the "Dateness" of an object is an characteristic of an instance not of what is on its inheritance chain.

In ES <= 5.1 this is defined in terms of the [[Class]] internal property which identifies the specific low level built-in form of an object. You might think about it as defining the private record structure of an instance and other specific per instance characteristics such as which internal methods are applicable to the instance.

Changing the prototype (via proto) doesn't change the shape of the instance or modify its [[Class]]. That aspect (which at an implementation level, might impact what is actually allocated) is immutably set for each instance when it is allocated. This is why the built-ins including Array in ES <=5.1 are not "subclassable".

In the the last couple ES6 specs drafts there is a mechanism defined in terms of @@create methods that allow subclasses to inherit such specialized instance allocation procedures. This allows subclasses instance of Array, Date, and other built-in to be fully functional with all the inherited built-in methods.

An ES programmer could directly invoked Date[@@alloc] like:

let newInst = Date[@@alloc]](); //allocate a new data-like instance Date.call(newInst); //initialize the private date state and make the instance as initalized newInst.proto= Object.create(Date.prototype) //give it a new prototype that inherits from Data.prototype

but its much easier to just say:

let newInst = new class extends Date{};

which does pretty much the same thing.

# Allen Wirfs-Brock (12 years ago)

(This message seems like a reasonable place to jump back into this thread. But, I'm not particularly picking on Tom here...)

On Jan 21, 2013, at 1:31 PM, Tom Van Cutsem wrote:

2013/1/21 Kevin Smith <khs4473 at gmail.com>

A root problem is that on method invocations through a proxy methods of the target object are invoked with the proxy, rather than the target, as the this value. This means that any assumption the methods have about valid this values are broken.

I'll +1 myself here.

I believe this is correct. It is a separate proxy issue not directly related to WeakMap/private symbol debate.

Actually, I think it is closely related. Private state state access (and branding) mechanism don't work if you don't have the right this value or if the private state access doesn't automatically forward through through a proxy this to the target this..

Indeed. But in Javascript, methods shouldn't (in general) make any assumptions about their |this| values. The |this| value can be any random object. It's just the zeroth parameter to a function. That's why in traits.js (which was designed for high-integrity abstractions) we decided to .bind() all methods so the |this| value could be relied upon.

I understand and largely agree with the spirit of this comment, but in fact Javascript methods make all sort of assumptions about their this value. Every time you invoke a method property or access a state property through this you are making assumptions. Every time you check the identify of this you are making assumptions. Every time you pass this to another function you are making assumptions. Many times the assumptions aren't verified (and I think that is typically a good design choice) but if they are violated, the program will misbehave.

Sometimes, methods preflight check that the this value is the "right kind" of object. Many of the built-in methods do this, presumably so they can take advantage of optimized native layout. If those checks aren't made an implementation could have memory corruption bugs.

Passing the proxy rather than the target as the this value creates problems because the mechanism that are used to verify the assumptions or or the assumptions thenselves depend upon either object identify (for example looking up the this value in a WeakMap based brand registry) or direct access to target object state (most of the built-ins).

The current Proxy design side-steps these issues in some special cases. For example [[Prototype]] access is a special MOP call that always reaches into the target. But, as far as I can tell there isn't a general solution to this problem in the current Proxy design. Certainly it isn't clear how the built-ins are supposed to be specified so that they work when handed a proxy as their this value.

We can probably fix the built-ins with some ad hoc language about them automatically resolving proxies to the target as the this value. Or perhaps we could expand the internal MOP api to include a resolve proxy to target operation.

Using private symbols for all of these cases, including the built-ins also seems like an alternative that may work.

# David Bruant (12 years ago)

Le 21/01/2013 22:31, Tom Van Cutsem a écrit :

Let's talk through Allen and Brandon's suggestion of auto-unwrapping private symbol access on proxies. If a membrane can intercept all exchanged private symbols I think this could be made to work.

Agreed. Unfortunately, I think the condition ("If a membrane can intercept all exchanged private symbols") cannot be fulfilled practically. Let's start with:

 var o = {a:{a:{a:new PrivateSymbol()}}}
 // send o across the membrane

The membrane has to traverse o to find the symbol. The membrane "can" do it, but it will cost the complete traversal of all objects being passed back and forth which has a cost. An arbitrarily big cost for arbitrarily complex objects.

I could stop the argument here, but for the fun of it, I'll go further :-)

 function PasswordProtectedSymbol(symbol, password){
     return new Proxy({}, {
         get: function(target, name){
             if(name === password)
                 return symbol;
         }
     })
 }

If such an object is crosses a membrane, the membrane needs to know the password to find the encapsulated symbol. The membrane "can" know the password from previous communication between untrusted parties, but it requires to know that such string was the password (note that the property name is not on the target, so Object.gOPN cannot help the membrane). Things can get trickier if the password is partially computed in both sides. For the membrane to know the password, it requires the membrane author to read and understand the code of both untrusted parties in order to understand the semantics of the communication between the 2 parties. This is very expensive and error prone.

Let's see another pattern. As an intermediate state in the demonstration, consider the following InfiniteObject abstraction:

 var infiniteHandler = {
     get: function(){
         return new InfiniteObject();
     }
 }
 var target = {};

 function InfiniteObject(){
     return new Proxy(target, infiniteHandler)
 }

 var do = new InfiniteObject();
 do.you.think.this.can.ever.end ? 'nope' : 'yep';

A slightly different implementation could accept all 1-char strings (why not even putting them in the target) and decide that the proxy-chain stops to provide a private symbol if you pass in a password as in "obj.p.a.s.s.w.o.r.d". In this case, fully traversing the object means doing an infinite loop and the above point about passwords and membrane being aware of communication semantics still stands.

The membrane can always capture the private symbols I think, but it may come at an unpractical price.

hmm... For all the above tricks to work, it requires that the untrusted parties can create their own functions that the membrane is unaware of. Maybe it could be considered to add a Loader option so that syntax-based initializations ({}, [], function(){}...) trigger a custom constructor in loaded code (only in loaded code, not globally of course). This custom constructor would make all new objects

If loaders don't have such an option, it's possible to parse the code and wrap all initializations. I have written such a tool recently for a completely unrelated purpose [1]. It's a ugly AST hack, please forgive the naivety of the implementation. Also the translated code does not have exactly the same semantics than the source one for hoisting reasons and a couple of other edge cases but I chose not to care in my case as a first approximation.

I have no idea if the perf cost would be more practical for either the loader or the rewriting solution. It seems worth investigating though.

David

[1] DavidBruant/HarmonyProxyLab/blob/ES3AndProxy/ES3AndProxy/tests/tools/Test262Converter/transform.js

# Tom Van Cutsem (12 years ago)

2013/1/22 David Bruant <bruant.d at gmail.com>

Le 21/01/2013 22:31, Tom Van Cutsem a écrit :

Let's talk through Allen and Brandon's suggestion of auto-unwrapping

private symbol access on proxies. If a membrane can intercept all exchanged private symbols I think this could be made to work.

Agreed. Unfortunately, I think the condition ("If a membrane can intercept all exchanged private symbols") cannot be fulfilled practically. Let's start with:

var o = {a:{a:{a:new PrivateSymbol()}}}
// send o across the membrane

The membrane has to traverse o to find the symbol. The membrane "can" do it, but it will cost the complete traversal of all objects being passed back and forth which has a cost. An arbitrarily big cost for arbitrarily complex objects.

This is not my understanding of how membranes work:

  • when o is passed through the membrane, a proxy |op| is created for it on the other side (let's call this the "inside")
  • when |op.a| is accessed inside the membrane, the membrane forwards the operation, and creates a new proxy |opp| for the value returned by |o.a|.
  • when |opp.a| is accessed inside the membrane, the membrane forwards the operation, so retrieves |o.a.a|, sees that the value is a private symbol, and returns a new private symbol instead.

The same argument applies to the other examples you gave: membranes only wrap lazily, and it's only at the point where an actual private symbol value is about to cross the membrane (as argument or return value of a forwarded operation) that one needs to detect (and wrap) private symbols.

# David Bruant (12 years ago)

Le 22/01/2013 16:02, Tom Van Cutsem a écrit :

2013/1/22 David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>>

Le 21/01/2013 22:31, Tom Van Cutsem a écrit :

    Let's talk through Allen and Brandon's suggestion of
    auto-unwrapping private symbol access on proxies.
    If a membrane can intercept all exchanged private symbols I
    think this could be made to work.

Agreed. Unfortunately, I think the condition ("If a membrane can
intercept all exchanged private symbols") cannot be fulfilled
practically. Let's start with:

    var o = {a:{a:{a:new PrivateSymbol()}}}
    // send o across the membrane

The membrane has to traverse o to find the symbol. The membrane
"can" do it, but it will cost the complete traversal of all
objects being passed back and forth which has a cost. An
arbitrarily big cost for arbitrarily complex objects.

This is not my understanding of how membranes work:

  • when o is passed through the membrane, a proxy |op| is created for it on the other side (let's call this the "inside")
  • when |op.a| is accessed inside the membrane, the membrane forwards the operation, and creates a new proxy |opp| for the value returned by |o.a|.
  • when |opp.a| is accessed inside the membrane, the membrane forwards the operation, so retrieves |o.a.a|, sees that the value is a private symbol, and returns a new private symbol instead.

The same argument applies to the other examples you gave: membranes only wrap lazily, and it's only at the point where an actual private symbol value is about to cross the membrane (as argument or return value of a forwarded operation) that one needs to detect (and wrap) private symbols.

True. I'm taking back what I said :-)