Skip to content

Commit 4ac2a13

Browse files
committed
test(lib/es2021): Add test for generic FinalizationRegistry
1 parent 4fc566f commit 4ac2a13

File tree

4 files changed

+1037
-0
lines changed

4 files changed

+1037
-0
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
//// [esNextWeakRefs_IterableWeakMap.ts]
2+
/** `static #cleanup` */
3+
const IterableWeakMap_cleanup = ({ ref, set }: {
4+
readonly ref: WeakRef<object>;
5+
readonly set: Set<WeakRef<object>>;
6+
}) => {
7+
set.delete(ref);
8+
};
9+
10+
// Based on: https://github.com/tc39/proposal-weakrefs/blob/master/README.md#iterable-weakmaps
11+
export class IterableWeakMap<K extends object, V> implements WeakMap<K, V> {
12+
declare readonly [Symbol.toStringTag]: "IterableWeakMap";
13+
14+
#weakMap = new WeakMap<K, { readonly ref: WeakRef<K>; value: V }>();
15+
#refSet = new Set<WeakRef<K>>();
16+
#finalizationGroup = new FinalizationRegistry(IterableWeakMap_cleanup);
17+
18+
constructor(iterable: Iterable<[key: K, value: V]> | null = null) {
19+
if (iterable !== null) {
20+
for (const { 0: key, 1: value } of iterable) {
21+
this.set(key, value);
22+
}
23+
}
24+
}
25+
26+
set(key: K, value: V): this {
27+
const entry = this.#weakMap.get(key);
28+
if (entry !== undefined) {
29+
entry.value = value;
30+
} else {
31+
const ref = new WeakRef(key);
32+
33+
this.#weakMap.set(key, { ref, value });
34+
this.#refSet.add(ref);
35+
this.#finalizationGroup.register(key, {
36+
set: this.#refSet,
37+
ref,
38+
}, ref);
39+
}
40+
return this;
41+
}
42+
43+
has(key: K): boolean {
44+
return this.#weakMap.has(key);
45+
}
46+
47+
get(key: K): V | undefined {
48+
return this.#weakMap.get(key)?.value;
49+
}
50+
51+
delete(key: K): boolean {
52+
const entry = this.#weakMap.get(key);
53+
if (entry === undefined) {
54+
return false;
55+
}
56+
57+
const { ref } = entry;
58+
this.#weakMap.delete(key);
59+
this.#refSet.delete(ref);
60+
this.#finalizationGroup.unregister(ref);
61+
return true;
62+
}
63+
64+
declare [Symbol.iterator]: this["entries"];
65+
*entries(): Generator<[key: K, value: V], void> {
66+
for (const ref of this.#refSet) {
67+
const key = ref.deref();
68+
if (key === undefined) continue;
69+
const { value } = this.#weakMap.get(key)!;
70+
yield [key, value];
71+
}
72+
}
73+
74+
*keys() {
75+
for (const { 0: key } of this) {
76+
yield key;
77+
}
78+
}
79+
80+
*values() {
81+
for (const { 1: value } of this) {
82+
yield value;
83+
}
84+
}
85+
}
86+
87+
Object.defineProperties(IterableWeakMap.prototype, {
88+
[Symbol.iterator]: {
89+
configurable: true,
90+
enumerable: false,
91+
writable: true,
92+
value: Object.getOwnPropertyDescriptor(
93+
IterableWeakMap.prototype,
94+
"entries",
95+
)!.value,
96+
},
97+
[Symbol.toStringTag]: {
98+
configurable: true,
99+
enumerable: false,
100+
writable: false,
101+
value: "IterableWeakMap",
102+
},
103+
});
104+
105+
106+
//// [esNextWeakRefs_IterableWeakMap.js]
107+
/** `static #cleanup` */
108+
const IterableWeakMap_cleanup = ({ ref, set }) => {
109+
set.delete(ref);
110+
};
111+
// Based on: https://github.com/tc39/proposal-weakrefs/blob/master/README.md#iterable-weakmaps
112+
export class IterableWeakMap {
113+
#weakMap = new WeakMap();
114+
#refSet = new Set();
115+
#finalizationGroup = new FinalizationRegistry(IterableWeakMap_cleanup);
116+
constructor(iterable = null) {
117+
if (iterable !== null) {
118+
for (const { 0: key, 1: value } of iterable) {
119+
this.set(key, value);
120+
}
121+
}
122+
}
123+
set(key, value) {
124+
const entry = this.#weakMap.get(key);
125+
if (entry !== undefined) {
126+
entry.value = value;
127+
}
128+
else {
129+
const ref = new WeakRef(key);
130+
this.#weakMap.set(key, { ref, value });
131+
this.#refSet.add(ref);
132+
this.#finalizationGroup.register(key, {
133+
set: this.#refSet,
134+
ref,
135+
}, ref);
136+
}
137+
return this;
138+
}
139+
has(key) {
140+
return this.#weakMap.has(key);
141+
}
142+
get(key) {
143+
return this.#weakMap.get(key)?.value;
144+
}
145+
delete(key) {
146+
const entry = this.#weakMap.get(key);
147+
if (entry === undefined) {
148+
return false;
149+
}
150+
const { ref } = entry;
151+
this.#weakMap.delete(key);
152+
this.#refSet.delete(ref);
153+
this.#finalizationGroup.unregister(ref);
154+
return true;
155+
}
156+
*entries() {
157+
for (const ref of this.#refSet) {
158+
const key = ref.deref();
159+
if (key === undefined)
160+
continue;
161+
const { value } = this.#weakMap.get(key);
162+
yield [key, value];
163+
}
164+
}
165+
*keys() {
166+
for (const { 0: key } of this) {
167+
yield key;
168+
}
169+
}
170+
*values() {
171+
for (const { 1: value } of this) {
172+
yield value;
173+
}
174+
}
175+
}
176+
Object.defineProperties(IterableWeakMap.prototype, {
177+
[Symbol.iterator]: {
178+
configurable: true,
179+
enumerable: false,
180+
writable: true,
181+
value: Object.getOwnPropertyDescriptor(IterableWeakMap.prototype, "entries").value,
182+
},
183+
[Symbol.toStringTag]: {
184+
configurable: true,
185+
enumerable: false,
186+
writable: false,
187+
value: "IterableWeakMap",
188+
},
189+
});

0 commit comments

Comments
 (0)