Skip to content

Commit

Permalink
Add tests for regular and well-known Symbols as weak values
Browse files Browse the repository at this point in the history
This adds tests to WeakMap, WeakSet, WeakRef, and FinalizationRegistry for
Symbols as weakly-held values. Regular symbols and well-known symbols are
both tested. These tests correspond to existing tests for Objects as
weakly-held values, but are put in separate files so that they can be
filtered out with the "symbols-as-weakmap-keys" feature flag.

Registered symbols are not allowed; this is already tested in the "cannot-
be-held-weakly" tests.

See: tc39#2850
  • Loading branch information
ptomato committed Sep 28, 2022
1 parent 78017f6 commit 6784859
Show file tree
Hide file tree
Showing 19 changed files with 624 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,26 @@ info: |
2. Perform ? RequireInternalSlot(_finalizationRegistry_, [[Cells]]).
3. If CanBeHeldWeakly(_target_) is *false*, throw a *TypeError* exception.
4. If SameValue(_target_, _heldValue_) is *true*, throw a *TypeError* exception.
features: [FinalizationRegistry]
features: [FinalizationRegistry, Symbol]
---*/

var finalizationRegistry = new FinalizationRegistry(function() {});

var target = {};
assert.throws(TypeError, () => finalizationRegistry.register(target, target));

// The following will fail regardless of whether the implementation supports
// Symbols as weak values. Step 3 if no, Step 4 if yes.

var symbolTarget = Symbol('a description');
assert.throws(
TypeError,
() => finalizationRegistry.register(symbolTarget, symbolTarget),
'target and heldValue are the same regular symbol'
);

assert.throws(
TypeError,
() => finalizationRegistry.register(Symbol.hasInstance, Symbol.hasInstance),
'target and heldValue are the same well-known symbol'
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-finalization-registry.prototype.register
description: Return undefined after registering a Symbol
info: |
FinalizationRegistry.prototype.register ( _target_ , _heldValue_ [, _unregisterToken_ ] )
8. Return *undefined*.
features: [FinalizationRegistry, Symbol, symbols-as-weakmap-keys]
---*/

var fn = function() {};
var reg = new FinalizationRegistry(fn);

var target = Symbol('a description');
assert.sameValue(reg.register(target), undefined, 'Register a regular symbol');
assert.sameValue(reg.register(target), undefined, 'Register the same symbol again');
assert.sameValue(reg.register(target), undefined, 'Register the same symbol a third time');

assert.sameValue(
reg.register(Symbol('a description')),
undefined,
'Register another symbol with the same description'
);
assert.sameValue(
reg.register(Symbol('a different description')),
undefined,
'Register another symbol with another description'
);

assert.sameValue(
reg.register(target, undefined, Symbol('unregister token')),
undefined,
'Register a regular symbol with a symbol unregister token'
);
assert.sameValue(
reg.register(target, undefined, target),
undefined,
'Register a regular symbol with itself as the unregister token'
);

assert.sameValue(
reg.register(target, undefined, undefined),
undefined,
'Register a regular symbol with explicit undefined unregister token'
);

assert.sameValue(reg.register(Symbol.hasInstance), undefined, 'Register a well-known symbol');
assert.sameValue(reg.register(Symbol.hasInstance), undefined, 'Register the same well-known symbol again');
assert.sameValue(reg.register(Symbol.hasInstance), undefined, 'Register the same well-known symbol a third time');

assert.sameValue(
reg.register(target, undefined, Symbol.hasInstance),
undefined,
'Register a regular symbol with a well-known symbol unregister token'
);
assert.sameValue(
reg.register(Symbol.hasInstance, undefined, Symbol.iterator),
undefined,
'Register a well-known symbol with a different well-known symbol as unregister token'
);
assert.sameValue(
reg.register(Symbol.hasInstance, undefined, Symbol.hasInstance),
undefined,
'Register a well-known symbol with itself as the unregister token'
);

assert.sameValue(
reg.register(Symbol.hasInstance, undefined, undefined),
undefined,
'Register a well-known symbol with explicit undefined unregister token'
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-finalization-registry.prototype.unregister
description: >
Return boolean values indicating unregistering of values with Symbol token
info: |
FinalizationRegistry.prototype.unregister ( _unregisterToken_ )
4. Let _removed_ be *false*.
5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] }
_cell_ of _finalizationRegistry_.[[Cells]], do
a. If _cell_.[[UnregisterToken]] is not ~empty~ and
SameValue(_cell_.[[UnregisterToken]], _unregisterToken_) is *true*, then
i. Remove _cell_ from _finalizationRegistry_.[[Cells]].
ii. Set _removed_ to *true*.
6. Return _removed_.
features: [FinalizationRegistry, Symbol, symbols-as-weakmap-keys]
---*/

