1 // Copyright 2014 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 <stdlib.h>
6 #include <utility>
7 
8 #include "src/v8.h"
9 
10 #include "src/compilation-cache.h"
11 #include "src/execution.h"
12 #include "src/factory.h"
13 #include "src/global-handles.h"
14 #include "src/heap/slots-buffer.h"
15 #include "src/ic/ic.h"
16 #include "src/macro-assembler.h"
17 #include "test/cctest/cctest.h"
18 #include "test/cctest/heap/utils-inl.h"
19 
20 using namespace v8::base;
21 using namespace v8::internal;
22 
23 #if V8_DOUBLE_FIELDS_UNBOXING
24 
25 
26 //
27 // Helper functions.
28 //
29 
30 
InitializeVerifiedMapDescriptors(Map * map,DescriptorArray * descriptors,LayoutDescriptor * layout_descriptor)31 static void InitializeVerifiedMapDescriptors(
32     Map* map, DescriptorArray* descriptors,
33     LayoutDescriptor* layout_descriptor) {
34   map->InitializeDescriptors(descriptors, layout_descriptor);
35   CHECK(layout_descriptor->IsConsistentWithMap(map, true));
36 }
37 
38 
MakeString(const char * str)39 static Handle<String> MakeString(const char* str) {
40   Isolate* isolate = CcTest::i_isolate();
41   Factory* factory = isolate->factory();
42   return factory->InternalizeUtf8String(str);
43 }
44 
45 
MakeName(const char * str,int suffix)46 static Handle<String> MakeName(const char* str, int suffix) {
47   EmbeddedVector<char, 128> buffer;
48   SNPrintF(buffer, "%s%d", str, suffix);
49   return MakeString(buffer.start());
50 }
51 
52 
GetObject(const char * name)53 Handle<JSObject> GetObject(const char* name) {
54   return Handle<JSObject>::cast(
55       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(
56           CcTest::global()
57               ->Get(v8::Isolate::GetCurrent()->GetCurrentContext(),
58                     v8_str(name))
59               .ToLocalChecked())));
60 }
61 
62 
GetDoubleFieldValue(JSObject * obj,FieldIndex field_index)63 static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) {
64   if (obj->IsUnboxedDoubleField(field_index)) {
65     return obj->RawFastDoublePropertyAt(field_index);
66   } else {
67     Object* value = obj->RawFastPropertyAt(field_index);
68     CHECK(value->IsMutableHeapNumber());
69     return HeapNumber::cast(value)->value();
70   }
71 }
72 
73 const int kNumberOfBits = 32;
74 
75 
76 enum TestPropertyKind {
77   PROP_CONSTANT,
78   PROP_SMI,
79   PROP_DOUBLE,
80   PROP_TAGGED,
81   PROP_KIND_NUMBER
82 };
83 
84 static Representation representations[PROP_KIND_NUMBER] = {
85     Representation::None(), Representation::Smi(), Representation::Double(),
86     Representation::Tagged()};
87 
88 
CreateDescriptorArray(Isolate * isolate,TestPropertyKind * props,int kPropsCount)89 static Handle<DescriptorArray> CreateDescriptorArray(Isolate* isolate,
90                                                      TestPropertyKind* props,
91                                                      int kPropsCount) {
92   Factory* factory = isolate->factory();
93 
94   Handle<String> func_name = factory->InternalizeUtf8String("func");
95   Handle<JSFunction> func = factory->NewFunction(func_name);
96 
97   Handle<DescriptorArray> descriptors =
98       DescriptorArray::Allocate(isolate, 0, kPropsCount);
99 
100   int next_field_offset = 0;
101   for (int i = 0; i < kPropsCount; i++) {
102     EmbeddedVector<char, 64> buffer;
103     SNPrintF(buffer, "prop%d", i);
104     Handle<String> name = factory->InternalizeUtf8String(buffer.start());
105 
106     TestPropertyKind kind = props[i];
107 
108     if (kind == PROP_CONSTANT) {
109       DataConstantDescriptor d(name, func, NONE);
110       descriptors->Append(&d);
111 
112     } else {
113       DataDescriptor f(name, next_field_offset, NONE, representations[kind]);
114       next_field_offset += f.GetDetails().field_width_in_words();
115       descriptors->Append(&f);
116     }
117   }
118   return descriptors;
119 }
120 
121 
TEST(LayoutDescriptorBasicFast)122 TEST(LayoutDescriptorBasicFast) {
123   CcTest::InitializeVM();
124   v8::HandleScope scope(CcTest::isolate());
125 
126   LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout();
127 
128   CHECK(!layout_desc->IsSlowLayout());
129   CHECK(layout_desc->IsFastPointerLayout());
130   CHECK_EQ(kSmiValueSize, layout_desc->capacity());
131 
132   for (int i = 0; i < kSmiValueSize + 13; i++) {
133     CHECK_EQ(true, layout_desc->IsTagged(i));
134   }
135   CHECK_EQ(true, layout_desc->IsTagged(-1));
136   CHECK_EQ(true, layout_desc->IsTagged(-12347));
137   CHECK_EQ(true, layout_desc->IsTagged(15635));
138   CHECK(layout_desc->IsFastPointerLayout());
139 
140   for (int i = 0; i < kSmiValueSize; i++) {
141     layout_desc = layout_desc->SetTaggedForTesting(i, false);
142     CHECK_EQ(false, layout_desc->IsTagged(i));
143     layout_desc = layout_desc->SetTaggedForTesting(i, true);
144     CHECK_EQ(true, layout_desc->IsTagged(i));
145   }
146   CHECK(layout_desc->IsFastPointerLayout());
147 
148   int sequence_length;
149   CHECK_EQ(true, layout_desc->IsTagged(0, std::numeric_limits<int>::max(),
150                                        &sequence_length));
151   CHECK_EQ(std::numeric_limits<int>::max(), sequence_length);
152 
153   CHECK_EQ(true, layout_desc->IsTagged(0, 7, &sequence_length));
154   CHECK_EQ(7, sequence_length);
155 }
156 
157 
TEST(LayoutDescriptorBasicSlow)158 TEST(LayoutDescriptorBasicSlow) {
159   CcTest::InitializeVM();
160   Isolate* isolate = CcTest::i_isolate();
161   v8::HandleScope scope(CcTest::isolate());
162 
163   Handle<LayoutDescriptor> layout_descriptor;
164   const int kPropsCount = kSmiValueSize * 3;
165   TestPropertyKind props[kPropsCount];
166   for (int i = 0; i < kPropsCount; i++) {
167     // All properties tagged.
168     props[i] = PROP_TAGGED;
169   }
170 
171   {
172     Handle<DescriptorArray> descriptors =
173         CreateDescriptorArray(isolate, props, kPropsCount);
174 
175     Handle<Map> map = Map::Create(isolate, kPropsCount);
176 
177     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
178     CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
179     CHECK_EQ(kSmiValueSize, layout_descriptor->capacity());
180     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
181   }
182 
183   props[0] = PROP_DOUBLE;
184   props[kPropsCount - 1] = PROP_DOUBLE;
185 
186   Handle<DescriptorArray> descriptors =
187       CreateDescriptorArray(isolate, props, kPropsCount);
188 
189   {
190     int inobject_properties = kPropsCount - 1;
191     Handle<Map> map = Map::Create(isolate, inobject_properties);
192 
193     // Should be fast as the only double property is the first one.
194     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
195     CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
196     CHECK(!layout_descriptor->IsSlowLayout());
197     CHECK(!layout_descriptor->IsFastPointerLayout());
198 
199     CHECK_EQ(false, layout_descriptor->IsTagged(0));
200     for (int i = 1; i < kPropsCount; i++) {
201       CHECK_EQ(true, layout_descriptor->IsTagged(i));
202     }
203     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
204   }
205 
206   {
207     int inobject_properties = kPropsCount;
208     Handle<Map> map = Map::Create(isolate, inobject_properties);
209 
210     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
211     CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
212     CHECK(layout_descriptor->IsSlowLayout());
213     CHECK(!layout_descriptor->IsFastPointerLayout());
214     CHECK(layout_descriptor->capacity() > kSmiValueSize);
215 
216     CHECK_EQ(false, layout_descriptor->IsTagged(0));
217     CHECK_EQ(false, layout_descriptor->IsTagged(kPropsCount - 1));
218     for (int i = 1; i < kPropsCount - 1; i++) {
219       CHECK_EQ(true, layout_descriptor->IsTagged(i));
220     }
221 
222     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
223 
224     // Here we have truly slow layout descriptor, so play with the bits.
225     CHECK_EQ(true, layout_descriptor->IsTagged(-1));
226     CHECK_EQ(true, layout_descriptor->IsTagged(-12347));
227     CHECK_EQ(true, layout_descriptor->IsTagged(15635));
228 
229     LayoutDescriptor* layout_desc = *layout_descriptor;
230     // Play with the bits but leave it in consistent state with map at the end.
231     for (int i = 1; i < kPropsCount - 1; i++) {
232       layout_desc = layout_desc->SetTaggedForTesting(i, false);
233       CHECK_EQ(false, layout_desc->IsTagged(i));
234       layout_desc = layout_desc->SetTaggedForTesting(i, true);
235       CHECK_EQ(true, layout_desc->IsTagged(i));
236     }
237     CHECK(layout_desc->IsSlowLayout());
238     CHECK(!layout_desc->IsFastPointerLayout());
239     CHECK(layout_descriptor->IsConsistentWithMap(*map, true));
240   }
241 }
242 
243 
TestLayoutDescriptorQueries(int layout_descriptor_length,int * bit_flip_positions,int max_sequence_length)244 static void TestLayoutDescriptorQueries(int layout_descriptor_length,
245                                         int* bit_flip_positions,
246                                         int max_sequence_length) {
247   Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::NewForTesting(
248       CcTest::i_isolate(), layout_descriptor_length);
249   layout_descriptor_length = layout_descriptor->capacity();
250   LayoutDescriptor* layout_desc = *layout_descriptor;
251 
252   {
253     // Fill in the layout descriptor.
254     int cur_bit_flip_index = 0;
255     bool tagged = true;
256     for (int i = 0; i < layout_descriptor_length; i++) {
257       if (i == bit_flip_positions[cur_bit_flip_index]) {
258         tagged = !tagged;
259         ++cur_bit_flip_index;
260         CHECK(i < bit_flip_positions[cur_bit_flip_index]);  // check test data
261       }
262       layout_desc = layout_desc->SetTaggedForTesting(i, tagged);
263     }
264   }
265 
266   if (layout_desc->IsFastPointerLayout()) {
267     return;
268   }
269 
270   {
271     // Check queries.
272     int cur_bit_flip_index = 0;
273     bool tagged = true;
274     for (int i = 0; i < layout_descriptor_length; i++) {
275       if (i == bit_flip_positions[cur_bit_flip_index]) {
276         tagged = !tagged;
277         ++cur_bit_flip_index;
278       }
279       CHECK_EQ(tagged, layout_desc->IsTagged(i));
280 
281       int next_bit_flip_position = bit_flip_positions[cur_bit_flip_index];
282       int expected_sequence_length;
283       if (next_bit_flip_position < layout_desc->capacity()) {
284         expected_sequence_length = next_bit_flip_position - i;
285       } else {
286         expected_sequence_length = tagged ? std::numeric_limits<int>::max()
287                                           : (layout_desc->capacity() - i);
288       }
289       expected_sequence_length =
290           Min(expected_sequence_length, max_sequence_length);
291       int sequence_length;
292       CHECK_EQ(tagged,
293                layout_desc->IsTagged(i, max_sequence_length, &sequence_length));
294       CHECK(sequence_length > 0);
295 
296       CHECK_EQ(expected_sequence_length, sequence_length);
297     }
298 
299     int sequence_length;
300     CHECK_EQ(true,
301              layout_desc->IsTagged(layout_descriptor_length,
302                                    max_sequence_length, &sequence_length));
303     CHECK_EQ(max_sequence_length, sequence_length);
304   }
305 }
306 
307 
TestLayoutDescriptorQueriesFast(int max_sequence_length)308 static void TestLayoutDescriptorQueriesFast(int max_sequence_length) {
309   {
310     LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout();
311     int sequence_length;
312     for (int i = 0; i < kNumberOfBits; i++) {
313       CHECK_EQ(true,
314                layout_desc->IsTagged(i, max_sequence_length, &sequence_length));
315       CHECK(sequence_length > 0);
316       CHECK_EQ(max_sequence_length, sequence_length);
317     }
318   }
319 
320   {
321     int bit_flip_positions[] = {1000};
322     TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
323                                 max_sequence_length);
324   }
325 
326   {
327     int bit_flip_positions[] = {0, 1000};
328     TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
329                                 max_sequence_length);
330   }
331 
332   {
333     int bit_flip_positions[kNumberOfBits + 1];
334     for (int i = 0; i <= kNumberOfBits; i++) {
335       bit_flip_positions[i] = i;
336     }
337     TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
338                                 max_sequence_length);
339   }
340 
341   {
342     int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30, 1000};
343     TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
344                                 max_sequence_length);
345   }
346 
347   {
348     int bit_flip_positions[] = {0,  1,  2,  3,  5,  7,  9,
349                                 12, 15, 18, 22, 26, 29, 1000};
350     TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
351                                 max_sequence_length);
352   }
353 }
354 
355 
TEST(LayoutDescriptorQueriesFastLimited7)356 TEST(LayoutDescriptorQueriesFastLimited7) {
357   CcTest::InitializeVM();
358   v8::HandleScope scope(CcTest::isolate());
359 
360   TestLayoutDescriptorQueriesFast(7);
361 }
362 
363 
TEST(LayoutDescriptorQueriesFastLimited13)364 TEST(LayoutDescriptorQueriesFastLimited13) {
365   CcTest::InitializeVM();
366   v8::HandleScope scope(CcTest::isolate());
367 
368   TestLayoutDescriptorQueriesFast(13);
369 }
370 
371 
TEST(LayoutDescriptorQueriesFastUnlimited)372 TEST(LayoutDescriptorQueriesFastUnlimited) {
373   CcTest::InitializeVM();
374   v8::HandleScope scope(CcTest::isolate());
375 
376   TestLayoutDescriptorQueriesFast(std::numeric_limits<int>::max());
377 }
378 
379 
TestLayoutDescriptorQueriesSlow(int max_sequence_length)380 static void TestLayoutDescriptorQueriesSlow(int max_sequence_length) {
381   {
382     int bit_flip_positions[] = {10000};
383     TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
384                                 max_sequence_length);
385   }
386 
387   {
388     int bit_flip_positions[] = {0, 10000};
389     TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
390                                 max_sequence_length);
391   }
392 
393   {
394     int bit_flip_positions[kMaxNumberOfDescriptors + 1];
395     for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
396       bit_flip_positions[i] = i;
397     }
398     bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
399     TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
400                                 max_sequence_length);
401   }
402 
403   {
404     int bit_flip_positions[] = {3,  7,  8,  10, 15,  21,   30,
405                                 37, 54, 80, 99, 383, 10000};
406     TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
407                                 max_sequence_length);
408   }
409 
410   {
411     int bit_flip_positions[] = {0,   10,  20,  30,  50,  70,  90,
412                                 120, 150, 180, 220, 260, 290, 10000};
413     TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
414                                 max_sequence_length);
415   }
416 
417   {
418     int bit_flip_positions[kMaxNumberOfDescriptors + 1];
419     int cur = 0;
420     for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
421       bit_flip_positions[i] = cur;
422       cur = (cur + 1) * 2;
423     }
424     CHECK(cur < 10000);
425     bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
426     TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
427                                 max_sequence_length);
428   }
429 
430   {
431     int bit_flip_positions[kMaxNumberOfDescriptors + 1];
432     int cur = 3;
433     for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
434       bit_flip_positions[i] = cur;
435       cur = (cur + 1) * 2;
436     }
437     CHECK(cur < 10000);
438     bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
439     TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
440                                 max_sequence_length);
441   }
442 }
443 
444 
TEST(LayoutDescriptorQueriesSlowLimited7)445 TEST(LayoutDescriptorQueriesSlowLimited7) {
446   CcTest::InitializeVM();
447   v8::HandleScope scope(CcTest::isolate());
448 
449   TestLayoutDescriptorQueriesSlow(7);
450 }
451 
452 
TEST(LayoutDescriptorQueriesSlowLimited13)453 TEST(LayoutDescriptorQueriesSlowLimited13) {
454   CcTest::InitializeVM();
455   v8::HandleScope scope(CcTest::isolate());
456 
457   TestLayoutDescriptorQueriesSlow(13);
458 }
459 
460 
TEST(LayoutDescriptorQueriesSlowLimited42)461 TEST(LayoutDescriptorQueriesSlowLimited42) {
462   CcTest::InitializeVM();
463   v8::HandleScope scope(CcTest::isolate());
464 
465   TestLayoutDescriptorQueriesSlow(42);
466 }
467 
468 
TEST(LayoutDescriptorQueriesSlowUnlimited)469 TEST(LayoutDescriptorQueriesSlowUnlimited) {
470   CcTest::InitializeVM();
471   v8::HandleScope scope(CcTest::isolate());
472 
473   TestLayoutDescriptorQueriesSlow(std::numeric_limits<int>::max());
474 }
475 
476 
TEST(LayoutDescriptorCreateNewFast)477 TEST(LayoutDescriptorCreateNewFast) {
478   CcTest::InitializeVM();
479   Isolate* isolate = CcTest::i_isolate();
480   v8::HandleScope scope(CcTest::isolate());
481 
482   Handle<LayoutDescriptor> layout_descriptor;
483   TestPropertyKind props[] = {
484       PROP_CONSTANT,
485       PROP_TAGGED,  // field #0
486       PROP_CONSTANT,
487       PROP_DOUBLE,  // field #1
488       PROP_CONSTANT,
489       PROP_TAGGED,  // field #2
490       PROP_CONSTANT,
491   };
492   const int kPropsCount = arraysize(props);
493 
494   Handle<DescriptorArray> descriptors =
495       CreateDescriptorArray(isolate, props, kPropsCount);
496 
497   {
498     Handle<Map> map = Map::Create(isolate, 0);
499     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
500     CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
501     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
502   }
503 
504   {
505     Handle<Map> map = Map::Create(isolate, 1);
506     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
507     CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
508     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
509   }
510 
511   {
512     Handle<Map> map = Map::Create(isolate, 2);
513     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
514     CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
515     CHECK(!layout_descriptor->IsSlowLayout());
516     CHECK_EQ(true, layout_descriptor->IsTagged(0));
517     CHECK_EQ(false, layout_descriptor->IsTagged(1));
518     CHECK_EQ(true, layout_descriptor->IsTagged(2));
519     CHECK_EQ(true, layout_descriptor->IsTagged(125));
520     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
521   }
522 }
523 
524 
TEST(LayoutDescriptorCreateNewSlow)525 TEST(LayoutDescriptorCreateNewSlow) {
526   CcTest::InitializeVM();
527   Isolate* isolate = CcTest::i_isolate();
528   v8::HandleScope scope(CcTest::isolate());
529 
530   Handle<LayoutDescriptor> layout_descriptor;
531   const int kPropsCount = kSmiValueSize * 3;
532   TestPropertyKind props[kPropsCount];
533   for (int i = 0; i < kPropsCount; i++) {
534     props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
535   }
536 
537   Handle<DescriptorArray> descriptors =
538       CreateDescriptorArray(isolate, props, kPropsCount);
539 
540   {
541     Handle<Map> map = Map::Create(isolate, 0);
542     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
543     CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
544     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
545   }
546 
547   {
548     Handle<Map> map = Map::Create(isolate, 1);
549     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
550     CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
551     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
552   }
553 
554   {
555     Handle<Map> map = Map::Create(isolate, 2);
556     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
557     CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
558     CHECK(!layout_descriptor->IsSlowLayout());
559     CHECK_EQ(true, layout_descriptor->IsTagged(0));
560     CHECK_EQ(false, layout_descriptor->IsTagged(1));
561     CHECK_EQ(true, layout_descriptor->IsTagged(2));
562     CHECK_EQ(true, layout_descriptor->IsTagged(125));
563     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
564   }
565 
566   {
567     int inobject_properties = kPropsCount / 2;
568     Handle<Map> map = Map::Create(isolate, inobject_properties);
569     layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
570     CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
571     CHECK(layout_descriptor->IsSlowLayout());
572     for (int i = 0; i < inobject_properties; i++) {
573       // PROP_DOUBLE has index 1 among DATA properties.
574       const bool tagged = (i % (PROP_KIND_NUMBER - 1)) != 1;
575       CHECK_EQ(tagged, layout_descriptor->IsTagged(i));
576     }
577     // Every property after inobject_properties must be tagged.
578     for (int i = inobject_properties; i < kPropsCount; i++) {
579       CHECK_EQ(true, layout_descriptor->IsTagged(i));
580     }
581     InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
582 
583     // Now test LayoutDescriptor::cast_gc_safe().
584     Handle<LayoutDescriptor> layout_descriptor_copy =
585         LayoutDescriptor::New(map, descriptors, kPropsCount);
586 
587     LayoutDescriptor* layout_desc = *layout_descriptor;
588     CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc));
589     CHECK_EQ(layout_desc, LayoutDescriptor::cast_gc_safe(layout_desc));
590     CHECK(layout_descriptor->IsFixedTypedArrayBase());
591     // Now make it look like a forwarding pointer to layout_descriptor_copy.
592     MapWord map_word = layout_desc->map_word();
593     CHECK(!map_word.IsForwardingAddress());
594     layout_desc->set_map_word(
595         MapWord::FromForwardingAddress(*layout_descriptor_copy));
596     CHECK(layout_desc->map_word().IsForwardingAddress());
597     CHECK_EQ(*layout_descriptor_copy,
598              LayoutDescriptor::cast_gc_safe(layout_desc));
599 
600     // Restore it back.
601     layout_desc->set_map_word(map_word);
602     CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc));
603   }
604 }
605 
606 
TestLayoutDescriptorAppend(Isolate * isolate,int inobject_properties,TestPropertyKind * props,int kPropsCount)607 static Handle<LayoutDescriptor> TestLayoutDescriptorAppend(
608     Isolate* isolate, int inobject_properties, TestPropertyKind* props,
609     int kPropsCount) {
610   Factory* factory = isolate->factory();
611 
612   Handle<String> func_name = factory->InternalizeUtf8String("func");
613   Handle<JSFunction> func = factory->NewFunction(func_name);
614 
615   Handle<DescriptorArray> descriptors =
616       DescriptorArray::Allocate(isolate, 0, kPropsCount);
617 
618   Handle<Map> map = Map::Create(isolate, inobject_properties);
619   map->InitializeDescriptors(*descriptors,
620                              LayoutDescriptor::FastPointerLayout());
621 
622   int next_field_offset = 0;
623   for (int i = 0; i < kPropsCount; i++) {
624     EmbeddedVector<char, 64> buffer;
625     SNPrintF(buffer, "prop%d", i);
626     Handle<String> name = factory->InternalizeUtf8String(buffer.start());
627 
628     Handle<LayoutDescriptor> layout_descriptor;
629     TestPropertyKind kind = props[i];
630     if (kind == PROP_CONSTANT) {
631       DataConstantDescriptor d(name, func, NONE);
632       layout_descriptor = LayoutDescriptor::ShareAppend(map, d.GetDetails());
633       descriptors->Append(&d);
634 
635     } else {
636       DataDescriptor f(name, next_field_offset, NONE, representations[kind]);
637       int field_width_in_words = f.GetDetails().field_width_in_words();
638       next_field_offset += field_width_in_words;
639       layout_descriptor = LayoutDescriptor::ShareAppend(map, f.GetDetails());
640       descriptors->Append(&f);
641 
642       int field_index = f.GetDetails().field_index();
643       bool is_inobject = field_index < map->GetInObjectProperties();
644       for (int bit = 0; bit < field_width_in_words; bit++) {
645         CHECK_EQ(is_inobject && (kind == PROP_DOUBLE),
646                  !layout_descriptor->IsTagged(field_index + bit));
647       }
648       CHECK(layout_descriptor->IsTagged(next_field_offset));
649     }
650     map->InitializeDescriptors(*descriptors, *layout_descriptor);
651   }
652   Handle<LayoutDescriptor> layout_descriptor(map->layout_descriptor(), isolate);
653   CHECK(layout_descriptor->IsConsistentWithMap(*map, true));
654   return layout_descriptor;
655 }
656 
657 
TEST(LayoutDescriptorAppend)658 TEST(LayoutDescriptorAppend) {
659   CcTest::InitializeVM();
660   Isolate* isolate = CcTest::i_isolate();
661   v8::HandleScope scope(CcTest::isolate());
662 
663   Handle<LayoutDescriptor> layout_descriptor;
664   const int kPropsCount = kSmiValueSize * 3;
665   TestPropertyKind props[kPropsCount];
666   for (int i = 0; i < kPropsCount; i++) {
667     props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
668   }
669 
670   layout_descriptor =
671       TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount);
672   CHECK(!layout_descriptor->IsSlowLayout());
673 
674   layout_descriptor =
675       TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount);
676   CHECK(!layout_descriptor->IsSlowLayout());
677 
678   layout_descriptor =
679       TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount);
680   CHECK(!layout_descriptor->IsSlowLayout());
681 
682   layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2,
683                                                  props, kPropsCount);
684   CHECK(layout_descriptor->IsSlowLayout());
685 
686   layout_descriptor =
687       TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount);
688   CHECK(layout_descriptor->IsSlowLayout());
689 }
690 
691 
TEST(LayoutDescriptorAppendAllDoubles)692 TEST(LayoutDescriptorAppendAllDoubles) {
693   CcTest::InitializeVM();
694   Isolate* isolate = CcTest::i_isolate();
695   v8::HandleScope scope(CcTest::isolate());
696 
697   Handle<LayoutDescriptor> layout_descriptor;
698   const int kPropsCount = kSmiValueSize * 3;
699   TestPropertyKind props[kPropsCount];
700   for (int i = 0; i < kPropsCount; i++) {
701     props[i] = PROP_DOUBLE;
702   }
703 
704   layout_descriptor =
705       TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount);
706   CHECK(!layout_descriptor->IsSlowLayout());
707 
708   layout_descriptor =
709       TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount);
710   CHECK(!layout_descriptor->IsSlowLayout());
711 
712   layout_descriptor =
713       TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount);
714   CHECK(!layout_descriptor->IsSlowLayout());
715 
716   layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize + 1,
717                                                  props, kPropsCount);
718   CHECK(layout_descriptor->IsSlowLayout());
719 
720   layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2,
721                                                  props, kPropsCount);
722   CHECK(layout_descriptor->IsSlowLayout());
723 
724   layout_descriptor =
725       TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount);
726   CHECK(layout_descriptor->IsSlowLayout());
727 
728   {
729     // Ensure layout descriptor switches into slow mode at the right moment.
730     layout_descriptor =
731         TestLayoutDescriptorAppend(isolate, kPropsCount, props, kSmiValueSize);
732     CHECK(!layout_descriptor->IsSlowLayout());
733 
734     layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props,
735                                                    kSmiValueSize + 1);
736     CHECK(layout_descriptor->IsSlowLayout());
737   }
738 }
739 
740 
TestLayoutDescriptorAppendIfFastOrUseFull(Isolate * isolate,int inobject_properties,Handle<DescriptorArray> descriptors,int number_of_descriptors)741 static Handle<LayoutDescriptor> TestLayoutDescriptorAppendIfFastOrUseFull(
742     Isolate* isolate, int inobject_properties,
743     Handle<DescriptorArray> descriptors, int number_of_descriptors) {
744   Handle<Map> initial_map = Map::Create(isolate, inobject_properties);
745 
746   Handle<LayoutDescriptor> full_layout_descriptor = LayoutDescriptor::New(
747       initial_map, descriptors, descriptors->number_of_descriptors());
748 
749   int nof = 0;
750   bool switched_to_slow_mode = false;
751 
752   // This method calls LayoutDescriptor::AppendIfFastOrUseFull() internally
753   // and does all the required map-descriptors related book keeping.
754   Handle<Map> last_map = Map::AddMissingTransitionsForTesting(
755       initial_map, descriptors, full_layout_descriptor);
756 
757   // Follow back pointers to construct a sequence of maps from |map|
758   // to |last_map|.
759   int descriptors_length = descriptors->number_of_descriptors();
760   std::vector<Handle<Map>> maps(descriptors_length);
761   {
762     CHECK(last_map->is_stable());
763     Map* map = *last_map;
764     for (int i = 0; i < descriptors_length; i++) {
765       maps[descriptors_length - 1 - i] = handle(map, isolate);
766       Object* maybe_map = map->GetBackPointer();
767       CHECK(maybe_map->IsMap());
768       map = Map::cast(maybe_map);
769       CHECK(!map->is_stable());
770     }
771     CHECK_EQ(1, maps[0]->NumberOfOwnDescriptors());
772   }
773 
774   Handle<Map> map;
775   // Now check layout descriptors of all intermediate maps.
776   for (int i = 0; i < number_of_descriptors; i++) {
777     PropertyDetails details = descriptors->GetDetails(i);
778     map = maps[i];
779     LayoutDescriptor* layout_desc = map->layout_descriptor();
780 
781     if (layout_desc->IsSlowLayout()) {
782       switched_to_slow_mode = true;
783       CHECK_EQ(*full_layout_descriptor, layout_desc);
784     } else {
785       CHECK(!switched_to_slow_mode);
786       if (details.type() == DATA) {
787         nof++;
788         int field_index = details.field_index();
789         int field_width_in_words = details.field_width_in_words();
790 
791         bool is_inobject = field_index < map->GetInObjectProperties();
792         for (int bit = 0; bit < field_width_in_words; bit++) {
793           CHECK_EQ(is_inobject && details.representation().IsDouble(),
794                    !layout_desc->IsTagged(field_index + bit));
795         }
796         CHECK(layout_desc->IsTagged(field_index + field_width_in_words));
797       }
798     }
799     CHECK(map->layout_descriptor()->IsConsistentWithMap(*map));
800   }
801 
802   Handle<LayoutDescriptor> layout_descriptor(map->GetLayoutDescriptor(),
803                                              isolate);
804   CHECK(layout_descriptor->IsConsistentWithMap(*map));
805   return layout_descriptor;
806 }
807 
808 
TEST(LayoutDescriptorAppendIfFastOrUseFull)809 TEST(LayoutDescriptorAppendIfFastOrUseFull) {
810   CcTest::InitializeVM();
811   Isolate* isolate = CcTest::i_isolate();
812   v8::HandleScope scope(CcTest::isolate());
813 
814   Handle<LayoutDescriptor> layout_descriptor;
815   const int kPropsCount = kSmiValueSize * 3;
816   TestPropertyKind props[kPropsCount];
817   for (int i = 0; i < kPropsCount; i++) {
818     props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
819   }
820   Handle<DescriptorArray> descriptors =
821       CreateDescriptorArray(isolate, props, kPropsCount);
822 
823   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
824       isolate, 0, descriptors, kPropsCount);
825   CHECK(!layout_descriptor->IsSlowLayout());
826 
827   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
828       isolate, 13, descriptors, kPropsCount);
829   CHECK(!layout_descriptor->IsSlowLayout());
830 
831   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
832       isolate, kSmiValueSize, descriptors, kPropsCount);
833   CHECK(!layout_descriptor->IsSlowLayout());
834 
835   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
836       isolate, kSmiValueSize * 2, descriptors, kPropsCount);
837   CHECK(layout_descriptor->IsSlowLayout());
838 
839   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
840       isolate, kPropsCount, descriptors, kPropsCount);
841   CHECK(layout_descriptor->IsSlowLayout());
842 }
843 
844 
TEST(LayoutDescriptorAppendIfFastOrUseFullAllDoubles)845 TEST(LayoutDescriptorAppendIfFastOrUseFullAllDoubles) {
846   CcTest::InitializeVM();
847   Isolate* isolate = CcTest::i_isolate();
848   v8::HandleScope scope(CcTest::isolate());
849 
850   Handle<LayoutDescriptor> layout_descriptor;
851   const int kPropsCount = kSmiValueSize * 3;
852   TestPropertyKind props[kPropsCount];
853   for (int i = 0; i < kPropsCount; i++) {
854     props[i] = PROP_DOUBLE;
855   }
856   Handle<DescriptorArray> descriptors =
857       CreateDescriptorArray(isolate, props, kPropsCount);
858 
859   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
860       isolate, 0, descriptors, kPropsCount);
861   CHECK(!layout_descriptor->IsSlowLayout());
862 
863   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
864       isolate, 13, descriptors, kPropsCount);
865   CHECK(!layout_descriptor->IsSlowLayout());
866 
867   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
868       isolate, kSmiValueSize, descriptors, kPropsCount);
869   CHECK(!layout_descriptor->IsSlowLayout());
870 
871   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
872       isolate, kSmiValueSize + 1, descriptors, kPropsCount);
873   CHECK(layout_descriptor->IsSlowLayout());
874 
875   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
876       isolate, kSmiValueSize * 2, descriptors, kPropsCount);
877   CHECK(layout_descriptor->IsSlowLayout());
878 
879   layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
880       isolate, kPropsCount, descriptors, kPropsCount);
881   CHECK(layout_descriptor->IsSlowLayout());
882 
883   {
884     // Ensure layout descriptor switches into slow mode at the right moment.
885     layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
886         isolate, kPropsCount, descriptors, kSmiValueSize);
887     CHECK(!layout_descriptor->IsSlowLayout());
888 
889     layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
890         isolate, kPropsCount, descriptors, kSmiValueSize + 1);
891     CHECK(layout_descriptor->IsSlowLayout());
892   }
893 }
894 
895 
TEST(Regress436816)896 TEST(Regress436816) {
897   CcTest::InitializeVM();
898   Isolate* isolate = CcTest::i_isolate();
899   Factory* factory = isolate->factory();
900   v8::HandleScope scope(CcTest::isolate());
901 
902   const int kPropsCount = kSmiValueSize * 3;
903   TestPropertyKind props[kPropsCount];
904   for (int i = 0; i < kPropsCount; i++) {
905     props[i] = PROP_DOUBLE;
906   }
907   Handle<DescriptorArray> descriptors =
908       CreateDescriptorArray(isolate, props, kPropsCount);
909 
910   Handle<Map> map = Map::Create(isolate, kPropsCount);
911   Handle<LayoutDescriptor> layout_descriptor =
912       LayoutDescriptor::New(map, descriptors, kPropsCount);
913   map->InitializeDescriptors(*descriptors, *layout_descriptor);
914 
915   Handle<JSObject> object = factory->NewJSObjectFromMap(map, TENURED);
916 
917   Address fake_address = reinterpret_cast<Address>(~kHeapObjectTagMask);
918   HeapObject* fake_object = HeapObject::FromAddress(fake_address);
919   CHECK(fake_object->IsHeapObject());
920 
921   double boom_value = bit_cast<double>(fake_object);
922   for (int i = 0; i < kPropsCount; i++) {
923     FieldIndex index = FieldIndex::ForDescriptor(*map, i);
924     CHECK(map->IsUnboxedDoubleField(index));
925     object->RawFastDoublePropertyAtPut(index, boom_value);
926   }
927   CHECK(object->HasFastProperties());
928   CHECK(!object->map()->HasFastPointerLayout());
929 
930   Handle<Map> normalized_map =
931       Map::Normalize(map, KEEP_INOBJECT_PROPERTIES, "testing");
932   JSObject::MigrateToMap(object, normalized_map);
933   CHECK(!object->HasFastProperties());
934   CHECK(object->map()->HasFastPointerLayout());
935 
936   // Trigger GCs and heap verification.
937   CcTest::heap()->CollectAllGarbage();
938 }
939 
940 
TEST(DescriptorArrayTrimming)941 TEST(DescriptorArrayTrimming) {
942   CcTest::InitializeVM();
943   v8::HandleScope scope(CcTest::isolate());
944   Isolate* isolate = CcTest::i_isolate();
945 
946   const int kFieldCount = 128;
947   const int kSplitFieldIndex = 32;
948   const int kTrimmedLayoutDescriptorLength = 64;
949 
950   Handle<HeapType> any_type = HeapType::Any(isolate);
951   Handle<Map> map = Map::Create(isolate, kFieldCount);
952   for (int i = 0; i < kSplitFieldIndex; i++) {
953     map = Map::CopyWithField(map, MakeName("prop", i), any_type, NONE,
954                              Representation::Smi(),
955                              INSERT_TRANSITION).ToHandleChecked();
956   }
957   map = Map::CopyWithField(map, MakeName("dbl", kSplitFieldIndex), any_type,
958                            NONE, Representation::Double(),
959                            INSERT_TRANSITION).ToHandleChecked();
960   CHECK(map->layout_descriptor()->IsConsistentWithMap(*map, true));
961   CHECK(map->layout_descriptor()->IsSlowLayout());
962   CHECK(map->owns_descriptors());
963   CHECK_EQ(2, map->layout_descriptor()->length());
964 
965   {
966     // Add transitions to double fields.
967     v8::HandleScope scope(CcTest::isolate());
968 
969     Handle<Map> tmp_map = map;
970     for (int i = kSplitFieldIndex + 1; i < kFieldCount; i++) {
971       tmp_map = Map::CopyWithField(tmp_map, MakeName("dbl", i), any_type, NONE,
972                                    Representation::Double(),
973                                    INSERT_TRANSITION).ToHandleChecked();
974       CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true));
975     }
976     // Check that descriptors are shared.
977     CHECK(tmp_map->owns_descriptors());
978     CHECK_EQ(map->instance_descriptors(), tmp_map->instance_descriptors());
979     CHECK_EQ(map->layout_descriptor(), tmp_map->layout_descriptor());
980   }
981   CHECK(map->layout_descriptor()->IsSlowLayout());
982   CHECK_EQ(4, map->layout_descriptor()->length());
983 
984   // The unused tail of the layout descriptor is now "durty" because of sharing.
985   CHECK(map->layout_descriptor()->IsConsistentWithMap(*map));
986   for (int i = kSplitFieldIndex + 1; i < kTrimmedLayoutDescriptorLength; i++) {
987     CHECK(!map->layout_descriptor()->IsTagged(i));
988   }
989   CHECK_LT(map->NumberOfOwnDescriptors(),
990            map->instance_descriptors()->number_of_descriptors());
991 
992   // Call GC that should trim both |map|'s descriptor array and layout
993   // descriptor.
994   CcTest::heap()->CollectAllGarbage();
995 
996   // The unused tail of the layout descriptor is now "clean" again.
997   CHECK(map->layout_descriptor()->IsConsistentWithMap(*map, true));
998   CHECK(map->owns_descriptors());
999   CHECK_EQ(map->NumberOfOwnDescriptors(),
1000            map->instance_descriptors()->number_of_descriptors());
1001   CHECK(map->layout_descriptor()->IsSlowLayout());
1002   CHECK_EQ(2, map->layout_descriptor()->length());
1003 
1004   {
1005     // Add transitions to tagged fields.
1006     v8::HandleScope scope(CcTest::isolate());
1007 
1008     Handle<Map> tmp_map = map;
1009     for (int i = kSplitFieldIndex + 1; i < kFieldCount - 1; i++) {
1010       tmp_map = Map::CopyWithField(tmp_map, MakeName("tagged", i), any_type,
1011                                    NONE, Representation::Tagged(),
1012                                    INSERT_TRANSITION).ToHandleChecked();
1013       CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true));
1014     }
1015     tmp_map = Map::CopyWithField(tmp_map, MakeString("dbl"), any_type, NONE,
1016                                  Representation::Double(),
1017                                  INSERT_TRANSITION).ToHandleChecked();
1018     CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true));
1019     // Check that descriptors are shared.
1020     CHECK(tmp_map->owns_descriptors());
1021     CHECK_EQ(map->instance_descriptors(), tmp_map->instance_descriptors());
1022   }
1023   CHECK(map->layout_descriptor()->IsSlowLayout());
1024 }
1025 
1026 
TEST(DoScavenge)1027 TEST(DoScavenge) {
1028   CcTest::InitializeVM();
1029   v8::HandleScope scope(CcTest::isolate());
1030   Isolate* isolate = CcTest::i_isolate();
1031   Factory* factory = isolate->factory();
1032 
1033   // The plan: create |obj| with double field in new space, do scanvenge so
1034   // that |obj| is moved to old space, construct a double value that looks like
1035   // a pointer to "from space" pointer. Do scavenge one more time and ensure
1036   // that it didn't crash or corrupt the double value stored in the object.
1037 
1038   Handle<HeapType> any_type = HeapType::Any(isolate);
1039   Handle<Map> map = Map::Create(isolate, 10);
1040   map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE,
1041                            Representation::Double(),
1042                            INSERT_TRANSITION).ToHandleChecked();
1043 
1044   // Create object in new space.
1045   Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED);
1046 
1047   Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5);
1048   obj->WriteToField(0, *heap_number);
1049 
1050   {
1051     // Ensure the object is properly set up.
1052     FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0);
1053     CHECK(field_index.is_inobject() && field_index.is_double());
1054     CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index));
1055     CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index));
1056   }
1057   CHECK(isolate->heap()->new_space()->Contains(*obj));
1058 
1059   // Do scavenge so that |obj| is moved to survivor space.
1060   CcTest::heap()->CollectGarbage(i::NEW_SPACE);
1061 
1062   // Create temp object in the new space.
1063   Handle<JSArray> temp = factory->NewJSArray(FAST_ELEMENTS);
1064   CHECK(isolate->heap()->new_space()->Contains(*temp));
1065 
1066   // Construct a double value that looks like a pointer to the new space object
1067   // and store it into the obj.
1068   Address fake_object = reinterpret_cast<Address>(*temp) + kPointerSize;
1069   double boom_value = bit_cast<double>(fake_object);
1070 
1071   FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0);
1072   Handle<HeapNumber> boom_number = factory->NewHeapNumber(boom_value, MUTABLE);
1073   obj->FastPropertyAtPut(field_index, *boom_number);
1074 
1075   // Now |obj| moves to old gen and it has a double field that looks like
1076   // a pointer to a from semi-space.
1077   CcTest::heap()->CollectGarbage(i::NEW_SPACE, "boom");
1078 
1079   CHECK(isolate->heap()->old_space()->Contains(*obj));
1080 
1081   CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index));
1082 }
1083 
1084 
TEST(DoScavengeWithIncrementalWriteBarrier)1085 TEST(DoScavengeWithIncrementalWriteBarrier) {
1086   if (FLAG_never_compact || !FLAG_incremental_marking) return;
1087   CcTest::InitializeVM();
1088   v8::HandleScope scope(CcTest::isolate());
1089   Isolate* isolate = CcTest::i_isolate();
1090   Factory* factory = isolate->factory();
1091   Heap* heap = CcTest::heap();
1092   PagedSpace* old_space = heap->old_space();
1093 
1094   // The plan: create |obj_value| in old space and ensure that it is allocated
1095   // on evacuation candidate page, create |obj| with double and tagged fields
1096   // in new space and write |obj_value| to tagged field of |obj|, do two
1097   // scavenges to promote |obj| to old space, a GC in old space and ensure that
1098   // the tagged value was properly updated after candidates evacuation.
1099 
1100   Handle<HeapType> any_type = HeapType::Any(isolate);
1101   Handle<Map> map = Map::Create(isolate, 10);
1102   map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE,
1103                            Representation::Double(),
1104                            INSERT_TRANSITION).ToHandleChecked();
1105   map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE,
1106                            Representation::Tagged(),
1107                            INSERT_TRANSITION).ToHandleChecked();
1108 
1109   // Create |obj_value| in old space.
1110   Handle<HeapObject> obj_value;
1111   Page* ec_page;
1112   {
1113     AlwaysAllocateScope always_allocate(isolate);
1114     // Make sure |obj_value| is placed on an old-space evacuation candidate.
1115     SimulateFullSpace(old_space);
1116     obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS,
1117                                     Strength::WEAK, TENURED);
1118     ec_page = Page::FromAddress(obj_value->address());
1119   }
1120 
1121   // Create object in new space.
1122   Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED);
1123 
1124   Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5);
1125   obj->WriteToField(0, *heap_number);
1126   obj->WriteToField(1, *obj_value);
1127 
1128   {
1129     // Ensure the object is properly set up.
1130     FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0);
1131     CHECK(field_index.is_inobject() && field_index.is_double());
1132     CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index));
1133     CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index));
1134 
1135     field_index = FieldIndex::ForDescriptor(*map, 1);
1136     CHECK(field_index.is_inobject() && !field_index.is_double());
1137     CHECK(!map->IsUnboxedDoubleField(field_index));
1138   }
1139   CHECK(isolate->heap()->new_space()->Contains(*obj));
1140 
1141   // Heap is ready, force |ec_page| to become an evacuation candidate and
1142   // simulate incremental marking.
1143   FLAG_stress_compaction = true;
1144   FLAG_manual_evacuation_candidates_selection = true;
1145   ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
1146   SimulateIncrementalMarking(heap);
1147   // Disable stress compaction mode in order to let GC do scavenge.
1148   FLAG_stress_compaction = false;
1149 
1150   // Check that everything is ready for triggering incremental write barrier
1151   // during scavenge (i.e. that |obj| is black and incremental marking is
1152   // in compacting mode and |obj_value|'s page is an evacuation candidate).
1153   IncrementalMarking* marking = heap->incremental_marking();
1154   CHECK(marking->IsCompacting());
1155   CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj)));
1156   CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1157 
1158   // Trigger GCs so that |obj| moves to old gen.
1159   heap->CollectGarbage(i::NEW_SPACE);  // in survivor space now
1160   heap->CollectGarbage(i::NEW_SPACE);  // in old gen now
1161 
1162   CHECK(isolate->heap()->old_space()->Contains(*obj));
1163   CHECK(isolate->heap()->old_space()->Contains(*obj_value));
1164   CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1165 
1166   heap->CollectGarbage(i::OLD_SPACE, "boom");
1167 
1168   // |obj_value| must be evacuated.
1169   CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1170 
1171   FieldIndex field_index = FieldIndex::ForDescriptor(*map, 1);
1172   CHECK_EQ(*obj_value, obj->RawFastPropertyAt(field_index));
1173 }
1174 
1175 
TestLayoutDescriptorHelper(Isolate * isolate,int inobject_properties,Handle<DescriptorArray> descriptors,int number_of_descriptors)1176 static void TestLayoutDescriptorHelper(Isolate* isolate,
1177                                        int inobject_properties,
1178                                        Handle<DescriptorArray> descriptors,
1179                                        int number_of_descriptors) {
1180   Handle<Map> map = Map::Create(isolate, inobject_properties);
1181 
1182   Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New(
1183       map, descriptors, descriptors->number_of_descriptors());
1184   InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor);
1185 
1186   LayoutDescriptorHelper helper(*map);
1187   bool all_fields_tagged = true;
1188 
1189   int instance_size = map->instance_size();
1190 
1191   int end_offset = instance_size * 2;
1192   int first_non_tagged_field_offset = end_offset;
1193   for (int i = 0; i < number_of_descriptors; i++) {
1194     PropertyDetails details = descriptors->GetDetails(i);
1195     if (details.type() != DATA) continue;
1196     FieldIndex index = FieldIndex::ForDescriptor(*map, i);
1197     if (!index.is_inobject()) continue;
1198     all_fields_tagged &= !details.representation().IsDouble();
1199     bool expected_tagged = !index.is_double();
1200     if (!expected_tagged) {
1201       first_non_tagged_field_offset =
1202           Min(first_non_tagged_field_offset, index.offset());
1203     }
1204 
1205     int end_of_region_offset;
1206     CHECK_EQ(expected_tagged, helper.IsTagged(index.offset()));
1207     CHECK_EQ(expected_tagged, helper.IsTagged(index.offset(), instance_size,
1208                                               &end_of_region_offset));
1209     CHECK(end_of_region_offset > 0);
1210     CHECK(end_of_region_offset % kPointerSize == 0);
1211     CHECK(end_of_region_offset <= instance_size);
1212 
1213     for (int offset = index.offset(); offset < end_of_region_offset;
1214          offset += kPointerSize) {
1215       CHECK_EQ(expected_tagged, helper.IsTagged(index.offset()));
1216     }
1217     if (end_of_region_offset < instance_size) {
1218       CHECK_EQ(!expected_tagged, helper.IsTagged(end_of_region_offset));
1219     } else {
1220       CHECK_EQ(true, helper.IsTagged(end_of_region_offset));
1221     }
1222   }
1223 
1224   for (int offset = 0; offset < JSObject::kHeaderSize; offset += kPointerSize) {
1225     // Header queries
1226     CHECK_EQ(true, helper.IsTagged(offset));
1227     int end_of_region_offset;
1228     CHECK_EQ(true, helper.IsTagged(offset, end_offset, &end_of_region_offset));
1229     CHECK_EQ(first_non_tagged_field_offset, end_of_region_offset);
1230 
1231     // Out of bounds queries
1232     CHECK_EQ(true, helper.IsTagged(offset + instance_size));
1233   }
1234 
1235   CHECK_EQ(all_fields_tagged, helper.all_fields_tagged());
1236 }
1237 
1238 
TEST(LayoutDescriptorHelperMixed)1239 TEST(LayoutDescriptorHelperMixed) {
1240   CcTest::InitializeVM();
1241   Isolate* isolate = CcTest::i_isolate();
1242   v8::HandleScope scope(CcTest::isolate());
1243 
1244   Handle<LayoutDescriptor> layout_descriptor;
1245   const int kPropsCount = kSmiValueSize * 3;
1246   TestPropertyKind props[kPropsCount];
1247   for (int i = 0; i < kPropsCount; i++) {
1248     props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
1249   }
1250   Handle<DescriptorArray> descriptors =
1251       CreateDescriptorArray(isolate, props, kPropsCount);
1252 
1253   TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
1254 
1255   TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
1256 
1257   TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
1258 
1259   TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
1260                              kPropsCount);
1261 
1262   TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
1263 }
1264 
1265 
TEST(LayoutDescriptorHelperAllTagged)1266 TEST(LayoutDescriptorHelperAllTagged) {
1267   CcTest::InitializeVM();
1268   Isolate* isolate = CcTest::i_isolate();
1269   v8::HandleScope scope(CcTest::isolate());
1270 
1271   Handle<LayoutDescriptor> layout_descriptor;
1272   const int kPropsCount = kSmiValueSize * 3;
1273   TestPropertyKind props[kPropsCount];
1274   for (int i = 0; i < kPropsCount; i++) {
1275     props[i] = PROP_TAGGED;
1276   }
1277   Handle<DescriptorArray> descriptors =
1278       CreateDescriptorArray(isolate, props, kPropsCount);
1279 
1280   TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
1281 
1282   TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
1283 
1284   TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
1285 
1286   TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
1287                              kPropsCount);
1288 
1289   TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
1290 }
1291 
1292 
TEST(LayoutDescriptorHelperAllDoubles)1293 TEST(LayoutDescriptorHelperAllDoubles) {
1294   CcTest::InitializeVM();
1295   Isolate* isolate = CcTest::i_isolate();
1296   v8::HandleScope scope(CcTest::isolate());
1297 
1298   Handle<LayoutDescriptor> layout_descriptor;
1299   const int kPropsCount = kSmiValueSize * 3;
1300   TestPropertyKind props[kPropsCount];
1301   for (int i = 0; i < kPropsCount; i++) {
1302     props[i] = PROP_DOUBLE;
1303   }
1304   Handle<DescriptorArray> descriptors =
1305       CreateDescriptorArray(isolate, props, kPropsCount);
1306 
1307   TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
1308 
1309   TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
1310 
1311   TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
1312 
1313   TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
1314                              kPropsCount);
1315 
1316   TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
1317 }
1318 
1319 
TEST(LayoutDescriptorSharing)1320 TEST(LayoutDescriptorSharing) {
1321   CcTest::InitializeVM();
1322   v8::HandleScope scope(CcTest::isolate());
1323   Isolate* isolate = CcTest::i_isolate();
1324   Handle<HeapType> any_type = HeapType::Any(isolate);
1325 
1326   Handle<Map> split_map;
1327   {
1328     Handle<Map> map = Map::Create(isolate, 64);
1329     for (int i = 0; i < 32; i++) {
1330       Handle<String> name = MakeName("prop", i);
1331       map = Map::CopyWithField(map, name, any_type, NONE, Representation::Smi(),
1332                                INSERT_TRANSITION).ToHandleChecked();
1333     }
1334     split_map = Map::CopyWithField(map, MakeString("dbl"), any_type, NONE,
1335                                    Representation::Double(),
1336                                    INSERT_TRANSITION).ToHandleChecked();
1337   }
1338   Handle<LayoutDescriptor> split_layout_descriptor(
1339       split_map->layout_descriptor(), isolate);
1340   CHECK(split_layout_descriptor->IsConsistentWithMap(*split_map, true));
1341   CHECK(split_layout_descriptor->IsSlowLayout());
1342   CHECK(split_map->owns_descriptors());
1343 
1344   Handle<Map> map1 = Map::CopyWithField(split_map, MakeString("foo"), any_type,
1345                                         NONE, Representation::Double(),
1346                                         INSERT_TRANSITION).ToHandleChecked();
1347   CHECK(!split_map->owns_descriptors());
1348   CHECK_EQ(*split_layout_descriptor, split_map->layout_descriptor());
1349 
1350   // Layout descriptors should be shared with |split_map|.
1351   CHECK(map1->owns_descriptors());
1352   CHECK_EQ(*split_layout_descriptor, map1->layout_descriptor());
1353   CHECK(map1->layout_descriptor()->IsConsistentWithMap(*map1, true));
1354 
1355   Handle<Map> map2 = Map::CopyWithField(split_map, MakeString("bar"), any_type,
1356                                         NONE, Representation::Tagged(),
1357                                         INSERT_TRANSITION).ToHandleChecked();
1358 
1359   // Layout descriptors should not be shared with |split_map|.
1360   CHECK(map2->owns_descriptors());
1361   CHECK_NE(*split_layout_descriptor, map2->layout_descriptor());
1362   CHECK(map2->layout_descriptor()->IsConsistentWithMap(*map2, true));
1363 }
1364 
1365 
TEST(StoreBufferScanOnScavenge)1366 TEST(StoreBufferScanOnScavenge) {
1367   CcTest::InitializeVM();
1368   Isolate* isolate = CcTest::i_isolate();
1369   Factory* factory = isolate->factory();
1370   v8::HandleScope scope(CcTest::isolate());
1371 
1372   Handle<HeapType> any_type = HeapType::Any(isolate);
1373   Handle<Map> map = Map::Create(isolate, 10);
1374   map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE,
1375                            Representation::Double(),
1376                            INSERT_TRANSITION).ToHandleChecked();
1377 
1378   // Create object in new space.
1379   Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED);
1380 
1381   Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5);
1382   obj->WriteToField(0, *heap_number);
1383 
1384   {
1385     // Ensure the object is properly set up.
1386     DescriptorArray* descriptors = map->instance_descriptors();
1387     CHECK(descriptors->GetDetails(0).representation().IsDouble());
1388     FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0);
1389     CHECK(field_index.is_inobject() && field_index.is_double());
1390     CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index));
1391     CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index));
1392   }
1393   CHECK(isolate->heap()->new_space()->Contains(*obj));
1394 
1395   // Trigger GCs so that the newly allocated object moves to old gen.
1396   CcTest::heap()->CollectGarbage(i::NEW_SPACE);  // in survivor space now
1397   CcTest::heap()->CollectGarbage(i::NEW_SPACE);  // in old gen now
1398 
1399   CHECK(isolate->heap()->old_space()->Contains(*obj));
1400 
1401   // Create temp object in the new space.
1402   Handle<JSArray> temp = factory->NewJSArray(FAST_ELEMENTS);
1403   CHECK(isolate->heap()->new_space()->Contains(*temp));
1404 
1405   // Construct a double value that looks like a pointer to the new space object
1406   // and store it into the obj.
1407   Address fake_object = reinterpret_cast<Address>(*temp) + kPointerSize;
1408   double boom_value = bit_cast<double>(fake_object);
1409 
1410   FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0);
1411   Handle<HeapNumber> boom_number = factory->NewHeapNumber(boom_value, MUTABLE);
1412   obj->FastPropertyAtPut(field_index, *boom_number);
1413 
1414   // Enforce scan on scavenge for the obj's page.
1415   MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
1416   chunk->set_scan_on_scavenge(true);
1417 
1418   // Trigger GCs and force evacuation. Should not crash there.
1419   CcTest::heap()->CollectAllGarbage();
1420 
1421   CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index));
1422 }
1423 
1424 
TestWriteBarrier(Handle<Map> map,Handle<Map> new_map,int tagged_descriptor,int double_descriptor,bool check_tagged_value=true)1425 static void TestWriteBarrier(Handle<Map> map, Handle<Map> new_map,
1426                              int tagged_descriptor, int double_descriptor,
1427                              bool check_tagged_value = true) {
1428   FLAG_stress_compaction = true;
1429   FLAG_manual_evacuation_candidates_selection = true;
1430   Isolate* isolate = CcTest::i_isolate();
1431   Factory* factory = isolate->factory();
1432   Heap* heap = CcTest::heap();
1433   PagedSpace* old_space = heap->old_space();
1434 
1435   // The plan: create |obj| by |map| in old space, create |obj_value| in
1436   // new space and ensure that write barrier is triggered when |obj_value| is
1437   // written to property |tagged_descriptor| of |obj|.
1438   // Then migrate object to |new_map| and set proper value for property
1439   // |double_descriptor|. Call GC and ensure that it did not crash during
1440   // store buffer entries updating.
1441 
1442   Handle<JSObject> obj;
1443   Handle<HeapObject> obj_value;
1444   {
1445     AlwaysAllocateScope always_allocate(isolate);
1446     obj = factory->NewJSObjectFromMap(map, TENURED);
1447     CHECK(old_space->Contains(*obj));
1448 
1449     obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS);
1450   }
1451 
1452   CHECK(heap->InNewSpace(*obj_value));
1453 
1454   {
1455     FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor);
1456     const int n = 153;
1457     for (int i = 0; i < n; i++) {
1458       obj->FastPropertyAtPut(index, *obj_value);
1459     }
1460   }
1461 
1462   // Migrate |obj| to |new_map| which should shift fields and put the
1463   // |boom_value| to the slot that was earlier recorded by write barrier.
1464   JSObject::MigrateToMap(obj, new_map);
1465 
1466   Address fake_object = reinterpret_cast<Address>(*obj_value) + kPointerSize;
1467   double boom_value = bit_cast<double>(fake_object);
1468 
1469   FieldIndex double_field_index =
1470       FieldIndex::ForDescriptor(*new_map, double_descriptor);
1471   CHECK(obj->IsUnboxedDoubleField(double_field_index));
1472   obj->RawFastDoublePropertyAtPut(double_field_index, boom_value);
1473 
1474   // Trigger GC to evacuate all candidates.
1475   CcTest::heap()->CollectGarbage(NEW_SPACE, "boom");
1476 
1477   if (check_tagged_value) {
1478     FieldIndex tagged_field_index =
1479         FieldIndex::ForDescriptor(*new_map, tagged_descriptor);
1480     CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index));
1481   }
1482   CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index));
1483 }
1484 
1485 
TestIncrementalWriteBarrier(Handle<Map> map,Handle<Map> new_map,int tagged_descriptor,int double_descriptor,bool check_tagged_value=true)1486 static void TestIncrementalWriteBarrier(Handle<Map> map, Handle<Map> new_map,
1487                                         int tagged_descriptor,
1488                                         int double_descriptor,
1489                                         bool check_tagged_value = true) {
1490   if (FLAG_never_compact || !FLAG_incremental_marking) return;
1491   FLAG_manual_evacuation_candidates_selection = true;
1492   Isolate* isolate = CcTest::i_isolate();
1493   Factory* factory = isolate->factory();
1494   Heap* heap = CcTest::heap();
1495   PagedSpace* old_space = heap->old_space();
1496 
1497   // The plan: create |obj| by |map| in old space, create |obj_value| in
1498   // old space and ensure it end up in evacuation candidate page. Start
1499   // incremental marking and ensure that incremental write barrier is triggered
1500   // when |obj_value| is written to property |tagged_descriptor| of |obj|.
1501   // Then migrate object to |new_map| and set proper value for property
1502   // |double_descriptor|. Call GC and ensure that it did not crash during
1503   // slots buffer entries updating.
1504 
1505   Handle<JSObject> obj;
1506   Handle<HeapObject> obj_value;
1507   Page* ec_page;
1508   {
1509     AlwaysAllocateScope always_allocate(isolate);
1510     obj = factory->NewJSObjectFromMap(map, TENURED);
1511     CHECK(old_space->Contains(*obj));
1512 
1513     // Make sure |obj_value| is placed on an old-space evacuation candidate.
1514     SimulateFullSpace(old_space);
1515     obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS,
1516                                     Strength::WEAK, TENURED);
1517     ec_page = Page::FromAddress(obj_value->address());
1518     CHECK_NE(ec_page, Page::FromAddress(obj->address()));
1519   }
1520 
1521   // Heap is ready, force |ec_page| to become an evacuation candidate and
1522   // simulate incremental marking.
1523   ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
1524   SimulateIncrementalMarking(heap);
1525 
1526   // Check that everything is ready for triggering incremental write barrier
1527   // (i.e. that both |obj| and |obj_value| are black and the marking phase is
1528   // still active and |obj_value|'s page is indeed an evacuation candidate).
1529   IncrementalMarking* marking = heap->incremental_marking();
1530   CHECK(marking->IsMarking());
1531   CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj)));
1532   CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj_value)));
1533   CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1534 
1535   // Trigger incremental write barrier, which should add a slot to |ec_page|'s
1536   // slots buffer.
1537   {
1538     int slots_buffer_len = SlotsBuffer::SizeOfChain(ec_page->slots_buffer());
1539     FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor);
1540     const int n = SlotsBuffer::kNumberOfElements + 10;
1541     for (int i = 0; i < n; i++) {
1542       obj->FastPropertyAtPut(index, *obj_value);
1543     }
1544     // Ensure that the slot was actually added to the |ec_page|'s slots buffer.
1545     CHECK_EQ(slots_buffer_len + n,
1546              SlotsBuffer::SizeOfChain(ec_page->slots_buffer()));
1547   }
1548 
1549   // Migrate |obj| to |new_map| which should shift fields and put the
1550   // |boom_value| to the slot that was earlier recorded by incremental write
1551   // barrier.
1552   JSObject::MigrateToMap(obj, new_map);
1553 
1554   double boom_value = bit_cast<double>(UINT64_C(0xbaad0176a37c28e1));
1555 
1556   FieldIndex double_field_index =
1557       FieldIndex::ForDescriptor(*new_map, double_descriptor);
1558   CHECK(obj->IsUnboxedDoubleField(double_field_index));
1559   obj->RawFastDoublePropertyAtPut(double_field_index, boom_value);
1560 
1561   // Trigger GC to evacuate all candidates.
1562   CcTest::heap()->CollectGarbage(OLD_SPACE, "boom");
1563 
1564   // Ensure that the values are still there and correct.
1565   CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1566 
1567   if (check_tagged_value) {
1568     FieldIndex tagged_field_index =
1569         FieldIndex::ForDescriptor(*new_map, tagged_descriptor);
1570     CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index));
1571   }
1572   CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index));
1573 }
1574 
1575 
1576 enum WriteBarrierKind { OLD_TO_OLD_WRITE_BARRIER, OLD_TO_NEW_WRITE_BARRIER };
TestWriteBarrierObjectShiftFieldsRight(WriteBarrierKind write_barrier_kind)1577 static void TestWriteBarrierObjectShiftFieldsRight(
1578     WriteBarrierKind write_barrier_kind) {
1579   CcTest::InitializeVM();
1580   Isolate* isolate = CcTest::i_isolate();
1581   v8::HandleScope scope(CcTest::isolate());
1582 
1583   Handle<HeapType> any_type = HeapType::Any(isolate);
1584 
1585   CompileRun("function func() { return 1; }");
1586 
1587   Handle<JSObject> func = GetObject("func");
1588 
1589   Handle<Map> map = Map::Create(isolate, 10);
1590   map = Map::CopyWithConstant(map, MakeName("prop", 0), func, NONE,
1591                               INSERT_TRANSITION).ToHandleChecked();
1592   map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE,
1593                            Representation::Double(),
1594                            INSERT_TRANSITION).ToHandleChecked();
1595   map = Map::CopyWithField(map, MakeName("prop", 2), any_type, NONE,
1596                            Representation::Tagged(),
1597                            INSERT_TRANSITION).ToHandleChecked();
1598 
1599   // Shift fields right by turning constant property to a field.
1600   Handle<Map> new_map = Map::ReconfigureProperty(
1601       map, 0, kData, NONE, Representation::Tagged(), any_type, FORCE_FIELD);
1602 
1603   if (write_barrier_kind == OLD_TO_NEW_WRITE_BARRIER) {
1604     TestWriteBarrier(map, new_map, 2, 1);
1605   } else {
1606     CHECK_EQ(OLD_TO_OLD_WRITE_BARRIER, write_barrier_kind);
1607     TestIncrementalWriteBarrier(map, new_map, 2, 1);
1608   }
1609 }
1610 
1611 
1612 // TODO(ishell): enable when this issue is fixed.
DISABLED_TEST(WriteBarrierObjectShiftFieldsRight)1613 DISABLED_TEST(WriteBarrierObjectShiftFieldsRight) {
1614   TestWriteBarrierObjectShiftFieldsRight(OLD_TO_NEW_WRITE_BARRIER);
1615 }
1616 
1617 
TEST(IncrementalWriteBarrierObjectShiftFieldsRight)1618 TEST(IncrementalWriteBarrierObjectShiftFieldsRight) {
1619   TestWriteBarrierObjectShiftFieldsRight(OLD_TO_OLD_WRITE_BARRIER);
1620 }
1621 
1622 
1623 // TODO(ishell): add respective tests for property kind reconfiguring from
1624 // accessor field to double, once accessor fields are supported by
1625 // Map::ReconfigureProperty().
1626 
1627 
1628 // TODO(ishell): add respective tests for fast property removal case once
1629 // Map::ReconfigureProperty() supports that.
1630 
1631 #endif
1632