tc39 / proposal-symbols-as-weakmap-keys

Permit Symbols as keys in WeakMaps, entries in WeakSets and WeakRefs, and registered in FinalizationRegistries
http://tc39.es/proposal-symbols-as-weakmap-keys
MIT License
90 stars 7 forks source link

Alternative solution: Identity Symbols #22

Open hax opened 2 years ago

hax commented 2 years ago

As #21 discussion, symbol as weakmap key have some arguments on registered symbols, both sides seems have good reasons, maybe we should find some tradeoffs which could satisfy both. I think the root cause of the issues is "symbol as weakmap keys" is too general, as I understand, the main use case is sharable identity, which do not need to be such general. Instead, we could introduce a much limited API:

Usage:

const obj = {}
const sym = Symbol.of(obj)
assert(symbol.object === obj)

Identity symbols (special symbol which ref to the object) would be somewhat like registered symbols (special symbol which ref to the string), so I expect it won't have much troubles to implement.

Note, such API is similar to Object.createSharableIdentity() idea except it use symbol for identity instead of frozen empty object.

acutmore commented 2 years ago

Similar discussion here: https://github.com/tc39/proposal-record-tuple/issues/273

EDIT: summary of other thread. If Symbol.prototype.object worked cross-ream that would break ShadowRealm isolation. If Symbol.prototype.object was per-realm that creates realm-specific state which was one of the objections to Box.

acutmore commented 2 years ago

One pattern that the weakMap approach enables is 'swappable objects'.

For example #[symbol] can be passed between shadowRealms. But each realm has its own weakMap which maps the symbol to the appropriate object for that realm.

hax commented 2 years ago

@acutmore What is "realm-specific state" means? I suppose it should throw TypeError or just return undefined if cross-realm.

acutmore commented 2 years ago

Example of a function having 'realm specific state':

const iFrameRealm = getIframeRealm();

const for1 = globalThis.Symbol.for;
const for2 = iFrameRealm.globalThis.Symbol.for;

const o = {};
for1(o) === for1(o); // the state of for1 is consistent with itself
for1(o) !== for2(o); //  'for' from a different realm has a different internal state
ljharb commented 2 years ago

If it doesn’t work cross-realm, then it’s realm-specific, and that’s not something the language does. Template objects are the only example I’m aware of.

acutmore commented 2 years ago

and Template objects are not technically realm-specific. They are tied to the 'parse node'.

function tag(arr) { return arr; }

function f() { return        tag``   ; }
function g() { return eval(' tag`` '); }

assert(f() === f());
assert(g() !== g());
hax commented 2 years ago

@acutmore Symbol.of(object) would behave like Symbol.for, always return same symbol value even cross-realm. But Symbol.prototype.object may throw cross-realm.

If it's an iframeRealm and u already have the right to access iframeRealm.globalThis.Symbol, I suppose it could get the object cross-realm. If the code do not have the right to access the other realm due to isolation, it throws. It's a security constraint, I don't treat it as realm-specific.

@ljharb said "any realm has the power to unwrap any realm's Box", I think it could be "any realm has the power to unwrap any realm's Box if it not violate the security isolation".

If we require "any realm has the power to unwrap any realm's Box in any case", it just contradict with the security requirement, so the only solution would be "symbol as weakmap key", as it just transfer the "realm-specific" to userland. And if we also require not differentiate symbols, maybe we have to introduce a new primitive type (for example Identity) for weakmap key. 😅

Another compromise solution is:

Introduce Symbol.identity() to create identity symbols, use Symbol.prototype.isIdentity() to check it, and only allow identity symbols as the weakmap key. It still differentiate the symbols, but at least it have much clear surface API and semantic for the programmers.

ljharb commented 2 years ago

There is no consensus on that security isolation being a viable constraint.

There continues to be no consensus that a subset of symbols being weakmap keys is acceptable.

mhofman commented 2 years ago

It seems you're reinventing ObjectPlaceholder and going through all the motions the champion group went through.

Identity symbols (special symbol which ref to the object) would be somewhat like registered symbols (special symbol which ref to the string), so I expect it won't have much troubles to implement.

Taking a step back, how does this solve one of the objection that all symbols be treated the same when adding to WeakMap keys? You've just created another kind of symbol, with unique identity, but registered and well-known symbols are still forgeable, and still share a typeof.

hax commented 2 years ago

how does this solve one of the objection

@mhofman This alternative actually do not introduce symbol as weakmap key, so there will be no observable difference. 😂

But as @acutmore said, it can't solve "swappable objects" case. I'm not sure whether "swappable objects" are the hard requirement, if that, it seems we must introduce symbol as weakmap key.

hax commented 2 years ago

There is no consensus on that security isolation being a viable constraint.

@ljharb

But such security isolation constraints are the consequence of the design of shadow realms, aren't they? And shadow realm is already stage 3 so doesn't it already have consensus ?

ljharb commented 2 years ago

@hax it's part of shadow realms, but it's still not a constraint on the language to maintain it.