Open bakkot opened 4 years ago
It looks wrong for me and somehow related to removing of WeakMap.prototype.clear
. IIRC one of the problems was a performance problem for implementations that want to implement WeakMaps
as internal slots on objects stored as keys.
They could still do that - by copying the internal slots over, for example.
In this case, it's also will be a performance problem. But IIRC it's only one of many reasons for removing WeakMap.prototype.clear
.
@ljharb BTW, for example, it's our primitive slot-based WeakMap
:
class WeakMap {
static #ids = Symbol();
#id = Symbol();
constructor(init) {
if (init) for (const el of init) this.set(el[0], el[1]);
}
delete(key) {
if (key[WeakMap.#ids]) return key[WeakMap.#ids].delete(this.#id);
}
get(key) {
if (key[WeakMap.#ids]) return key[WeakMap.#ids].get(this.#id);
}
has(key) {
return !!key[WeakMap.#ids] && key[WeakMap.#ids].has(this.#id);
}
set(key, value) {
if (!hasOwnProperty.call(key, WeakMap.#ids)) {
Object.defineProperty(key, WeakMap.#ids, { value: new Map() });
}
key[WeakMap.#ids].set(this.#id, value);
return this;
}
}
How do you implement cloning without performance degradation (at least on cloned weak maps)?
static from(weakMap) {
const wm = new WeakMap():
wm[ids] = weakMap[ids];
return wm;
}
And, in this case, any changes on clone affect original, on original - clone (if I understood your idea correctly, since ids
is not a weakmap field). It's not a clone, it's just a new reference to old weakmap. If each weakmap will have a set of ids - it will cause performance degradation for clones - at least 2x for the first, 1001x for 1000th.
Ah, that's true. In that case, there's no way to avoid enumeration for a polyfill, but performance considerations of userland implementations don't tend to hold much weight when designing a feature, historically.
One more time - it's not about userland, it's about engines implementations which works in this way, see my first comment.
I'd be happy to hear from engine implementors if that in fact is a concern.
I don't know how it works in popular engines, but at least it will cause problems for developers of new engines. Also interesting opinions of implementors -)
Also, it will cause some security issues. For example:
// frozen env where we can't patch WeakSet and methods
// somehow we got access to a weakset
const clone = WeakSet.from(weakset);
// sometime later the `weakset` is cleared or just `object` key removed from it
// and now we have access to the `object`
clone.has(object);
// we could know that the `object` was in the `weakset` in the moment of creating a clone
It's far-fetched, but the idea of WeakMap
/ WeakSet
is that we can't observe keys.
I agree that this is a capability which was not previous present, but I don't think it's likely to be a security issue. The important guarantee that a WeakMap provides is that having the map does not allow you to access its contents. That guarantee is preserved.
In this case, making all private fields public in the next version of an abstract language is not a security issue, just a new possibility.
I think that it should solve the committee.
... what?
I don't think this makes keys observable
It makes keys observable, an example above.
If you have object
, you already know if it's in weakset
, so knowing if it's in clone
isn't new information.
The example above does not demonstrate keys being observable.
@ljharb at the moment of creation clone we haven't access to the object
, when we have access to the object
it's missed in the weakset
. It's observability of a private flag.
@bakkot one more time - lets the committee solve is this a security issue or not. In my opinion - yes, since I see some places where it can be exploited.
I see what you're saying, but you also can't know if someone did clone.add(object)
or weakset.delete(object)
in the "some time after" interim (and if you can know that, there's no new information to be had).
@ljharb since we create a clone
, we have full control over it. About weakset.delete(object)
or another operation - yes, but now we have a chance to find it out - that's what I wrote.
Currently
WeakMap.from(x)
requiresx
to be an iterable, whichWeakMap
s are not. It might make sense to allowx
to be aWeakMap
, in which caseWeakMap.from(x)
would return a copy ofx
. That's not currently possible in the language, but I am pretty confident it wouldn't violate any important guarantees.Alternatively, we might say that
WeakSet.prototype.union
(see https://github.com/tc39/proposal-set-methods/issues/23) is the right place for that ability. I am neutral on that question.