WeakMap API questions?

# Erik Arvidsson (14 years ago)

I have a few questions regarding the WeakMap API.

  1. Why isn't there a way to check for presence of a key (using has/contains)?

Given that undefined is a valid value it is not sufficient to just return undefined for get

  1. Why isn't there a way to remove a key-value-pair?

Setting the value to undefined is not the same.

  1. Why isn't there a way to iterate over the keys?

I can see that this might be a security issue but iteration is useful and security sensitive code can prevent iteration in several ways.

  1. Why does set throw if the key is not an object but get doesn't?

Same would go for delete and has if those are added.

# Ash Berlin (14 years ago)

On 14 Aug 2010, at 07:22, Erik Arvidsson wrote:

I have a few questions regarding the WeakMap API.

  1. Why isn't there a way to check for presence of a key (using has/contains)?

Given that undefined is a valid value it is not sufficient to just return undefined for get

Does the standard trick of:

if (key in weakMapInstance) { }

not work?

  1. Why isn't there a way to remove a key-value-pair?

Setting the value to undefined is not the same.

Again:

delete weakMapInstance[key];

  1. Why isn't there a way to iterate over the keys?

I can see that this might be a security issue but iteration is useful and security sensitive code can prevent iteration in several ways.

Object.keys(weakMapInstance)

# Mark S. Miller (14 years ago)

On Fri, Aug 13, 2010 at 11:22 PM, Erik Arvidsson <erik.arvidsson at gmail.com>wrote:

I have a few questions regarding the WeakMap API.

  1. Why isn't there a way to check for presence of a key (using has/contains)?

Given that undefined is a valid value it is not sufficient to just return undefined for get

Simplicity without loss of generality. The example at < harmony:weak_maps#explicit_soft_own_fields>

shows how to build a WeakMap-like API as a thin layer on WeakMaps that implements the four method API you have in mind.

Of course, layering this the other way would work too. Since both are plausible, it seems a better layering to have the primitive abstraction be the simpler one.

Semantically, the way to understand the simpler API is that each WeakMap starts out as a mapping from all possible object identities to undefined. Thus, they only need to record those cases that map to something other than undefined.

  1. Why isn't there a way to remove a key-value-pair?

Setting the value to undefined is not the same.

Depends on the meaning of the abstraction. For WeakMaps, setting to value to map to undefined is to restore the mapping to its initial state, which requires no storage. For < harmony:weak_maps#explicit_soft_own_fields>,

setting the value to undefined is indeed not the same as deleting it, so that abstraction provides an explicit delete operation.

  1. Why isn't there a way to iterate over the keys?

I can see that this might be a security issue but iteration is useful and security sensitive code can prevent iteration in several ways.

Little code should be "security sensitive" in the sense of "my security relies on the correctness or good intentions of this code". Virtually all code should be "security sensitive" in the sense of "my security should not rely on the correctness or good intentions of this code".

The classic covert channel problem is to prevent untrusted code under our control from signaling to untrusted code not under our control. I agree with the general (but not universal) consensus that this is too hard to be practical.

However, the inverse problem is practical: To prevent untrusted code under our control from being able to read from covert channels on which others are signaling. Exposing the non-determinism of garbage collection opens up the ability read from a huge high bandwidth covert channel. Although JavaScript today has various forms of non-determinism, I do not know of any other hard to plug covert channels in JavaScript which are this bad.

  1. Why does set throw if the key is not an object but get doesn't?

Set throws because it is preventing these from becoming keys in the table. Set thus is an LSP (Liskov Substitutability Principle) subtype of Map.set only in the weaker sense of obeying the super contract in those cases where it does not throw. Since non-object keys are absent from the table, get can fulfil the full supertype contract by correctly and successfully reporting their absence.

Same would go for delete and has if those are added.

In < harmony:weak_maps#explicit_soft_own_fields>,

both set and delete throw. Neither get nor has throw. I think this is right for the same LSP reasons.

# Mark S. Miller (14 years ago)

On Sat, Aug 14, 2010 at 1:01 PM, Ash Berlin <ash_js at firemirror.com> wrote:

On 14 Aug 2010, at 07:22, Erik Arvidsson wrote:

I have a few questions regarding the WeakMap API.

  1. Why isn't there a way to check for presence of a key (using has/contains)?

Given that undefined is a valid value it is not sufficient to just return undefined for get

Does the standard trick of:

if (key in weakMapInstance) { }

not work?

It does not. A key is not a property name. A weak map is an object with two own properties, names "get" and "set", whose values are the methods that constitute the weak map API.

  1. Why isn't there a way to remove a key-value-pair?

Setting the value to undefined is not the same.

Again:

delete weakMapInstance[key];

No. This syntax deletes named properties.

  1. Why isn't there a way to iterate over the keys?

I can see that this might be a security issue but iteration is useful and security sensitive code can prevent iteration in several ways.

