Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/handles-inl.h"
6 : #include "src/heap/factory-inl.h"
7 : #include "src/isolate.h"
8 : #include "src/microtask-queue.h"
9 : #include "src/objects/js-objects.h"
10 : #include "src/objects/js-weak-refs-inl.h"
11 : #include "test/cctest/cctest.h"
12 : #include "test/cctest/heap/heap-utils.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 : namespace {
18 :
19 55 : Handle<JSFinalizationGroup> ConstructJSFinalizationGroup(Isolate* isolate) {
20 : Factory* factory = isolate->factory();
21 : Handle<String> finalization_group_name =
22 55 : factory->NewStringFromStaticChars("FinalizationGroup");
23 : Handle<Object> global =
24 165 : handle(isolate->native_context()->global_object(), isolate);
25 : Handle<JSFunction> finalization_group_fun = Handle<JSFunction>::cast(
26 110 : Object::GetProperty(isolate, global, finalization_group_name)
27 : .ToHandleChecked());
28 : auto finalization_group = Handle<JSFinalizationGroup>::cast(
29 110 : JSObject::New(finalization_group_fun, finalization_group_fun,
30 55 : Handle<AllocationSite>::null())
31 : .ToHandleChecked());
32 : #ifdef VERIFY_HEAP
33 : finalization_group->JSFinalizationGroupVerify(isolate);
34 : #endif // VERIFY_HEAP
35 55 : return finalization_group;
36 : }
37 :
38 20 : Handle<JSWeakRef> ConstructJSWeakRef(Handle<JSReceiver> target,
39 : Isolate* isolate) {
40 : Factory* factory = isolate->factory();
41 : Handle<String> weak_ref_name = factory->WeakRef_string();
42 : Handle<Object> global =
43 60 : handle(isolate->native_context()->global_object(), isolate);
44 : Handle<JSFunction> weak_ref_fun = Handle<JSFunction>::cast(
45 40 : Object::GetProperty(isolate, global, weak_ref_name).ToHandleChecked());
46 : auto weak_ref = Handle<JSWeakRef>::cast(
47 40 : JSObject::New(weak_ref_fun, weak_ref_fun, Handle<AllocationSite>::null())
48 : .ToHandleChecked());
49 40 : weak_ref->set_target(*target);
50 : #ifdef VERIFY_HEAP
51 : weak_ref->JSWeakRefVerify(isolate);
52 : #endif // VERIFY_HEAP
53 20 : return weak_ref;
54 : }
55 :
56 50 : Handle<JSObject> CreateKey(const char* key_prop_value, Isolate* isolate) {
57 : Factory* factory = isolate->factory();
58 50 : Handle<String> key_string = factory->NewStringFromStaticChars("key_string");
59 : Handle<JSObject> key =
60 50 : isolate->factory()->NewJSObject(isolate->object_function());
61 100 : JSObject::AddProperty(isolate, key, key_string,
62 : factory->NewStringFromAsciiChecked(key_prop_value),
63 50 : NONE);
64 50 : return key;
65 : }
66 :
67 120 : Handle<WeakCell> FinalizationGroupRegister(
68 : Handle<JSFinalizationGroup> finalization_group, Handle<JSObject> target,
69 : Handle<Object> holdings, Handle<Object> key, Isolate* isolate) {
70 120 : JSFinalizationGroup::Register(finalization_group, target, holdings, key,
71 120 : isolate);
72 120 : CHECK(finalization_group->active_cells()->IsWeakCell());
73 : Handle<WeakCell> weak_cell =
74 : handle(WeakCell::cast(finalization_group->active_cells()), isolate);
75 : #ifdef VERIFY_HEAP
76 : weak_cell->WeakCellVerify(isolate);
77 : #endif // VERIFY_HEAP
78 120 : return weak_cell;
79 : }
80 :
81 30 : Handle<WeakCell> FinalizationGroupRegister(
82 : Handle<JSFinalizationGroup> finalization_group, Handle<JSObject> target,
83 : Isolate* isolate) {
84 : Handle<Object> undefined =
85 : handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
86 : return FinalizationGroupRegister(finalization_group, target, undefined,
87 30 : undefined, isolate);
88 : }
89 :
90 55 : void NullifyWeakCell(Handle<WeakCell> weak_cell, Isolate* isolate) {
91 : auto empty_func = [](HeapObject object, ObjectSlot slot, Object target) {};
92 110 : weak_cell->Nullify(isolate, empty_func);
93 : #ifdef VERIFY_HEAP
94 : weak_cell->WeakCellVerify(isolate);
95 : #endif // VERIFY_HEAP
96 55 : }
97 :
98 : // Usage: VerifyWeakCellChain(isolate, list_head, n, cell1, cell2, ..., celln);
99 : // verifies that list_head == cell1 and cell1, cell2, ..., celln. form a list.
100 100 : void VerifyWeakCellChain(Isolate* isolate, Object list_head, int n_args, ...) {
101 100 : CHECK_GE(n_args, 0);
102 :
103 : va_list args;
104 100 : va_start(args, n_args);
105 :
106 100 : if (n_args == 0) {
107 : // Verify empty list
108 60 : CHECK(list_head->IsUndefined(isolate));
109 : } else {
110 40 : WeakCell current = WeakCell::cast(Object(va_arg(args, Address)));
111 40 : CHECK_EQ(current, list_head);
112 40 : CHECK(current->prev()->IsUndefined(isolate));
113 :
114 120 : for (int i = 1; i < n_args; i++) {
115 40 : WeakCell next = WeakCell::cast(Object(va_arg(args, Address)));
116 40 : CHECK_EQ(current->next(), next);
117 40 : CHECK_EQ(next->prev(), current);
118 : current = next;
119 : }
120 40 : CHECK(current->next()->IsUndefined(isolate));
121 : }
122 100 : va_end(args);
123 100 : }
124 :
125 : // Like VerifyWeakCellChain but verifies the chain created with key_list_prev
126 : // and key_list_next instead of prev and next.
127 110 : void VerifyWeakCellKeyChain(Isolate* isolate, Object list_head, int n_args,
128 : ...) {
129 110 : CHECK_GE(n_args, 0);
130 :
131 : va_list args;
132 110 : va_start(args, n_args);
133 :
134 110 : if (n_args == 0) {
135 : // Verify empty list
136 40 : CHECK(list_head->IsTheHole(isolate));
137 : } else {
138 70 : WeakCell current = WeakCell::cast(Object(va_arg(args, Address)));
139 70 : CHECK_EQ(current, list_head);
140 70 : CHECK(current->key_list_prev()->IsUndefined(isolate));
141 :
142 150 : for (int i = 1; i < n_args; i++) {
143 40 : WeakCell next = WeakCell::cast(Object(va_arg(args, Address)));
144 40 : CHECK_EQ(current->key_list_next(), next);
145 40 : CHECK_EQ(next->key_list_prev(), current);
146 : current = next;
147 : }
148 70 : CHECK(current->key_list_next()->IsUndefined(isolate));
149 : }
150 110 : va_end(args);
151 110 : }
152 :
153 : } // namespace
154 :
155 26644 : TEST(TestRegister) {
156 5 : FLAG_harmony_weak_refs = true;
157 5 : CcTest::InitializeVM();
158 5 : LocalContext context;
159 : Isolate* isolate = CcTest::i_isolate();
160 : HandleScope outer_scope(isolate);
161 : Handle<JSFinalizationGroup> finalization_group =
162 5 : ConstructJSFinalizationGroup(isolate);
163 : Handle<JSObject> js_object =
164 5 : isolate->factory()->NewJSObject(isolate->object_function());
165 :
166 : // Register a weak reference and verify internal data structures.
167 : Handle<WeakCell> weak_cell1 =
168 5 : FinalizationGroupRegister(finalization_group, js_object, isolate);
169 :
170 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 1,
171 5 : *weak_cell1);
172 5 : CHECK(weak_cell1->key_list_prev()->IsUndefined(isolate));
173 5 : CHECK(weak_cell1->key_list_next()->IsUndefined(isolate));
174 :
175 5 : CHECK(finalization_group->cleared_cells()->IsUndefined(isolate));
176 :
177 : // No key was used during registration, key-based map stays uninitialized.
178 5 : CHECK(finalization_group->key_map()->IsUndefined(isolate));
179 :
180 : // Register another weak reference and verify internal data structures.
181 : Handle<WeakCell> weak_cell2 =
182 5 : FinalizationGroupRegister(finalization_group, js_object, isolate);
183 :
184 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 2,
185 5 : *weak_cell2, *weak_cell1);
186 5 : CHECK(weak_cell2->key_list_prev()->IsUndefined(isolate));
187 5 : CHECK(weak_cell2->key_list_next()->IsUndefined(isolate));
188 :
189 5 : CHECK(finalization_group->cleared_cells()->IsUndefined(isolate));
190 5 : CHECK(finalization_group->key_map()->IsUndefined(isolate));
191 5 : }
192 :
193 26644 : TEST(TestRegisterWithKey) {
194 5 : FLAG_harmony_weak_refs = true;
195 5 : CcTest::InitializeVM();
196 5 : LocalContext context;
197 : Isolate* isolate = CcTest::i_isolate();
198 : HandleScope outer_scope(isolate);
199 : Handle<JSFinalizationGroup> finalization_group =
200 5 : ConstructJSFinalizationGroup(isolate);
201 : Handle<JSObject> js_object =
202 5 : isolate->factory()->NewJSObject(isolate->object_function());
203 :
204 5 : Handle<JSObject> key1 = CreateKey("key1", isolate);
205 5 : Handle<JSObject> key2 = CreateKey("key2", isolate);
206 : Handle<Object> undefined =
207 : handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
208 :
209 : // Register a weak reference with a key and verify internal data structures.
210 : Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
211 5 : finalization_group, js_object, undefined, key1, isolate);
212 :
213 : {
214 5 : CHECK(finalization_group->key_map()->IsObjectHashTable());
215 : Handle<ObjectHashTable> key_map =
216 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
217 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 1, *weak_cell1);
218 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 0);
219 : }
220 :
221 : // Register another weak reference with a different key and verify internal
222 : // data structures.
223 : Handle<WeakCell> weak_cell2 = FinalizationGroupRegister(
224 5 : finalization_group, js_object, undefined, key2, isolate);
225 :
226 : {
227 5 : CHECK(finalization_group->key_map()->IsObjectHashTable());
228 : Handle<ObjectHashTable> key_map =
229 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
230 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 1, *weak_cell1);
231 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 1, *weak_cell2);
232 : }
233 :
234 : // Register another weak reference with key1 and verify internal data
235 : // structures.
236 : Handle<WeakCell> weak_cell3 = FinalizationGroupRegister(
237 5 : finalization_group, js_object, undefined, key1, isolate);
238 :
239 : {
240 5 : CHECK(finalization_group->key_map()->IsObjectHashTable());
241 : Handle<ObjectHashTable> key_map =
242 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
243 10 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell3,
244 5 : *weak_cell1);
245 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 1, *weak_cell2);
246 : }
247 5 : }
248 :
249 26644 : TEST(TestWeakCellNullify1) {
250 5 : FLAG_harmony_weak_refs = true;
251 5 : CcTest::InitializeVM();
252 5 : LocalContext context;
253 : Isolate* isolate = CcTest::i_isolate();
254 : HandleScope outer_scope(isolate);
255 : Handle<JSFinalizationGroup> finalization_group =
256 5 : ConstructJSFinalizationGroup(isolate);
257 : Handle<JSObject> js_object =
258 5 : isolate->factory()->NewJSObject(isolate->object_function());
259 :
260 : Handle<WeakCell> weak_cell1 =
261 5 : FinalizationGroupRegister(finalization_group, js_object, isolate);
262 : Handle<WeakCell> weak_cell2 =
263 5 : FinalizationGroupRegister(finalization_group, js_object, isolate);
264 :
265 : // Nullify the first WeakCell and verify internal data structures.
266 5 : NullifyWeakCell(weak_cell1, isolate);
267 5 : CHECK_EQ(finalization_group->active_cells(), *weak_cell2);
268 5 : CHECK(weak_cell2->prev()->IsUndefined(isolate));
269 5 : CHECK(weak_cell2->next()->IsUndefined(isolate));
270 5 : CHECK_EQ(finalization_group->cleared_cells(), *weak_cell1);
271 5 : CHECK(weak_cell1->prev()->IsUndefined(isolate));
272 5 : CHECK(weak_cell1->next()->IsUndefined(isolate));
273 :
274 : // Nullify the second WeakCell and verify internal data structures.
275 5 : NullifyWeakCell(weak_cell2, isolate);
276 5 : CHECK(finalization_group->active_cells()->IsUndefined(isolate));
277 5 : CHECK_EQ(finalization_group->cleared_cells(), *weak_cell2);
278 5 : CHECK_EQ(weak_cell2->next(), *weak_cell1);
279 5 : CHECK(weak_cell2->prev()->IsUndefined(isolate));
280 5 : CHECK_EQ(weak_cell1->prev(), *weak_cell2);
281 5 : CHECK(weak_cell1->next()->IsUndefined(isolate));
282 5 : }
283 :
284 26644 : TEST(TestWeakCellNullify2) {
285 5 : FLAG_harmony_weak_refs = true;
286 5 : CcTest::InitializeVM();
287 5 : LocalContext context;
288 : Isolate* isolate = CcTest::i_isolate();
289 : HandleScope outer_scope(isolate);
290 : Handle<JSFinalizationGroup> finalization_group =
291 5 : ConstructJSFinalizationGroup(isolate);
292 : Handle<JSObject> js_object =
293 5 : isolate->factory()->NewJSObject(isolate->object_function());
294 :
295 : Handle<WeakCell> weak_cell1 =
296 5 : FinalizationGroupRegister(finalization_group, js_object, isolate);
297 : Handle<WeakCell> weak_cell2 =
298 5 : FinalizationGroupRegister(finalization_group, js_object, isolate);
299 :
300 : // Like TestWeakCellNullify1 but nullify the WeakCells in opposite order.
301 5 : NullifyWeakCell(weak_cell2, isolate);
302 5 : CHECK_EQ(finalization_group->active_cells(), *weak_cell1);
303 5 : CHECK(weak_cell1->prev()->IsUndefined(isolate));
304 5 : CHECK(weak_cell1->next()->IsUndefined(isolate));
305 5 : CHECK_EQ(finalization_group->cleared_cells(), *weak_cell2);
306 5 : CHECK(weak_cell2->prev()->IsUndefined(isolate));
307 5 : CHECK(weak_cell2->next()->IsUndefined(isolate));
308 :
309 5 : NullifyWeakCell(weak_cell1, isolate);
310 5 : CHECK(finalization_group->active_cells()->IsUndefined(isolate));
311 5 : CHECK_EQ(finalization_group->cleared_cells(), *weak_cell1);
312 5 : CHECK_EQ(weak_cell1->next(), *weak_cell2);
313 5 : CHECK(weak_cell1->prev()->IsUndefined(isolate));
314 5 : CHECK_EQ(weak_cell2->prev(), *weak_cell1);
315 5 : CHECK(weak_cell2->next()->IsUndefined(isolate));
316 5 : }
317 :
318 26644 : TEST(TestJSFinalizationGroupPopClearedCellHoldings1) {
319 5 : FLAG_harmony_weak_refs = true;
320 5 : CcTest::InitializeVM();
321 5 : LocalContext context;
322 : Isolate* isolate = CcTest::i_isolate();
323 : Factory* factory = isolate->factory();
324 : HandleScope outer_scope(isolate);
325 : Handle<JSFinalizationGroup> finalization_group =
326 5 : ConstructJSFinalizationGroup(isolate);
327 : Handle<JSObject> js_object =
328 5 : isolate->factory()->NewJSObject(isolate->object_function());
329 : Handle<Object> undefined =
330 : handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
331 :
332 5 : Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
333 : Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
334 5 : finalization_group, js_object, holdings1, undefined, isolate);
335 5 : Handle<Object> holdings2 = factory->NewStringFromAsciiChecked("holdings2");
336 : Handle<WeakCell> weak_cell2 = FinalizationGroupRegister(
337 5 : finalization_group, js_object, holdings2, undefined, isolate);
338 5 : Handle<Object> holdings3 = factory->NewStringFromAsciiChecked("holdings3");
339 : Handle<WeakCell> weak_cell3 = FinalizationGroupRegister(
340 5 : finalization_group, js_object, holdings3, undefined, isolate);
341 :
342 5 : NullifyWeakCell(weak_cell2, isolate);
343 5 : NullifyWeakCell(weak_cell3, isolate);
344 :
345 5 : CHECK(finalization_group->NeedsCleanup());
346 : Object cleared1 =
347 5 : JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
348 5 : CHECK_EQ(cleared1, *holdings3);
349 5 : CHECK(weak_cell3->prev()->IsUndefined(isolate));
350 5 : CHECK(weak_cell3->next()->IsUndefined(isolate));
351 :
352 5 : CHECK(finalization_group->NeedsCleanup());
353 : Object cleared2 =
354 5 : JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
355 5 : CHECK_EQ(cleared2, *holdings2);
356 5 : CHECK(weak_cell2->prev()->IsUndefined(isolate));
357 5 : CHECK(weak_cell2->next()->IsUndefined(isolate));
358 :
359 5 : CHECK(!finalization_group->NeedsCleanup());
360 :
361 5 : NullifyWeakCell(weak_cell1, isolate);
362 :
363 5 : CHECK(finalization_group->NeedsCleanup());
364 : Object cleared3 =
365 5 : JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
366 5 : CHECK_EQ(cleared3, *holdings1);
367 5 : CHECK(weak_cell1->prev()->IsUndefined(isolate));
368 5 : CHECK(weak_cell1->next()->IsUndefined(isolate));
369 :
370 5 : CHECK(!finalization_group->NeedsCleanup());
371 5 : CHECK(finalization_group->active_cells()->IsUndefined(isolate));
372 5 : CHECK(finalization_group->cleared_cells()->IsUndefined(isolate));
373 5 : }
374 :
375 26644 : TEST(TestJSFinalizationGroupPopClearedCellHoldings2) {
376 : // Test that when all WeakCells for a key are popped, the key is removed from
377 : // the key map.
378 5 : FLAG_harmony_weak_refs = true;
379 5 : CcTest::InitializeVM();
380 5 : LocalContext context;
381 : Isolate* isolate = CcTest::i_isolate();
382 : Factory* factory = isolate->factory();
383 : HandleScope outer_scope(isolate);
384 : Handle<JSFinalizationGroup> finalization_group =
385 5 : ConstructJSFinalizationGroup(isolate);
386 : Handle<JSObject> js_object =
387 5 : isolate->factory()->NewJSObject(isolate->object_function());
388 5 : Handle<JSObject> key1 = CreateKey("key1", isolate);
389 :
390 5 : Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
391 : Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
392 5 : finalization_group, js_object, holdings1, key1, isolate);
393 5 : Handle<Object> holdings2 = factory->NewStringFromAsciiChecked("holdings2");
394 : Handle<WeakCell> weak_cell2 = FinalizationGroupRegister(
395 5 : finalization_group, js_object, holdings2, key1, isolate);
396 :
397 5 : NullifyWeakCell(weak_cell1, isolate);
398 5 : NullifyWeakCell(weak_cell2, isolate);
399 :
400 : // Nullifying doesn't affect the key chains (just moves WeakCells from
401 : // active_cells to cleared_cells).
402 : {
403 : Handle<ObjectHashTable> key_map =
404 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
405 10 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell2,
406 5 : *weak_cell1);
407 : }
408 :
409 : Object cleared1 =
410 5 : JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
411 5 : CHECK_EQ(cleared1, *holdings2);
412 :
413 : {
414 : Handle<ObjectHashTable> key_map =
415 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
416 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 1, *weak_cell1);
417 : }
418 :
419 : Object cleared2 =
420 5 : JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
421 5 : CHECK_EQ(cleared2, *holdings1);
422 :
423 : {
424 : Handle<ObjectHashTable> key_map =
425 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
426 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0);
427 : }
428 5 : }
429 :
430 26644 : TEST(TestUnregisterActiveCells) {
431 5 : FLAG_harmony_weak_refs = true;
432 5 : CcTest::InitializeVM();
433 5 : LocalContext context;
434 : Isolate* isolate = CcTest::i_isolate();
435 : HandleScope outer_scope(isolate);
436 : Handle<JSFinalizationGroup> finalization_group =
437 5 : ConstructJSFinalizationGroup(isolate);
438 : Handle<JSObject> js_object =
439 5 : isolate->factory()->NewJSObject(isolate->object_function());
440 :
441 5 : Handle<JSObject> key1 = CreateKey("key1", isolate);
442 5 : Handle<JSObject> key2 = CreateKey("key2", isolate);
443 : Handle<Object> undefined =
444 : handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
445 :
446 : Handle<WeakCell> weak_cell1a = FinalizationGroupRegister(
447 5 : finalization_group, js_object, undefined, key1, isolate);
448 : Handle<WeakCell> weak_cell1b = FinalizationGroupRegister(
449 5 : finalization_group, js_object, undefined, key1, isolate);
450 :
451 : Handle<WeakCell> weak_cell2a = FinalizationGroupRegister(
452 5 : finalization_group, js_object, undefined, key2, isolate);
453 : Handle<WeakCell> weak_cell2b = FinalizationGroupRegister(
454 5 : finalization_group, js_object, undefined, key2, isolate);
455 :
456 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 4,
457 5 : *weak_cell2b, *weak_cell2a, *weak_cell1b, *weak_cell1a);
458 5 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
459 : {
460 : Handle<ObjectHashTable> key_map =
461 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
462 10 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell1b,
463 5 : *weak_cell1a);
464 10 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 2, *weak_cell2b,
465 5 : *weak_cell2a);
466 : }
467 :
468 5 : JSFinalizationGroup::Unregister(finalization_group, key1, isolate);
469 : {
470 : Handle<ObjectHashTable> key_map =
471 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
472 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0);
473 10 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 2, *weak_cell2b,
474 5 : *weak_cell2a);
475 : }
476 :
477 : // Both weak_cell1a and weak_cell1b removed from active_cells.
478 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 2,
479 5 : *weak_cell2b, *weak_cell2a);
480 5 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
481 5 : }
482 :
483 26644 : TEST(TestUnregisterActiveAndClearedCells) {
484 5 : FLAG_harmony_weak_refs = true;
485 5 : CcTest::InitializeVM();
486 5 : LocalContext context;
487 : Isolate* isolate = CcTest::i_isolate();
488 : HandleScope outer_scope(isolate);
489 : Handle<JSFinalizationGroup> finalization_group =
490 5 : ConstructJSFinalizationGroup(isolate);
491 : Handle<JSObject> js_object =
492 5 : isolate->factory()->NewJSObject(isolate->object_function());
493 :
494 5 : Handle<JSObject> key1 = CreateKey("key1", isolate);
495 5 : Handle<JSObject> key2 = CreateKey("key2", isolate);
496 : Handle<Object> undefined =
497 : handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
498 :
499 : Handle<WeakCell> weak_cell1a = FinalizationGroupRegister(
500 5 : finalization_group, js_object, undefined, key1, isolate);
501 : Handle<WeakCell> weak_cell1b = FinalizationGroupRegister(
502 5 : finalization_group, js_object, undefined, key1, isolate);
503 :
504 : Handle<WeakCell> weak_cell2a = FinalizationGroupRegister(
505 5 : finalization_group, js_object, undefined, key2, isolate);
506 : Handle<WeakCell> weak_cell2b = FinalizationGroupRegister(
507 5 : finalization_group, js_object, undefined, key2, isolate);
508 :
509 5 : NullifyWeakCell(weak_cell2a, isolate);
510 :
511 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 3,
512 5 : *weak_cell2b, *weak_cell1b, *weak_cell1a);
513 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 1,
514 5 : *weak_cell2a);
515 : {
516 : Handle<ObjectHashTable> key_map =
517 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
518 10 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell1b,
519 5 : *weak_cell1a);
520 10 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 2, *weak_cell2b,
521 5 : *weak_cell2a);
522 : }
523 :
524 5 : JSFinalizationGroup::Unregister(finalization_group, key2, isolate);
525 :
526 : // Both weak_cell2a and weak_cell2b removed.
527 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 2,
528 5 : *weak_cell1b, *weak_cell1a);
529 5 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
530 : {
531 : Handle<ObjectHashTable> key_map =
532 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
533 10 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 2, *weak_cell1b,
534 5 : *weak_cell1a);
535 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key2), 0);
536 : }
537 5 : }
538 :
539 26644 : TEST(TestWeakCellUnregisterTwice) {
540 5 : FLAG_harmony_weak_refs = true;
541 5 : CcTest::InitializeVM();
542 5 : LocalContext context;
543 : Isolate* isolate = CcTest::i_isolate();
544 : HandleScope outer_scope(isolate);
545 : Handle<JSFinalizationGroup> finalization_group =
546 5 : ConstructJSFinalizationGroup(isolate);
547 : Handle<JSObject> js_object =
548 5 : isolate->factory()->NewJSObject(isolate->object_function());
549 :
550 5 : Handle<JSObject> key1 = CreateKey("key1", isolate);
551 : Handle<Object> undefined =
552 : handle(ReadOnlyRoots(isolate).undefined_value(), isolate);
553 :
554 : Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
555 5 : finalization_group, js_object, undefined, key1, isolate);
556 :
557 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 1,
558 5 : *weak_cell1);
559 5 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
560 : {
561 : Handle<ObjectHashTable> key_map =
562 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
563 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 1, *weak_cell1);
564 : }
565 :
566 5 : JSFinalizationGroup::Unregister(finalization_group, key1, isolate);
567 :
568 5 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0);
569 5 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
570 : {
571 : Handle<ObjectHashTable> key_map =
572 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
573 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0);
574 : }
575 :
576 5 : JSFinalizationGroup::Unregister(finalization_group, key1, isolate);
577 :
578 5 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0);
579 5 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
580 : {
581 : Handle<ObjectHashTable> key_map =
582 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
583 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0);
584 : }
585 5 : }
586 :
587 26644 : TEST(TestWeakCellUnregisterPopped) {
588 5 : FLAG_harmony_weak_refs = true;
589 5 : CcTest::InitializeVM();
590 5 : LocalContext context;
591 : Isolate* isolate = CcTest::i_isolate();
592 : Factory* factory = isolate->factory();
593 : HandleScope outer_scope(isolate);
594 : Handle<JSFinalizationGroup> finalization_group =
595 5 : ConstructJSFinalizationGroup(isolate);
596 : Handle<JSObject> js_object =
597 5 : isolate->factory()->NewJSObject(isolate->object_function());
598 5 : Handle<JSObject> key1 = CreateKey("key1", isolate);
599 5 : Handle<Object> holdings1 = factory->NewStringFromAsciiChecked("holdings1");
600 : Handle<WeakCell> weak_cell1 = FinalizationGroupRegister(
601 5 : finalization_group, js_object, holdings1, key1, isolate);
602 :
603 5 : NullifyWeakCell(weak_cell1, isolate);
604 :
605 5 : CHECK(finalization_group->NeedsCleanup());
606 : Object cleared1 =
607 5 : JSFinalizationGroup::PopClearedCellHoldings(finalization_group, isolate);
608 5 : CHECK_EQ(cleared1, *holdings1);
609 :
610 5 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0);
611 5 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
612 : {
613 : Handle<ObjectHashTable> key_map =
614 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
615 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0);
616 : }
617 :
618 5 : JSFinalizationGroup::Unregister(finalization_group, key1, isolate);
619 :
620 5 : VerifyWeakCellChain(isolate, finalization_group->active_cells(), 0);
621 5 : VerifyWeakCellChain(isolate, finalization_group->cleared_cells(), 0);
622 : {
623 : Handle<ObjectHashTable> key_map =
624 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
625 5 : VerifyWeakCellKeyChain(isolate, key_map->Lookup(key1), 0);
626 : }
627 5 : }
628 :
629 26644 : TEST(TestWeakCellUnregisterNonexistentKey) {
630 5 : FLAG_harmony_weak_refs = true;
631 5 : CcTest::InitializeVM();
632 5 : LocalContext context;
633 : Isolate* isolate = CcTest::i_isolate();
634 : HandleScope outer_scope(isolate);
635 : Handle<JSFinalizationGroup> finalization_group =
636 5 : ConstructJSFinalizationGroup(isolate);
637 5 : Handle<JSObject> key1 = CreateKey("key1", isolate);
638 :
639 5 : JSFinalizationGroup::Unregister(finalization_group, key1, isolate);
640 5 : }
641 :
642 26644 : TEST(TestJSWeakRef) {
643 5 : FLAG_harmony_weak_refs = true;
644 5 : CcTest::InitializeVM();
645 5 : LocalContext context;
646 :
647 : Isolate* isolate = CcTest::i_isolate();
648 : HandleScope outer_scope(isolate);
649 : Handle<JSWeakRef> weak_ref;
650 : {
651 : HandleScope inner_scope(isolate);
652 :
653 : Handle<JSObject> js_object =
654 5 : isolate->factory()->NewJSObject(isolate->object_function());
655 : // This doesn't add the target into the KeepDuringJob set.
656 5 : Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
657 :
658 5 : CcTest::CollectAllGarbage();
659 5 : CHECK(!inner_weak_ref->target()->IsUndefined(isolate));
660 :
661 5 : weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
662 : }
663 :
664 5 : CHECK(!weak_ref->target()->IsUndefined(isolate));
665 :
666 5 : CcTest::CollectAllGarbage();
667 :
668 5 : CHECK(weak_ref->target()->IsUndefined(isolate));
669 5 : }
670 :
671 26644 : TEST(TestJSWeakRefIncrementalMarking) {
672 5 : FLAG_harmony_weak_refs = true;
673 5 : if (!FLAG_incremental_marking) {
674 0 : return;
675 : }
676 : ManualGCScope manual_gc_scope;
677 5 : CcTest::InitializeVM();
678 5 : LocalContext context;
679 :
680 : Isolate* isolate = CcTest::i_isolate();
681 : Heap* heap = isolate->heap();
682 : HandleScope outer_scope(isolate);
683 : Handle<JSWeakRef> weak_ref;
684 : {
685 : HandleScope inner_scope(isolate);
686 :
687 : Handle<JSObject> js_object =
688 5 : isolate->factory()->NewJSObject(isolate->object_function());
689 : // This doesn't add the target into the KeepDuringJob set.
690 5 : Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
691 :
692 5 : heap::SimulateIncrementalMarking(heap, true);
693 5 : CcTest::CollectAllGarbage();
694 5 : CHECK(!inner_weak_ref->target()->IsUndefined(isolate));
695 :
696 5 : weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
697 : }
698 :
699 5 : CHECK(!weak_ref->target()->IsUndefined(isolate));
700 :
701 5 : heap::SimulateIncrementalMarking(heap, true);
702 5 : CcTest::CollectAllGarbage();
703 :
704 5 : CHECK(weak_ref->target()->IsUndefined(isolate));
705 : }
706 :
707 26644 : TEST(TestJSWeakRefKeepDuringJob) {
708 5 : FLAG_harmony_weak_refs = true;
709 5 : CcTest::InitializeVM();
710 5 : LocalContext context;
711 :
712 : Isolate* isolate = CcTest::i_isolate();
713 : Heap* heap = isolate->heap();
714 : HandleScope outer_scope(isolate);
715 : Handle<JSWeakRef> weak_ref;
716 : {
717 : HandleScope inner_scope(isolate);
718 :
719 : Handle<JSObject> js_object =
720 5 : isolate->factory()->NewJSObject(isolate->object_function());
721 5 : Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
722 5 : heap->AddKeepDuringJobTarget(js_object);
723 :
724 5 : weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
725 : }
726 :
727 5 : CHECK(!weak_ref->target()->IsUndefined(isolate));
728 :
729 5 : CcTest::CollectAllGarbage();
730 :
731 5 : CHECK(!weak_ref->target()->IsUndefined(isolate));
732 :
733 : // Clears the KeepDuringJob set.
734 5 : isolate->default_microtask_queue()->RunMicrotasks(isolate);
735 5 : CcTest::CollectAllGarbage();
736 :
737 5 : CHECK(weak_ref->target()->IsUndefined(isolate));
738 5 : }
739 :
740 26644 : TEST(TestJSWeakRefKeepDuringJobIncrementalMarking) {
741 5 : FLAG_harmony_weak_refs = true;
742 5 : if (!FLAG_incremental_marking) {
743 0 : return;
744 : }
745 : ManualGCScope manual_gc_scope;
746 5 : CcTest::InitializeVM();
747 5 : LocalContext context;
748 :
749 : Isolate* isolate = CcTest::i_isolate();
750 : Heap* heap = isolate->heap();
751 : HandleScope outer_scope(isolate);
752 : Handle<JSWeakRef> weak_ref;
753 : {
754 : HandleScope inner_scope(isolate);
755 :
756 : Handle<JSObject> js_object =
757 5 : isolate->factory()->NewJSObject(isolate->object_function());
758 5 : Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
759 5 : heap->AddKeepDuringJobTarget(js_object);
760 :
761 5 : weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
762 : }
763 :
764 5 : CHECK(!weak_ref->target()->IsUndefined(isolate));
765 :
766 5 : heap::SimulateIncrementalMarking(heap, true);
767 5 : CcTest::CollectAllGarbage();
768 :
769 5 : CHECK(!weak_ref->target()->IsUndefined(isolate));
770 :
771 : // Clears the KeepDuringJob set.
772 5 : isolate->default_microtask_queue()->RunMicrotasks(isolate);
773 5 : heap::SimulateIncrementalMarking(heap, true);
774 5 : CcTest::CollectAllGarbage();
775 :
776 5 : CHECK(weak_ref->target()->IsUndefined(isolate));
777 : }
778 :
779 : } // namespace internal
780 79917 : } // namespace v8
|