test-weak-references.cc 25.2 KB
Newer Older
1 2 3 4
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/api/api-inl.h"
6
#include "src/codegen/assembler-inl.h"
7
#include "src/execution/isolate.h"
8
#include "src/heap/factory.h"
9
#include "src/heap/heap-inl.h"
10
#include "src/objects/smi.h"
11
#include "test/cctest/cctest.h"
12
#include "test/cctest/heap/heap-tester.h"
13 14 15 16 17 18
#include "test/cctest/heap/heap-utils.h"

namespace v8 {
namespace internal {
namespace heap {

19 20 21
Handle<LoadHandler> CreateLoadHandlerForTest(
    Factory* factory, AllocationType allocation = AllocationType::kYoung) {
  Handle<LoadHandler> result = factory->NewLoadHandler(1, allocation);
22 23
  result->set_smi_handler(Smi::zero());
  result->set_validity_cell(Smi::zero());
24 25
  result->set_data1(MaybeObject::FromSmi(Smi::zero()));
  return result;
26 27 28 29 30 31 32 33
}

TEST(WeakReferencesBasic) {
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  HandleScope outer_scope(isolate);

34
  Handle<LoadHandler> lh = CreateLoadHandlerForTest(factory);
35 36

  if (!FLAG_single_generation) CHECK(Heap::InYoungGeneration(*lh));
37

38
  MaybeObject code_object = lh->data1();
39 40
  CHECK(code_object->IsSmi());
  CcTest::CollectAllGarbage();
41 42
  CHECK(FLAG_always_promote_young_mc ? !Heap::InYoungGeneration(*lh)
                                     : Heap::InYoungGeneration(*lh));
43
  CHECK_EQ(code_object, lh->data1());
44 45 46 47 48

  {
    HandleScope inner_scope(isolate);

    // Create a new Code.
49
    Assembler assm(AssemblerOptions{});
50 51 52
    assm.nop();  // supported on all architectures
    CodeDesc desc;
    assm.GetCode(isolate, &desc);
53
    Handle<Code> code = Factory::CodeBuilder(isolate, desc, Code::STUB).Build();
54 55
    CHECK(code->IsCode());

56
    lh->set_data1(HeapObjectReference::Weak(*code));
57
    HeapObject code_heap_object;
58
    CHECK(lh->data1()->GetHeapObjectIfWeak(&code_heap_object));
59 60 61 62
    CHECK_EQ(*code, code_heap_object);

    CcTest::CollectAllGarbage();

63
    CHECK(lh->data1()->GetHeapObjectIfWeak(&code_heap_object));
64 65 66 67
    CHECK_EQ(*code, code_heap_object);
  }  // code will go out of scope.

  CcTest::CollectAllGarbage();
68
  CHECK(lh->data1()->IsCleared());
69 70 71 72 73 74 75 76 77 78 79 80 81
}

TEST(WeakReferencesOldToOld) {
  // Like WeakReferencesBasic, but the updated weak slot is in the old space,
  // and referring to an old space object.
  ManualGCScope manual_gc_scope;
  FLAG_manual_evacuation_candidates_selection = true;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
82 83 84
  Handle<LoadHandler> lh =
      CreateLoadHandlerForTest(factory, AllocationType::kOld);
  CHECK(heap->InOldSpace(*lh));
85

86
  // Create a new FixedArray which the LoadHandler will point to.
87 88
  Handle<FixedArray> fixed_array =
      factory->NewFixedArray(1, AllocationType::kOld);
89
  CHECK(heap->InOldSpace(*fixed_array));
90
  lh->set_data1(HeapObjectReference::Weak(*fixed_array));
91

92
  Page* page_before_gc = Page::FromHeapObject(*fixed_array);
93 94 95 96
  heap::ForceEvacuationCandidate(page_before_gc);
  CcTest::CollectAllGarbage();
  CHECK(heap->InOldSpace(*fixed_array));

97
  HeapObject heap_object;
98
  CHECK(lh->data1()->GetHeapObjectIfWeak(&heap_object));
99 100 101 102 103 104
  CHECK_EQ(heap_object, *fixed_array);
}

TEST(WeakReferencesOldToNew) {
  // Like WeakReferencesBasic, but the updated weak slot is in the old space,
  // and referring to an new space object.
105
  if (FLAG_single_generation) return;
106 107 108 109 110 111
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
112 113 114
  Handle<LoadHandler> lh =
      CreateLoadHandlerForTest(factory, AllocationType::kOld);
  CHECK(heap->InOldSpace(*lh));
115

116
  // Create a new FixedArray which the LoadHandler will point to.
117
  Handle<FixedArray> fixed_array = factory->NewFixedArray(1);
118
  CHECK(Heap::InYoungGeneration(*fixed_array));
119
  lh->set_data1(HeapObjectReference::Weak(*fixed_array));
120 121 122

  CcTest::CollectAllGarbage();

123
  HeapObject heap_object;
124
  CHECK(lh->data1()->GetHeapObjectIfWeak(&heap_object));
125 126 127 128
  CHECK_EQ(heap_object, *fixed_array);
}

TEST(WeakReferencesOldToNewScavenged) {
129
  if (FLAG_single_generation) return;
130 131 132 133 134 135 136 137
  // Like WeakReferencesBasic, but the updated weak slot is in the old space,
  // and referring to an new space object, which is then scavenged.
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
138 139 140
  Handle<LoadHandler> lh =
      CreateLoadHandlerForTest(factory, AllocationType::kOld);
  CHECK(heap->InOldSpace(*lh));
141

142
  // Create a new FixedArray which the LoadHandler will point to.
143
  Handle<FixedArray> fixed_array = factory->NewFixedArray(1);
144
  CHECK(Heap::InYoungGeneration(*fixed_array));
145
  lh->set_data1(HeapObjectReference::Weak(*fixed_array));
146 147 148

  CcTest::CollectGarbage(NEW_SPACE);

149
  HeapObject heap_object;
150
  CHECK(lh->data1()->GetHeapObjectIfWeak(&heap_object));
151 152 153 154 155 156 157 158 159 160 161 162 163 164
  CHECK_EQ(heap_object, *fixed_array);
}

TEST(WeakReferencesOldToCleared) {
  // Like WeakReferencesBasic, but the updated weak slot is in the old space,
  // and is cleared.
  ManualGCScope manual_gc_scope;
  FLAG_manual_evacuation_candidates_selection = true;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
165 166 167 168
  Handle<LoadHandler> lh =
      CreateLoadHandlerForTest(factory, AllocationType::kOld);
  CHECK(heap->InOldSpace(*lh));
  lh->set_data1(HeapObjectReference::ClearedValue(isolate));
169 170

  CcTest::CollectAllGarbage();
171
  CHECK(lh->data1()->IsCleared());
172 173 174
}

TEST(ObjectMovesBeforeClearingWeakField) {
175
  if (!FLAG_incremental_marking || FLAG_single_generation) {
176 177 178 179 180 181 182 183 184
    return;
  }
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
185
  Handle<LoadHandler> lh = CreateLoadHandlerForTest(factory);
186
  CHECK(InCorrectGeneration(*lh));
187
  LoadHandler lh_location = *lh;
188 189
  {
    HandleScope inner_scope(isolate);
190
    // Create a new FixedArray which the LoadHandler will point to.
191
    Handle<FixedArray> fixed_array = factory->NewFixedArray(1);
192
    CHECK(Heap::InYoungGeneration(*fixed_array));
193
    lh->set_data1(HeapObjectReference::Weak(*fixed_array));
194 195 196 197
    // inner_scope will go out of scope, so when marking the next time,
    // *fixed_array will stay white.
  }

198
  // Do marking steps; this will store *lh into the list for later processing
199 200 201
  // (since it points to a white object).
  SimulateIncrementalMarking(heap, true);

202
  // Scavenger will move *lh.
203
  CcTest::CollectGarbage(NEW_SPACE);
204 205 206
  LoadHandler new_lh_location = *lh;
  CHECK_NE(lh_location, new_lh_location);
  CHECK(lh->data1()->IsWeak());
207

208
  // Now we try to clear *lh.
209
  CcTest::CollectAllGarbage();
210
  CHECK(lh->data1()->IsCleared());
211 212
}

213 214 215 216 217 218 219 220 221 222 223 224
TEST(ObjectWithWeakFieldDies) {
  if (!FLAG_incremental_marking) {
    return;
  }
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  {
    HandleScope outer_scope(isolate);
225
    Handle<LoadHandler> lh = CreateLoadHandlerForTest(factory);
226
    CHECK(InCorrectGeneration(*lh));
227 228
    {
      HandleScope inner_scope(isolate);
229
      // Create a new FixedArray which the LoadHandler will point to.
230
      Handle<FixedArray> fixed_array = factory->NewFixedArray(1);
231
      CHECK(InCorrectGeneration(*fixed_array));
232
      lh->set_data1(HeapObjectReference::Weak(*fixed_array));
233 234 235 236
      // inner_scope will go out of scope, so when marking the next time,
      // *fixed_array will stay white.
    }

237
    // Do marking steps; this will store *lh into the list for later processing
238 239 240 241
    // (since it points to a white object).
    SimulateIncrementalMarking(heap, true);
  }  // outer_scope goes out of scope

242
  // lh will die
243 244 245 246 247 248
  CcTest::CollectGarbage(NEW_SPACE);

  // This used to crash when processing the dead weak reference.
  CcTest::CollectAllGarbage();
}

249
TEST(ObjectWithWeakReferencePromoted) {
250
  if (FLAG_single_generation) return;
251 252 253 254 255 256
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
257 258
  Handle<LoadHandler> lh = CreateLoadHandlerForTest(factory);
  CHECK(Heap::InYoungGeneration(*lh));
259

260
  // Create a new FixedArray which the LoadHandler will point to.
261
  Handle<FixedArray> fixed_array = factory->NewFixedArray(1);
262
  CHECK(Heap::InYoungGeneration(*fixed_array));
263
  lh->set_data1(HeapObjectReference::Weak(*fixed_array));
264 265 266

  CcTest::CollectGarbage(NEW_SPACE);
  CcTest::CollectGarbage(NEW_SPACE);
267
  CHECK(heap->InOldSpace(*lh));
268 269
  CHECK(heap->InOldSpace(*fixed_array));

270
  HeapObject heap_object;
271
  CHECK(lh->data1()->GetHeapObjectIfWeak(&heap_object));
272 273 274 275
  CHECK_EQ(heap_object, *fixed_array);
}

TEST(ObjectWithClearedWeakReferencePromoted) {
276
  if (FLAG_single_generation) return;
277 278 279 280 281 282
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
283 284
  Handle<LoadHandler> lh = CreateLoadHandlerForTest(factory);
  CHECK(Heap::InYoungGeneration(*lh));
285

286
  lh->set_data1(HeapObjectReference::ClearedValue(isolate));
287 288

  CcTest::CollectGarbage(NEW_SPACE);
289 290
  CHECK(Heap::InYoungGeneration(*lh));
  CHECK(lh->data1()->IsCleared());
291 292

  CcTest::CollectGarbage(NEW_SPACE);
293 294
  CHECK(heap->InOldSpace(*lh));
  CHECK(lh->data1()->IsCleared());
295 296

  CcTest::CollectAllGarbage();
297
  CHECK(lh->data1()->IsCleared());
298 299 300 301 302 303 304 305 306 307 308 309 310 311
}

TEST(WeakReferenceWriteBarrier) {
  if (!FLAG_incremental_marking) {
    return;
  }

  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();

  HandleScope outer_scope(isolate);
312
  Handle<LoadHandler> lh = CreateLoadHandlerForTest(factory);
313
  CHECK(InCorrectGeneration(*lh));
314 315 316 317

  {
    HandleScope inner_scope(isolate);

318
    // Create a new FixedArray which the LoadHandler will point to.
319
    Handle<FixedArray> fixed_array1 = factory->NewFixedArray(1);
320
    CHECK(InCorrectGeneration(*fixed_array1));
321
    lh->set_data1(HeapObjectReference::Weak(*fixed_array1));
322 323 324 325

    SimulateIncrementalMarking(heap, true);

    Handle<FixedArray> fixed_array2 = factory->NewFixedArray(1);
326
    CHECK(InCorrectGeneration(*fixed_array2));
327
    // This write will trigger the write barrier.
328
    lh->set_data1(HeapObjectReference::Weak(*fixed_array2));
329 330 331 332 333
  }

  CcTest::CollectAllGarbage();

  // Check that the write barrier treated the weak reference as strong.
334
  CHECK(lh->data1()->IsWeak());
335 336
}

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
TEST(EmptyWeakArray) {
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  HandleScope outer_scope(isolate);

  Handle<WeakFixedArray> array = factory->empty_weak_fixed_array();
  CHECK(array->IsWeakFixedArray());
  CHECK(!array->IsFixedArray());
  CHECK_EQ(array->length(), 0);
}

TEST(WeakArraysBasic) {
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();
  HandleScope outer_scope(isolate);

  const int length = 4;
  Handle<WeakFixedArray> array = factory->NewWeakFixedArray(length);
  CHECK(array->IsWeakFixedArray());
  CHECK(!array->IsFixedArray());
  CHECK_EQ(array->length(), length);
362 363

  if (!FLAG_single_generation) CHECK(Heap::InYoungGeneration(*array));
364 365

  for (int i = 0; i < length; ++i) {
366
    HeapObject heap_object;
367
    CHECK(array->Get(i)->GetHeapObjectIfStrong(&heap_object));
368
    CHECK_EQ(heap_object, ReadOnlyRoots(heap).undefined_value());
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
  }

  Handle<HeapObject> saved;
  {
    HandleScope inner_scope(isolate);
    Handle<FixedArray> index0 = factory->NewFixedArray(1);
    index0->set(0, Smi::FromInt(2016));
    Handle<FixedArray> index1 = factory->NewFixedArray(1);
    index1->set(0, Smi::FromInt(2017));

    Handle<FixedArray> index2 = factory->NewFixedArray(1);
    index2->set(0, Smi::FromInt(2018));
    Handle<FixedArray> index3 = factory->NewFixedArray(1);
    index3->set(0, Smi::FromInt(2019));

    array->Set(0, HeapObjectReference::Weak(*index0));
    array->Set(1, HeapObjectReference::Weak(*index1));
    array->Set(2, HeapObjectReference::Strong(*index2));
    array->Set(3, HeapObjectReference::Weak(*index3));
    saved = inner_scope.CloseAndEscape(index1);
  }  // inner_scope goes out of scope.

  // The references are only cleared by the mark-compact (scavenger treats weak
  // references as strong). Thus we need to GC until the array reaches old
  // space.

  // TODO(marja): update this when/if we do handle weak references in the new
  // space.
  CcTest::CollectGarbage(NEW_SPACE);
398
  HeapObject heap_object;
399
  CHECK(array->Get(0)->GetHeapObjectIfWeak(&heap_object));
400
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2016);
401
  CHECK(array->Get(1)->GetHeapObjectIfWeak(&heap_object));
402
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2017);
403
  CHECK(array->Get(2)->GetHeapObjectIfStrong(&heap_object));
404
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2018);
405
  CHECK(array->Get(3)->GetHeapObjectIfWeak(&heap_object));
406
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2019);
407 408 409

  CcTest::CollectAllGarbage();
  CHECK(heap->InOldSpace(*array));