Object.keys(weakMapInstance)

No. Object.keys enumerates property names.

# Erik Arvidsson (14 years ago)

Thanks for the answers Mark.

On Sat, Aug 14, 2010 at 14:01, Mark S. Miller <erights at google.com> wrote:

On Fri, Aug 13, 2010 at 11:22 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

I have a few questions regarding the WeakMap API.

  1. Why isn't there a way to check for presence of a key (using has/contains)?

Given that undefined is a valid value it is not sufficient to just return undefined for get

Simplicity without loss of generality. The example at harmony:weak_maps#explicit_soft_own_fields shows how to build a WeakMap-like API as a thin layer on WeakMaps that implements the four method API you have in mind. Of course, layering this the other way would work too. Since both are plausible, it seems a better layering to have the primitive abstraction be the simpler one. Semantically, the way to understand the simpler API is that each WeakMap starts out as a mapping from all possible object identities to undefined. Thus, they only need to record those cases that map to something other than undefined.

I agree that your proposed API is the minimal API needed to build useful abstractions on top of WeakMaps but I'm very concerned about usability, code reuse and latency. I would hate to see Dojo, jQuery, Prototype, Closure, YUI etc all providing their own slightly incompatible wrappers around WeakMap.

# Erik Arvidsson (14 years ago)

I think Ash brings up a valid point here. From a usability point it is a clear win to use [], delete and in for WeakMaps and Maps.

# Mark S. Miller (14 years ago)

[+openstrat, +istvan]

On Sat, Aug 14, 2010 at 3:12 PM, Erik Arvidsson <erik.arvidsson at gmail.com>wrote:

Thanks for the answers Mark.

On Sat, Aug 14, 2010 at 14:01, Mark S. Miller <erights at google.com> wrote:

On Fri, Aug 13, 2010 at 11:22 PM, Erik Arvidsson < erik.arvidsson at gmail.com> wrote:

I have a few questions regarding the WeakMap API.

  1. Why isn't there a way to check for presence of a key (using has/contains)?

Given that undefined is a valid value it is not sufficient to just return undefined for get

Simplicity without loss of generality. The example at < harmony:weak_maps#explicit_soft_own_fields

shows how to build a WeakMap-like API as a thin layer on WeakMaps that implements the four method API you have in mind. Of course, layering this the other way would work too. Since both are plausible, it seems a better layering to have the primitive abstraction be the simpler one. Semantically, the way to understand the simpler API is that each WeakMap starts out as a mapping from all possible object identities to undefined. Thus, they only need to record those cases that map to something other than undefined.

I agree that your proposed API is the minimal API needed to build useful abstractions on top of WeakMaps but I'm very concerned about usability, code reuse and latency. I would hate to see Dojo, jQuery, Prototype, Closure, YUI etc all providing their own slightly incompatible wrappers around WeakMap.

I agree that it makes perfect sense to standardize some derived abstractions as well, including an explicit form of Inherited Soft Fields < harmony:weak_maps#inherited_soft_fields>.

Although these should be specified as wrapper implementations, this one in particular could probably perform much better if it were implemented primitively. Standardizing these derived abstractions enables them to be primitively provided across browsers in like manner.

I have created strawman page < strawman:inherited_explicit_soft_fields>

to suggest that this addition be promoted to proposal.

John & Istvan, could you add this to the agenda for our next meeting? Thanks.

# Mark S. Miller (14 years ago)

On Sat, Aug 14, 2010 at 3:16 PM, Erik Arvidsson <erik.arvidsson at gmail.com>wrote:

I think Ash brings up a valid point here. From a usability point it is a clear win to use [], delete and in for WeakMaps and Maps.

We've been over this territory before. How would you reconcile this (quite sensible) desire with the existing semantics of [], delete, and in, and with the existing code that depends on this semantics?

Another pain point is implementation cost on uninvolved code. I had earlier proposed a change to Proxy semantics to achieve exactly this. (Because, again, I do find the idea desirable.) It failed because it would have required postponing the coercion of an index from object to string until one knew the type of object one was indexing into. IIRC, this was not free for uninvolved code on some implementations. I'm rather confident that any new feature that slows down code not using that feature will fail to achieve consensus.

If a painless reconciliation can be found, I'd probably be supportive. Suggestions welcome.

# Ash Berlin (14 years ago)

On 14 Aug 2010, at 23:58, Mark S. Miller wrote:

On Sat, Aug 14, 2010 at 3:16 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote: I think Ash brings up a valid point here. From a usability point it is a clear win to use [], delete and in for WeakMaps and Maps.

We've been over this territory before. How would you reconcile this (quite sensible) desire with the existing semantics of [], delete, and in, and with the existing code that depends on this semantics?

