Open c-smile opened 3 years ago
WeakMaps must not be enumerable in any way - the objects held as keys must not be reachable unless you already have the object.
the objects held as keys must not be reachable ...
Ok, but why? That's the first question.
And the second, consider PubSub scenario:
let map = new WeakMap();
export function subscribe(element,signal) {
map.set(element,signal);
}
export function notify(bySignal) {
for(let [element,signal] of map.getEntries())
if( signal == bySignal )
element[signal]();
}
By using WeakMap here I want to avoid need for unsubscribe (not always feasible).
How to do that otherwise (without getEntries) ?
b) will be sufficient for most cases.
I don't think that's the case, unfortunately. Being able to associate a string key to a weakly held value is core need here, and it's not possible via a WeakMap
.
Ok, but why? That's the first question.
Technically, it'd be possible, but it'd probably cause WeakMap
s to be even slower than they already are. Allowing iteration by default would mean any holder of the WeakMap
can now suddenly resurrect an otherwise dead object for as long as this snapshot is alive.
How to do that otherwise (without getEntries) ?
You can build getEntries
on top of a regular Map
with WeakRef
.
the objects held as keys must not be reachable ...
Ok, but why? That's the first question.
It makes garbage collection visible. You can tell whether something has been collected or not, which should be an internal detail of the engine. Exposing it has two main problems. First, it's a side channel that makes it possible to exfiltrate information from higher privilege code. Second, and more practically, it means your code will behave differently depending on the JS engine, version, other stuff that's running, time you've been idle in an interactive embedding like a browser, etc. It makes it very easy for code that works in a development environment to break in a production environment. If such code is in a popular enough site, framework, or library, it could also prevent future GC optimizations.
WeakMaps are specifically designed to keep GC invisible. The implementation is called "ephemerons", though note that the Wikipedia page isn't very good. With a WeakMap, the GC can't collect an entry until you no longer have a way of looking it up (ie, either the key or the WeakMap is unreachable). If you could enumerate the keys, you would have a way of looking them up.
WeakRefs have all of the problems I listed above, but were deemed valuable enough to add to the language anyway. At least with WeakRef and FinalizationRegistry, you know you're making things nondeterministic. Also, the spec went to some length to make it less nondeterministic than a naive version would be.
And the second, consider PubSub scenario:
let map = new WeakMap(); export function subscribe(element,signal) { map.set(element,signal); } export function notify(bySignal) { for(let [element,signal] of map.getEntries()) if( signal == bySignal ) element[signal](); }
By using WeakMap here I want to avoid need for unsubscribe (not always feasible).
How to do that otherwise (without getEntries) ?
For full correctness, you don't. The GC is not guaranteed to collect anything, ever. And there are realistic scenarios where it won't. Also, the engine's internal notion of reachability does not necessarily match your application's. On one hand, it might cache a reference that is not visible to you -- JIT code could hold onto a key in an IC, for example. On the other hand, it might not see something that is still available in your embedding -- a reference to a DOM node, say, that you could re-retrieve and then may or may not be missing from your WeakMap. (I think there's magic to prevent this specific case from happening in the browser.)
If you're willing to ignore the semantic problem, you can do it with WeakRef/FinalizationRegistry. You will then get the nondeterminism -- if unsubscription is visible in any way, then you'll find that the timing of it is random.
To be fully correct, you really have to use your application's definition of when a given element
is "active"/"alive" or not. Which means doing explicit unsubscribes, which are admittedly a pain. But it's the only way to be 100% correct.
Breaking WeakMap by allowing its keys to be retrieved would be great for some uses (albeit with nondeterminism), but make them useless for the core set of problems they were introduced to solve.
The non-enumerability of WeakMaps is, in some ways, more fundamental to their purpose than the fact that they hold onto their keys weakly.
Steve Fink correctly points out that having them be enumerable would expose GC and non-determinism, which is certainly reason enough.
However, another property they have is that you can only get a value out of a WeakMap if you already possess the key, which means that by withholding access to the key object you can withhold access the the value object. Much security machinery depends critically on this property to function correctly. The semantics maps very directly onto some of the affordances provided by public key cryptography but without requiring any cryptographic calculations. Making WeakMaps enumerable would open up a whole raft of massive security vulnerabilities all over the place.
On Mar 24, 2021, at 10:53 AM, Andrew @.***> wrote:
Referring to this use case: https://github.com/tc39/proposal-weakrefs#iterable-weakmaps https://github.com/tc39/proposal-weakrefs#iterable-weakmaps If we would have WeakMap.prototype.getEntries() method that will return current set of valid entries {key,value} then enumeration will be trivial:
for(let entry of weakMap.getEntries()) ... getEntries will return current WeakMap snapshot - not an iterator.
That is a) dead simple and b) will be sufficient for most cases.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/tc39/proposal-weakrefs/issues/217, or unsubscribe https://github.com/notifications/unsubscribe-auth/AB5D4WAY4EKBAOAMLDK4OBDTFIRKBANCNFSM4ZXYGIPQ.
Referring to this use case: https://github.com/tc39/proposal-weakrefs#iterable-weakmaps
If we would have
WeakMap.prototype.getEntries()
method that will return current set of valid entries{key,value}
then enumeration will be trivial:getEntries will return current WeakMap snapshot - not an iterator.
That is a) dead simple and b) will be sufficient for most cases.