410 411
  CHECK(array->Get(0)->IsCleared());
  CHECK(array->Get(1)->GetHeapObjectIfWeak(&heap_object));
412
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2017);
413
  CHECK(array->Get(2)->GetHeapObjectIfStrong(&heap_object));
414
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2018);
415
  CHECK(array->Get(3)->IsCleared());
416 417
}

418 419 420 421 422 423 424 425
TEST(WeakArrayListBasic) {
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();
  HandleScope outer_scope(isolate);

426 427
  Handle<WeakArrayList> array(ReadOnlyRoots(heap).empty_weak_array_list(),
                              isolate);
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
  CHECK(array->IsWeakArrayList());
  CHECK(!array->IsFixedArray());
  CHECK(!array->IsWeakFixedArray());
  CHECK_EQ(array->length(), 0);

  Handle<FixedArray> index2 = factory->NewFixedArray(1);
  index2->set(0, Smi::FromInt(2017));

  Handle<HeapObject> saved;
  {
    HandleScope inner_scope(isolate);
    Handle<FixedArray> index0 = factory->NewFixedArray(1);
    index0->set(0, Smi::FromInt(2016));
    Handle<FixedArray> index4 = factory->NewFixedArray(1);
    index4->set(0, Smi::FromInt(2018));
    Handle<FixedArray> index6 = factory->NewFixedArray(1);
    index6->set(0, Smi::FromInt(2019));

446 447
    array = WeakArrayList::AddToEnd(isolate, array,
                                    MaybeObjectHandle::Weak(index0));
448
    array = WeakArrayList::AddToEnd(
449
        isolate, array, MaybeObjectHandle(Smi::FromInt(1), isolate));
450 451
    CHECK_EQ(array->length(), 2);

452 453
    array = WeakArrayList::AddToEnd(isolate, array,
                                    MaybeObjectHandle::Weak(index2));
454
    array = WeakArrayList::AddToEnd(
455
        isolate, array, MaybeObjectHandle(Smi::FromInt(3), isolate));
456 457
    CHECK_EQ(array->length(), 4);

458 459
    array = WeakArrayList::AddToEnd(isolate, array,
                                    MaybeObjectHandle::Weak(index4));
460
    array = WeakArrayList::AddToEnd(
461
        isolate, array, MaybeObjectHandle(Smi::FromInt(5), isolate));
462 463
    CHECK_EQ(array->length(), 6);

464 465
    array = WeakArrayList::AddToEnd(isolate, array,
                                    MaybeObjectHandle::Weak(index6));
466
    array = WeakArrayList::AddToEnd(
467
        isolate, array, MaybeObjectHandle(Smi::FromInt(7), isolate));
468 469
    CHECK_EQ(array->length(), 8);

470
    CHECK(InCorrectGeneration(*array));
471 472

    CHECK_EQ(array->Get(0), HeapObjectReference::Weak(*index0));
473
    CHECK_EQ(array->Get(1).ToSmi().value(), 1);
474 475

    CHECK_EQ(array->Get(2), HeapObjectReference::Weak(*index2));
476
    CHECK_EQ(array->Get(3).ToSmi().value(), 3);
477 478

    CHECK_EQ(array->Get(4), HeapObjectReference::Weak(*index4));
479
    CHECK_EQ(array->Get(5).ToSmi().value(), 5);
480 481 482 483 484 485 486 487 488 489 490 491

    CHECK_EQ(array->Get(6), HeapObjectReference::Weak(*index6));
    array = inner_scope.CloseAndEscape(array);
  }  // inner_scope goes out of scope.

  // The references are only cleared by the mark-compact (scavenger treats weak
  // references as strong). Thus we need to GC until the array reaches old
  // space.

  // TODO(marja): update this when/if we do handle weak references in the new
  // space.
  CcTest::CollectGarbage(NEW_SPACE);
492
  HeapObject heap_object;
493
  CHECK_EQ(array->length(), 8);
494
  CHECK(array->Get(0)->GetHeapObjectIfWeak(&heap_object));
495
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2016);
496
  CHECK_EQ(array->Get(1).ToSmi().value(), 1);