var fn = function() {};
var reg = new FinalizationRegistry(fn);

var target1 = {};
var target2 = {};
var target3 = {};
var token = Symbol('unregister');

assert.sameValue(reg.unregister(token), false, 'unregistering regular symbol from empty registry');
assert.sameValue(reg.unregister(Symbol.hasInstance), false, 'unregistering well-known symbol from empty registry');

reg.register(target1, undefined, token);
reg.register(target1, undefined, token); // Repeat registering on purpose
reg.register(target2, undefined, Symbol.hasInstance);
reg.register(target3, undefined, Symbol.hasInstance);

assert.sameValue(reg.unregister(token), true, 'unregistering regular symbol from finalization registry');
assert.sameValue(reg.unregister(token), false, 'unregistering regular symbol again from finalization registry');
assert.sameValue(
reg.unregister(Symbol.hasInstance),
true,
'unregistering well-known symbol to remove target2 and target3'
);
assert.sameValue(
reg.unregister(Symbol.hasInstance),
false,
'unregistering well-known again from finalization registry'
);

// Notice these assertions take advantage of adding targets previously added
// with a token, but now they have no token so it won't be used to remove them.
reg.register(target1, token); // heldValue, not unregisterToken
reg.register(target2, Symbol.hasInstance); // heldValue, not unregisterToken
reg.register(target3);

assert.sameValue(reg.unregister(token), false, 'nothing to remove with regular symbol unregister token');
assert.sameValue(
reg.unregister(Symbol.hasInstance),
false,
'nothing to remove with well-known symbol unregister token'
);
45 changes: 45 additions & 0 deletions test/built-ins/WeakMap/iterable-with-symbol-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-weakmap-iterable
description: >
Returns the new WeakMap adding entries from the iterable parameter, with
Symbol keys.
info: |
WeakMap ( [ _iterable_ ] )
5. Let _adder_ be ? Get(_map_, *"set"*).
6. Return ? AddEntriesFromIterable(_map_, _iterable_, _adder_).
AddEntriesFromIterable:
3. Repeat,
i. Let _status_ be Completion(Call(_adder_, _target_, « _k_, _v_ »)).
WeakMap.prototype.set( _key_, _value_ ):
6. Let _p_ be the Record {[[Key]]: _key_, [[Value]]: _value_}.
7. Append _p_ as the last element of _entries_.
features: [Symbol, WeakMap, symbols-as-weakmap-keys]
---*/

var sym = Symbol('a description');
var results = [];
var set = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value) {
results.push({
_this: this,
key: key,
value: value
});
return set.call(this, key, value);
};
var map = new WeakMap([
[sym, 42],
[Symbol.hasInstance, 43],
]);

assert.sameValue(results.length, 2, 'Called set() for each entry');
assert.sameValue(results[0].key, sym, 'Adds object in order - first key, regular symbol');
assert.sameValue(results[0].value, 42, 'Adds object in order - first value');
assert.sameValue(results[0]._this, map, 'Adds object in order - this');
assert.sameValue(results[1].key, Symbol.hasInstance, 'Adds object in order - second key, well-known symbol');
assert.sameValue(results[1].value, 43, 'Adds object in order - second value');
assert.sameValue(results[1]._this, map, 'Adds object in order - this');
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-weakmap.prototype.delete
description: >
Delete an entry with a Symbol key, added from initial iterable.
info: |
WeakMap.prototype.delete ( _key_ )
3. Let _entries_ be the List that is _M_.[[WeakMapData]].
4. If CanBeHeldWeakly(_key_) is *false*, return *false*.
5. For each Record {[[Key]], [[Value]]} _p_ of _entries_, do
a. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _key_) is
*true*, then
i. Set _p_.[[Key]] to ~empty~.
ii. Set _p_.[[Value]] to ~empty~.
iii. Return *true*.
features: [Symbol, WeakMap, symbols-as-weakmap-keys]
---*/

var foo = Symbol('a description');
var bar = Symbol('a description');
var map = new WeakMap([
[foo, 42],
[bar, 43],
[Symbol.hasInstance, 44],
]);

var result = map.delete(foo);
assert(!map.has(foo), 'Regular symbol was deleted from map');
assert(map.has(bar), "Symbols with the same description don't alias to each other");
assert.sameValue(result, true, 'delete() returns true for regular symbol');

