export default class WeakKeyMap {
constructor(items = []) {
this._wrapperForKey = new WeakMap;
this._valueForWrapper = new Map;
for (let [key, value] of items)
this.set(key, value);
}
// Public
get size() {
let size = 0;
for (let wrapper of this._valueForWrapper.keys()) {
if (wrapper.deref())
++size;
}
return size;
}
has(key) {
let result = this._wrapperForKey.has(key);
console.assert(Array.from(this._valueForWrapper.keys()).some((wrapper) => wrapper.deref() === key) === result, this, key);
return result;
}
get(key) {
let wrapper = this._wrapperForKey.has(key);
if (!wrapper)
return undefined;
return this._valueForWrapper.get(wrapper);
}
set(key, value) {
console.assert(typeof key === "object", key);
console.assert(key !== null, key);
this.delete(key);
let wrapper = new WeakRef(key);
this._wrapperForKey.set(key, wrapper);
this._valueForWrapper.set(wrapper, value);
this._finalizationRegistry.register(key, {weakThis: new WeakRef(this), wrapper}, wrapper);
}
delete(key) {
let wrapper = this._wrapperForKey.get(key);
if (!wrapper)
return false;
let keyDeleted = this._wrapperForKey.delete(key);
console.assert(keyDeleted, this, key);
let wrapperDeleted = this._valueForWrapper.delete(wrapper);
console.assert(wrapperDeleted, this, key);
this._finalizationRegistry.unregister(wrapper);
console.assert(wrapper.deref() === key, this, key);
return true;
}
take(key) {
let wrapper = this._wrapperForKey.get(key);
if (!wrapper)
return undefined;
let value = this._valueForWrapper.get(value);
let keyDeleted = this._wrapperForKey.delete(key);
console.assert(keyDeleted, this, key);
let wrapperDeleted = this._valueForWrapper.delete(wrapper);
console.assert(wrapperDeleted, this, key);
this._finalizationRegistry.unregister(wrapper);
console.assert(wrapper.deref() === key, this, key);
return value;
}
clear() {
for (let wrapper of this._valueForWrapper.keys()) {
this._wrapperForKey.delete(wrapper);
this._finalizationRegistry.unregister(wrapper);
}
this._valueForWrapper.clear();
}
*keys() {
for (let entry of this.entries())
yield entry[0];
}
*values() {
for (let entry of this.entries())
yield entry[1];
}
*entries() {
for (let [wrapper, value] of this._valueForWrapper) {
let key = wrapper.deref();
console.assert(!key === !this._wrapperForKey.has(key), this, key);
if (key)
yield [key, value];
}
}
[Symbol.iterator]() {
return this.entries();
}
toJSON() {
return Array.from(this);
}
// Private
get _finalizationRegistry() {
return WeakKeyMap._finalizationRegistry ??= new FinalizationRegistry(function(heldValue) {
heldValue.weakThis.deref()?._valueForWrapper.delete(heldValue.wrapper);
});
}
}