497

498
  CHECK(array->Get(2)->GetHeapObjectIfWeak(&heap_object));
499
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2017);
500
  CHECK_EQ(array->Get(3).ToSmi().value(), 3);
501

502
  CHECK(array->Get(4)->GetHeapObjectIfWeak(&heap_object));
503
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2018);
504
  CHECK_EQ(array->Get(5).ToSmi().value(), 5);
505

506
  CHECK(array->Get(6)->GetHeapObjectIfWeak(&heap_object));
507
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2019);
508
  CHECK_EQ(array->Get(7).ToSmi().value(), 7);
509 510 511 512

  CcTest::CollectAllGarbage();
  CHECK(heap->InOldSpace(*array));
  CHECK_EQ(array->length(), 8);
513
  CHECK(array->Get(0)->IsCleared());
514
  CHECK_EQ(array->Get(1).ToSmi().value(), 1);
515

516
  CHECK(array->Get(2)->GetHeapObjectIfWeak(&heap_object));
517
  CHECK_EQ(Smi::cast(FixedArray::cast(heap_object).get(0)).value(), 2017);
518
  CHECK_EQ(array->Get(3).ToSmi().value(), 3);
519

520
  CHECK(array->Get(4)->IsCleared());
521
  CHECK_EQ(array->Get(5).ToSmi().value(), 5);
