Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Normative: Permit Symbol as WeakMap key and WeakSet entry #2038

Closed
wants to merge 3 commits into from

Conversation

littledan
Copy link
Member

@littledan littledan commented Jun 9, 2020

Specification PR for https://github.com/tc39/proposal-symbols-as-weakmap-keys
Note that both registered and unregistered Symbols are permitted here.
Not intended to land until this proposal reaches Stage 4 (currently: Stage 1).

Note, the intention would be to permit Symbols in WeakRefs and registered in FinalizationRegistry as well. This includes creating a definition of liveness for Symbols, which is expected to generally match that of objects. This logic will be added to the PR once WeakRefs reach Stage 4 (which is hoped to happen before this proposal reaches Stage 3).

(I'm not sure if this PR is missing something; it ended up being very straightforward.)

Closes #1194

Visual diff (thanks @arai-a!) https://arai-a.github.io/ecma262-compare/?pr=2038

@littledan littledan added needs consensus This needs committee consensus before it can be eligible to be merged. needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 pending stage 4 This proposal has not yet achieved stage 4, but may otherwise be ready to merge. proposal This is related to a specific proposal, and will be closed/merged when the proposal reaches stage 4. labels Jun 9, 2020
@littledan littledan marked this pull request as ready for review June 9, 2020 17:56
@littledan
Copy link
Member Author

(Marking as "ready for review" just with the hope that it shows up in https://arai-a.github.io/ecma262-compare/ . This should be understood to be a draft.)

@ljharb ljharb added normative change Affects behavior required to correctly evaluate some ECMAScript source text and removed needs consensus This needs committee consensus before it can be eligible to be merged. labels Jun 9, 2020
spec.html Outdated
@@ -36582,7 +36582,7 @@ <h1>WeakMap.prototype.delete ( _key_ )</h1>
1. Let _M_ be the *this* value.
1. Perform ? RequireInternalSlot(_M_, [[WeakMapData]]).
1. Let _entries_ be the List that is _M_.[[WeakMapData]].
1. If Type(_key_) is not Object, return *false*.
1. If Type(_key_) is not Object or Symbol, return *false*.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this would be overkill, but it might be nice to have a single abstract operation that owns this definition instead of having to change it in 4-8 places

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'm open to that. I guess the general question would be, do we want to define a term for this concept of being an Object or Symbol? For example, "referenceable" or "identity-full"? If someone can think of a real word describing this concept, that'd probably be better than those two...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgeable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgeable means something different :-)

"weakable" sounds weird tho

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that the general concept is something like "distinguishable".

Copy link
Member

@ljharb ljharb Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"distinguishable" might be a bit too broad, since 3 and 4 are distinguishable but depending on your mental model:

  • if each 3 is a different 3, but 3 === 3 compares them by value and not by identity/reference, then symbols and objects "have identity" but the rest don't.
  • if every 3 is the same 3, then you can create symbols and objects, but the rest are more like singletons referenced by syntax, in which case i'm not sure what you'd call symbols and objects.

Copy link
Member Author

@littledan littledan Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're going to be able to poke holes in whatever term we come up with; the key will be to socially agree on some shared language here. I could see using either "unforgeable" or "distinguishable" if we agreed on it, and adjusted our usages in other places appropriately. Other terms could also be fine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HasIdentity, perhaps? (It needn't be a single adjective.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we used a phrase, "has identity"? We could still define and linkify this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer it as an abstract operation, personally, to match IsCallable and friends. Don't feel strongly though.

Copy link
Contributor

@gibson042 gibson042 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec text looks good to me; just a few minor suggestions regarding the prose.

spec.html Outdated
@@ -36508,11 +36508,11 @@ <h1>Properties of Set Iterator Instances</h1>

<emu-clause id="sec-weakmap-objects">
<h1>WeakMap Objects</h1>
<p>WeakMap objects are collections of key/value pairs where the keys are objects and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the objects it holds as keys. If an object that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.</p>
<p>WeakMap objects are collections of key/value pairs where the keys are objects and/or symbols and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the objects and/or symbols it holds as keys. If an object or symbol that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should refer to "value being used as a key" rather than "object and/or symbol being used as a key".

Suggested change
<p>WeakMap objects are collections of key/value pairs where the keys are objects and/or symbols and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the objects and/or symbols it holds as keys. If an object or symbol that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.</p>
<p>WeakMap objects are collections of key/value pairs where the keys are objects and/or symbols and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the keys. If a value that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.</p>

spec.html Outdated
<p>An implementation may impose an arbitrarily determined latency between the time a key/value pair of a WeakMap becomes inaccessible and the time when the key/value pair is removed from the WeakMap. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to observe a key of a WeakMap that does not require the observer to present the observed key.</p>
<p>WeakMap objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of key/value pairs in the collection. The data structure used in this WeakMap objects specification are only intended to describe the required observable semantics of WeakMap objects. It is not intended to be a viable implementation model.</p>
<emu-note>
<p>WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object in a manner that does not &ldquo;leak&rdquo; memory resources if, in the absence of the WeakMap or WeakSet, the object otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object mapping of weak map instances to keys. Alternatively each weak map may internally store its key to value mappings but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSets:</p>
<p>WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object or symbol in a manner that does not &ldquo;leak&rdquo; memory resources if, in the absence of the WeakMap or WeakSet, the object or symbol otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object/symbol mapping of weak map instances to keys. Alternatively, each WeakMap may internally store its key to value mappings, but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSet:</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<p>WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object or symbol in a manner that does not &ldquo;leak&rdquo; memory resources if, in the absence of the WeakMap or WeakSet, the object or symbol otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object/symbol mapping of weak map instances to keys. Alternatively, each WeakMap may internally store its key to value mappings, but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSet:</p>
<p>WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with a value in a manner that does not &ldquo;leak&rdquo; memory resources if, in the absence of the WeakMap or WeakSet, the value otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-entry mapping of weak map instances to keys. Alternatively, each WeakMap may internally store its key to value mappings, but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSet:</p>

spec.html Outdated
@@ -36656,8 +36656,8 @@ <h1>Properties of WeakMap Instances</h1>

<emu-clause id="sec-weakset-objects">
<h1>WeakSet Objects</h1>
<p>WeakSet objects are collections of objects. A distinct object may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific object, but no mechanism is provided for enumerating the objects it holds. If an object that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that object is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such objects and any associated resources.</p>
<p>An implementation may impose an arbitrarily determined latency between the time an object contained in a WeakSet becomes inaccessible and the time when the object is removed from the WeakSet. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to determine if a WeakSet contains a particular object that does not require the observer to present the observed object.</p>
<p>WeakSet objects are collections of objects and/or symbols. A distinct object or symbol may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific object or symbol, but no mechanism is provided for enumerating the objects and symbols it holds. If an object or symbol that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that value is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such values and any associated resources.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<p>WeakSet objects are collections of objects and/or symbols. A distinct object or symbol may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific object or symbol, but no mechanism is provided for enumerating the objects and symbols it holds. If an object or symbol that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that value is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such values and any associated resources.</p>
<p>WeakSet objects are collections of objects and/or symbols. A distinct value may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific value, but no mechanism is provided for enumerating its values. If a value that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that value is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such values and any associated resources.</p>

@littledan
Copy link
Member Author

Thanks for the comments everyone. I've factored out the HasIdentity algorithm and applied @gibson042 's wording suggestions.

spec.html Outdated
@@ -5534,6 +5534,16 @@ <h1>IsStringPrefix ( _p_, _q_ )</h1>
</emu-note>
</emu-clause>

<emu-clause id="sec-hasidentity" aoid="HasIdentity">
<h1>HasIdentity ( _argument_ )</h1>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<h1>HasIdentity ( _argument_ )</h1>
<h1>HasUniqueIdentity ( _argument_ )</h1>

🚲 🏠 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsUnforgeable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"unforgeable" means something very different to me, not dealing with identity but with observable characteristics of a value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I liked @bakkot's suggestion, so I have adopted it here. Do you have a particular concern with it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I view all primitives as having identity, and the “unique identity” phrasing you used in the prose seemed more precise to me.

Copy link
Member Author

@littledan littledan Jun 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<small>isn't it still a unique identity if it's the same 3?</small>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my reading of your prose suggested that "unique" meant "can not be manifested at a distinct callsite"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I see it, the observable difference between 2 and @@iterator, is that, for the first one you can construct (“forge”) the exact SameValue with just syntax (e.g., 2), including built-in operations (e.g., 1 + 1), while for the second one, you must have prior reference to some objects and/or functions in order to retrieve it (e.g., from Symbol with Symbol.iterator, or from Array.prototype and Object.getOwnPropertySymbols with Object.getOwnPropertySymbols(Array.prototype)[0].

That leads to “unforgeable”...

Or, equivalently, “collectable [from a WeakSet, by the GC]”, as the GC can drop the value from the WeakSet as soon as you lose any way to retrieve a reference to it. A non-symbol primitive is never collected because you can always forge a reference to it.

(Also, “unique identity” does not make sense for me: by definition, an identity is unique; or is there a double-agent hidden in the spec?)

Copy link
Contributor

@syg syg Jun 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused by @ljharb's use of the word "identity", which is quite opposite of my understanding of how it's used usually.

"Identity" is the thing that distinguishes encapsulated things as different from one another. JS encapsulates objects and thus objects have identity: ({a:42}) !== ({a:42}). The two objects there have the same structure, but different identities. JS does not encapsulate numbers and strings: "foo" === "foo", and 3 === 3.

It is wrong to teach "identity" as the reason that 3 === 3. 3 === 3 precisely because we don't encapsulate numbers. Python is the most infamous here, I think, in that it doesn't encapsulates number up to 256 due to implementation reasons.

Copy link
Member Author

@littledan littledan Jun 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be happy with @claudepache's suggestions of "collectable" or "unforgeable" to describe predicate, as well as "has identity". I think we can defer settling on a conclusion here until we're getting ready to merge this PR.

spec.html Outdated
<p>An implementation may impose an arbitrarily determined latency between the time a key/value pair of a WeakMap becomes inaccessible and the time when the key/value pair is removed from the WeakMap. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to observe a key of a WeakMap that does not require the observer to present the observed key.</p>
<p>WeakMap objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of key/value pairs in the collection. The data structure used in this WeakMap objects specification are only intended to describe the required observable semantics of WeakMap objects. It is not intended to be a viable implementation model.</p>
<emu-note>
<p>WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object in a manner that does not &ldquo;leak&rdquo; memory resources if, in the absence of the WeakMap or WeakSet, the object otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object mapping of weak map instances to keys. Alternatively each weak map may internally store its key to value mappings but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSets:</p>
<p>WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object or symbol in a manner that does not &ldquo;leak&rdquo; memory resources if, in the absence of the WeakMap or WeakSet, the object or symbol otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object/symbol mapping of weak map instances to keys. Alternatively, each WeakMap may internally store its key to value mappings, but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSet:</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is trailing whitespace on this line.


https://travis-ci.org/github/tc39/ecma262/jobs/696618002#L226-L233

@ljharb ljharb marked this pull request as draft July 15, 2020 21:55
rwaldron added a commit to tc39/test262 that referenced this pull request Nov 2, 2020
@rwaldron rwaldron added has test262 tests and removed needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 labels Nov 2, 2020
rwaldron added a commit to tc39/test262 that referenced this pull request Nov 4, 2020
@leobalter
Copy link
Member

I'm writing some changes on top of this PR but it's being hard to properly sync the branches with this current one. Here is a preview:

master...leobalter:symbol-as-weakmap-key

@ljharb ljharb force-pushed the master branch 3 times, most recently from 3d0c24c to 7a79833 Compare June 29, 2021 02:21
@rwaldron
Copy link
Contributor

rwaldron commented Dec 2, 2021

Update to @leobalter's link: main...leobalter:symbol-as-weakmap-key

rwaldron added a commit to tc39/test262 that referenced this pull request Dec 2, 2021
@Josh-Cena
Copy link
Contributor

Closed by #2777?

@ljharb
Copy link
Member

ljharb commented Mar 18, 2023

Indeed; replaced by #2777.

@ljharb ljharb closed this Mar 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has test262 tests normative change Affects behavior required to correctly evaluate some ECMAScript source text pending stage 4 This proposal has not yet achieved stage 4, but may otherwise be ready to merge. proposal This is related to a specific proposal, and will be closed/merged when the proposal reaches stage 4.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Symbols can't be used as WeakMap keys