Another pain point is implementation cost on uninvolved code. I had earlier proposed a change to Proxy semantics to achieve exactly this. (Because, again, I do find the idea desirable.) It failed because it would have required postponing the coercion of an index from object to string until one knew the type of object one was indexing into. IIRC, this was not free for uninvolved code on some implementations. I'm rather confident that any new feature that slows down code not using that feature will fail to achieve consensus.

If a painless reconciliation can be found, I'd probably be supportive. Suggestions welcome.

Okay I can see (from an implementation PoV if not usability) why |[]|, |delete| and |in| can't work, but is there anything that says the Object.keys has to return strings and can't just return an array of Objects for a WeakMap? Not sure if this makes the Object MOP layer cleaner or not...

# Waldemar Horwat (14 years ago)

Erik Arvidsson wrote:

  1. Why isn't there a way to iterate over the keys?

Because then it would be a regular map, not a weak map. This is something that only a debugger should be able to do.

Waldemar
# Bradley Meck (14 years ago)

I am not sure that would make it a regular map. Having the keys available does not ensure that the keys will not be collected. I personally use the keys on a personal implementation to check and see what callbacks may be outstanding, and if I need to reference all the Objects of a given type at once.

# Brendan Eich (14 years ago)

On Aug 26, 2010, at 8:19 PM, Bradley Meck wrote:

I am not sure that would make it a regular map. Having the keys available does not ensure that the keys will not be collected.

That's the crux of the matter. Mark Miller should weigh in, since he has argued this point in the past with some good arguments.

The issue is that enumeration or even a weakmap.length property would convey what has been collected as garbage, and what has not yet been collected even though weakly referenced by a map entry. There could be strong refs elsewhere, or the GC might just not have gotten around to collecting the still-enumerated or counted-by-length entry.

This makes a covert channel from the implementation's garbage collector, and it can be abused. The GC schedule may be deterministic, so as I understand it the issue is not non-determinism (or it's more than ND vs. D -- it's really the ability of attackers to tell what the GC considers live still).

I personally use the keys on a personal implementation to check and see what callbacks may be outstanding, and if I need to reference all the Objects of a given type at once.

How does your implementation work (or is it native code)?

# Bradley Meck (14 years ago)

It is native code, all ECMAScript based solutions would leak as of right now.

I am also unsure you could tell what has been collected since you would need to maintains a reference to that object, you could however tell how many objects are left for possible collection. You could do a complex reverse hashing possibly on a WeakMap an attacker creates, but then, if that information was privy, why make the WeakMap key exposed outside of a closure? A last point I would make on this if an Object used as a key is near death (available for collection but 1 reference), if that reference were in a non-key WeakMap, it would be possible to find out this information anyway if i understand the gc algorithm. All you would need is to set a map to contain the first object and have the second object point to the first weakly. All in all I wanted to point out some valid uses of the keys that I currently use. Both of which would leak terribly / not work if using anything else.

# Mark S. Miller (14 years ago)

On Thu, Aug 26, 2010 at 9:03 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Aug 26, 2010, at 8:19 PM, Bradley Meck wrote:

I am not sure that would make it a regular map. Having the keys available does not ensure that the keys will not be collected.

That's the crux of the matter. Mark Miller should weigh in, since he has argued this point in the past with some good arguments.

The issue is that enumeration or even a weakmap.length property would convey what has been collected as garbage, and what has not yet been collected even though weakly referenced by a map entry. There could be strong refs elsewhere, or the GC might just not have gotten around to collecting the still-enumerated or counted-by-length entry.

This makes a covert channel from the implementation's garbage collector, and it can be abused. The GC schedule may be deterministic, so as I understand it the issue is not non-determinism (or it's more than ND vs. D -- it's really the ability of attackers to tell what the GC considers live still).

Thanks Brendan, that's an elegant summary. To make the covert channel problem here more concrete for those interested, here's an example:

Say a.js wishes to mashup two libraries, b.js and c.js, and to keep them isolated. Using our new SES prototype < esdiscuss/2010-August/011684> to

make this concrete,

a.js:

// cajaVM.protect freezes its argument and all data transitively reflectively // reachable from that argument. For the simple json data resulting from a

// one-arg JSON.parse, the result is transitively immutable, as so is safe // to share among isolated components such as b.js and c.js var immutableData = cajaVM.protect(JSON.parse(dataFromSomewhere));

var bSrc = //... Text of b.js var cSrc = //... Text of c.js

// For simplicity, let secretBits by a frozen array of 0s and 1s. Let's say that immutableData // happens to be a pair of arrays of unique objects, just as long. // Since b and c are isolated, it's ok to give b the secrets because b can't communicate. // And it is ok to give c the ability to communicate, since it doesn't know the secret.

var b = eval(bSrc)(immutableData, secretBits); var c = eval(cSrc)(immutableData, internetAccess, setInterval);

// ... for whatever reason, a happens to retain both b and c.

b.js