522

523
  CHECK(array->Get(6)->IsCleared());
524
  CHECK_EQ(array->Get(7).ToSmi().value(), 7);
525 526
}

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
TEST(WeakArrayListRemove) {
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();
  HandleScope outer_scope(isolate);

  Handle<WeakArrayList> array(ReadOnlyRoots(heap).empty_weak_array_list(),
                              isolate);

  Handle<FixedArray> elem0 = factory->NewFixedArray(1);
  Handle<FixedArray> elem1 = factory->NewFixedArray(1);
  Handle<FixedArray> elem2 = factory->NewFixedArray(1);

  array =
      WeakArrayList::AddToEnd(isolate, array, MaybeObjectHandle::Weak(elem0));
  array =
      WeakArrayList::AddToEnd(isolate, array, MaybeObjectHandle::Weak(elem1));
  array =
      WeakArrayList::AddToEnd(isolate, array, MaybeObjectHandle::Weak(elem2));

  CHECK_EQ(array->length(), 3);
  CHECK_EQ(array->Get(0), HeapObjectReference::Weak(*elem0));
  CHECK_EQ(array->Get(1), HeapObjectReference::Weak(*elem1));
  CHECK_EQ(array->Get(2), HeapObjectReference::Weak(*elem2));

  CHECK(array->RemoveOne(MaybeObjectHandle::Weak(elem1)));

556
  CHECK_EQ(array->length(), 2);
557
  CHECK_EQ(array->Get(0), HeapObjectReference::Weak(*elem0));
558
  CHECK_EQ(array->Get(1), HeapObjectReference::Weak(*elem2));
559 560 561

  CHECK(!array->RemoveOne(MaybeObjectHandle::Weak(elem1)));

562
  CHECK_EQ(array->length(), 2);
563
  CHECK_EQ(array->Get(0), HeapObjectReference::Weak(*elem0));
564
  CHECK_EQ(array->Get(1), HeapObjectReference::Weak(*elem2));
565 566 567

  CHECK(array->RemoveOne(MaybeObjectHandle::Weak(elem0)));

568 569
  CHECK_EQ(array->length(), 1);
  CHECK_EQ(array->Get(0), HeapObjectReference::Weak(*elem2));
570 571 572

  CHECK(array->RemoveOne(MaybeObjectHandle::Weak(elem2)));

573
  CHECK_EQ(array->length(), 0);
574 575
}

