proposal-symbols-as-weakmap-keys icon indicating copy to clipboard operation
proposal-symbols-as-weakmap-keys copied to clipboard

Alternative solution: Identity Symbols

Open hax opened this issue 3 years ago • 12 comments

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:

  • Symbol.of(object) create the identity symbol for the object
  • get Symbol.prototype.object return the object of the identity symbol (return undefined for non-identity symbols)

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.

hax avatar Jan 14 '22 12:01 hax

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 avatar Jan 14 '22 13:01 acutmore

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.

acutmore avatar Jan 14 '22 14:01 acutmore

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

hax avatar Jan 14 '22 15:01 hax

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

acutmore avatar Jan 14 '22 15:01 acutmore

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.

ljharb avatar Jan 14 '22 15:01 ljharb

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());

acutmore avatar Jan 14 '22 15:01 acutmore

@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.

hax avatar Jan 14 '22 15:01 hax

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.

ljharb avatar Jan 14 '22 15:01 ljharb

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.

mhofman avatar Jan 14 '22 15:01 mhofman

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 avatar Jan 24 '22 03:01 hax

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 ?

hax avatar Jan 24 '22 03:01 hax

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

ljharb avatar Jan 24 '22 04:01 ljharb