result = map.delete(Symbol.hasInstance);
assert(!map.has(Symbol.hasInstance), 'Well-known symbol was deleted from map');
assert.sameValue(result, true, 'delete() returns true for well-known symbol');
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-weakmap.prototype.delete
description: >
Delete an entry with a Symbol key.
info: |
WeakMap.prototype.delete ( _key_ )
3. Let _entries_ be the List that is _M_.[[WeakMapData]].
4. If CanBeHeldWeakly(_key_) is *false*, return *false*.
5. For each Record {[[Key]], [[Value]]} _p_ of _entries_, do
a. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _key_) is
*true*, then
i. Set _p_.[[Key]] to ~empty~.
ii. Set _p_.[[Value]] to ~empty~.
iii. Return *true*.
features: [Symbol, WeakMap, symbols-as-weakmap-keys]
---*/

var foo = Symbol('a description');
var bar = Symbol('a description');
var map = new WeakMap();

map.set(foo, 42);
map.set(bar, 43);

var result = map.delete(foo);

assert(!map.has(foo), 'Regular symbol was deleted from map');
assert(map.has(bar), "Symbols with the same description don't alias each other");
assert.sameValue(result, true, 'delete() returns true for regular symbol');

map.set(Symbol.hasInstance, 44);

result = map.delete(Symbol.hasInstance);

assert(!map.has(Symbol.hasInstance), 'Well-known symbol was deleted from map');
assert.sameValue(result, true, 'delete() returns true for well-known symbol');
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-weakmap.prototype.delete
description: >
Return false if a Symbol key is not in the WeakMap.
info: |
WeakMap.prototype.delete ( _key_ )
6. Return *false*.
features: [Symbol, WeakMap, symbols-as-weakmap-keys]
---*/

var map = new WeakMap();
var foo = Symbol('a description');
var bar = Symbol('a description');
var baz = Symbol('another description');

map.set(foo, 42);

assert.sameValue(map.delete(baz), false, 'Regular symbol key not present')
assert.sameValue(map.delete(bar), false, "Symbols with the same description don't alias to each other");
assert.sameValue(map.delete(Symbol.hasInstance), false, 'Well-known symbol key not present');
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-weakmap.prototype.get
description: >
Returns undefined when a Symbol key is not on the WeakMap object.
info: |
WeakMap.prototype.get ( _key_ )
3. Let _entries_ be the List that is _M_.[[WeakMapData]].
4. If CanBeHeldWeakly(_key_) is *false*, return *undefined*.
5. For each Record {[[Key]], [[Value]]} _p_ of _entries_, do
a. If _p_.[[Key]] is not empty and SameValue(_p_.[[Key]], _key_) is *true*,
return _p_.[[Value]].
6. Return *undefined*.
features: [Symbol, WeakMap, symbols-as-weakmap-keys]
---*/

var map = new WeakMap();
var key = Symbol('a description');

assert.sameValue(map.get(key), undefined, 'returns undefined for regular symbol on initially empty map');
assert.sameValue(
map.get(Symbol.hasInstance),
undefined,
'returns undefined for well-known symbol on initially empty map'
);

map.set(key, 1);
map.set(Symbol.hasInstance, 2);
map.delete(key);
map.delete(Symbol.hasInstance);

assert.sameValue(map.get(key), undefined, 'returns undefined for deleted regular symbol');
assert.sameValue(map.get(Symbol.hasInstance), undefined, 'returns undefined for deleted well-known symbol');
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-weakmap.prototype.get
description: >
Returns the value from the specified Symbol key
info: |
WeakMap.prototype.get ( _key_ )
3. Let _entries_ be the List that is _M_.[[WeakMapData]].
4. If CanBeHeldWeakly(_key_) is *false*, return *undefined*.
5. For each Record {[[Key]], [[Value]]} _p_ of _entries_, do
a. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _key_) is
*true*, return _p_.[[Value]].
features: [Symbol, WeakMap, symbols-as-weakmap-keys]
---*/

var foo = Symbol('a description');
var bar = Symbol('a description');
var baz = Symbol('different description');
var map = new WeakMap([
[foo, 0],
]);

assert.sameValue(map.get(foo), 0, 'Regular symbol as key, added in constructor');

map.set(bar, 1);
map.set(baz, 2);
assert.sameValue(map.get(baz), 2, 'Regular symbol as key, added with set()');
assert.sameValue(map.get(bar), 1, "Symbols with the same description don't overwrite each other");

map.set(Symbol.hasInstance, 3);
assert.sameValue(map.get(Symbol.hasInstance), 3, 'Well-known symbol as key');
Loading

0 comments on commit 6784859

Please sign in to comment.