576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
TEST(Regress7768) {
  i::FLAG_allow_natives_syntax = true;
  i::FLAG_turbo_inlining = false;
  if (!FLAG_incremental_marking) {
    return;
  }
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  Heap* heap = isolate->heap();
  HandleScope outer_scope(isolate);
  // Create an optimized code which will contain a weak reference to another
  // function ("f"). The weak reference is the only reference to the function.
  CompileRun(
      "function myfunc(f) { f(); } "
592
      "%PrepareFunctionForOptimization(myfunc); "
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
      "(function wrapper() { "
      "   function f() {}; myfunc(f); myfunc(f); "
      "   %OptimizeFunctionOnNextCall(myfunc); myfunc(f); "
      "   %ClearFunctionFeedback(wrapper);"
      "})(); "
      "%ClearFunctionFeedback(myfunc);");

  // Do marking steps; this will store the objects pointed by myfunc for later
  // processing.
  SimulateIncrementalMarking(heap, true);

  // Deoptimize the code; now the pointers inside it will be replaced with
  // undefined, and the weak_objects_in_code is the only place pointing to the
  // function f.
  CompileRun("%DeoptimizeFunction(myfunc);");

  // The object pointed to by the weak reference won't be scavenged.
  CcTest::CollectGarbage(NEW_SPACE);

  // Make sure the memory where it's stored is invalidated, so that we'll crash
  // if we try to access it.
  HeapTester::UncommitFromSpace(heap);

  // This used to crash when processing the dead weak reference.
  CcTest::CollectAllGarbage();
}