(function(data, secretBits) { return secretBits.map(function(bit, i) { return immutableData[bit][i]; }); });

c.js

(function(data, internetAccess, setInterval) { var len = data.length; var wm = WeakMapWithEnumerableKeys(); for (var i = 0; i < len; i++) { for (var bit = 0; bit < 2; bit++) { wm.set(data[bit][i], {bit:bit, i:i}); } }

function poll() {
  var clues = [];
  var keys = wm.getKeys(); // only those not yet collected
  for (var j = 0; j < keys.length; j++) {
    var v = wm.get(keys[j]);
    clues[v.i] = (clues[v.i] || 0) | (1 << v.bit);
  }
  internetAccess(clues); // 1 ==> 0, 2 ==> 1, 3 ==> don't know
}
setInterval(poll, 10000);

});

Note that the "security critical code", a.js, did everything right. By the normal reasoning we use to think about object computation, b.js and c.js are indeed isolated, in the sense that there is no vert communications possible between them. With WeakMaps as currently specified, gc is unobservable and c.js evil job is harder. With the addition of a getKeys() operation, c.js could observe which shared immutable objects b.js has dropped.

The example above is of a covert channel, in the sense that b.js intends to send a signal that c.js can read. When b.js does not intend to signal, we have a side channel rather than a covert channel. Preventing a covert channel is generally much harder than preventing a side channel, so one might argue that the addition of this covert channel doesn't matter much.

But JavaScript currently doesn't have any side channels as juicy as this one. If b.js is not intending to signal to c.js, but rather is just innocently doing some internal data-dependent algorithmic task, how much can c.js ascertain about b.js's internals by seeing which shared immutable objects get dropped? Securing JavaScript is already hard enough as it is. I'd rather not have to add to it the burden of trying to answer this very hard question.

Ok, say you don't buy this whole safe mashup fantasy and think we should just depend on the next and future generations of browsers to secure iframes from different origins against each other. Well, these are able to communicate with postMessage. Currently, postMessage just passes copies of JSON data so there are no object to share. When the frames in communication are in the same address space and the JSON data is (somehow cheaply known to be) transitively immutable, a natural and safe optimization is to pass these by pointer sharing rather than copying. If transitively immutable data is thread-safe, as it probably is, then this optimization even works between workers that are running in separate threads in the same address space. (E does this optimization and I believe it is important. See section 14.3 of < erights.org/talks/thesis>.)

But if WeakMaps with getKeys() makes gc observable, then this optimization would open up this juicy side-channel for spying between frames and workers from different origins.

# Erik Corry (14 years ago)

2010/8/14 Mark S. Miller <erights at google.com>:

On Sat, Aug 14, 2010 at 1:01 PM, Ash Berlin <ash_js at firemirror.com> wrote:

On 14 Aug 2010, at 07:22, Erik Arvidsson wrote:

I have a few questions regarding the WeakMap API.

  1. Why isn't there a way to check for presence of a key (using has/contains)?

Given that undefined is a valid value it is not sufficient to just return undefined for get

Does the standard trick of:

if (key in weakMapInstance) { }

not work?

It does not. A key is not a property name. A weak map is an object with two own properties, names "get" and "set", whose values are the methods that constitute the weak map API.

  1. Why isn't there a way to remove a key-value-pair?

Setting the value to undefined is not the same.

Again:

delete weakMapInstance[key];

No. This syntax deletes named properties.

  1. Why isn't there a way to iterate over the keys?

I can see that this might be a security issue but iteration is useful and security sensitive code can prevent iteration in several ways.

Object.keys(weakMapInstance)

No. Object.keys enumerates property names.

And this is as it should be. As it stands the weak map can be used as an object with private members. The object key acts as a capability that controls whether or not you have access to the private member. If you are allowed to enumerate the keys then privacy goes out of the window.

# Bradley Meck (14 years ago)

I would like to agree that .keys would expose some of the internal workings of the GC. This is true, but the solution to that could be to remove .keys() off the individual WeakMap for secure operations, however in some situations where a WeakMap is sandboxed within a closure or such, having .keys allows for sharing private variables between objects discretely without exposing them to the public eye (similair to the friendly idea in c++). Lets take a look at a use case of weak maps to hold private information:

