// Copyright 2014 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. #include #include #include "src/v8.h" #include "src/compilation-cache.h" #include "src/execution.h" #include "src/factory.h" #include "src/global-handles.h" #include "src/ic/ic.h" #include "src/macro-assembler.h" #include "test/cctest/cctest.h" using namespace v8::base; using namespace v8::internal; #if (V8_DOUBLE_FIELDS_UNBOXING) static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) { if (obj->IsUnboxedDoubleField(field_index)) { return obj->RawFastDoublePropertyAt(field_index); } else { Object* value = obj->RawFastPropertyAt(field_index); DCHECK(value->IsMutableHeapNumber()); return HeapNumber::cast(value)->value(); } } enum PropertyKind { PROP_CONSTANT, PROP_SMI, PROP_DOUBLE, PROP_TAGGED, PROP_KIND_NUMBER }; static Representation representations[PROP_KIND_NUMBER] = { Representation::None(), Representation::Smi(), Representation::Double(), Representation::Tagged()}; static Handle CreateDescriptorArray(Isolate* isolate, PropertyKind* props, int kPropsCount) { Factory* factory = isolate->factory(); Handle func_name = factory->InternalizeUtf8String("func"); Handle func = factory->NewFunction(func_name); Handle descriptors = DescriptorArray::Allocate(isolate, 0, kPropsCount); int next_field_offset = 0; for (int i = 0; i < kPropsCount; i++) { EmbeddedVector buffer; SNPrintF(buffer, "prop%d", i); Handle name = factory->InternalizeUtf8String(buffer.start()); PropertyKind kind = props[i]; if (kind == PROP_CONSTANT) { ConstantDescriptor d(name, func, NONE); descriptors->Append(&d); } else { FieldDescriptor f(name, next_field_offset, NONE, representations[kind]); next_field_offset += f.GetDetails().field_width_in_words(); descriptors->Append(&f); } } return descriptors; } TEST(LayoutDescriptorBasicFast) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout(); CHECK(!layout_desc->IsSlowLayout()); CHECK(layout_desc->IsFastPointerLayout()); CHECK_EQ(kSmiValueSize, layout_desc->capacity()); for (int i = 0; i < kSmiValueSize + 13; i++) { CHECK_EQ(true, layout_desc->IsTagged(i)); } CHECK_EQ(true, layout_desc->IsTagged(-1)); CHECK_EQ(true, layout_desc->IsTagged(-12347)); CHECK_EQ(true, layout_desc->IsTagged(15635)); CHECK(layout_desc->IsFastPointerLayout()); for (int i = 0; i < kSmiValueSize; i++) { layout_desc = layout_desc->SetTaggedForTesting(i, false); CHECK_EQ(false, layout_desc->IsTagged(i)); layout_desc = layout_desc->SetTaggedForTesting(i, true); CHECK_EQ(true, layout_desc->IsTagged(i)); } CHECK(layout_desc->IsFastPointerLayout()); } TEST(LayoutDescriptorBasicSlow) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); v8::HandleScope scope(CcTest::isolate()); Handle layout_descriptor; const int kPropsCount = kSmiValueSize * 3; PropertyKind props[kPropsCount]; for (int i = 0; i < kPropsCount; i++) { // All properties tagged. props[i] = PROP_TAGGED; } { Handle descriptors = CreateDescriptorArray(isolate, props, kPropsCount); Handle map = Map::Create(isolate, kPropsCount); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); CHECK_EQ(kSmiValueSize, layout_descriptor->capacity()); map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } props[0] = PROP_DOUBLE; props[kPropsCount - 1] = PROP_DOUBLE; Handle descriptors = CreateDescriptorArray(isolate, props, kPropsCount); { int inobject_properties = kPropsCount - 1; Handle map = Map::Create(isolate, inobject_properties); // Should be fast as the only double property is the first one. layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); CHECK(!layout_descriptor->IsSlowLayout()); CHECK(!layout_descriptor->IsFastPointerLayout()); CHECK_EQ(false, layout_descriptor->IsTagged(0)); for (int i = 1; i < kPropsCount; i++) { CHECK_EQ(true, layout_descriptor->IsTagged(i)); } map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } { int inobject_properties = kPropsCount; Handle map = Map::Create(isolate, inobject_properties); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); CHECK(layout_descriptor->IsSlowLayout()); CHECK(!layout_descriptor->IsFastPointerLayout()); CHECK(layout_descriptor->capacity() > kSmiValueSize); CHECK_EQ(false, layout_descriptor->IsTagged(0)); CHECK_EQ(false, layout_descriptor->IsTagged(kPropsCount - 1)); for (int i = 1; i < kPropsCount - 1; i++) { CHECK_EQ(true, layout_descriptor->IsTagged(i)); } map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); // Here we have truly slow layout descriptor, so play with the bits. CHECK_EQ(true, layout_descriptor->IsTagged(-1)); CHECK_EQ(true, layout_descriptor->IsTagged(-12347)); CHECK_EQ(true, layout_descriptor->IsTagged(15635)); LayoutDescriptor* layout_desc = *layout_descriptor; // Play with the bits but leave it in consistent state with map at the end. for (int i = 1; i < kPropsCount - 1; i++) { layout_desc = layout_desc->SetTaggedForTesting(i, false); CHECK_EQ(false, layout_desc->IsTagged(i)); layout_desc = layout_desc->SetTaggedForTesting(i, true); CHECK_EQ(true, layout_desc->IsTagged(i)); } CHECK(layout_desc->IsSlowLayout()); CHECK(!layout_desc->IsFastPointerLayout()); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } } TEST(LayoutDescriptorCreateNewFast) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); v8::HandleScope scope(CcTest::isolate()); Handle layout_descriptor; PropertyKind props[] = { PROP_CONSTANT, PROP_TAGGED, // field #0 PROP_CONSTANT, PROP_DOUBLE, // field #1 PROP_CONSTANT, PROP_TAGGED, // field #2 PROP_CONSTANT, }; const int kPropsCount = arraysize(props); Handle descriptors = CreateDescriptorArray(isolate, props, kPropsCount); { Handle map = Map::Create(isolate, 0); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } { Handle map = Map::Create(isolate, 1); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } { Handle map = Map::Create(isolate, 2); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); CHECK(!layout_descriptor->IsSlowLayout()); CHECK_EQ(true, layout_descriptor->IsTagged(0)); CHECK_EQ(false, layout_descriptor->IsTagged(1)); CHECK_EQ(true, layout_descriptor->IsTagged(2)); CHECK_EQ(true, layout_descriptor->IsTagged(125)); map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } } TEST(LayoutDescriptorCreateNewSlow) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); v8::HandleScope scope(CcTest::isolate()); Handle layout_descriptor; const int kPropsCount = kSmiValueSize * 3; PropertyKind props[kPropsCount]; for (int i = 0; i < kPropsCount; i++) { props[i] = static_cast(i % PROP_KIND_NUMBER); } Handle descriptors = CreateDescriptorArray(isolate, props, kPropsCount); { Handle map = Map::Create(isolate, 0); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } { Handle map = Map::Create(isolate, 1); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } { Handle map = Map::Create(isolate, 2); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); CHECK(!layout_descriptor->IsSlowLayout()); CHECK_EQ(true, layout_descriptor->IsTagged(0)); CHECK_EQ(false, layout_descriptor->IsTagged(1)); CHECK_EQ(true, layout_descriptor->IsTagged(2)); CHECK_EQ(true, layout_descriptor->IsTagged(125)); map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); } { int inobject_properties = kPropsCount / 2; Handle map = Map::Create(isolate, inobject_properties); layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount); CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor); CHECK(layout_descriptor->IsSlowLayout()); for (int i = 0; i < inobject_properties; i++) { // PROP_DOUBLE has index 1 among FIELD properties. const bool tagged = (i % (PROP_KIND_NUMBER - 1)) != 1; CHECK_EQ(tagged, layout_descriptor->IsTagged(i)); } // Every property after inobject_properties must be tagged. for (int i = inobject_properties; i < kPropsCount; i++) { CHECK_EQ(true, layout_descriptor->IsTagged(i)); } map->InitializeDescriptors(*descriptors, *layout_descriptor); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); // Now test LayoutDescriptor::cast_gc_safe(). Handle layout_descriptor_copy = LayoutDescriptor::New(map, descriptors, kPropsCount); LayoutDescriptor* layout_desc = *layout_descriptor; CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc)); CHECK_EQ(layout_desc, LayoutDescriptor::cast_gc_safe(layout_desc)); CHECK(layout_descriptor->IsFixedTypedArrayBase()); // Now make it look like a forwarding pointer to layout_descriptor_copy. MapWord map_word = layout_desc->map_word(); CHECK(!map_word.IsForwardingAddress()); layout_desc->set_map_word( MapWord::FromForwardingAddress(*layout_descriptor_copy)); CHECK(layout_desc->map_word().IsForwardingAddress()); CHECK_EQ(*layout_descriptor_copy, LayoutDescriptor::cast_gc_safe(layout_desc)); // Restore it back. layout_desc->set_map_word(map_word); CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc)); } } static Handle TestLayoutDescriptorAppend( Isolate* isolate, int inobject_properties, PropertyKind* props, int kPropsCount) { Factory* factory = isolate->factory(); Handle func_name = factory->InternalizeUtf8String("func"); Handle func = factory->NewFunction(func_name); Handle descriptors = DescriptorArray::Allocate(isolate, 0, kPropsCount); Handle map = Map::Create(isolate, inobject_properties); map->InitializeDescriptors(*descriptors, LayoutDescriptor::FastPointerLayout()); int next_field_offset = 0; for (int i = 0; i < kPropsCount; i++) { EmbeddedVector buffer; SNPrintF(buffer, "prop%d", i); Handle name = factory->InternalizeUtf8String(buffer.start()); Handle layout_descriptor; PropertyKind kind = props[i]; if (kind == PROP_CONSTANT) { ConstantDescriptor d(name, func, NONE); layout_descriptor = LayoutDescriptor::Append(map, d.GetDetails()); descriptors->Append(&d); } else { FieldDescriptor f(name, next_field_offset, NONE, representations[kind]); int field_width_in_words = f.GetDetails().field_width_in_words(); next_field_offset += field_width_in_words; layout_descriptor = LayoutDescriptor::Append(map, f.GetDetails()); descriptors->Append(&f); int field_index = f.GetDetails().field_index(); bool is_inobject = field_index < map->inobject_properties(); for (int bit = 0; bit < field_width_in_words; bit++) { CHECK_EQ(is_inobject && (kind == PROP_DOUBLE), !layout_descriptor->IsTagged(field_index + bit)); } CHECK(layout_descriptor->IsTagged(next_field_offset)); } map->InitializeDescriptors(*descriptors, *layout_descriptor); } Handle layout_descriptor(map->layout_descriptor(), isolate); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); return layout_descriptor; } TEST(LayoutDescriptorAppend) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); v8::HandleScope scope(CcTest::isolate()); Handle layout_descriptor; const int kPropsCount = kSmiValueSize * 3; PropertyKind props[kPropsCount]; for (int i = 0; i < kPropsCount; i++) { props[i] = static_cast(i % PROP_KIND_NUMBER); } layout_descriptor = TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2, props, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); } TEST(LayoutDescriptorAppendAllDoubles) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); v8::HandleScope scope(CcTest::isolate()); Handle layout_descriptor; const int kPropsCount = kSmiValueSize * 3; PropertyKind props[kPropsCount]; for (int i = 0; i < kPropsCount; i++) { props[i] = PROP_DOUBLE; } layout_descriptor = TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize + 1, props, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2, props, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); { // Ensure layout descriptor switches into slow mode at the right moment. layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props, kSmiValueSize); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props, kSmiValueSize + 1); CHECK(layout_descriptor->IsSlowLayout()); } } static Handle TestLayoutDescriptorAppendIfFastOrUseFull( Isolate* isolate, int inobject_properties, Handle descriptors, int number_of_descriptors) { Handle map = Map::Create(isolate, inobject_properties); Handle full_layout_descriptor = LayoutDescriptor::New( map, descriptors, descriptors->number_of_descriptors()); int nof = 0; bool switched_to_slow_mode = false; for (int i = 0; i < number_of_descriptors; i++) { PropertyDetails details = descriptors->GetDetails(i); // This method calls LayoutDescriptor::AppendIfFastOrUseFull() internally // and does all the required map-descriptors related book keeping. map = Map::CopyInstallDescriptorsForTesting(map, i, descriptors, full_layout_descriptor); LayoutDescriptor* layout_desc = map->layout_descriptor(); if (layout_desc->IsSlowLayout()) { switched_to_slow_mode = true; CHECK_EQ(*full_layout_descriptor, layout_desc); } else { CHECK(!switched_to_slow_mode); if (details.type() == FIELD) { nof++; int field_index = details.field_index(); int field_width_in_words = details.field_width_in_words(); bool is_inobject = field_index < map->inobject_properties(); for (int bit = 0; bit < field_width_in_words; bit++) { CHECK_EQ(is_inobject && details.representation().IsDouble(), !layout_desc->IsTagged(field_index + bit)); } CHECK(layout_desc->IsTagged(field_index + field_width_in_words)); } } DCHECK(map->layout_descriptor()->IsConsistentWithMap(*map)); } Handle layout_descriptor(map->GetLayoutDescriptor(), isolate); DCHECK(layout_descriptor->IsConsistentWithMap(*map)); return layout_descriptor; } TEST(LayoutDescriptorAppendIfFastOrUseFull) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); v8::HandleScope scope(CcTest::isolate()); Handle layout_descriptor; const int kPropsCount = kSmiValueSize * 3; PropertyKind props[kPropsCount]; for (int i = 0; i < kPropsCount; i++) { props[i] = static_cast(i % PROP_KIND_NUMBER); } Handle descriptors = CreateDescriptorArray(isolate, props, kPropsCount); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, 0, descriptors, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, 13, descriptors, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kSmiValueSize, descriptors, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kSmiValueSize * 2, descriptors, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kPropsCount, descriptors, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); } TEST(LayoutDescriptorAppendIfFastOrUseFullAllDoubles) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); v8::HandleScope scope(CcTest::isolate()); Handle layout_descriptor; const int kPropsCount = kSmiValueSize * 3; PropertyKind props[kPropsCount]; for (int i = 0; i < kPropsCount; i++) { props[i] = PROP_DOUBLE; } Handle descriptors = CreateDescriptorArray(isolate, props, kPropsCount); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, 0, descriptors, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, 13, descriptors, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kSmiValueSize, descriptors, kPropsCount); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kSmiValueSize + 1, descriptors, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kSmiValueSize * 2, descriptors, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kPropsCount, descriptors, kPropsCount); CHECK(layout_descriptor->IsSlowLayout()); { // Ensure layout descriptor switches into slow mode at the right moment. layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kPropsCount, descriptors, kSmiValueSize); CHECK(!layout_descriptor->IsSlowLayout()); layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull( isolate, kPropsCount, descriptors, kSmiValueSize + 1); CHECK(layout_descriptor->IsSlowLayout()); } } TEST(StoreBufferScanOnScavenge) { CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); v8::HandleScope scope(CcTest::isolate()); CompileRun( "function A() {" " this.x = 42.5;" " this.o = {};" "};" "var o = new A();"); Handle obj_name = factory->InternalizeUtf8String("o"); Handle obj_value = Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked(); CHECK(obj_value->IsJSObject()); Handle obj = Handle::cast(obj_value); { // Ensure the object is properly set up. Map* map = obj->map(); DescriptorArray* descriptors = map->instance_descriptors(); CHECK(map->NumberOfOwnDescriptors() == 2); CHECK(descriptors->GetDetails(0).representation().IsDouble()); CHECK(descriptors->GetDetails(1).representation().IsHeapObject()); FieldIndex field_index = FieldIndex::ForDescriptor(map, 0); CHECK(field_index.is_inobject() && field_index.is_double()); CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); } CHECK(isolate->heap()->new_space()->Contains(*obj)); // Trigger GCs so that the newly allocated object moves to old gen. CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now CHECK(isolate->heap()->old_pointer_space()->Contains(*obj)); // Create temp object in the new space. Handle temp = factory->NewJSArray(FAST_ELEMENTS, NOT_TENURED); CHECK(isolate->heap()->new_space()->Contains(*temp)); // Construct a double value that looks like a pointer to the new space object // and store it into the obj. Address fake_object = reinterpret_cast
(*temp) + kPointerSize; double boom_value = bit_cast(fake_object); FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0); Handle boom_number = factory->NewHeapNumber(boom_value, MUTABLE); obj->FastPropertyAtPut(field_index, *boom_number); // Enforce scan on scavenge for the obj's page. MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address()); chunk->set_scan_on_scavenge(true); // Trigger GCs and force evacuation. Should not crash there. CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index)); } #endif