620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
TEST(PrototypeUsersBasic) {
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();
  HandleScope outer_scope(isolate);

  Handle<WeakArrayList> array(ReadOnlyRoots(heap).empty_weak_array_list(),
                              isolate);

  // Add some objects into the array.
  int index = -1;
  {
    Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    array = PrototypeUsers::Add(isolate, array, map, &index);
    CHECK_EQ(array->length(), index + 1);
  }
  CHECK_EQ(index, 1);

  int empty_index = index;
  PrototypeUsers::MarkSlotEmpty(*array, empty_index);

  // Even though we have an empty slot, we still add to the end.
  int last_index = index;
  int old_capacity = array->capacity();
  while (!array->IsFull()) {
    Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    array = PrototypeUsers::Add(isolate, array, map, &index);
    CHECK_EQ(index, last_index + 1);
    CHECK_EQ(array->length(), index + 1);
    last_index = index;
  }

  // The next addition will fill the empty slot.
  {
    Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    array = PrototypeUsers::Add(isolate, array, map, &index);
  }
  CHECK_EQ(index, empty_index);

  // The next addition will make the arrow grow again.
  {
    Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    array = PrototypeUsers::Add(isolate, array, map, &index);
    CHECK_EQ(array->length(), index + 1);
    last_index = index;
  }
  CHECK_GT(array->capacity(), old_capacity);

  // Make multiple slots empty.
  int empty_index1 = 1;
  int empty_index2 = 2;
  PrototypeUsers::MarkSlotEmpty(*array, empty_index1);
  PrototypeUsers::MarkSlotEmpty(*array, empty_index2);

  // Fill the array (still adding to the end)
  old_capacity = array->capacity();
  while (!array->IsFull()) {
    Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    array = PrototypeUsers::Add(isolate, array, map, &index);
    CHECK_EQ(index, last_index + 1);
    CHECK_EQ(array->length(), index + 1);
    last_index = index;
  }

  // Make sure we use the empty slots in (reverse) order.
  {
    Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    array = PrototypeUsers::Add(isolate, array, map, &index);
  }
  CHECK_EQ(index, empty_index2);

  {
    Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    array = PrototypeUsers::Add(isolate, array, map, &index);
  }
  CHECK_EQ(index, empty_index1);
}