(function(){ var hiddenMap = new WeakMap() //constructor function Foo = function Foo() { var private = {friends:[]} hiddenMap.set(this,private) } Foo.prototype.verifyAgainst = function(otherFoo) { var otherFoo_private = hiddenMap.get(otherFoo) var thisFoo_private = hiddenMap.get(this) if(otherFoo_private.friends.indexOf(this)!==-1&&thisFoo_private.friends.indexOf(otherFoo)!==-1) { //do something only friends can do } } Foo.prototype.extend = function(subconstructorFactory) { return subconstructorFactory(hiddenMap) } })() ... known to be secure code ... Foo.prototype.extend = null ... non-secure code ...

Now say we extend Foo with Bar (inside the secure code area), and Bar wants to request access to all of the Foos in order to track which Foos are friends with each other on both ends. How will we be able to get a list of all the Foos in existence without leaking right now? The only possibilities I see are to not allow these sort of operations or to leak. On the level of use, it seems to me like a strict mode case vs a normal mode case unless we gain a collector callback.

# Mark S. Miller (14 years ago)

On Thu, Sep 2, 2010 at 8:05 AM, Bradley Meck <bradley.meck at gmail.com> wrote:

I would like to agree that .keys would expose some of the internal workings of the GC. This is true, but the solution to that could be to remove .keys() off the individual WeakMap for secure operations,

Remove off of which individual WeakMap? In the example attack I posted, the WeakMap used by the attacker (c.js) is created and used only by the attacker. The defender code interested in secure operation (a.js) does not create or use any WeakMaps, and shouldn't need to worry about them in order to stay secure. The alternative -- that every time we add a new feature to the language we add to the (already almost impossible) burden that defenders need to worry about -- is, well, at least in the web world it is traditional ;). In any case, even if we do add this to a.js's burden, it's not clear what it could do about this threat anyway. If we added .getKeys() to WeakMaps, code out of the defenders control could not be stopped from creating new WeakMaps and using them to spy on GC liveness that was none of its business.

however in some situations where a WeakMap is sandboxed within a closure or such, having .keys allows for sharing private variables between objects discretely without exposing them to the public eye (similair to the friendly idea in c++). Lets take a look at a use case of weak maps to hold private information:

(function(){ var hiddenMap = new WeakMap() //constructor function Foo = function Foo() { var private = {friends:[]} hiddenMap.set(this,private) } Foo.prototype.verifyAgainst = function(otherFoo) { var otherFoo_private = hiddenMap.get(otherFoo) var thisFoo_private = hiddenMap.get(this) if(otherFoo_private.friends.indexOf(this)!==-1&&thisFoo_private.friends.indexOf(otherFoo)!==-1) { //do something only friends can do } } Foo.prototype.extend = function(subconstructorFactory) { return subconstructorFactory(hiddenMap) } })() ... known to be secure code ... Foo.prototype.extend = null ... non-secure code ...

In the example above, how does anything get into any friends lists? I could guess a plausible answer, but given the subtleties of the issues, I'll just wait for you to clarify. Thanks.

Now say we extend Foo with Bar (inside the secure code area), and Bar wants to request access to all of the Foos in order to track which Foos are friends with each other on both ends.

Why would Bar want that? There are all sorts of things that one might want, that we used to have, and that are occasionally useful, like reading arbitrary addresses of memory, that we have learned to deny ourselves because they're too dangerous. Note that my example here is only reading -- which threatens only confidentiality or encapsulation of information, not integrity. I agree that .getKeys() poses no integrity threat.

One well motivated use case for such encapsulation-violating magic powers is debuggers. Indeed, debuggers can already do all sorts of magic things to the computation being debugged that are outside the powers of unprivileged programs within the language, such as peeking into closures. In adding WeakMaps to JS implementations, it does make perfect sense to add a .getKeys() operation to the debugger's eye view of WeakMaps. Likewise with other potentially even more powerful and dangerous instrumentation or intervention in the local GC.

How will we be able to get a list of all the Foos in existence without leaking right now? The only possibilities I see are to not allow these sort of operations or to leak. On the level of use, it seems to me like a strict mode case vs a normal mode case unless we gain a collector callback.

I don't get it. How would you hang this distinction on strict mode vs non-strict mode.

(Btw, please don't call non-strict mode "normal".)

# Mike Shaver (14 years ago)

On Thu, Sep 2, 2010 at 12:46 AM, Erik Corry <erik.corry at gmail.com> wrote:

And this is as it should be.  As it stands the weak map can be used as an object with private members.  The object key acts as a capability that controls whether or not you have access to the private member.

If I were to be using an object with private members, I would certainly expect that those members would keep their values alive.

Wouldn't it be better to just use a regular object, and add private members via defineProperty to make them non-enumerable?

I'm not in favour of WeakMap enumerability, really, but it seems that there's an easier way to address this particular use case.

Mike

# David Herman (14 years ago)

This is what the private names proposal is for. The page is a little stale but Sam Tobin-Hochstadt is hoping to give it some love in the not-too-distant future.

http://wiki.ecmascript.org/doku.php?id=strawman:names

The names go in the object, the link is strong rather than weak, the property is non-enumerable, and even better than string keys, you can't guess the name (either on purpose or by accident) unless you have a reference to the name object. So it's a more reliable version of what you described, but still straightforward to use.

# Mark S. Miller (14 years ago)

That page currently has TBD semantics. Two questions:

Syntax aside, is the observable semantics of Names different from < strawman:inherited_explicit_soft_fields>?

How? If the only semantic difference is (not normally observable) less aggressive GC obligations, great. I'm confident we can converge those. Anything else?

On the syntax, the class-private instance field support at < strawman:classes_as_sugar> allows,

for example a shape class to have an class-private instance field named "draw" but nevertheless be able to address an unrelated normally-named "draw" property of other objects such as guns. It syntactically distinguishes normal property access from amplifying access:

class Shape { private draw() {...} public coDraw(other) { draw(); private(other).draw(); } public shoot(gun) { draw(); } }

In the names proposal, it seems that once in scope of a "private draw" declaration, all apparent uses of "draw" as a property name are amplifying. Even if the object being accessed has a normally named "draw" property, "gun.draw()" will fail to access it. Is that right?

If all this will be addressed in the forthcoming love, I'm happy to postpone these questions till then. Thanks.

# Mark S. Miller (14 years ago)

On Thu, Sep 2, 2010 at 9:47 AM, Mark S. Miller <erights at google.com> wrote:

That page currently has TBD semantics. Two questions:

Syntax aside, is the observable semantics of Names different from < strawman:inherited_explicit_soft_fields>? How? If the only semantic difference is (not normally observable) less aggressive GC obligations, great. I'm confident we can converge those. Anything else?

On the syntax, the class-private instance field support at < strawman:classes_as_sugar> allows, for example a shape class to have an class-private instance field named "draw" but nevertheless be able to address an unrelated normally-named "draw" property of other objects such as guns. It syntactically distinguishes normal property access from amplifying access:

class Shape { private draw() {...} public coDraw(other) { draw(); private(other).draw(); } public shoot(gun) { draw();

Sigh. I sent too quickly. Here I meant

     gun.draw();
# David Herman (14 years ago)

That page currently has TBD semantics.

Yeah, that's part of the work that needs to be done. Intuitively, it's a simple idea: ToName essentially generalizes the current semantics of property lookup; instead of trying to convert the property to a string, you try to convert it to a name-or-string. If it's already a name, it's a no-op; otherwise, it goes through the existing mechanisms.

IOW, everything behaves as before, except name objects are not converted to strings.

Syntax aside, is the observable semantics of Names different from strawman:inherited_explicit_soft_fields? How? If the only semantic difference is (not normally observable) less aggressive GC obligations, great. I'm confident we can converge those. Anything else?

The interface is different. With weak maps, you store soft fields off to the side; with private names, you actually get/set the properties directly on the object.

class Shape { private draw() {...} public coDraw(other) { draw(); private(other).draw(); } public shoot(gun) { gun.draw(); } }

In the names proposal, it seems that once in scope of a "private draw" declaration, all apparent uses of "draw" as a property name are amplifying. Even if the object being accessed has a normally named "draw" property, "gun.draw()" will fail to access it. Is that right?

If all this will be addressed in the forthcoming love, I'm happy to postpone these questions till then. Thanks.

These are good questions, but probably best to postpone till Sam has time to flesh out the strawman page.

# Erik Corry (14 years ago)

2010/9/2 Mike Shaver <mike.shaver at gmail.com>:

On Thu, Sep 2, 2010 at 12:46 AM, Erik Corry <erik.corry at gmail.com> wrote:

And this is as it should be.  As it stands the weak map can be used as an object with private members.  The object key acts as a capability that controls whether or not you have access to the private member.

If I were to be using an object with private members, I would certainly expect that those members would keep their values alive.

Surely that is the case with WeakMap? At least unless you lost the key and don't have any other references to the value. In which case you can't reach the value any more, so why would you care whether it is kept alive?

Wouldn't it be better to just use a regular object, and add private members via defineProperty to make them non-enumerable?

That is certainly a fine way to do private members if they only need to be private by convention and there are no security issues around them being private.

# Mike Shaver (14 years ago)

On Thu, Sep 2, 2010 at 11:32 AM, Erik Corry <erik.corry at gmail.com> wrote:

Surely that is the case with WeakMap?  At least unless you lost the key and don't have any other references to the value.  In which case you can't reach the value any more, so why would you care whether it is kept alive?

You're right; I forgot about the fact that the keys were not necessarily value types. Sorry for the noise.

Mike

# Brendan Eich (14 years ago)

On Sep 2, 2010, at 9:14 AM, Mark S. Miller wrote:

(Btw, please don't call non-strict mode "normal".)

To emphasize Mark's point, ES-Harmony is based on strict mode. It's the new normal :-P.

# Erik Corry (14 years ago)

2010/9/2 Mike Shaver <mike.shaver at gmail.com>:

On Thu, Sep 2, 2010 at 11:32 AM, Erik Corry <erik.corry at gmail.com> wrote:

Surely that is the case with WeakMap?  At least unless you lost the key and don't have any other references to the value.  In which case you can't reach the value any more, so why would you care whether it is kept alive?

You're right; I forgot about the fact that the keys were not necessarily value types.  Sorry for the noise.

I wonder if this points to potential confusion stemming from the WeakMap name. It seems obvious (and wrong) that a WeakMap would act a little like a HashMap where the values were WeakReferences.

Perhaps ObjectMap would be better?

# David Herman (14 years ago)

But HashMaps and WeakMaps both map objects to values. The difference is just that, with WeakMaps, the mapping is weak. The name is excellent, short, and clear.

Perhaps ObjectMap would be better?

That wouldn't distinguish them from HashMaps, since they are both object maps.

WeakMap is a really, really good name. Nay, an /incredibly awesome/ name. Good names are so hard to come by. Let's not overthink this in the effort to prevent all possible confusion. Mike momentarily forgot what they mean, but there aren't really any API docs as such and it's not like he was actually writing code with them. I'd imagine after spending 30 seconds writing code with WeakMaps, no one would confuse about the types.

It ain't broke -- don't fix it!

# Mike Shaver (14 years ago)

On Fri, Sep 3, 2010 at 8:22 AM, David Herman <dherman at mozilla.com> wrote:

Mike momentarily forgot what they mean

Yes, it was a lapse from a casual observer reading the conversations quickly; please don't let my brain-blip harm the sweet naming.

Mike

# Mark S. Miller (14 years ago)
# Brendan Eich (14 years ago)

On Sep 3, 2010, at 10:31 AM, Mark S. Miller wrote:

From email on this thread and from private email I've received, I'm a bit worried that my earlier message was misunderstood.

I wrote "covert channel" too, when inviiting you to reply -- sorry about that, you're right that the side channel is the bigger worry.

I agree that we're not going to secure JavaScript well enough to solve the covert channel problem.

(Will anything solve that problem, short of the kind of communication technology described in science fiction, e.g the phones in www.amazon.com/Dead-Lines-Bear-Greg/dp/0345448375 ?)

(Fortunately, regarding the WeakMap enumerability question, we seem to have consensus to stick with non-enumerability on other grounds anyway. But this side channel question will reappear so we may as well understand the limits on how well we can resist them.)

Leo does not concur and I suspect others (Arv) agree with him.

IMHO Leo's point about weak listener maps and FRP is a good challenge to the consensus that you, Erik Corry, and I among others do share. I need to read Greg Cooper's thesis and medidate on our myriad C++ weak listener use cases before replying further.

# Mark S. Miller (14 years ago)

On Fri, Sep 3, 2010 at 11:32 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Sep 3, 2010, at 10:31 AM, Mark S. Miller wrote:

From email on this thread and from private email I've received, I'm a bit worried that my earlier message was misunderstood.

I wrote "covert channel" too, when inviiting you to reply -- sorry about that, you're right that the side channel is the bigger worry.

I agree that we're not going to secure JavaScript well enough to solve the covert channel problem.

(Will anything solve that problem, short of the kind of communication technology described in science fiction, e.g the phones in www.amazon.com/Dead-Lines-Bear-Greg/dp/0345448375 ?)

Thanks for the link. I just ordered it. Short answer: many people think they know how to build practical systems that can limit the bandwidth of outward covert channels. (Specifically, problem #2.a.i below) They may be right. But valuable secrets are often short, so it's not clear how useful bandwidth limits are. Also, as Moore's law continues, presumably the bandwidth of the covert channels scales up with everything else.

Longer answer:

(apologies for the repetition to those who've already seen this on private email. I though it was worth reposting publicly. I've also further clarified my position on side channel bandwidth issues below.)

The EnumerableWeakMap debate is helping me to see a new distinction. Till now, we've only had the following taxonomy:

1) overt -- channel guaranteed by platform semantics, like reading &

writing a shared variable. 2) non-overt 2.a) covert - where the communicating parties intent to communicate 2.a.i) outward confinement - where the good guy is trying to suppress the signaling ability of a bad guy presumably under his control. 2.a.ii) inward confinement - where the good guy is trying to suppress the listening ability of a bad guy presumably under his control. 2.b) side - where the signaling party does not intend to signal.

Object-capability languages, including Caja and SES, prevent #1. The classic covert channel problem is #2.a.i, and it is indeed too hard. Deterministic object-capability languages such as E and Joe-E solve #2.a.ii. For #2.b as well, zero leakage is too hard and I would be foolish to attempt it. But because none of the signals on this channel are reliable, various countermeasures are possible to impede information leakage, such as introduction of timing noise. In fact, as a side-channel, the various other activities of the signaling app are itself a free source of timing noise absent from a covert channel. As apps get larger and do more, they accidentally provide more free noise for interfering with their own side channels. This is why the limited bandwidth argument has more force for side channels, and why the Moore's law counter-argument has less force.

Punch line:

Any observable weak reference system, including the EnumerableWeakMap example discussed here, is a taxonomy breaker, in that it is a channel which is half overt and half covert. If c.js observes that an object was collected, it must really have been collected. The only sense in which the channel is unreliable is that a collectable object might never get collected. Thus, the only countermeasure possible within the platform's semantics is to not collect objects which are used as keys in WeakMaps, which destroys the entire motivation for introducing WeakMaps in the first place.

# Mark S. Miller (14 years ago)

On Fri, Sep 3, 2010 at 12:20 PM, Mark S. Miller <erights at google.com> wrote:

On Fri, Sep 3, 2010 at 11:32 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Sep 3, 2010, at 10:31 AM, Mark S. Miller wrote:

From email on this thread and from private email I've received, I'm a bit worried that my earlier message was misunderstood.

I wrote "covert channel" too, when inviiting you to reply -- sorry about that, you're right that the side channel is the bigger worry.

I agree that we're not going to secure JavaScript well enough to solve the covert channel problem.

(Will anything solve that problem, short of the kind of communication technology described in science fiction, e.g the phones in www.amazon.com/Dead-Lines-Bear-Greg/dp/0345448375 ?)

Thanks for the link. I just ordered it. Short answer: many people think they know how to build practical systems that can limit the bandwidth of outward covert channels. (Specifically, problem #2.a.i below) They may be right. But valuable secrets are often short, so it's not clear how useful bandwidth limits are. Also, as Moore's law continues, presumably the bandwidth of the covert channels scales up with everything else.

Longer answer:

(apologies for the repetition to those who've already seen this on private email. I though it was worth reposting publicly. I've also further clarified my position on side channel bandwidth issues below.)

The EnumerableWeakMap debate is helping me to see a new distinction. Till now, we've only had the following taxonomy:

1) overt -- channel guaranteed by platform semantics, like reading &

writing a shared variable. 2) non-overt 2.a) covert - where the communicating parties intent to communicate 2.a.i) outward confinement - where the good guy is trying to suppress the signaling ability of a bad guy presumably under his control. 2.a.ii) inward confinement - where the good guy is trying to suppress the listening ability of a bad guy presumably under his control. 2.b) side - where the signaling party does not intend to signal.