namespace {

702
HeapObject saved_heap_object;
703

704
static void TestCompactCallback(HeapObject value, int old_index,
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
                                int new_index) {
  saved_heap_object = value;
  CHECK_EQ(old_index, 2);
  CHECK_EQ(new_index, 1);
}

}  // namespace

TEST(PrototypeUsersCompacted) {
  ManualGCScope manual_gc_scope;
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  Factory* factory = isolate->factory();
  Heap* heap = isolate->heap();
  HandleScope outer_scope(isolate);

  Handle<WeakArrayList> array(ReadOnlyRoots(heap).empty_weak_array_list(),
                              isolate);

  // Add some objects into the array.
  int index = -1;
  Handle<Map> map_cleared_by_user =
      factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
  array = PrototypeUsers::Add(isolate, array, map_cleared_by_user, &index);
  CHECK_EQ(index, 1);
  Handle<Map> live_map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
  array = PrototypeUsers::Add(isolate, array, live_map, &index);
  CHECK_EQ(index, 2);
  {
    HandleScope inner_scope(isolate);
    Handle<Map> soon_dead_map =
        factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
    array = PrototypeUsers::Add(isolate, array, soon_dead_map, &index);
    CHECK_EQ(index, 3);

    array = inner_scope.CloseAndEscape(array);
  }

  PrototypeUsers::MarkSlotEmpty(*array, 1);
  CcTest::CollectAllGarbage();
746
  CHECK(array->Get(3)->IsCleared());
747 748

  CHECK_EQ(array->length(), 3 + PrototypeUsers::kFirstIndex);
749
  WeakArrayList new_array =
750
      PrototypeUsers::Compact(array, heap, TestCompactCallback);
751
  CHECK_EQ(new_array.length(), 1 + PrototypeUsers::kFirstIndex);
752 753 754
  CHECK_EQ(saved_heap_object, *live_map);
}

755 756 757
}  // namespace heap
}  // namespace internal
}  // namespace v8