Object-capability languages, including Caja and SES, prevent #1. The classic covert channel problem is #2.a.i, and it is indeed too hard. Deterministic object-capability languages such as E and Joe-E solve #2.a.ii. For #2.b as well, zero leakage is too hard and I would be foolish to attempt it. But because none of the signals on this channel are reliable, various countermeasures are possible to impede information leakage, such as introduction of timing noise. In fact, as a side-channel, the various other activities of the signaling app are itself a free source of timing noise absent from a covert channel. As apps get larger and do more, they accidentally provide more free noise for interfering with their own side channels. This is why the limited bandwidth argument has more force for side channels, and why the Moore's law counter-argument has less force.

Punch line:

Any observable weak reference system, including the EnumerableWeakMap example discussed here, is a taxonomy breaker, in that it is a channel which is half overt and half covert.

Sigh. Even when trying to be careful I make the same mistake I just previously tried to correct. I meant "half overt and half non-overt". Whether the non-overt half is covert or side depends on the signaling party's intention, which is orthogonal to the new distinction.

# Sam Tobin-Hochstadt (14 years ago)

[replying to the two issues separately]

On Thu, Sep 2, 2010 at 12:47 PM, Mark S. Miller <erights at google.com> wrote:

Syntax aside, is the observable semantics of Names different from strawman:inherited_explicit_soft_fields? How? If the only semantic difference is (not normally observable) less aggressive GC obligations, great. I'm confident we can converge those. Anything else?

I don't believe that the GC obligations differ. In particular, I don't believe that the spec rules out a no-GC-ever implementation, in which the two would have identical space behavior. That is, in your table on the inherited_explicit_soft_fields page, I don't think that 'must not retain' is ever an obligation we impose on ES implementations.

# Sam Tobin-Hochstadt (14 years ago)

On Thu, Sep 2, 2010 at 12:47 PM, Mark S. Miller <erights at google.com> wrote:

In the names proposal, it seems that once in scope of a "private draw" declaration, all apparent uses of "draw" as a property name are amplifying. Even if the object being accessed has a normally named "draw" property, "gun.draw()" will fail to access it. Is that right?

Yes, that's correct. In the scope of a 'private x' declaration, all uses of F.x are rewritten to F[x] instead of the usual F["x"]. If you want F["x"], it can be written explicitly, of course.

It's important to have this property so that names can be used to add non-interfering additional properties to existing objects. In particular, a rendering library should be able to add 'draw' methods to cowboys without interfering with any of their existing properties.

Also, I really don't think that rewriting f.x to f[x] is amplifying, since that expression is necessarily already able to access the private name, since it is in scope.