• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include <stdlib.h>
29 #include <utility>
30 
31 #include "src/compilation-cache.h"
32 #include "src/context-measure.h"
33 #include "src/deoptimizer.h"
34 #include "src/elements.h"
35 #include "src/execution.h"
36 #include "src/factory.h"
37 #include "src/field-type.h"
38 #include "src/global-handles.h"
39 #include "src/heap/gc-tracer.h"
40 #include "src/heap/memory-reducer.h"
41 #include "src/ic/ic.h"
42 #include "src/macro-assembler.h"
43 #include "src/regexp/jsregexp.h"
44 #include "src/snapshot/snapshot.h"
45 #include "test/cctest/cctest.h"
46 #include "test/cctest/heap/heap-tester.h"
47 #include "test/cctest/heap/heap-utils.h"
48 #include "test/cctest/test-feedback-vector.h"
49 
50 
51 namespace v8 {
52 namespace internal {
53 
CheckMap(Map * map,int type,int instance_size)54 static void CheckMap(Map* map, int type, int instance_size) {
55   CHECK(map->IsHeapObject());
56 #ifdef DEBUG
57   CHECK(CcTest::heap()->Contains(map));
58 #endif
59   CHECK_EQ(CcTest::heap()->meta_map(), map->map());
60   CHECK_EQ(type, map->instance_type());
61   CHECK_EQ(instance_size, map->instance_size());
62 }
63 
64 
TEST(HeapMaps)65 TEST(HeapMaps) {
66   CcTest::InitializeVM();
67   Heap* heap = CcTest::heap();
68   CheckMap(heap->meta_map(), MAP_TYPE, Map::kSize);
69   CheckMap(heap->heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize);
70 #define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \
71   CheckMap(heap->type##_map(), SIMD128_VALUE_TYPE, Type::kSize);
72   SIMD128_TYPES(SIMD128_TYPE)
73 #undef SIMD128_TYPE
74   CheckMap(heap->fixed_array_map(), FIXED_ARRAY_TYPE, kVariableSizeSentinel);
75   CheckMap(heap->string_map(), STRING_TYPE, kVariableSizeSentinel);
76 }
77 
78 
CheckOddball(Isolate * isolate,Object * obj,const char * string)79 static void CheckOddball(Isolate* isolate, Object* obj, const char* string) {
80   CHECK(obj->IsOddball());
81   Handle<Object> handle(obj, isolate);
82   Object* print_string = *Object::ToString(isolate, handle).ToHandleChecked();
83   CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string)));
84 }
85 
86 
CheckSmi(Isolate * isolate,int value,const char * string)87 static void CheckSmi(Isolate* isolate, int value, const char* string) {
88   Handle<Object> handle(Smi::FromInt(value), isolate);
89   Object* print_string = *Object::ToString(isolate, handle).ToHandleChecked();
90   CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string)));
91 }
92 
93 
CheckNumber(Isolate * isolate,double value,const char * string)94 static void CheckNumber(Isolate* isolate, double value, const char* string) {
95   Handle<Object> number = isolate->factory()->NewNumber(value);
96   CHECK(number->IsNumber());
97   Handle<Object> print_string =
98       Object::ToString(isolate, number).ToHandleChecked();
99   CHECK(String::cast(*print_string)->IsUtf8EqualTo(CStrVector(string)));
100 }
101 
102 
CheckFindCodeObject(Isolate * isolate)103 static void CheckFindCodeObject(Isolate* isolate) {
104   // Test FindCodeObject
105 #define __ assm.
106 
107   Assembler assm(isolate, NULL, 0);
108 
109   __ nop();  // supported on all architectures
110 
111   CodeDesc desc;
112   assm.GetCode(&desc);
113   Handle<Code> code = isolate->factory()->NewCode(
114       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
115   CHECK(code->IsCode());
116 
117   HeapObject* obj = HeapObject::cast(*code);
118   Address obj_addr = obj->address();
119 
120   for (int i = 0; i < obj->Size(); i += kPointerSize) {
121     Object* found = isolate->FindCodeObject(obj_addr + i);
122     CHECK_EQ(*code, found);
123   }
124 
125   Handle<Code> copy = isolate->factory()->NewCode(
126       desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
127   HeapObject* obj_copy = HeapObject::cast(*copy);
128   Object* not_right = isolate->FindCodeObject(obj_copy->address() +
129                                               obj_copy->Size() / 2);
130   CHECK(not_right != *code);
131 }
132 
133 
TEST(HandleNull)134 TEST(HandleNull) {
135   CcTest::InitializeVM();
136   Isolate* isolate = CcTest::i_isolate();
137   HandleScope outer_scope(isolate);
138   LocalContext context;
139   Handle<Object> n(static_cast<Object*>(nullptr), isolate);
140   CHECK(!n.is_null());
141 }
142 
143 
TEST(HeapObjects)144 TEST(HeapObjects) {
145   CcTest::InitializeVM();
146   Isolate* isolate = CcTest::i_isolate();
147   Factory* factory = isolate->factory();
148   Heap* heap = isolate->heap();
149 
150   HandleScope sc(isolate);
151   Handle<Object> value = factory->NewNumber(1.000123);
152   CHECK(value->IsHeapNumber());
153   CHECK(value->IsNumber());
154   CHECK_EQ(1.000123, value->Number());
155 
156   value = factory->NewNumber(1.0);
157   CHECK(value->IsSmi());
158   CHECK(value->IsNumber());
159   CHECK_EQ(1.0, value->Number());
160 
161   value = factory->NewNumberFromInt(1024);
162   CHECK(value->IsSmi());
163   CHECK(value->IsNumber());
164   CHECK_EQ(1024.0, value->Number());
165 
166   value = factory->NewNumberFromInt(Smi::kMinValue);
167   CHECK(value->IsSmi());
168   CHECK(value->IsNumber());
169   CHECK_EQ(Smi::kMinValue, Handle<Smi>::cast(value)->value());
170 
171   value = factory->NewNumberFromInt(Smi::kMaxValue);
172   CHECK(value->IsSmi());
173   CHECK(value->IsNumber());
174   CHECK_EQ(Smi::kMaxValue, Handle<Smi>::cast(value)->value());
175 
176 #if !defined(V8_TARGET_ARCH_64_BIT)
177   // TODO(lrn): We need a NumberFromIntptr function in order to test this.
178   value = factory->NewNumberFromInt(Smi::kMinValue - 1);
179   CHECK(value->IsHeapNumber());
180   CHECK(value->IsNumber());
181   CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number());
182 #endif
183 
184   value = factory->NewNumberFromUint(static_cast<uint32_t>(Smi::kMaxValue) + 1);
185   CHECK(value->IsHeapNumber());
186   CHECK(value->IsNumber());
187   CHECK_EQ(static_cast<double>(static_cast<uint32_t>(Smi::kMaxValue) + 1),
188            value->Number());
189 
190   value = factory->NewNumberFromUint(static_cast<uint32_t>(1) << 31);
191   CHECK(value->IsHeapNumber());
192   CHECK(value->IsNumber());
193   CHECK_EQ(static_cast<double>(static_cast<uint32_t>(1) << 31),
194            value->Number());
195 
196   // nan oddball checks
197   CHECK(factory->nan_value()->IsNumber());
198   CHECK(std::isnan(factory->nan_value()->Number()));
199 
200   Handle<String> s = factory->NewStringFromStaticChars("fisk hest ");
201   CHECK(s->IsString());
202   CHECK_EQ(10, s->length());
203 
204   Handle<String> object_string = Handle<String>::cast(factory->Object_string());
205   Handle<JSGlobalObject> global(
206       CcTest::i_isolate()->context()->global_object());
207   CHECK(Just(true) == JSReceiver::HasOwnProperty(global, object_string));
208 
209   // Check ToString for oddballs
210   CheckOddball(isolate, heap->true_value(), "true");
211   CheckOddball(isolate, heap->false_value(), "false");
212   CheckOddball(isolate, heap->null_value(), "null");
213   CheckOddball(isolate, heap->undefined_value(), "undefined");
214 
215   // Check ToString for Smis
216   CheckSmi(isolate, 0, "0");
217   CheckSmi(isolate, 42, "42");
218   CheckSmi(isolate, -42, "-42");
219 
220   // Check ToString for Numbers
221   CheckNumber(isolate, 1.1, "1.1");
222 
223   CheckFindCodeObject(isolate);
224 }
225 
226 
227 template <typename T, typename LANE_TYPE, int LANES>
CheckSimdValue(T * value,LANE_TYPE lane_values[LANES],LANE_TYPE other_value)228 static void CheckSimdValue(T* value, LANE_TYPE lane_values[LANES],
229                            LANE_TYPE other_value) {
230   // Check against lane_values, and check that all lanes can be set to
231   // other_value without disturbing the other lanes.
232   for (int i = 0; i < LANES; i++) {
233     CHECK_EQ(lane_values[i], value->get_lane(i));
234   }
235   for (int i = 0; i < LANES; i++) {
236     value->set_lane(i, other_value);  // change the value
237     for (int j = 0; j < LANES; j++) {
238       if (i != j)
239         CHECK_EQ(lane_values[j], value->get_lane(j));
240       else
241         CHECK_EQ(other_value, value->get_lane(j));
242     }
243     value->set_lane(i, lane_values[i]);  // restore the lane
244   }
245   CHECK(value->BooleanValue());  // SIMD values are 'true'.
246 }
247 
248 
TEST(SimdObjects)249 TEST(SimdObjects) {
250   CcTest::InitializeVM();
251   Isolate* isolate = CcTest::i_isolate();
252   Factory* factory = isolate->factory();
253 
254   HandleScope sc(isolate);
255 
256   // Float32x4
257   {
258     float lanes[4] = {1, 2, 3, 4};
259     float quiet_NaN = std::numeric_limits<float>::quiet_NaN();
260     float signaling_NaN = std::numeric_limits<float>::signaling_NaN();
261 
262     Handle<Float32x4> value = factory->NewFloat32x4(lanes);
263     CHECK(value->IsFloat32x4());
264     CheckSimdValue<Float32x4, float, 4>(*value, lanes, 3.14f);
265 
266     // Check special lane values.
267     value->set_lane(1, -0.0);
268     CHECK_EQ(-0.0f, value->get_lane(1));
269     CHECK(std::signbit(value->get_lane(1)));  // Sign bit should be preserved.
270     value->set_lane(2, quiet_NaN);
271     CHECK(std::isnan(value->get_lane(2)));
272     value->set_lane(3, signaling_NaN);
273     CHECK(std::isnan(value->get_lane(3)));
274 
275 #ifdef OBJECT_PRINT
276     // Check value printing.
277     {
278       value = factory->NewFloat32x4(lanes);
279       std::ostringstream os;
280       value->Float32x4Print(os);
281       CHECK_EQ("1, 2, 3, 4", os.str());
282     }
283     {
284       float special_lanes[4] = {0, -0.0, quiet_NaN, signaling_NaN};
285       value = factory->NewFloat32x4(special_lanes);
286       std::ostringstream os;
287       value->Float32x4Print(os);
288       // Value printing doesn't preserve signed zeroes.
289       CHECK_EQ("0, 0, NaN, NaN", os.str());
290     }
291 #endif  // OBJECT_PRINT
292   }
293   // Int32x4
294   {
295     int32_t lanes[4] = {1, 2, 3, 4};
296 
297     Handle<Int32x4> value = factory->NewInt32x4(lanes);
298     CHECK(value->IsInt32x4());
299     CheckSimdValue<Int32x4, int32_t, 4>(*value, lanes, 3);
300 
301 #ifdef OBJECT_PRINT
302     std::ostringstream os;
303     value->Int32x4Print(os);
304     CHECK_EQ("1, 2, 3, 4", os.str());
305 #endif  // OBJECT_PRINT
306   }
307   // Uint32x4
308   {
309     uint32_t lanes[4] = {1, 2, 3, 4};
310 
311     Handle<Uint32x4> value = factory->NewUint32x4(lanes);
312     CHECK(value->IsUint32x4());
313     CheckSimdValue<Uint32x4, uint32_t, 4>(*value, lanes, 3);
314 
315 #ifdef OBJECT_PRINT
316     std::ostringstream os;
317     value->Uint32x4Print(os);
318     CHECK_EQ("1, 2, 3, 4", os.str());
319 #endif  // OBJECT_PRINT
320   }
321   // Bool32x4
322   {
323     bool lanes[4] = {true, false, true, false};
324 
325     Handle<Bool32x4> value = factory->NewBool32x4(lanes);
326     CHECK(value->IsBool32x4());
327     CheckSimdValue<Bool32x4, bool, 4>(*value, lanes, false);
328 
329 #ifdef OBJECT_PRINT
330     std::ostringstream os;
331     value->Bool32x4Print(os);
332     CHECK_EQ("true, false, true, false", os.str());
333 #endif  // OBJECT_PRINT
334   }
335   // Int16x8
336   {
337     int16_t lanes[8] = {1, 2, 3, 4, 5, 6, 7, 8};
338 
339     Handle<Int16x8> value = factory->NewInt16x8(lanes);
340     CHECK(value->IsInt16x8());
341     CheckSimdValue<Int16x8, int16_t, 8>(*value, lanes, 32767);
342 
343 #ifdef OBJECT_PRINT
344     std::ostringstream os;
345     value->Int16x8Print(os);
346     CHECK_EQ("1, 2, 3, 4, 5, 6, 7, 8", os.str());
347 #endif  // OBJECT_PRINT
348   }
349   // Uint16x8
350   {
351     uint16_t lanes[8] = {1, 2, 3, 4, 5, 6, 7, 8};
352 
353     Handle<Uint16x8> value = factory->NewUint16x8(lanes);
354     CHECK(value->IsUint16x8());
355     CheckSimdValue<Uint16x8, uint16_t, 8>(*value, lanes, 32767);
356 
357 #ifdef OBJECT_PRINT
358     std::ostringstream os;
359     value->Uint16x8Print(os);
360     CHECK_EQ("1, 2, 3, 4, 5, 6, 7, 8", os.str());
361 #endif  // OBJECT_PRINT
362   }
363   // Bool16x8
364   {
365     bool lanes[8] = {true, false, true, false, true, false, true, false};
366 
367     Handle<Bool16x8> value = factory->NewBool16x8(lanes);
368     CHECK(value->IsBool16x8());
369     CheckSimdValue<Bool16x8, bool, 8>(*value, lanes, false);
370 
371 #ifdef OBJECT_PRINT
372     std::ostringstream os;
373     value->Bool16x8Print(os);
374     CHECK_EQ("true, false, true, false, true, false, true, false", os.str());
375 #endif  // OBJECT_PRINT
376   }
377   // Int8x16
378   {
379     int8_t lanes[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
380 
381     Handle<Int8x16> value = factory->NewInt8x16(lanes);
382     CHECK(value->IsInt8x16());
383     CheckSimdValue<Int8x16, int8_t, 16>(*value, lanes, 127);
384 
385 #ifdef OBJECT_PRINT
386     std::ostringstream os;
387     value->Int8x16Print(os);
388     CHECK_EQ("1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16", os.str());
389 #endif  // OBJECT_PRINT
390   }
391   // Uint8x16
392   {
393     uint8_t lanes[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
394 
395     Handle<Uint8x16> value = factory->NewUint8x16(lanes);
396     CHECK(value->IsUint8x16());
397     CheckSimdValue<Uint8x16, uint8_t, 16>(*value, lanes, 127);
398 
399 #ifdef OBJECT_PRINT
400     std::ostringstream os;
401     value->Uint8x16Print(os);
402     CHECK_EQ("1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16", os.str());
403 #endif  // OBJECT_PRINT
404   }
405   // Bool8x16
406   {
407     bool lanes[16] = {true, false, true, false, true, false, true, false,
408                       true, false, true, false, true, false, true, false};
409 
410     Handle<Bool8x16> value = factory->NewBool8x16(lanes);
411     CHECK(value->IsBool8x16());
412     CheckSimdValue<Bool8x16, bool, 16>(*value, lanes, false);
413 
414 #ifdef OBJECT_PRINT
415     std::ostringstream os;
416     value->Bool8x16Print(os);
417     CHECK_EQ(
418         "true, false, true, false, true, false, true, false, true, false, "
419         "true, false, true, false, true, false",
420         os.str());
421 #endif  // OBJECT_PRINT
422   }
423 }
424 
425 
TEST(Tagging)426 TEST(Tagging) {
427   CcTest::InitializeVM();
428   int request = 24;
429   CHECK_EQ(request, static_cast<int>(OBJECT_POINTER_ALIGN(request)));
430   CHECK(Smi::FromInt(42)->IsSmi());
431   CHECK(Smi::FromInt(Smi::kMinValue)->IsSmi());
432   CHECK(Smi::FromInt(Smi::kMaxValue)->IsSmi());
433 }
434 
435 
TEST(GarbageCollection)436 TEST(GarbageCollection) {
437   CcTest::InitializeVM();
438   Isolate* isolate = CcTest::i_isolate();
439   Heap* heap = isolate->heap();
440   Factory* factory = isolate->factory();
441 
442   HandleScope sc(isolate);
443   // Check GC.
444   heap->CollectGarbage(NEW_SPACE);
445 
446   Handle<JSGlobalObject> global(
447       CcTest::i_isolate()->context()->global_object());
448   Handle<String> name = factory->InternalizeUtf8String("theFunction");
449   Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
450   Handle<String> prop_namex = factory->InternalizeUtf8String("theSlotx");
451   Handle<String> obj_name = factory->InternalizeUtf8String("theObject");
452   Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
453   Handle<Smi> twenty_four(Smi::FromInt(24), isolate);
454 
455   {
456     HandleScope inner_scope(isolate);
457     // Allocate a function and keep it in global object's property.
458     Handle<JSFunction> function = factory->NewFunction(name);
459     JSReceiver::SetProperty(global, name, function, SLOPPY).Check();
460     // Allocate an object.  Unrooted after leaving the scope.
461     Handle<JSObject> obj = factory->NewJSObject(function);
462     JSReceiver::SetProperty(obj, prop_name, twenty_three, SLOPPY).Check();
463     JSReceiver::SetProperty(obj, prop_namex, twenty_four, SLOPPY).Check();
464 
465     CHECK_EQ(Smi::FromInt(23),
466              *Object::GetProperty(obj, prop_name).ToHandleChecked());
467     CHECK_EQ(Smi::FromInt(24),
468              *Object::GetProperty(obj, prop_namex).ToHandleChecked());
469   }
470 
471   heap->CollectGarbage(NEW_SPACE);
472 
473   // Function should be alive.
474   CHECK(Just(true) == JSReceiver::HasOwnProperty(global, name));
475   // Check function is retained.
476   Handle<Object> func_value =
477       Object::GetProperty(global, name).ToHandleChecked();
478   CHECK(func_value->IsJSFunction());
479   Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
480 
481   {
482     HandleScope inner_scope(isolate);
483     // Allocate another object, make it reachable from global.
484     Handle<JSObject> obj = factory->NewJSObject(function);
485     JSReceiver::SetProperty(global, obj_name, obj, SLOPPY).Check();
486     JSReceiver::SetProperty(obj, prop_name, twenty_three, SLOPPY).Check();
487   }
488 
489   // After gc, it should survive.
490   heap->CollectGarbage(NEW_SPACE);
491 
492   CHECK(Just(true) == JSReceiver::HasOwnProperty(global, obj_name));
493   Handle<Object> obj =
494       Object::GetProperty(global, obj_name).ToHandleChecked();
495   CHECK(obj->IsJSObject());
496   CHECK_EQ(Smi::FromInt(23),
497            *Object::GetProperty(obj, prop_name).ToHandleChecked());
498 }
499 
500 
VerifyStringAllocation(Isolate * isolate,const char * string)501 static void VerifyStringAllocation(Isolate* isolate, const char* string) {
502   HandleScope scope(isolate);
503   Handle<String> s = isolate->factory()->NewStringFromUtf8(
504       CStrVector(string)).ToHandleChecked();
505   CHECK_EQ(StrLength(string), s->length());
506   for (int index = 0; index < s->length(); index++) {
507     CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index));
508   }
509 }
510 
511 
TEST(String)512 TEST(String) {
513   CcTest::InitializeVM();
514   Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
515 
516   VerifyStringAllocation(isolate, "a");
517   VerifyStringAllocation(isolate, "ab");
518   VerifyStringAllocation(isolate, "abc");
519   VerifyStringAllocation(isolate, "abcd");
520   VerifyStringAllocation(isolate, "fiskerdrengen er paa havet");
521 }
522 
523 
TEST(LocalHandles)524 TEST(LocalHandles) {
525   CcTest::InitializeVM();
526   Isolate* isolate = CcTest::i_isolate();
527   Factory* factory = isolate->factory();
528 
529   v8::HandleScope scope(CcTest::isolate());
530   const char* name = "Kasper the spunky";
531   Handle<String> string = factory->NewStringFromAsciiChecked(name);
532   CHECK_EQ(StrLength(name), string->length());
533 }
534 
535 
TEST(GlobalHandles)536 TEST(GlobalHandles) {
537   CcTest::InitializeVM();
538   Isolate* isolate = CcTest::i_isolate();
539   Heap* heap = isolate->heap();
540   Factory* factory = isolate->factory();
541   GlobalHandles* global_handles = isolate->global_handles();
542 
543   Handle<Object> h1;
544   Handle<Object> h2;
545   Handle<Object> h3;
546   Handle<Object> h4;
547 
548   {
549     HandleScope scope(isolate);
550 
551     Handle<Object> i = factory->NewStringFromStaticChars("fisk");
552     Handle<Object> u = factory->NewNumber(1.12344);
553 
554     h1 = global_handles->Create(*i);
555     h2 = global_handles->Create(*u);
556     h3 = global_handles->Create(*i);
557     h4 = global_handles->Create(*u);
558   }
559 
560   // after gc, it should survive
561   heap->CollectGarbage(NEW_SPACE);
562 
563   CHECK((*h1)->IsString());
564   CHECK((*h2)->IsHeapNumber());
565   CHECK((*h3)->IsString());
566   CHECK((*h4)->IsHeapNumber());
567 
568   CHECK_EQ(*h3, *h1);
569   GlobalHandles::Destroy(h1.location());
570   GlobalHandles::Destroy(h3.location());
571 
572   CHECK_EQ(*h4, *h2);
573   GlobalHandles::Destroy(h2.location());
574   GlobalHandles::Destroy(h4.location());
575 }
576 
577 
578 static bool WeakPointerCleared = false;
579 
TestWeakGlobalHandleCallback(const v8::WeakCallbackInfo<void> & data)580 static void TestWeakGlobalHandleCallback(
581     const v8::WeakCallbackInfo<void>& data) {
582   std::pair<v8::Persistent<v8::Value>*, int>* p =
583       reinterpret_cast<std::pair<v8::Persistent<v8::Value>*, int>*>(
584           data.GetParameter());
585   if (p->second == 1234) WeakPointerCleared = true;
586   p->first->Reset();
587 }
588 
589 
TEST(WeakGlobalHandlesScavenge)590 TEST(WeakGlobalHandlesScavenge) {
591   i::FLAG_stress_compaction = false;
592   CcTest::InitializeVM();
593   Isolate* isolate = CcTest::i_isolate();
594   Heap* heap = isolate->heap();
595   Factory* factory = isolate->factory();
596   GlobalHandles* global_handles = isolate->global_handles();
597 
598   WeakPointerCleared = false;
599 
600   Handle<Object> h1;
601   Handle<Object> h2;
602 
603   {
604     HandleScope scope(isolate);
605 
606     Handle<Object> i = factory->NewStringFromStaticChars("fisk");
607     Handle<Object> u = factory->NewNumber(1.12344);
608 
609     h1 = global_handles->Create(*i);
610     h2 = global_handles->Create(*u);
611   }
612 
613   std::pair<Handle<Object>*, int> handle_and_id(&h2, 1234);
614   GlobalHandles::MakeWeak(
615       h2.location(), reinterpret_cast<void*>(&handle_and_id),
616       &TestWeakGlobalHandleCallback, v8::WeakCallbackType::kParameter);
617 
618   // Scavenge treats weak pointers as normal roots.
619   heap->CollectGarbage(NEW_SPACE);
620 
621   CHECK((*h1)->IsString());
622   CHECK((*h2)->IsHeapNumber());
623 
624   CHECK(!WeakPointerCleared);
625   CHECK(!global_handles->IsNearDeath(h2.location()));
626   CHECK(!global_handles->IsNearDeath(h1.location()));
627 
628   GlobalHandles::Destroy(h1.location());
629   GlobalHandles::Destroy(h2.location());
630 }
631 
632 
TEST(WeakGlobalHandlesMark)633 TEST(WeakGlobalHandlesMark) {
634   CcTest::InitializeVM();
635   Isolate* isolate = CcTest::i_isolate();
636   Heap* heap = isolate->heap();
637   Factory* factory = isolate->factory();
638   GlobalHandles* global_handles = isolate->global_handles();
639 
640   WeakPointerCleared = false;
641 
642   Handle<Object> h1;
643   Handle<Object> h2;
644 
645   {
646     HandleScope scope(isolate);
647 
648     Handle<Object> i = factory->NewStringFromStaticChars("fisk");
649     Handle<Object> u = factory->NewNumber(1.12344);
650 
651     h1 = global_handles->Create(*i);
652     h2 = global_handles->Create(*u);
653   }
654 
655   // Make sure the objects are promoted.
656   heap->CollectGarbage(OLD_SPACE);
657   heap->CollectGarbage(NEW_SPACE);
658   CHECK(!heap->InNewSpace(*h1) && !heap->InNewSpace(*h2));
659 
660   std::pair<Handle<Object>*, int> handle_and_id(&h2, 1234);
661   GlobalHandles::MakeWeak(
662       h2.location(), reinterpret_cast<void*>(&handle_and_id),
663       &TestWeakGlobalHandleCallback, v8::WeakCallbackType::kParameter);
664   CHECK(!GlobalHandles::IsNearDeath(h1.location()));
665   CHECK(!GlobalHandles::IsNearDeath(h2.location()));
666 
667   // Incremental marking potentially marked handles before they turned weak.
668   heap->CollectAllGarbage();
669 
670   CHECK((*h1)->IsString());
671 
672   CHECK(WeakPointerCleared);
673   CHECK(!GlobalHandles::IsNearDeath(h1.location()));
674 
675   GlobalHandles::Destroy(h1.location());
676 }
677 
678 
TEST(DeleteWeakGlobalHandle)679 TEST(DeleteWeakGlobalHandle) {
680   i::FLAG_stress_compaction = false;
681   CcTest::InitializeVM();
682   Isolate* isolate = CcTest::i_isolate();
683   Heap* heap = isolate->heap();
684   Factory* factory = isolate->factory();
685   GlobalHandles* global_handles = isolate->global_handles();
686 
687   WeakPointerCleared = false;
688 
689   Handle<Object> h;
690 
691   {
692     HandleScope scope(isolate);
693 
694     Handle<Object> i = factory->NewStringFromStaticChars("fisk");
695     h = global_handles->Create(*i);
696   }
697 
698   std::pair<Handle<Object>*, int> handle_and_id(&h, 1234);
699   GlobalHandles::MakeWeak(h.location(), reinterpret_cast<void*>(&handle_and_id),
700                           &TestWeakGlobalHandleCallback,
701                           v8::WeakCallbackType::kParameter);
702 
703   // Scanvenge does not recognize weak reference.
704   heap->CollectGarbage(NEW_SPACE);
705 
706   CHECK(!WeakPointerCleared);
707 
708   // Mark-compact treats weak reference properly.
709   heap->CollectGarbage(OLD_SPACE);
710 
711   CHECK(WeakPointerCleared);
712 }
713 
TEST(DoNotPromoteWhiteObjectsOnScavenge)714 TEST(DoNotPromoteWhiteObjectsOnScavenge) {
715   CcTest::InitializeVM();
716   Isolate* isolate = CcTest::i_isolate();
717   Heap* heap = isolate->heap();
718   Factory* factory = isolate->factory();
719 
720   HandleScope scope(isolate);
721   Handle<Object> white = factory->NewStringFromStaticChars("white");
722 
723   CHECK(Marking::IsWhite(Marking::MarkBitFrom(HeapObject::cast(*white))));
724 
725   heap->CollectGarbage(NEW_SPACE);
726 
727   CHECK(heap->InNewSpace(*white));
728 }
729 
TEST(PromoteGreyOrBlackObjectsOnScavenge)730 TEST(PromoteGreyOrBlackObjectsOnScavenge) {
731   CcTest::InitializeVM();
732   Isolate* isolate = CcTest::i_isolate();
733   Heap* heap = isolate->heap();
734   Factory* factory = isolate->factory();
735 
736   HandleScope scope(isolate);
737   Handle<Object> marked = factory->NewStringFromStaticChars("marked");
738 
739   IncrementalMarking* marking = heap->incremental_marking();
740   marking->Stop();
741   heap->StartIncrementalMarking();
742   while (Marking::IsWhite(Marking::MarkBitFrom(HeapObject::cast(*marked)))) {
743     marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD,
744                   IncrementalMarking::FORCE_MARKING,
745                   IncrementalMarking::DO_NOT_FORCE_COMPLETION);
746   }
747 
748   heap->CollectGarbage(NEW_SPACE);
749 
750   CHECK(!heap->InNewSpace(*marked));
751 }
752 
TEST(BytecodeArray)753 TEST(BytecodeArray) {
754   static const uint8_t kRawBytes[] = {0xc3, 0x7e, 0xa5, 0x5a};
755   static const int kRawBytesSize = sizeof(kRawBytes);
756   static const int kFrameSize = 32;
757   static const int kParameterCount = 2;
758 
759   i::FLAG_manual_evacuation_candidates_selection = true;
760   CcTest::InitializeVM();
761   Isolate* isolate = CcTest::i_isolate();
762   Heap* heap = isolate->heap();
763   Factory* factory = isolate->factory();
764   HandleScope scope(isolate);
765 
766   heap::SimulateFullSpace(heap->old_space());
767   Handle<FixedArray> constant_pool = factory->NewFixedArray(5, TENURED);
768   for (int i = 0; i < 5; i++) {
769     Handle<Object> number = factory->NewHeapNumber(i);
770     constant_pool->set(i, *number);
771   }
772 
773   // Allocate and initialize BytecodeArray
774   Handle<BytecodeArray> array = factory->NewBytecodeArray(
775       kRawBytesSize, kRawBytes, kFrameSize, kParameterCount, constant_pool);
776 
777   CHECK(array->IsBytecodeArray());
778   CHECK_EQ(array->length(), (int)sizeof(kRawBytes));
779   CHECK_EQ(array->frame_size(), kFrameSize);
780   CHECK_EQ(array->parameter_count(), kParameterCount);
781   CHECK_EQ(array->constant_pool(), *constant_pool);
782   CHECK_LE(array->address(), array->GetFirstBytecodeAddress());
783   CHECK_GE(array->address() + array->BytecodeArraySize(),
784            array->GetFirstBytecodeAddress() + array->length());
785   for (int i = 0; i < kRawBytesSize; i++) {
786     CHECK_EQ(array->GetFirstBytecodeAddress()[i], kRawBytes[i]);
787     CHECK_EQ(array->get(i), kRawBytes[i]);
788   }
789 
790   FixedArray* old_constant_pool_address = *constant_pool;
791 
792   // Perform a full garbage collection and force the constant pool to be on an
793   // evacuation candidate.
794   Page* evac_page = Page::FromAddress(constant_pool->address());
795   evac_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
796   heap->CollectAllGarbage();
797 
798   // BytecodeArray should survive.
799   CHECK_EQ(array->length(), kRawBytesSize);
800   CHECK_EQ(array->frame_size(), kFrameSize);
801   for (int i = 0; i < kRawBytesSize; i++) {
802     CHECK_EQ(array->get(i), kRawBytes[i]);
803     CHECK_EQ(array->GetFirstBytecodeAddress()[i], kRawBytes[i]);
804   }
805 
806   // Constant pool should have been migrated.
807   CHECK_EQ(array->constant_pool(), *constant_pool);
808   CHECK_NE(array->constant_pool(), old_constant_pool_address);
809 }
810 
811 
812 static const char* not_so_random_string_table[] = {
813   "abstract",
814   "boolean",
815   "break",
816   "byte",
817   "case",
818   "catch",
819   "char",
820   "class",
821   "const",
822   "continue",
823   "debugger",
824   "default",
825   "delete",
826   "do",
827   "double",
828   "else",
829   "enum",
830   "export",
831   "extends",
832   "false",
833   "final",
834   "finally",
835   "float",
836   "for",
837   "function",
838   "goto",
839   "if",
840   "implements",
841   "import",
842   "in",
843   "instanceof",
844   "int",
845   "interface",
846   "long",
847   "native",
848   "new",
849   "null",
850   "package",
851   "private",
852   "protected",
853   "public",
854   "return",
855   "short",
856   "static",
857   "super",
858   "switch",
859   "synchronized",
860   "this",
861   "throw",
862   "throws",
863   "transient",
864   "true",
865   "try",
866   "typeof",
867   "var",
868   "void",
869   "volatile",
870   "while",
871   "with",
872   0
873 };
874 
875 
CheckInternalizedStrings(const char ** strings)876 static void CheckInternalizedStrings(const char** strings) {
877   Isolate* isolate = CcTest::i_isolate();
878   Factory* factory = isolate->factory();
879   for (const char* string = *strings; *strings != 0; string = *strings++) {
880     HandleScope scope(isolate);
881     Handle<String> a =
882         isolate->factory()->InternalizeUtf8String(CStrVector(string));
883     // InternalizeUtf8String may return a failure if a GC is needed.
884     CHECK(a->IsInternalizedString());
885     Handle<String> b = factory->InternalizeUtf8String(string);
886     CHECK_EQ(*b, *a);
887     CHECK(b->IsUtf8EqualTo(CStrVector(string)));
888     b = isolate->factory()->InternalizeUtf8String(CStrVector(string));
889     CHECK_EQ(*b, *a);
890     CHECK(b->IsUtf8EqualTo(CStrVector(string)));
891   }
892 }
893 
894 
TEST(StringTable)895 TEST(StringTable) {
896   CcTest::InitializeVM();
897 
898   v8::HandleScope sc(CcTest::isolate());
899   CheckInternalizedStrings(not_so_random_string_table);
900   CheckInternalizedStrings(not_so_random_string_table);
901 }
902 
903 
TEST(FunctionAllocation)904 TEST(FunctionAllocation) {
905   CcTest::InitializeVM();
906   Isolate* isolate = CcTest::i_isolate();
907   Factory* factory = isolate->factory();
908 
909   v8::HandleScope sc(CcTest::isolate());
910   Handle<String> name = factory->InternalizeUtf8String("theFunction");
911   Handle<JSFunction> function = factory->NewFunction(name);
912 
913   Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
914   Handle<Smi> twenty_four(Smi::FromInt(24), isolate);
915 
916   Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
917   Handle<JSObject> obj = factory->NewJSObject(function);
918   JSReceiver::SetProperty(obj, prop_name, twenty_three, SLOPPY).Check();
919   CHECK_EQ(Smi::FromInt(23),
920            *Object::GetProperty(obj, prop_name).ToHandleChecked());
921   // Check that we can add properties to function objects.
922   JSReceiver::SetProperty(function, prop_name, twenty_four, SLOPPY).Check();
923   CHECK_EQ(Smi::FromInt(24),
924            *Object::GetProperty(function, prop_name).ToHandleChecked());
925 }
926 
927 
TEST(ObjectProperties)928 TEST(ObjectProperties) {
929   CcTest::InitializeVM();
930   Isolate* isolate = CcTest::i_isolate();
931   Factory* factory = isolate->factory();
932 
933   v8::HandleScope sc(CcTest::isolate());
934   Handle<String> object_string(String::cast(CcTest::heap()->Object_string()));
935   Handle<Object> object = Object::GetProperty(
936       CcTest::i_isolate()->global_object(), object_string).ToHandleChecked();
937   Handle<JSFunction> constructor = Handle<JSFunction>::cast(object);
938   Handle<JSObject> obj = factory->NewJSObject(constructor);
939   Handle<String> first = factory->InternalizeUtf8String("first");
940   Handle<String> second = factory->InternalizeUtf8String("second");
941 
942   Handle<Smi> one(Smi::FromInt(1), isolate);
943   Handle<Smi> two(Smi::FromInt(2), isolate);
944 
945   // check for empty
946   CHECK(Just(false) == JSReceiver::HasOwnProperty(obj, first));
947 
948   // add first
949   JSReceiver::SetProperty(obj, first, one, SLOPPY).Check();
950   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, first));
951 
952   // delete first
953   CHECK(Just(true) == JSReceiver::DeleteProperty(obj, first, SLOPPY));
954   CHECK(Just(false) == JSReceiver::HasOwnProperty(obj, first));
955 
956   // add first and then second
957   JSReceiver::SetProperty(obj, first, one, SLOPPY).Check();
958   JSReceiver::SetProperty(obj, second, two, SLOPPY).Check();
959   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, first));
960   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, second));
961 
962   // delete first and then second
963   CHECK(Just(true) == JSReceiver::DeleteProperty(obj, first, SLOPPY));
964   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, second));
965   CHECK(Just(true) == JSReceiver::DeleteProperty(obj, second, SLOPPY));
966   CHECK(Just(false) == JSReceiver::HasOwnProperty(obj, first));
967   CHECK(Just(false) == JSReceiver::HasOwnProperty(obj, second));
968 
969   // add first and then second
970   JSReceiver::SetProperty(obj, first, one, SLOPPY).Check();
971   JSReceiver::SetProperty(obj, second, two, SLOPPY).Check();
972   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, first));
973   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, second));
974 
975   // delete second and then first
976   CHECK(Just(true) == JSReceiver::DeleteProperty(obj, second, SLOPPY));
977   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, first));
978   CHECK(Just(true) == JSReceiver::DeleteProperty(obj, first, SLOPPY));
979   CHECK(Just(false) == JSReceiver::HasOwnProperty(obj, first));
980   CHECK(Just(false) == JSReceiver::HasOwnProperty(obj, second));
981 
982   // check string and internalized string match
983   const char* string1 = "fisk";
984   Handle<String> s1 = factory->NewStringFromAsciiChecked(string1);
985   JSReceiver::SetProperty(obj, s1, one, SLOPPY).Check();
986   Handle<String> s1_string = factory->InternalizeUtf8String(string1);
987   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, s1_string));
988 
989   // check internalized string and string match
990   const char* string2 = "fugl";
991   Handle<String> s2_string = factory->InternalizeUtf8String(string2);
992   JSReceiver::SetProperty(obj, s2_string, one, SLOPPY).Check();
993   Handle<String> s2 = factory->NewStringFromAsciiChecked(string2);
994   CHECK(Just(true) == JSReceiver::HasOwnProperty(obj, s2));
995 }
996 
997 
TEST(JSObjectMaps)998 TEST(JSObjectMaps) {
999   CcTest::InitializeVM();
1000   Isolate* isolate = CcTest::i_isolate();
1001   Factory* factory = isolate->factory();
1002 
1003   v8::HandleScope sc(CcTest::isolate());
1004   Handle<String> name = factory->InternalizeUtf8String("theFunction");
1005   Handle<JSFunction> function = factory->NewFunction(name);
1006 
1007   Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
1008   Handle<JSObject> obj = factory->NewJSObject(function);
1009   Handle<Map> initial_map(function->initial_map());
1010 
1011   // Set a propery
1012   Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
1013   JSReceiver::SetProperty(obj, prop_name, twenty_three, SLOPPY).Check();
1014   CHECK_EQ(Smi::FromInt(23),
1015            *Object::GetProperty(obj, prop_name).ToHandleChecked());
1016 
1017   // Check the map has changed
1018   CHECK(*initial_map != obj->map());
1019 }
1020 
1021 
TEST(JSArray)1022 TEST(JSArray) {
1023   CcTest::InitializeVM();
1024   Isolate* isolate = CcTest::i_isolate();
1025   Factory* factory = isolate->factory();
1026 
1027   v8::HandleScope sc(CcTest::isolate());
1028   Handle<String> name = factory->InternalizeUtf8String("Array");
1029   Handle<Object> fun_obj = Object::GetProperty(
1030       CcTest::i_isolate()->global_object(), name).ToHandleChecked();
1031   Handle<JSFunction> function = Handle<JSFunction>::cast(fun_obj);
1032 
1033   // Allocate the object.
1034   Handle<Object> element;
1035   Handle<JSObject> object = factory->NewJSObject(function);
1036   Handle<JSArray> array = Handle<JSArray>::cast(object);
1037   // We just initialized the VM, no heap allocation failure yet.
1038   JSArray::Initialize(array, 0);
1039 
1040   // Set array length to 0.
1041   JSArray::SetLength(array, 0);
1042   CHECK_EQ(Smi::FromInt(0), array->length());
1043   // Must be in fast mode.
1044   CHECK(array->HasFastSmiOrObjectElements());
1045 
1046   // array[length] = name.
1047   JSReceiver::SetElement(isolate, array, 0, name, SLOPPY).Check();
1048   CHECK_EQ(Smi::FromInt(1), array->length());
1049   element = i::Object::GetElement(isolate, array, 0).ToHandleChecked();
1050   CHECK_EQ(*element, *name);
1051 
1052   // Set array length with larger than smi value.
1053   JSArray::SetLength(array, static_cast<uint32_t>(Smi::kMaxValue) + 1);
1054 
1055   uint32_t int_length = 0;
1056   CHECK(array->length()->ToArrayIndex(&int_length));
1057   CHECK_EQ(static_cast<uint32_t>(Smi::kMaxValue) + 1, int_length);
1058   CHECK(array->HasDictionaryElements());  // Must be in slow mode.
1059 
1060   // array[length] = name.
1061   JSReceiver::SetElement(isolate, array, int_length, name, SLOPPY).Check();
1062   uint32_t new_int_length = 0;
1063   CHECK(array->length()->ToArrayIndex(&new_int_length));
1064   CHECK_EQ(static_cast<double>(int_length), new_int_length - 1);
1065   element = Object::GetElement(isolate, array, int_length).ToHandleChecked();
1066   CHECK_EQ(*element, *name);
1067   element = Object::GetElement(isolate, array, 0).ToHandleChecked();
1068   CHECK_EQ(*element, *name);
1069 }
1070 
1071 
TEST(JSObjectCopy)1072 TEST(JSObjectCopy) {
1073   CcTest::InitializeVM();
1074   Isolate* isolate = CcTest::i_isolate();
1075   Factory* factory = isolate->factory();
1076 
1077   v8::HandleScope sc(CcTest::isolate());
1078   Handle<String> object_string(String::cast(CcTest::heap()->Object_string()));
1079   Handle<Object> object = Object::GetProperty(
1080       CcTest::i_isolate()->global_object(), object_string).ToHandleChecked();
1081   Handle<JSFunction> constructor = Handle<JSFunction>::cast(object);
1082   Handle<JSObject> obj = factory->NewJSObject(constructor);
1083   Handle<String> first = factory->InternalizeUtf8String("first");
1084   Handle<String> second = factory->InternalizeUtf8String("second");
1085 
1086   Handle<Smi> one(Smi::FromInt(1), isolate);
1087   Handle<Smi> two(Smi::FromInt(2), isolate);
1088 
1089   JSReceiver::SetProperty(obj, first, one, SLOPPY).Check();
1090   JSReceiver::SetProperty(obj, second, two, SLOPPY).Check();
1091 
1092   JSReceiver::SetElement(isolate, obj, 0, first, SLOPPY).Check();
1093   JSReceiver::SetElement(isolate, obj, 1, second, SLOPPY).Check();
1094 
1095   // Make the clone.
1096   Handle<Object> value1, value2;
1097   Handle<JSObject> clone = factory->CopyJSObject(obj);
1098   CHECK(!clone.is_identical_to(obj));
1099 
1100   value1 = Object::GetElement(isolate, obj, 0).ToHandleChecked();
1101   value2 = Object::GetElement(isolate, clone, 0).ToHandleChecked();
1102   CHECK_EQ(*value1, *value2);
1103   value1 = Object::GetElement(isolate, obj, 1).ToHandleChecked();
1104   value2 = Object::GetElement(isolate, clone, 1).ToHandleChecked();
1105   CHECK_EQ(*value1, *value2);
1106 
1107   value1 = Object::GetProperty(obj, first).ToHandleChecked();
1108   value2 = Object::GetProperty(clone, first).ToHandleChecked();
1109   CHECK_EQ(*value1, *value2);
1110   value1 = Object::GetProperty(obj, second).ToHandleChecked();
1111   value2 = Object::GetProperty(clone, second).ToHandleChecked();
1112   CHECK_EQ(*value1, *value2);
1113 
1114   // Flip the values.
1115   JSReceiver::SetProperty(clone, first, two, SLOPPY).Check();
1116   JSReceiver::SetProperty(clone, second, one, SLOPPY).Check();
1117 
1118   JSReceiver::SetElement(isolate, clone, 0, second, SLOPPY).Check();
1119   JSReceiver::SetElement(isolate, clone, 1, first, SLOPPY).Check();
1120 
1121   value1 = Object::GetElement(isolate, obj, 1).ToHandleChecked();
1122   value2 = Object::GetElement(isolate, clone, 0).ToHandleChecked();
1123   CHECK_EQ(*value1, *value2);
1124   value1 = Object::GetElement(isolate, obj, 0).ToHandleChecked();
1125   value2 = Object::GetElement(isolate, clone, 1).ToHandleChecked();
1126   CHECK_EQ(*value1, *value2);
1127 
1128   value1 = Object::GetProperty(obj, second).ToHandleChecked();
1129   value2 = Object::GetProperty(clone, first).ToHandleChecked();
1130   CHECK_EQ(*value1, *value2);
1131   value1 = Object::GetProperty(obj, first).ToHandleChecked();
1132   value2 = Object::GetProperty(clone, second).ToHandleChecked();
1133   CHECK_EQ(*value1, *value2);
1134 }
1135 
1136 
TEST(StringAllocation)1137 TEST(StringAllocation) {
1138   CcTest::InitializeVM();
1139   Isolate* isolate = CcTest::i_isolate();
1140   Factory* factory = isolate->factory();
1141 
1142   const unsigned char chars[] = { 0xe5, 0xa4, 0xa7 };
1143   for (int length = 0; length < 100; length++) {
1144     v8::HandleScope scope(CcTest::isolate());
1145     char* non_one_byte = NewArray<char>(3 * length + 1);
1146     char* one_byte = NewArray<char>(length + 1);
1147     non_one_byte[3 * length] = 0;
1148     one_byte[length] = 0;
1149     for (int i = 0; i < length; i++) {
1150       one_byte[i] = 'a';
1151       non_one_byte[3 * i] = chars[0];
1152       non_one_byte[3 * i + 1] = chars[1];
1153       non_one_byte[3 * i + 2] = chars[2];
1154     }
1155     Handle<String> non_one_byte_sym = factory->InternalizeUtf8String(
1156         Vector<const char>(non_one_byte, 3 * length));
1157     CHECK_EQ(length, non_one_byte_sym->length());
1158     Handle<String> one_byte_sym =
1159         factory->InternalizeOneByteString(OneByteVector(one_byte, length));
1160     CHECK_EQ(length, one_byte_sym->length());
1161     Handle<String> non_one_byte_str =
1162         factory->NewStringFromUtf8(Vector<const char>(non_one_byte, 3 * length))
1163             .ToHandleChecked();
1164     non_one_byte_str->Hash();
1165     CHECK_EQ(length, non_one_byte_str->length());
1166     Handle<String> one_byte_str =
1167         factory->NewStringFromUtf8(Vector<const char>(one_byte, length))
1168             .ToHandleChecked();
1169     one_byte_str->Hash();
1170     CHECK_EQ(length, one_byte_str->length());
1171     DeleteArray(non_one_byte);
1172     DeleteArray(one_byte);
1173   }
1174 }
1175 
1176 
ObjectsFoundInHeap(Heap * heap,Handle<Object> objs[],int size)1177 static int ObjectsFoundInHeap(Heap* heap, Handle<Object> objs[], int size) {
1178   // Count the number of objects found in the heap.
1179   int found_count = 0;
1180   HeapIterator iterator(heap);
1181   for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
1182     for (int i = 0; i < size; i++) {
1183       if (*objs[i] == obj) {
1184         found_count++;
1185       }
1186     }
1187   }
1188   return found_count;
1189 }
1190 
1191 
TEST(Iteration)1192 TEST(Iteration) {
1193   CcTest::InitializeVM();
1194   Isolate* isolate = CcTest::i_isolate();
1195   Factory* factory = isolate->factory();
1196   v8::HandleScope scope(CcTest::isolate());
1197 
1198   // Array of objects to scan haep for.
1199   const int objs_count = 6;
1200   Handle<Object> objs[objs_count];
1201   int next_objs_index = 0;
1202 
1203   // Allocate a JS array to OLD_SPACE and NEW_SPACE
1204   objs[next_objs_index++] = factory->NewJSArray(10);
1205   objs[next_objs_index++] =
1206       factory->NewJSArray(10, FAST_HOLEY_ELEMENTS, TENURED);
1207 
1208   // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE
1209   objs[next_objs_index++] = factory->NewStringFromStaticChars("abcdefghij");
1210   objs[next_objs_index++] =
1211       factory->NewStringFromStaticChars("abcdefghij", TENURED);
1212 
1213   // Allocate a large string (for large object space).
1214   int large_size = Page::kMaxRegularHeapObjectSize + 1;
1215   char* str = new char[large_size];
1216   for (int i = 0; i < large_size - 1; ++i) str[i] = 'a';
1217   str[large_size - 1] = '\0';
1218   objs[next_objs_index++] = factory->NewStringFromAsciiChecked(str, TENURED);
1219   delete[] str;
1220 
1221   // Add a Map object to look for.
1222   objs[next_objs_index++] = Handle<Map>(HeapObject::cast(*objs[0])->map());
1223 
1224   CHECK_EQ(objs_count, next_objs_index);
1225   CHECK_EQ(objs_count, ObjectsFoundInHeap(CcTest::heap(), objs, objs_count));
1226 }
1227 
1228 
UNINITIALIZED_TEST(TestCodeFlushing)1229 UNINITIALIZED_TEST(TestCodeFlushing) {
1230   // If we do not flush code this test is invalid.
1231   if (!FLAG_flush_code) return;
1232   i::FLAG_allow_natives_syntax = true;
1233   i::FLAG_optimize_for_size = false;
1234   v8::Isolate::CreateParams create_params;
1235   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
1236   v8::Isolate* isolate = v8::Isolate::New(create_params);
1237   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1238   isolate->Enter();
1239   Factory* factory = i_isolate->factory();
1240   {
1241     v8::HandleScope scope(isolate);
1242     v8::Context::New(isolate)->Enter();
1243     const char* source =
1244         "function foo() {"
1245         "  var x = 42;"
1246         "  var y = 42;"
1247         "  var z = x + y;"
1248         "};"
1249         "foo()";
1250     Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1251 
1252     // This compile will add the code to the compilation cache.
1253     {
1254       v8::HandleScope scope(isolate);
1255       CompileRun(source);
1256     }
1257 
1258     // Check function is compiled.
1259     Handle<Object> func_value = Object::GetProperty(i_isolate->global_object(),
1260                                                     foo_name).ToHandleChecked();
1261     CHECK(func_value->IsJSFunction());
1262     Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1263     CHECK(function->shared()->is_compiled());
1264 
1265     // The code will survive at least two GCs.
1266     i_isolate->heap()->CollectAllGarbage();
1267     i_isolate->heap()->CollectAllGarbage();
1268     CHECK(function->shared()->is_compiled());
1269 
1270     // Simulate several GCs that use full marking.
1271     const int kAgingThreshold = 6;
1272     for (int i = 0; i < kAgingThreshold; i++) {
1273       i_isolate->heap()->CollectAllGarbage();
1274     }
1275 
1276     // foo should no longer be in the compilation cache
1277     CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1278     CHECK(!function->is_compiled() || function->IsOptimized());
1279     // Call foo to get it recompiled.
1280     CompileRun("foo()");
1281     CHECK(function->shared()->is_compiled());
1282     CHECK(function->is_compiled());
1283   }
1284   isolate->Exit();
1285   isolate->Dispose();
1286 }
1287 
1288 
TEST(TestCodeFlushingPreAged)1289 TEST(TestCodeFlushingPreAged) {
1290   // If we do not flush code this test is invalid.
1291   if (!FLAG_flush_code) return;
1292   i::FLAG_allow_natives_syntax = true;
1293   i::FLAG_optimize_for_size = true;
1294   CcTest::InitializeVM();
1295   Isolate* isolate = CcTest::i_isolate();
1296   Factory* factory = isolate->factory();
1297   v8::HandleScope scope(CcTest::isolate());
1298   const char* source = "function foo() {"
1299                        "  var x = 42;"
1300                        "  var y = 42;"
1301                        "  var z = x + y;"
1302                        "};"
1303                        "foo()";
1304   Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1305 
1306   // Compile foo, but don't run it.
1307   { v8::HandleScope scope(CcTest::isolate());
1308     CompileRun(source);
1309   }
1310 
1311   // Check function is compiled.
1312   Handle<Object> func_value =
1313       Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked();
1314   CHECK(func_value->IsJSFunction());
1315   Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1316   CHECK(function->shared()->is_compiled());
1317 
1318   // The code has been run so will survive at least one GC.
1319   CcTest::heap()->CollectAllGarbage();
1320   CHECK(function->shared()->is_compiled());
1321 
1322   // The code was only run once, so it should be pre-aged and collected on the
1323   // next GC.
1324   CcTest::heap()->CollectAllGarbage();
1325   CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1326 
1327   // Execute the function again twice, and ensure it is reset to the young age.
1328   { v8::HandleScope scope(CcTest::isolate());
1329     CompileRun("foo();"
1330                "foo();");
1331   }
1332 
1333   // The code will survive at least two GC now that it is young again.
1334   CcTest::heap()->CollectAllGarbage();
1335   CcTest::heap()->CollectAllGarbage();
1336   CHECK(function->shared()->is_compiled());
1337 
1338   // Simulate several GCs that use full marking.
1339   const int kAgingThreshold = 6;
1340   for (int i = 0; i < kAgingThreshold; i++) {
1341     CcTest::heap()->CollectAllGarbage();
1342   }
1343 
1344   // foo should no longer be in the compilation cache
1345   CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1346   CHECK(!function->is_compiled() || function->IsOptimized());
1347   // Call foo to get it recompiled.
1348   CompileRun("foo()");
1349   CHECK(function->shared()->is_compiled());
1350   CHECK(function->is_compiled());
1351 }
1352 
1353 
TEST(TestCodeFlushingIncremental)1354 TEST(TestCodeFlushingIncremental) {
1355   // If we do not flush code this test is invalid.
1356   if (!FLAG_flush_code) return;
1357   i::FLAG_allow_natives_syntax = true;
1358   i::FLAG_optimize_for_size = false;
1359   CcTest::InitializeVM();
1360   Isolate* isolate = CcTest::i_isolate();
1361   Factory* factory = isolate->factory();
1362   v8::HandleScope scope(CcTest::isolate());
1363   const char* source = "function foo() {"
1364                        "  var x = 42;"
1365                        "  var y = 42;"
1366                        "  var z = x + y;"
1367                        "};"
1368                        "foo()";
1369   Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1370 
1371   // This compile will add the code to the compilation cache.
1372   { v8::HandleScope scope(CcTest::isolate());
1373     CompileRun(source);
1374   }
1375 
1376   // Check function is compiled.
1377   Handle<Object> func_value =
1378       Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked();
1379   CHECK(func_value->IsJSFunction());
1380   Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1381   CHECK(function->shared()->is_compiled());
1382 
1383   // The code will survive at least two GCs.
1384   CcTest::heap()->CollectAllGarbage();
1385   CcTest::heap()->CollectAllGarbage();
1386   CHECK(function->shared()->is_compiled());
1387 
1388   // Simulate several GCs that use incremental marking.
1389   const int kAgingThreshold = 6;
1390   for (int i = 0; i < kAgingThreshold; i++) {
1391     heap::SimulateIncrementalMarking(CcTest::heap());
1392     CcTest::heap()->CollectAllGarbage();
1393   }
1394   CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1395   CHECK(!function->is_compiled() || function->IsOptimized());
1396 
1397   // This compile will compile the function again.
1398   { v8::HandleScope scope(CcTest::isolate());
1399     CompileRun("foo();");
1400   }
1401 
1402   // Simulate several GCs that use incremental marking but make sure
1403   // the loop breaks once the function is enqueued as a candidate.
1404   for (int i = 0; i < kAgingThreshold; i++) {
1405     heap::SimulateIncrementalMarking(CcTest::heap());
1406     if (!function->next_function_link()->IsUndefined(CcTest::i_isolate()))
1407       break;
1408     CcTest::heap()->CollectAllGarbage();
1409   }
1410 
1411   // Force optimization while incremental marking is active and while
1412   // the function is enqueued as a candidate.
1413   { v8::HandleScope scope(CcTest::isolate());
1414     CompileRun("%OptimizeFunctionOnNextCall(foo); foo();");
1415   }
1416 
1417   // Simulate one final GC to make sure the candidate queue is sane.
1418   CcTest::heap()->CollectAllGarbage();
1419   CHECK(function->shared()->is_compiled() || !function->IsOptimized());
1420   CHECK(function->is_compiled() || !function->IsOptimized());
1421 }
1422 
1423 
TEST(TestCodeFlushingIncrementalScavenge)1424 TEST(TestCodeFlushingIncrementalScavenge) {
1425   // If we do not flush code this test is invalid.
1426   if (!FLAG_flush_code) return;
1427   i::FLAG_allow_natives_syntax = true;
1428   i::FLAG_optimize_for_size = false;
1429   CcTest::InitializeVM();
1430   Isolate* isolate = CcTest::i_isolate();
1431   Factory* factory = isolate->factory();
1432   v8::HandleScope scope(CcTest::isolate());
1433   const char* source = "var foo = function() {"
1434                        "  var x = 42;"
1435                        "  var y = 42;"
1436                        "  var z = x + y;"
1437                        "};"
1438                        "foo();"
1439                        "var bar = function() {"
1440                        "  var x = 23;"
1441                        "};"
1442                        "bar();";
1443   Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1444   Handle<String> bar_name = factory->InternalizeUtf8String("bar");
1445 
1446   // Perfrom one initial GC to enable code flushing.
1447   CcTest::heap()->CollectAllGarbage();
1448 
1449   // This compile will add the code to the compilation cache.
1450   { v8::HandleScope scope(CcTest::isolate());
1451     CompileRun(source);
1452   }
1453 
1454   // Check functions are compiled.
1455   Handle<Object> func_value =
1456       Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked();
1457   CHECK(func_value->IsJSFunction());
1458   Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1459   CHECK(function->shared()->is_compiled());
1460   Handle<Object> func_value2 =
1461       Object::GetProperty(isolate->global_object(), bar_name).ToHandleChecked();
1462   CHECK(func_value2->IsJSFunction());
1463   Handle<JSFunction> function2 = Handle<JSFunction>::cast(func_value2);
1464   CHECK(function2->shared()->is_compiled());
1465 
1466   // Clear references to functions so that one of them can die.
1467   { v8::HandleScope scope(CcTest::isolate());
1468     CompileRun("foo = 0; bar = 0;");
1469   }
1470 
1471   // Bump the code age so that flushing is triggered while the function
1472   // object is still located in new-space.
1473   const int kAgingThreshold = 6;
1474   for (int i = 0; i < kAgingThreshold; i++) {
1475     function->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
1476     function2->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
1477   }
1478 
1479   // Simulate incremental marking so that the functions are enqueued as
1480   // code flushing candidates. Then kill one of the functions. Finally
1481   // perform a scavenge while incremental marking is still running.
1482   heap::SimulateIncrementalMarking(CcTest::heap());
1483   *function2.location() = NULL;
1484   CcTest::heap()->CollectGarbage(NEW_SPACE, "test scavenge while marking");
1485 
1486   // Simulate one final GC to make sure the candidate queue is sane.
1487   CcTest::heap()->CollectAllGarbage();
1488   CHECK(!function->shared()->is_compiled() || function->IsOptimized());
1489   CHECK(!function->is_compiled() || function->IsOptimized());
1490 }
1491 
1492 
TEST(TestCodeFlushingIncrementalAbort)1493 TEST(TestCodeFlushingIncrementalAbort) {
1494   // If we do not flush code this test is invalid.
1495   if (!FLAG_flush_code) return;
1496   i::FLAG_allow_natives_syntax = true;
1497   i::FLAG_optimize_for_size = false;
1498   CcTest::InitializeVM();
1499   Isolate* isolate = CcTest::i_isolate();
1500   Factory* factory = isolate->factory();
1501   Heap* heap = isolate->heap();
1502   v8::HandleScope scope(CcTest::isolate());
1503   const char* source = "function foo() {"
1504                        "  var x = 42;"
1505                        "  var y = 42;"
1506                        "  var z = x + y;"
1507                        "};"
1508                        "foo()";
1509   Handle<String> foo_name = factory->InternalizeUtf8String("foo");
1510 
1511   // This compile will add the code to the compilation cache.
1512   { v8::HandleScope scope(CcTest::isolate());
1513     CompileRun(source);
1514   }
1515 
1516   // Check function is compiled.
1517   Handle<Object> func_value =
1518       Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked();
1519   CHECK(func_value->IsJSFunction());
1520   Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
1521   CHECK(function->shared()->is_compiled());
1522 
1523   // The code will survive at least two GCs.
1524   heap->CollectAllGarbage();
1525   heap->CollectAllGarbage();
1526   CHECK(function->shared()->is_compiled());
1527 
1528   // Bump the code age so that flushing is triggered.
1529   const int kAgingThreshold = 6;
1530   for (int i = 0; i < kAgingThreshold; i++) {
1531     function->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
1532   }
1533 
1534   // Simulate incremental marking so that the function is enqueued as
1535   // code flushing candidate.
1536   heap::SimulateIncrementalMarking(heap);
1537 
1538   // Enable the debugger and add a breakpoint while incremental marking
1539   // is running so that incremental marking aborts and code flushing is
1540   // disabled.
1541   int position = 0;
1542   Handle<Object> breakpoint_object(Smi::FromInt(0), isolate);
1543   EnableDebugger(CcTest::isolate());
1544   isolate->debug()->SetBreakPoint(function, breakpoint_object, &position);
1545   isolate->debug()->ClearAllBreakPoints();
1546   DisableDebugger(CcTest::isolate());
1547 
1548   // Force optimization now that code flushing is disabled.
1549   { v8::HandleScope scope(CcTest::isolate());
1550     CompileRun("%OptimizeFunctionOnNextCall(foo); foo();");
1551   }
1552 
1553   // Simulate one final GC to make sure the candidate queue is sane.
1554   heap->CollectAllGarbage();
1555   CHECK(function->shared()->is_compiled() || !function->IsOptimized());
1556   CHECK(function->is_compiled() || !function->IsOptimized());
1557 }
1558 
TEST(TestUseOfIncrementalBarrierOnCompileLazy)1559 TEST(TestUseOfIncrementalBarrierOnCompileLazy) {
1560   // Turn off always_opt because it interferes with running the built-in for
1561   // the last call to g().
1562   i::FLAG_always_opt = false;
1563   i::FLAG_allow_natives_syntax = true;
1564   CcTest::InitializeVM();
1565   Isolate* isolate = CcTest::i_isolate();
1566   Factory* factory = isolate->factory();
1567   Heap* heap = isolate->heap();
1568   v8::HandleScope scope(CcTest::isolate());
1569 
1570   CompileRun(
1571       "function make_closure(x) {"
1572       "  return function() { return x + 3 };"
1573       "}"
1574       "var f = make_closure(5); f();"
1575       "var g = make_closure(5);");
1576 
1577   // Check f is compiled.
1578   Handle<String> f_name = factory->InternalizeUtf8String("f");
1579   Handle<Object> f_value =
1580       Object::GetProperty(isolate->global_object(), f_name).ToHandleChecked();
1581   Handle<JSFunction> f_function = Handle<JSFunction>::cast(f_value);
1582   CHECK(f_function->is_compiled());
1583 
1584   // Check g is not compiled.
1585   Handle<String> g_name = factory->InternalizeUtf8String("g");
1586   Handle<Object> g_value =
1587       Object::GetProperty(isolate->global_object(), g_name).ToHandleChecked();
1588   Handle<JSFunction> g_function = Handle<JSFunction>::cast(g_value);
1589   CHECK(!g_function->is_compiled());
1590 
1591   heap::SimulateIncrementalMarking(heap);
1592   CompileRun("%OptimizeFunctionOnNextCall(f); f();");
1593 
1594   // g should now have available an optimized function, unmarked by gc. The
1595   // CompileLazy built-in will discover it and install it in the closure, and
1596   // the incremental write barrier should be used.
1597   CompileRun("g();");
1598   CHECK(g_function->is_compiled());
1599 }
1600 
TEST(CompilationCacheCachingBehavior)1601 TEST(CompilationCacheCachingBehavior) {
1602   // If we do not flush code, or have the compilation cache turned off, this
1603   // test is invalid.
1604   if (!FLAG_flush_code || !FLAG_compilation_cache) {
1605     return;
1606   }
1607   CcTest::InitializeVM();
1608   Isolate* isolate = CcTest::i_isolate();
1609   Factory* factory = isolate->factory();
1610   Heap* heap = isolate->heap();
1611   CompilationCache* compilation_cache = isolate->compilation_cache();
1612   LanguageMode language_mode = construct_language_mode(FLAG_use_strict);
1613 
1614   v8::HandleScope scope(CcTest::isolate());
1615   const char* raw_source =
1616       "function foo() {"
1617       "  var x = 42;"
1618       "  var y = 42;"
1619       "  var z = x + y;"
1620       "};"
1621       "foo()";
1622   Handle<String> source = factory->InternalizeUtf8String(raw_source);
1623   Handle<Context> native_context = isolate->native_context();
1624 
1625   {
1626     v8::HandleScope scope(CcTest::isolate());
1627     CompileRun(raw_source);
1628   }
1629 
1630   // The script should be in the cache now.
1631   MaybeHandle<SharedFunctionInfo> info = compilation_cache->LookupScript(
1632       source, Handle<Object>(), 0, 0,
1633       v8::ScriptOriginOptions(false, true, false), native_context,
1634       language_mode);
1635   CHECK(!info.is_null());
1636 
1637   // Check that the code cache entry survives at least on GC.
1638   // (Unless --optimize-for-size, in which case it might get collected
1639   // immediately.)
1640   if (!FLAG_optimize_for_size) {
1641     heap->CollectAllGarbage();
1642     info = compilation_cache->LookupScript(
1643         source, Handle<Object>(), 0, 0,
1644         v8::ScriptOriginOptions(false, true, false), native_context,
1645         language_mode);
1646     CHECK(!info.is_null());
1647   }
1648 
1649   // Progress code age until it's old and ready for GC.
1650   while (!info.ToHandleChecked()->code()->IsOld()) {
1651     // To guarantee progress, we have to MakeOlder with different parities.
1652     // We can't just use NO_MARKING_PARITY, since e.g. kExecutedOnceCodeAge is
1653     // always NO_MARKING_PARITY and the code age only progresses if the parity
1654     // is different.
1655     info.ToHandleChecked()->code()->MakeOlder(ODD_MARKING_PARITY);
1656     info.ToHandleChecked()->code()->MakeOlder(EVEN_MARKING_PARITY);
1657   }
1658 
1659   heap->CollectAllGarbage();
1660   // Ensure code aging cleared the entry from the cache.
1661   info = compilation_cache->LookupScript(
1662       source, Handle<Object>(), 0, 0,
1663       v8::ScriptOriginOptions(false, true, false), native_context,
1664       language_mode);
1665   CHECK(info.is_null());
1666 }
1667 
1668 
OptimizeEmptyFunction(const char * name)1669 static void OptimizeEmptyFunction(const char* name) {
1670   HandleScope scope(CcTest::i_isolate());
1671   EmbeddedVector<char, 256> source;
1672   SNPrintF(source,
1673            "function %s() { return 0; }"
1674            "%s(); %s();"
1675            "%%OptimizeFunctionOnNextCall(%s);"
1676            "%s();",
1677            name, name, name, name, name);
1678   CompileRun(source.start());
1679 }
1680 
1681 
1682 // Count the number of native contexts in the weak list of native contexts.
CountNativeContexts()1683 int CountNativeContexts() {
1684   int count = 0;
1685   Object* object = CcTest::heap()->native_contexts_list();
1686   while (!object->IsUndefined(CcTest::i_isolate())) {
1687     count++;
1688     object = Context::cast(object)->next_context_link();
1689   }
1690   return count;
1691 }
1692 
1693 
1694 // Count the number of user functions in the weak list of optimized
1695 // functions attached to a native context.
CountOptimizedUserFunctions(v8::Local<v8::Context> context)1696 static int CountOptimizedUserFunctions(v8::Local<v8::Context> context) {
1697   int count = 0;
1698   Handle<Context> icontext = v8::Utils::OpenHandle(*context);
1699   Object* object = icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST);
1700   while (object->IsJSFunction() &&
1701          !JSFunction::cast(object)->shared()->IsBuiltin()) {
1702     count++;
1703     object = JSFunction::cast(object)->next_function_link();
1704   }
1705   return count;
1706 }
1707 
1708 
TEST(TestInternalWeakLists)1709 TEST(TestInternalWeakLists) {
1710   FLAG_always_opt = false;
1711   FLAG_allow_natives_syntax = true;
1712   v8::V8::Initialize();
1713 
1714   // Some flags turn Scavenge collections into Mark-sweep collections
1715   // and hence are incompatible with this test case.
1716   if (FLAG_gc_global || FLAG_stress_compaction) return;
1717   FLAG_retain_maps_for_n_gc = 0;
1718 
1719   static const int kNumTestContexts = 10;
1720 
1721   Isolate* isolate = CcTest::i_isolate();
1722   Heap* heap = isolate->heap();
1723   HandleScope scope(isolate);
1724   v8::Local<v8::Context> ctx[kNumTestContexts];
1725   if (!isolate->use_crankshaft()) return;
1726 
1727   CHECK_EQ(0, CountNativeContexts());
1728 
1729   // Create a number of global contests which gets linked together.
1730   for (int i = 0; i < kNumTestContexts; i++) {
1731     ctx[i] = v8::Context::New(CcTest::isolate());
1732 
1733     // Collect garbage that might have been created by one of the
1734     // installed extensions.
1735     isolate->compilation_cache()->Clear();
1736     heap->CollectAllGarbage();
1737 
1738     CHECK_EQ(i + 1, CountNativeContexts());
1739 
1740     ctx[i]->Enter();
1741 
1742     // Create a handle scope so no function objects get stuck in the outer
1743     // handle scope.
1744     HandleScope scope(isolate);
1745     CHECK_EQ(0, CountOptimizedUserFunctions(ctx[i]));
1746     OptimizeEmptyFunction("f1");
1747     CHECK_EQ(1, CountOptimizedUserFunctions(ctx[i]));
1748     OptimizeEmptyFunction("f2");
1749     CHECK_EQ(2, CountOptimizedUserFunctions(ctx[i]));
1750     OptimizeEmptyFunction("f3");
1751     CHECK_EQ(3, CountOptimizedUserFunctions(ctx[i]));
1752     OptimizeEmptyFunction("f4");
1753     CHECK_EQ(4, CountOptimizedUserFunctions(ctx[i]));
1754     OptimizeEmptyFunction("f5");
1755     CHECK_EQ(5, CountOptimizedUserFunctions(ctx[i]));
1756 
1757     // Remove function f1, and
1758     CompileRun("f1=null");
1759 
1760     // Scavenge treats these references as strong.
1761     for (int j = 0; j < 10; j++) {
1762       CcTest::heap()->CollectGarbage(NEW_SPACE);
1763       CHECK_EQ(5, CountOptimizedUserFunctions(ctx[i]));
1764     }
1765 
1766     // Mark compact handles the weak references.
1767     isolate->compilation_cache()->Clear();
1768     heap->CollectAllGarbage();
1769     CHECK_EQ(4, CountOptimizedUserFunctions(ctx[i]));
1770 
1771     // Get rid of f3 and f5 in the same way.
1772     CompileRun("f3=null");
1773     for (int j = 0; j < 10; j++) {
1774       CcTest::heap()->CollectGarbage(NEW_SPACE);
1775       CHECK_EQ(4, CountOptimizedUserFunctions(ctx[i]));
1776     }
1777     CcTest::heap()->CollectAllGarbage();
1778     CHECK_EQ(3, CountOptimizedUserFunctions(ctx[i]));
1779     CompileRun("f5=null");
1780     for (int j = 0; j < 10; j++) {
1781       CcTest::heap()->CollectGarbage(NEW_SPACE);
1782       CHECK_EQ(3, CountOptimizedUserFunctions(ctx[i]));
1783     }
1784     CcTest::heap()->CollectAllGarbage();
1785     CHECK_EQ(2, CountOptimizedUserFunctions(ctx[i]));
1786 
1787     ctx[i]->Exit();
1788   }
1789 
1790   // Force compilation cache cleanup.
1791   CcTest::heap()->NotifyContextDisposed(true);
1792   CcTest::heap()->CollectAllGarbage();
1793 
1794   // Dispose the native contexts one by one.
1795   for (int i = 0; i < kNumTestContexts; i++) {
1796     // TODO(dcarney): is there a better way to do this?
1797     i::Object** unsafe = reinterpret_cast<i::Object**>(*ctx[i]);
1798     *unsafe = CcTest::heap()->undefined_value();
1799     ctx[i].Clear();
1800 
1801     // Scavenge treats these references as strong.
1802     for (int j = 0; j < 10; j++) {
1803       CcTest::heap()->CollectGarbage(i::NEW_SPACE);
1804       CHECK_EQ(kNumTestContexts - i, CountNativeContexts());
1805     }
1806 
1807     // Mark compact handles the weak references.
1808     CcTest::heap()->CollectAllGarbage();
1809     CHECK_EQ(kNumTestContexts - i - 1, CountNativeContexts());
1810   }
1811 
1812   CHECK_EQ(0, CountNativeContexts());
1813 }
1814 
1815 
1816 // Count the number of native contexts in the weak list of native contexts
1817 // causing a GC after the specified number of elements.
CountNativeContextsWithGC(Isolate * isolate,int n)1818 static int CountNativeContextsWithGC(Isolate* isolate, int n) {
1819   Heap* heap = isolate->heap();
1820   int count = 0;
1821   Handle<Object> object(heap->native_contexts_list(), isolate);
1822   while (!object->IsUndefined(isolate)) {
1823     count++;
1824     if (count == n) heap->CollectAllGarbage();
1825     object =
1826         Handle<Object>(Context::cast(*object)->next_context_link(), isolate);
1827   }
1828   return count;
1829 }
1830 
1831 
1832 // Count the number of user functions in the weak list of optimized
1833 // functions attached to a native context causing a GC after the
1834 // specified number of elements.
CountOptimizedUserFunctionsWithGC(v8::Local<v8::Context> context,int n)1835 static int CountOptimizedUserFunctionsWithGC(v8::Local<v8::Context> context,
1836                                              int n) {
1837   int count = 0;
1838   Handle<Context> icontext = v8::Utils::OpenHandle(*context);
1839   Isolate* isolate = icontext->GetIsolate();
1840   Handle<Object> object(icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST),
1841                         isolate);
1842   while (object->IsJSFunction() &&
1843          !Handle<JSFunction>::cast(object)->shared()->IsBuiltin()) {
1844     count++;
1845     if (count == n) isolate->heap()->CollectAllGarbage();
1846     object = Handle<Object>(
1847         Object::cast(JSFunction::cast(*object)->next_function_link()),
1848         isolate);
1849   }
1850   return count;
1851 }
1852 
1853 
TEST(TestInternalWeakListsTraverseWithGC)1854 TEST(TestInternalWeakListsTraverseWithGC) {
1855   FLAG_always_opt = false;
1856   FLAG_allow_natives_syntax = true;
1857   v8::V8::Initialize();
1858 
1859   static const int kNumTestContexts = 10;
1860 
1861   Isolate* isolate = CcTest::i_isolate();
1862   HandleScope scope(isolate);
1863   v8::Local<v8::Context> ctx[kNumTestContexts];
1864   if (!isolate->use_crankshaft()) return;
1865 
1866   CHECK_EQ(0, CountNativeContexts());
1867 
1868   // Create an number of contexts and check the length of the weak list both
1869   // with and without GCs while iterating the list.
1870   for (int i = 0; i < kNumTestContexts; i++) {
1871     ctx[i] = v8::Context::New(CcTest::isolate());
1872     CHECK_EQ(i + 1, CountNativeContexts());
1873     CHECK_EQ(i + 1, CountNativeContextsWithGC(isolate, i / 2 + 1));
1874   }
1875 
1876   ctx[0]->Enter();
1877 
1878   // Compile a number of functions the length of the weak list of optimized
1879   // functions both with and without GCs while iterating the list.
1880   CHECK_EQ(0, CountOptimizedUserFunctions(ctx[0]));
1881   OptimizeEmptyFunction("f1");
1882   CHECK_EQ(1, CountOptimizedUserFunctions(ctx[0]));
1883   CHECK_EQ(1, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1884   OptimizeEmptyFunction("f2");
1885   CHECK_EQ(2, CountOptimizedUserFunctions(ctx[0]));
1886   CHECK_EQ(2, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1887   OptimizeEmptyFunction("f3");
1888   CHECK_EQ(3, CountOptimizedUserFunctions(ctx[0]));
1889   CHECK_EQ(3, CountOptimizedUserFunctionsWithGC(ctx[0], 1));
1890   OptimizeEmptyFunction("f4");
1891   CHECK_EQ(4, CountOptimizedUserFunctions(ctx[0]));
1892   CHECK_EQ(4, CountOptimizedUserFunctionsWithGC(ctx[0], 2));
1893   OptimizeEmptyFunction("f5");
1894   CHECK_EQ(5, CountOptimizedUserFunctions(ctx[0]));
1895   CHECK_EQ(5, CountOptimizedUserFunctionsWithGC(ctx[0], 4));
1896 
1897   ctx[0]->Exit();
1898 }
1899 
1900 
TEST(TestSizeOfRegExpCode)1901 TEST(TestSizeOfRegExpCode) {
1902   if (!FLAG_regexp_optimization) return;
1903 
1904   v8::V8::Initialize();
1905 
1906   Isolate* isolate = CcTest::i_isolate();
1907   HandleScope scope(isolate);
1908 
1909   LocalContext context;
1910 
1911   // Adjust source below and this check to match
1912   // RegExpImple::kRegExpTooLargeToOptimize.
1913   CHECK_EQ(i::RegExpImpl::kRegExpTooLargeToOptimize, 20 * KB);
1914 
1915   // Compile a regexp that is much larger if we are using regexp optimizations.
1916   CompileRun(
1917       "var reg_exp_source = '(?:a|bc|def|ghij|klmno|pqrstu)';"
1918       "var half_size_reg_exp;"
1919       "while (reg_exp_source.length < 20 * 1024) {"
1920       "  half_size_reg_exp = reg_exp_source;"
1921       "  reg_exp_source = reg_exp_source + reg_exp_source;"
1922       "}"
1923       // Flatten string.
1924       "reg_exp_source.match(/f/);");
1925 
1926   // Get initial heap size after several full GCs, which will stabilize
1927   // the heap size and return with sweeping finished completely.
1928   CcTest::heap()->CollectAllGarbage();
1929   CcTest::heap()->CollectAllGarbage();
1930   CcTest::heap()->CollectAllGarbage();
1931   CcTest::heap()->CollectAllGarbage();
1932   CcTest::heap()->CollectAllGarbage();
1933   MarkCompactCollector* collector = CcTest::heap()->mark_compact_collector();
1934   if (collector->sweeping_in_progress()) {
1935     collector->EnsureSweepingCompleted();
1936   }
1937   int initial_size = static_cast<int>(CcTest::heap()->SizeOfObjects());
1938 
1939   CompileRun("'foo'.match(reg_exp_source);");
1940   CcTest::heap()->CollectAllGarbage();
1941   int size_with_regexp = static_cast<int>(CcTest::heap()->SizeOfObjects());
1942 
1943   CompileRun("'foo'.match(half_size_reg_exp);");
1944   CcTest::heap()->CollectAllGarbage();
1945   int size_with_optimized_regexp =
1946       static_cast<int>(CcTest::heap()->SizeOfObjects());
1947 
1948   int size_of_regexp_code = size_with_regexp - initial_size;
1949 
1950   // On some platforms the debug-code flag causes huge amounts of regexp code
1951   // to be emitted, breaking this test.
1952   if (!FLAG_debug_code) {
1953     CHECK_LE(size_of_regexp_code, 1 * MB);
1954   }
1955 
1956   // Small regexp is half the size, but compiles to more than twice the code
1957   // due to the optimization steps.
1958   CHECK_GE(size_with_optimized_regexp,
1959            size_with_regexp + size_of_regexp_code * 2);
1960 }
1961 
1962 
HEAP_TEST(TestSizeOfObjects)1963 HEAP_TEST(TestSizeOfObjects) {
1964   v8::V8::Initialize();
1965 
1966   // Get initial heap size after several full GCs, which will stabilize
1967   // the heap size and return with sweeping finished completely.
1968   CcTest::heap()->CollectAllGarbage();
1969   CcTest::heap()->CollectAllGarbage();
1970   CcTest::heap()->CollectAllGarbage();
1971   CcTest::heap()->CollectAllGarbage();
1972   CcTest::heap()->CollectAllGarbage();
1973   MarkCompactCollector* collector = CcTest::heap()->mark_compact_collector();
1974   if (collector->sweeping_in_progress()) {
1975     collector->EnsureSweepingCompleted();
1976   }
1977   int initial_size = static_cast<int>(CcTest::heap()->SizeOfObjects());
1978 
1979   {
1980     // Allocate objects on several different old-space pages so that
1981     // concurrent sweeper threads will be busy sweeping the old space on
1982     // subsequent GC runs.
1983     AlwaysAllocateScope always_allocate(CcTest::i_isolate());
1984     int filler_size = static_cast<int>(FixedArray::SizeFor(8192));
1985     for (int i = 1; i <= 100; i++) {
1986       CcTest::heap()->AllocateFixedArray(8192, TENURED).ToObjectChecked();
1987       CHECK_EQ(initial_size + i * filler_size,
1988                static_cast<int>(CcTest::heap()->SizeOfObjects()));
1989     }
1990   }
1991 
1992   // The heap size should go back to initial size after a full GC, even
1993   // though sweeping didn't finish yet.
1994   CcTest::heap()->CollectAllGarbage();
1995 
1996   // Normally sweeping would not be complete here, but no guarantees.
1997 
1998   CHECK_EQ(initial_size, static_cast<int>(CcTest::heap()->SizeOfObjects()));
1999 
2000   // Waiting for sweeper threads should not change heap size.
2001   if (collector->sweeping_in_progress()) {
2002     collector->EnsureSweepingCompleted();
2003   }
2004   CHECK_EQ(initial_size, static_cast<int>(CcTest::heap()->SizeOfObjects()));
2005 }
2006 
2007 
TEST(TestAlignmentCalculations)2008 TEST(TestAlignmentCalculations) {
2009   // Maximum fill amounts are consistent.
2010   int maximum_double_misalignment = kDoubleSize - kPointerSize;
2011   int maximum_simd128_misalignment = kSimd128Size - kPointerSize;
2012   int max_word_fill = Heap::GetMaximumFillToAlign(kWordAligned);
2013   CHECK_EQ(0, max_word_fill);
2014   int max_double_fill = Heap::GetMaximumFillToAlign(kDoubleAligned);
2015   CHECK_EQ(maximum_double_misalignment, max_double_fill);
2016   int max_double_unaligned_fill = Heap::GetMaximumFillToAlign(kDoubleUnaligned);
2017   CHECK_EQ(maximum_double_misalignment, max_double_unaligned_fill);
2018   int max_simd128_unaligned_fill =
2019       Heap::GetMaximumFillToAlign(kSimd128Unaligned);
2020   CHECK_EQ(maximum_simd128_misalignment, max_simd128_unaligned_fill);
2021 
2022   Address base = static_cast<Address>(NULL);
2023   int fill = 0;
2024 
2025   // Word alignment never requires fill.
2026   fill = Heap::GetFillToAlign(base, kWordAligned);
2027   CHECK_EQ(0, fill);
2028   fill = Heap::GetFillToAlign(base + kPointerSize, kWordAligned);
2029   CHECK_EQ(0, fill);
2030 
2031   // No fill is required when address is double aligned.
2032   fill = Heap::GetFillToAlign(base, kDoubleAligned);
2033   CHECK_EQ(0, fill);
2034   // Fill is required if address is not double aligned.
2035   fill = Heap::GetFillToAlign(base + kPointerSize, kDoubleAligned);
2036   CHECK_EQ(maximum_double_misalignment, fill);
2037   // kDoubleUnaligned has the opposite fill amounts.
2038   fill = Heap::GetFillToAlign(base, kDoubleUnaligned);
2039   CHECK_EQ(maximum_double_misalignment, fill);
2040   fill = Heap::GetFillToAlign(base + kPointerSize, kDoubleUnaligned);
2041   CHECK_EQ(0, fill);
2042 
2043   // 128 bit SIMD types have 2 or 4 possible alignments, depending on platform.
2044   fill = Heap::GetFillToAlign(base, kSimd128Unaligned);
2045   CHECK_EQ((3 * kPointerSize) & kSimd128AlignmentMask, fill);
2046   fill = Heap::GetFillToAlign(base + kPointerSize, kSimd128Unaligned);
2047   CHECK_EQ((2 * kPointerSize) & kSimd128AlignmentMask, fill);
2048   fill = Heap::GetFillToAlign(base + 2 * kPointerSize, kSimd128Unaligned);
2049   CHECK_EQ(kPointerSize, fill);
2050   fill = Heap::GetFillToAlign(base + 3 * kPointerSize, kSimd128Unaligned);
2051   CHECK_EQ(0, fill);
2052 }
2053 
2054 
NewSpaceAllocateAligned(int size,AllocationAlignment alignment)2055 static HeapObject* NewSpaceAllocateAligned(int size,
2056                                            AllocationAlignment alignment) {
2057   Heap* heap = CcTest::heap();
2058   AllocationResult allocation =
2059       heap->new_space()->AllocateRawAligned(size, alignment);
2060   HeapObject* obj = NULL;
2061   allocation.To(&obj);
2062   heap->CreateFillerObjectAt(obj->address(), size, ClearRecordedSlots::kNo);
2063   return obj;
2064 }
2065 
2066 
2067 // Get new space allocation into the desired alignment.
AlignNewSpace(AllocationAlignment alignment,int offset)2068 static Address AlignNewSpace(AllocationAlignment alignment, int offset) {
2069   Address* top_addr = CcTest::heap()->new_space()->allocation_top_address();
2070   int fill = Heap::GetFillToAlign(*top_addr, alignment);
2071   if (fill) {
2072     NewSpaceAllocateAligned(fill + offset, kWordAligned);
2073   }
2074   return *top_addr;
2075 }
2076 
2077 
TEST(TestAlignedAllocation)2078 TEST(TestAlignedAllocation) {
2079   // Double misalignment is 4 on 32-bit platforms, 0 on 64-bit ones.
2080   const intptr_t double_misalignment = kDoubleSize - kPointerSize;
2081   Address* top_addr = CcTest::heap()->new_space()->allocation_top_address();
2082   Address start;
2083   HeapObject* obj;
2084   HeapObject* filler;
2085   if (double_misalignment) {
2086     // Allocate a pointer sized object that must be double aligned at an
2087     // aligned address.
2088     start = AlignNewSpace(kDoubleAligned, 0);
2089     obj = NewSpaceAllocateAligned(kPointerSize, kDoubleAligned);
2090     CHECK(IsAddressAligned(obj->address(), kDoubleAlignment));
2091     // There is no filler.
2092     CHECK_EQ(kPointerSize, *top_addr - start);
2093 
2094     // Allocate a second pointer sized object that must be double aligned at an
2095     // unaligned address.
2096     start = AlignNewSpace(kDoubleAligned, kPointerSize);
2097     obj = NewSpaceAllocateAligned(kPointerSize, kDoubleAligned);
2098     CHECK(IsAddressAligned(obj->address(), kDoubleAlignment));
2099     // There is a filler object before the object.
2100     filler = HeapObject::FromAddress(start);
2101     CHECK(obj != filler && filler->IsFiller() &&
2102           filler->Size() == kPointerSize);
2103     CHECK_EQ(kPointerSize + double_misalignment, *top_addr - start);
2104 
2105     // Similarly for kDoubleUnaligned.
2106     start = AlignNewSpace(kDoubleUnaligned, 0);
2107     obj = NewSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
2108     CHECK(IsAddressAligned(obj->address(), kDoubleAlignment, kPointerSize));
2109     CHECK_EQ(kPointerSize, *top_addr - start);
2110     start = AlignNewSpace(kDoubleUnaligned, kPointerSize);
2111     obj = NewSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
2112     CHECK(IsAddressAligned(obj->address(), kDoubleAlignment, kPointerSize));
2113     // There is a filler object before the object.
2114     filler = HeapObject::FromAddress(start);
2115     CHECK(obj != filler && filler->IsFiller() &&
2116           filler->Size() == kPointerSize);
2117     CHECK_EQ(kPointerSize + double_misalignment, *top_addr - start);
2118   }
2119 
2120   // Now test SIMD alignment. There are 2 or 4 possible alignments, depending
2121   // on platform.
2122   start = AlignNewSpace(kSimd128Unaligned, 0);
2123   obj = NewSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
2124   CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
2125   // There is no filler.
2126   CHECK_EQ(kPointerSize, *top_addr - start);
2127   start = AlignNewSpace(kSimd128Unaligned, kPointerSize);
2128   obj = NewSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
2129   CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
2130   // There is a filler object before the object.
2131   filler = HeapObject::FromAddress(start);
2132   CHECK(obj != filler && filler->IsFiller() &&
2133         filler->Size() == kSimd128Size - kPointerSize);
2134   CHECK_EQ(kPointerSize + kSimd128Size - kPointerSize, *top_addr - start);
2135 
2136   if (double_misalignment) {
2137     // Test the 2 other alignments possible on 32 bit platforms.
2138     start = AlignNewSpace(kSimd128Unaligned, 2 * kPointerSize);
2139     obj = NewSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
2140     CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
2141     // There is a filler object before the object.
2142     filler = HeapObject::FromAddress(start);
2143     CHECK(obj != filler && filler->IsFiller() &&
2144           filler->Size() == 2 * kPointerSize);
2145     CHECK_EQ(kPointerSize + 2 * kPointerSize, *top_addr - start);
2146     start = AlignNewSpace(kSimd128Unaligned, 3 * kPointerSize);
2147     obj = NewSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
2148     CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
2149     // There is a filler object before the object.
2150     filler = HeapObject::FromAddress(start);
2151     CHECK(obj != filler && filler->IsFiller() &&
2152           filler->Size() == kPointerSize);
2153     CHECK_EQ(kPointerSize + kPointerSize, *top_addr - start);
2154   }
2155 }
2156 
2157 
OldSpaceAllocateAligned(int size,AllocationAlignment alignment)2158 static HeapObject* OldSpaceAllocateAligned(int size,
2159                                            AllocationAlignment alignment) {
2160   Heap* heap = CcTest::heap();
2161   AllocationResult allocation =
2162       heap->old_space()->AllocateRawAligned(size, alignment);
2163   HeapObject* obj = NULL;
2164   allocation.To(&obj);
2165   heap->CreateFillerObjectAt(obj->address(), size, ClearRecordedSlots::kNo);
2166   return obj;
2167 }
2168 
2169 
2170 // Get old space allocation into the desired alignment.
AlignOldSpace(AllocationAlignment alignment,int offset)2171 static Address AlignOldSpace(AllocationAlignment alignment, int offset) {
2172   Address* top_addr = CcTest::heap()->old_space()->allocation_top_address();
2173   int fill = Heap::GetFillToAlign(*top_addr, alignment);
2174   int allocation = fill + offset;
2175   if (allocation) {
2176     OldSpaceAllocateAligned(allocation, kWordAligned);
2177   }
2178   Address top = *top_addr;
2179   // Now force the remaining allocation onto the free list.
2180   CcTest::heap()->old_space()->EmptyAllocationInfo();
2181   return top;
2182 }
2183 
2184 
2185 // Test the case where allocation must be done from the free list, so filler
2186 // may precede or follow the object.
TEST(TestAlignedOverAllocation)2187 TEST(TestAlignedOverAllocation) {
2188   // Double misalignment is 4 on 32-bit platforms, 0 on 64-bit ones.
2189   const intptr_t double_misalignment = kDoubleSize - kPointerSize;
2190   Address start;
2191   HeapObject* obj;
2192   HeapObject* filler1;
2193   HeapObject* filler2;
2194   if (double_misalignment) {
2195     start = AlignOldSpace(kDoubleAligned, 0);
2196     obj = OldSpaceAllocateAligned(kPointerSize, kDoubleAligned);
2197     // The object is aligned, and a filler object is created after.
2198     CHECK(IsAddressAligned(obj->address(), kDoubleAlignment));
2199     filler1 = HeapObject::FromAddress(start + kPointerSize);
2200     CHECK(obj != filler1 && filler1->IsFiller() &&
2201           filler1->Size() == kPointerSize);
2202     // Try the opposite alignment case.
2203     start = AlignOldSpace(kDoubleAligned, kPointerSize);
2204     obj = OldSpaceAllocateAligned(kPointerSize, kDoubleAligned);
2205     CHECK(IsAddressAligned(obj->address(), kDoubleAlignment));
2206     filler1 = HeapObject::FromAddress(start);
2207     CHECK(obj != filler1);
2208     CHECK(filler1->IsFiller());
2209     CHECK(filler1->Size() == kPointerSize);
2210     CHECK(obj != filler1 && filler1->IsFiller() &&
2211           filler1->Size() == kPointerSize);
2212 
2213     // Similarly for kDoubleUnaligned.
2214     start = AlignOldSpace(kDoubleUnaligned, 0);
2215     obj = OldSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
2216     // The object is aligned, and a filler object is created after.
2217     CHECK(IsAddressAligned(obj->address(), kDoubleAlignment, kPointerSize));
2218     filler1 = HeapObject::FromAddress(start + kPointerSize);
2219     CHECK(obj != filler1 && filler1->IsFiller() &&
2220           filler1->Size() == kPointerSize);
2221     // Try the opposite alignment case.
2222     start = AlignOldSpace(kDoubleUnaligned, kPointerSize);
2223     obj = OldSpaceAllocateAligned(kPointerSize, kDoubleUnaligned);
2224     CHECK(IsAddressAligned(obj->address(), kDoubleAlignment, kPointerSize));
2225     filler1 = HeapObject::FromAddress(start);
2226     CHECK(obj != filler1 && filler1->IsFiller() &&
2227           filler1->Size() == kPointerSize);
2228   }
2229 
2230   // Now test SIMD alignment. There are 2 or 4 possible alignments, depending
2231   // on platform.
2232   start = AlignOldSpace(kSimd128Unaligned, 0);
2233   obj = OldSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
2234   CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
2235   // There is a filler object after the object.
2236   filler1 = HeapObject::FromAddress(start + kPointerSize);
2237   CHECK(obj != filler1 && filler1->IsFiller() &&
2238         filler1->Size() == kSimd128Size - kPointerSize);
2239   start = AlignOldSpace(kSimd128Unaligned, kPointerSize);
2240   obj = OldSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
2241   CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
2242   // There is a filler object before the object.
2243   filler1 = HeapObject::FromAddress(start);
2244   CHECK(obj != filler1 && filler1->IsFiller() &&
2245         filler1->Size() == kSimd128Size - kPointerSize);
2246 
2247   if (double_misalignment) {
2248     // Test the 2 other alignments possible on 32 bit platforms.
2249     start = AlignOldSpace(kSimd128Unaligned, 2 * kPointerSize);
2250     obj = OldSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
2251     CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
2252     // There are filler objects before and after the object.
2253     filler1 = HeapObject::FromAddress(start);
2254     CHECK(obj != filler1 && filler1->IsFiller() &&
2255           filler1->Size() == 2 * kPointerSize);
2256     filler2 = HeapObject::FromAddress(start + 3 * kPointerSize);
2257     CHECK(obj != filler2 && filler2->IsFiller() &&
2258           filler2->Size() == kPointerSize);
2259     start = AlignOldSpace(kSimd128Unaligned, 3 * kPointerSize);
2260     obj = OldSpaceAllocateAligned(kPointerSize, kSimd128Unaligned);
2261     CHECK(IsAddressAligned(obj->address(), kSimd128Alignment, kPointerSize));
2262     // There are filler objects before and after the object.
2263     filler1 = HeapObject::FromAddress(start);
2264     CHECK(obj != filler1 && filler1->IsFiller() &&
2265           filler1->Size() == kPointerSize);
2266     filler2 = HeapObject::FromAddress(start + 2 * kPointerSize);
2267     CHECK(obj != filler2 && filler2->IsFiller() &&
2268           filler2->Size() == 2 * kPointerSize);
2269   }
2270 }
2271 
2272 
TEST(TestSizeOfObjectsVsHeapIteratorPrecision)2273 TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
2274   CcTest::InitializeVM();
2275   HeapIterator iterator(CcTest::heap());
2276   intptr_t size_of_objects_1 = CcTest::heap()->SizeOfObjects();
2277   intptr_t size_of_objects_2 = 0;
2278   for (HeapObject* obj = iterator.next();
2279        obj != NULL;
2280        obj = iterator.next()) {
2281     if (!obj->IsFreeSpace()) {
2282       size_of_objects_2 += obj->Size();
2283     }
2284   }
2285   // Delta must be within 5% of the larger result.
2286   // TODO(gc): Tighten this up by distinguishing between byte
2287   // arrays that are real and those that merely mark free space
2288   // on the heap.
2289   if (size_of_objects_1 > size_of_objects_2) {
2290     intptr_t delta = size_of_objects_1 - size_of_objects_2;
2291     PrintF("Heap::SizeOfObjects: %" V8PRIdPTR
2292            ", "
2293            "Iterator: %" V8PRIdPTR
2294            ", "
2295            "delta: %" V8PRIdPTR "\n",
2296            size_of_objects_1, size_of_objects_2, delta);
2297     CHECK_GT(size_of_objects_1 / 20, delta);
2298   } else {
2299     intptr_t delta = size_of_objects_2 - size_of_objects_1;
2300     PrintF("Heap::SizeOfObjects: %" V8PRIdPTR
2301            ", "
2302            "Iterator: %" V8PRIdPTR
2303            ", "
2304            "delta: %" V8PRIdPTR "\n",
2305            size_of_objects_1, size_of_objects_2, delta);
2306     CHECK_GT(size_of_objects_2 / 20, delta);
2307   }
2308 }
2309 
2310 
FillUpNewSpace(NewSpace * new_space)2311 static void FillUpNewSpace(NewSpace* new_space) {
2312   // Fill up new space to the point that it is completely full. Make sure
2313   // that the scavenger does not undo the filling.
2314   Heap* heap = new_space->heap();
2315   Isolate* isolate = heap->isolate();
2316   Factory* factory = isolate->factory();
2317   HandleScope scope(isolate);
2318   AlwaysAllocateScope always_allocate(isolate);
2319   intptr_t available = new_space->Capacity() - new_space->Size();
2320   intptr_t number_of_fillers = (available / FixedArray::SizeFor(32)) - 1;
2321   for (intptr_t i = 0; i < number_of_fillers; i++) {
2322     CHECK(heap->InNewSpace(*factory->NewFixedArray(32, NOT_TENURED)));
2323   }
2324 }
2325 
2326 
TEST(GrowAndShrinkNewSpace)2327 TEST(GrowAndShrinkNewSpace) {
2328   CcTest::InitializeVM();
2329   Heap* heap = CcTest::heap();
2330   NewSpace* new_space = heap->new_space();
2331 
2332   if (heap->MaxSemiSpaceSize() == heap->InitialSemiSpaceSize()) {
2333     return;
2334   }
2335 
2336   // Explicitly growing should double the space capacity.
2337   intptr_t old_capacity, new_capacity;
2338   old_capacity = new_space->TotalCapacity();
2339   new_space->Grow();
2340   new_capacity = new_space->TotalCapacity();
2341   CHECK(2 * old_capacity == new_capacity);
2342 
2343   old_capacity = new_space->TotalCapacity();
2344   FillUpNewSpace(new_space);
2345   new_capacity = new_space->TotalCapacity();
2346   CHECK(old_capacity == new_capacity);
2347 
2348   // Explicitly shrinking should not affect space capacity.
2349   old_capacity = new_space->TotalCapacity();
2350   new_space->Shrink();
2351   new_capacity = new_space->TotalCapacity();
2352   CHECK(old_capacity == new_capacity);
2353 
2354   // Let the scavenger empty the new space.
2355   heap->CollectGarbage(NEW_SPACE);
2356   CHECK_LE(new_space->Size(), old_capacity);
2357 
2358   // Explicitly shrinking should halve the space capacity.
2359   old_capacity = new_space->TotalCapacity();
2360   new_space->Shrink();
2361   new_capacity = new_space->TotalCapacity();
2362   CHECK(old_capacity == 2 * new_capacity);
2363 
2364   // Consecutive shrinking should not affect space capacity.
2365   old_capacity = new_space->TotalCapacity();
2366   new_space->Shrink();
2367   new_space->Shrink();
2368   new_space->Shrink();
2369   new_capacity = new_space->TotalCapacity();
2370   CHECK(old_capacity == new_capacity);
2371 }
2372 
2373 
TEST(CollectingAllAvailableGarbageShrinksNewSpace)2374 TEST(CollectingAllAvailableGarbageShrinksNewSpace) {
2375   CcTest::InitializeVM();
2376   Heap* heap = CcTest::heap();
2377   if (heap->MaxSemiSpaceSize() == heap->InitialSemiSpaceSize()) {
2378     return;
2379   }
2380 
2381   v8::HandleScope scope(CcTest::isolate());
2382   NewSpace* new_space = heap->new_space();
2383   intptr_t old_capacity, new_capacity;
2384   old_capacity = new_space->TotalCapacity();
2385   new_space->Grow();
2386   new_capacity = new_space->TotalCapacity();
2387   CHECK(2 * old_capacity == new_capacity);
2388   FillUpNewSpace(new_space);
2389   heap->CollectAllAvailableGarbage();
2390   new_capacity = new_space->TotalCapacity();
2391   CHECK(old_capacity == new_capacity);
2392 }
2393 
2394 
NumberOfGlobalObjects()2395 static int NumberOfGlobalObjects() {
2396   int count = 0;
2397   HeapIterator iterator(CcTest::heap());
2398   for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
2399     if (obj->IsJSGlobalObject()) count++;
2400   }
2401   return count;
2402 }
2403 
2404 
2405 // Test that we don't embed maps from foreign contexts into
2406 // optimized code.
TEST(LeakNativeContextViaMap)2407 TEST(LeakNativeContextViaMap) {
2408   i::FLAG_allow_natives_syntax = true;
2409   v8::Isolate* isolate = CcTest::isolate();
2410   v8::HandleScope outer_scope(isolate);
2411   v8::Persistent<v8::Context> ctx1p;
2412   v8::Persistent<v8::Context> ctx2p;
2413   {
2414     v8::HandleScope scope(isolate);
2415     ctx1p.Reset(isolate, v8::Context::New(isolate));
2416     ctx2p.Reset(isolate, v8::Context::New(isolate));
2417     v8::Local<v8::Context>::New(isolate, ctx1p)->Enter();
2418   }
2419 
2420   CcTest::heap()->CollectAllAvailableGarbage();
2421   CHECK_EQ(2, NumberOfGlobalObjects());
2422 
2423   {
2424     v8::HandleScope inner_scope(isolate);
2425     CompileRun("var v = {x: 42}");
2426     v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p);
2427     v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p);
2428     v8::Local<v8::Value> v =
2429         ctx1->Global()->Get(ctx1, v8_str("v")).ToLocalChecked();
2430     ctx2->Enter();
2431     CHECK(ctx2->Global()->Set(ctx2, v8_str("o"), v).FromJust());
2432     v8::Local<v8::Value> res = CompileRun(
2433         "function f() { return o.x; }"
2434         "for (var i = 0; i < 10; ++i) f();"
2435         "%OptimizeFunctionOnNextCall(f);"
2436         "f();");
2437     CHECK_EQ(42, res->Int32Value(ctx2).FromJust());
2438     CHECK(ctx2->Global()
2439               ->Set(ctx2, v8_str("o"), v8::Int32::New(isolate, 0))
2440               .FromJust());
2441     ctx2->Exit();
2442     v8::Local<v8::Context>::New(isolate, ctx1)->Exit();
2443     ctx1p.Reset();
2444     isolate->ContextDisposedNotification();
2445   }
2446   CcTest::heap()->CollectAllAvailableGarbage();
2447   CHECK_EQ(1, NumberOfGlobalObjects());
2448   ctx2p.Reset();
2449   CcTest::heap()->CollectAllAvailableGarbage();
2450   CHECK_EQ(0, NumberOfGlobalObjects());
2451 }
2452 
2453 
2454 // Test that we don't embed functions from foreign contexts into
2455 // optimized code.
TEST(LeakNativeContextViaFunction)2456 TEST(LeakNativeContextViaFunction) {
2457   i::FLAG_allow_natives_syntax = true;
2458   v8::Isolate* isolate = CcTest::isolate();
2459   v8::HandleScope outer_scope(isolate);
2460   v8::Persistent<v8::Context> ctx1p;
2461   v8::Persistent<v8::Context> ctx2p;
2462   {
2463     v8::HandleScope scope(isolate);
2464     ctx1p.Reset(isolate, v8::Context::New(isolate));
2465     ctx2p.Reset(isolate, v8::Context::New(isolate));
2466     v8::Local<v8::Context>::New(isolate, ctx1p)->Enter();
2467   }
2468 
2469   CcTest::heap()->CollectAllAvailableGarbage();
2470   CHECK_EQ(2, NumberOfGlobalObjects());
2471 
2472   {
2473     v8::HandleScope inner_scope(isolate);
2474     CompileRun("var v = function() { return 42; }");
2475     v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p);
2476     v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p);
2477     v8::Local<v8::Value> v =
2478         ctx1->Global()->Get(ctx1, v8_str("v")).ToLocalChecked();
2479     ctx2->Enter();
2480     CHECK(ctx2->Global()->Set(ctx2, v8_str("o"), v).FromJust());
2481     v8::Local<v8::Value> res = CompileRun(
2482         "function f(x) { return x(); }"
2483         "for (var i = 0; i < 10; ++i) f(o);"
2484         "%OptimizeFunctionOnNextCall(f);"
2485         "f(o);");
2486     CHECK_EQ(42, res->Int32Value(ctx2).FromJust());
2487     CHECK(ctx2->Global()
2488               ->Set(ctx2, v8_str("o"), v8::Int32::New(isolate, 0))
2489               .FromJust());
2490     ctx2->Exit();
2491     ctx1->Exit();
2492     ctx1p.Reset();
2493     isolate->ContextDisposedNotification();
2494   }
2495   CcTest::heap()->CollectAllAvailableGarbage();
2496   CHECK_EQ(1, NumberOfGlobalObjects());
2497   ctx2p.Reset();
2498   CcTest::heap()->CollectAllAvailableGarbage();
2499   CHECK_EQ(0, NumberOfGlobalObjects());
2500 }
2501 
2502 
TEST(LeakNativeContextViaMapKeyed)2503 TEST(LeakNativeContextViaMapKeyed) {
2504   i::FLAG_allow_natives_syntax = true;
2505   v8::Isolate* isolate = CcTest::isolate();
2506   v8::HandleScope outer_scope(isolate);
2507   v8::Persistent<v8::Context> ctx1p;
2508   v8::Persistent<v8::Context> ctx2p;
2509   {
2510     v8::HandleScope scope(isolate);
2511     ctx1p.Reset(isolate, v8::Context::New(isolate));
2512     ctx2p.Reset(isolate, v8::Context::New(isolate));
2513     v8::Local<v8::Context>::New(isolate, ctx1p)->Enter();
2514   }
2515 
2516   CcTest::heap()->CollectAllAvailableGarbage();
2517   CHECK_EQ(2, NumberOfGlobalObjects());
2518 
2519   {
2520     v8::HandleScope inner_scope(isolate);
2521     CompileRun("var v = [42, 43]");
2522     v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p);
2523     v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p);
2524     v8::Local<v8::Value> v =
2525         ctx1->Global()->Get(ctx1, v8_str("v")).ToLocalChecked();
2526     ctx2->Enter();
2527     CHECK(ctx2->Global()->Set(ctx2, v8_str("o"), v).FromJust());
2528     v8::Local<v8::Value> res = CompileRun(
2529         "function f() { return o[0]; }"
2530         "for (var i = 0; i < 10; ++i) f();"
2531         "%OptimizeFunctionOnNextCall(f);"
2532         "f();");
2533     CHECK_EQ(42, res->Int32Value(ctx2).FromJust());
2534     CHECK(ctx2->Global()
2535               ->Set(ctx2, v8_str("o"), v8::Int32::New(isolate, 0))
2536               .FromJust());
2537     ctx2->Exit();
2538     ctx1->Exit();
2539     ctx1p.Reset();
2540     isolate->ContextDisposedNotification();
2541   }
2542   CcTest::heap()->CollectAllAvailableGarbage();
2543   CHECK_EQ(1, NumberOfGlobalObjects());
2544   ctx2p.Reset();
2545   CcTest::heap()->CollectAllAvailableGarbage();
2546   CHECK_EQ(0, NumberOfGlobalObjects());
2547 }
2548 
2549 
TEST(LeakNativeContextViaMapProto)2550 TEST(LeakNativeContextViaMapProto) {
2551   i::FLAG_allow_natives_syntax = true;
2552   v8::Isolate* isolate = CcTest::isolate();
2553   v8::HandleScope outer_scope(isolate);
2554   v8::Persistent<v8::Context> ctx1p;
2555   v8::Persistent<v8::Context> ctx2p;
2556   {
2557     v8::HandleScope scope(isolate);
2558     ctx1p.Reset(isolate, v8::Context::New(isolate));
2559     ctx2p.Reset(isolate, v8::Context::New(isolate));
2560     v8::Local<v8::Context>::New(isolate, ctx1p)->Enter();
2561   }
2562 
2563   CcTest::heap()->CollectAllAvailableGarbage();
2564   CHECK_EQ(2, NumberOfGlobalObjects());
2565 
2566   {
2567     v8::HandleScope inner_scope(isolate);
2568     CompileRun("var v = { y: 42}");
2569     v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p);
2570     v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p);
2571     v8::Local<v8::Value> v =
2572         ctx1->Global()->Get(ctx1, v8_str("v")).ToLocalChecked();
2573     ctx2->Enter();
2574     CHECK(ctx2->Global()->Set(ctx2, v8_str("o"), v).FromJust());
2575     v8::Local<v8::Value> res = CompileRun(
2576         "function f() {"
2577         "  var p = {x: 42};"
2578         "  p.__proto__ = o;"
2579         "  return p.x;"
2580         "}"
2581         "for (var i = 0; i < 10; ++i) f();"
2582         "%OptimizeFunctionOnNextCall(f);"
2583         "f();");
2584     CHECK_EQ(42, res->Int32Value(ctx2).FromJust());
2585     CHECK(ctx2->Global()
2586               ->Set(ctx2, v8_str("o"), v8::Int32::New(isolate, 0))
2587               .FromJust());
2588     ctx2->Exit();
2589     ctx1->Exit();
2590     ctx1p.Reset();
2591     isolate->ContextDisposedNotification();
2592   }
2593   CcTest::heap()->CollectAllAvailableGarbage();
2594   CHECK_EQ(1, NumberOfGlobalObjects());
2595   ctx2p.Reset();
2596   CcTest::heap()->CollectAllAvailableGarbage();
2597   CHECK_EQ(0, NumberOfGlobalObjects());
2598 }
2599 
2600 
TEST(InstanceOfStubWriteBarrier)2601 TEST(InstanceOfStubWriteBarrier) {
2602   i::FLAG_allow_natives_syntax = true;
2603 #ifdef VERIFY_HEAP
2604   i::FLAG_verify_heap = true;
2605 #endif
2606 
2607   CcTest::InitializeVM();
2608   if (!CcTest::i_isolate()->use_crankshaft()) return;
2609   if (i::FLAG_force_marking_deque_overflows) return;
2610   v8::HandleScope outer_scope(CcTest::isolate());
2611   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
2612 
2613   {
2614     v8::HandleScope scope(CcTest::isolate());
2615     CompileRun(
2616         "function foo () { }"
2617         "function mkbar () { return new (new Function(\"\")) (); }"
2618         "function f (x) { return (x instanceof foo); }"
2619         "function g () { f(mkbar()); }"
2620         "f(new foo()); f(new foo());"
2621         "%OptimizeFunctionOnNextCall(f);"
2622         "f(new foo()); g();");
2623   }
2624 
2625   IncrementalMarking* marking = CcTest::heap()->incremental_marking();
2626   marking->Stop();
2627   CcTest::heap()->StartIncrementalMarking();
2628 
2629   i::Handle<JSFunction> f = i::Handle<JSFunction>::cast(
2630       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
2631           CcTest::global()->Get(ctx, v8_str("f")).ToLocalChecked())));
2632 
2633   CHECK(f->IsOptimized());
2634 
2635   while (!Marking::IsBlack(Marking::MarkBitFrom(f->code())) &&
2636          !marking->IsStopped()) {
2637     // Discard any pending GC requests otherwise we will get GC when we enter
2638     // code below.
2639     marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
2640   }
2641 
2642   CHECK(marking->IsMarking());
2643 
2644   {
2645     v8::HandleScope scope(CcTest::isolate());
2646     v8::Local<v8::Object> global = CcTest::global();
2647     v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
2648         global->Get(ctx, v8_str("g")).ToLocalChecked());
2649     g->Call(ctx, global, 0, nullptr).ToLocalChecked();
2650   }
2651 
2652   CcTest::heap()->incremental_marking()->set_should_hurry(true);
2653   CcTest::heap()->CollectGarbage(OLD_SPACE);
2654 }
2655 
2656 namespace {
2657 
GetProfilerTicks(SharedFunctionInfo * shared)2658 int GetProfilerTicks(SharedFunctionInfo* shared) {
2659   return FLAG_ignition ? shared->profiler_ticks()
2660                        : shared->code()->profiler_ticks();
2661 }
2662 
2663 }  // namespace
2664 
TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking)2665 TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) {
2666   i::FLAG_stress_compaction = false;
2667   i::FLAG_allow_natives_syntax = true;
2668 #ifdef VERIFY_HEAP
2669   i::FLAG_verify_heap = true;
2670 #endif
2671 
2672   CcTest::InitializeVM();
2673   if (!CcTest::i_isolate()->use_crankshaft()) return;
2674   v8::HandleScope outer_scope(CcTest::isolate());
2675   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
2676 
2677   {
2678     v8::HandleScope scope(CcTest::isolate());
2679     CompileRun(
2680         "function f () {"
2681         "  var s = 0;"
2682         "  for (var i = 0; i < 100; i++)  s += i;"
2683         "  return s;"
2684         "}"
2685         "f(); f();"
2686         "%OptimizeFunctionOnNextCall(f);"
2687         "f();");
2688   }
2689   i::Handle<JSFunction> f = i::Handle<JSFunction>::cast(
2690       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
2691           CcTest::global()->Get(ctx, v8_str("f")).ToLocalChecked())));
2692   CHECK(f->IsOptimized());
2693 
2694   // Make sure incremental marking it not running.
2695   CcTest::heap()->incremental_marking()->Stop();
2696 
2697   CcTest::heap()->StartIncrementalMarking();
2698   // The following calls will increment CcTest::heap()->global_ic_age().
2699   CcTest::isolate()->ContextDisposedNotification();
2700   heap::SimulateIncrementalMarking(CcTest::heap());
2701   CcTest::heap()->CollectAllGarbage();
2702 
2703   CHECK_EQ(CcTest::heap()->global_ic_age(), f->shared()->ic_age());
2704   CHECK_EQ(0, f->shared()->opt_count());
2705   CHECK_EQ(0, GetProfilerTicks(f->shared()));
2706 }
2707 
2708 
TEST(ResetSharedFunctionInfoCountersDuringMarkSweep)2709 TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) {
2710   i::FLAG_stress_compaction = false;
2711   i::FLAG_allow_natives_syntax = true;
2712 #ifdef VERIFY_HEAP
2713   i::FLAG_verify_heap = true;
2714 #endif
2715 
2716   CcTest::InitializeVM();
2717   if (!CcTest::i_isolate()->use_crankshaft()) return;
2718   v8::HandleScope outer_scope(CcTest::isolate());
2719   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
2720 
2721   {
2722     v8::HandleScope scope(CcTest::isolate());
2723     CompileRun(
2724         "function f () {"
2725         "  var s = 0;"
2726         "  for (var i = 0; i < 100; i++)  s += i;"
2727         "  return s;"
2728         "}"
2729         "f(); f();"
2730         "%OptimizeFunctionOnNextCall(f);"
2731         "f();");
2732   }
2733   i::Handle<JSFunction> f = i::Handle<JSFunction>::cast(
2734       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
2735           CcTest::global()->Get(ctx, v8_str("f")).ToLocalChecked())));
2736   CHECK(f->IsOptimized());
2737 
2738   // Make sure incremental marking it not running.
2739   CcTest::heap()->incremental_marking()->Stop();
2740 
2741   // The following two calls will increment CcTest::heap()->global_ic_age().
2742   CcTest::isolate()->ContextDisposedNotification();
2743   CcTest::heap()->CollectAllGarbage();
2744 
2745   CHECK_EQ(CcTest::heap()->global_ic_age(), f->shared()->ic_age());
2746   CHECK_EQ(0, f->shared()->opt_count());
2747   CHECK_EQ(0, GetProfilerTicks(f->shared()));
2748 }
2749 
2750 
HEAP_TEST(GCFlags)2751 HEAP_TEST(GCFlags) {
2752   CcTest::InitializeVM();
2753   Heap* heap = CcTest::heap();
2754 
2755   heap->set_current_gc_flags(Heap::kNoGCFlags);
2756   CHECK_EQ(Heap::kNoGCFlags, heap->current_gc_flags_);
2757 
2758   // Set the flags to check whether we appropriately resets them after the GC.
2759   heap->set_current_gc_flags(Heap::kAbortIncrementalMarkingMask);
2760   heap->CollectAllGarbage(Heap::kReduceMemoryFootprintMask);
2761   CHECK_EQ(Heap::kNoGCFlags, heap->current_gc_flags_);
2762 
2763   MarkCompactCollector* collector = heap->mark_compact_collector();
2764   if (collector->sweeping_in_progress()) {
2765     collector->EnsureSweepingCompleted();
2766   }
2767 
2768   IncrementalMarking* marking = heap->incremental_marking();
2769   marking->Stop();
2770   heap->StartIncrementalMarking(Heap::kReduceMemoryFootprintMask);
2771   CHECK_NE(0, heap->current_gc_flags_ & Heap::kReduceMemoryFootprintMask);
2772 
2773   heap->CollectGarbage(NEW_SPACE);
2774   // NewSpace scavenges should not overwrite the flags.
2775   CHECK_NE(0, heap->current_gc_flags_ & Heap::kReduceMemoryFootprintMask);
2776 
2777   heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
2778   CHECK_EQ(Heap::kNoGCFlags, heap->current_gc_flags_);
2779 }
2780 
2781 
TEST(IdleNotificationFinishMarking)2782 TEST(IdleNotificationFinishMarking) {
2783   i::FLAG_allow_natives_syntax = true;
2784   CcTest::InitializeVM();
2785   const int initial_gc_count = CcTest::heap()->gc_count();
2786   heap::SimulateFullSpace(CcTest::heap()->old_space());
2787   IncrementalMarking* marking = CcTest::heap()->incremental_marking();
2788   marking->Stop();
2789   CcTest::heap()->StartIncrementalMarking();
2790 
2791   CHECK_EQ(CcTest::heap()->gc_count(), initial_gc_count);
2792 
2793   // TODO(hpayer): We cannot write proper unit test right now for heap.
2794   // The ideal test would call kMaxIdleMarkingDelayCounter to test the
2795   // marking delay counter.
2796 
2797   // Perform a huge incremental marking step but don't complete marking.
2798   intptr_t bytes_processed = 0;
2799   do {
2800     bytes_processed =
2801         marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD,
2802                       IncrementalMarking::FORCE_MARKING,
2803                       IncrementalMarking::DO_NOT_FORCE_COMPLETION);
2804     CHECK(!marking->IsIdleMarkingDelayCounterLimitReached());
2805   } while (bytes_processed);
2806 
2807   // The next invocations of incremental marking are not going to complete
2808   // marking
2809   // since the completion threshold is not reached
2810   for (size_t i = 0; i < IncrementalMarking::kMaxIdleMarkingDelayCounter - 2;
2811        i++) {
2812     marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD,
2813                   IncrementalMarking::FORCE_MARKING,
2814                   IncrementalMarking::DO_NOT_FORCE_COMPLETION);
2815     CHECK(!marking->IsIdleMarkingDelayCounterLimitReached());
2816   }
2817 
2818   marking->SetWeakClosureWasOverApproximatedForTesting(true);
2819 
2820   // The next idle notification has to finish incremental marking.
2821   const double kLongIdleTime = 1000.0;
2822   CcTest::isolate()->IdleNotificationDeadline(
2823       (v8::base::TimeTicks::HighResolutionNow().ToInternalValue() /
2824        static_cast<double>(v8::base::Time::kMicrosecondsPerSecond)) +
2825       kLongIdleTime);
2826   CHECK_EQ(CcTest::heap()->gc_count(), initial_gc_count + 1);
2827 }
2828 
2829 
2830 // Test that HAllocateObject will always return an object in new-space.
TEST(OptimizedAllocationAlwaysInNewSpace)2831 TEST(OptimizedAllocationAlwaysInNewSpace) {
2832   i::FLAG_allow_natives_syntax = true;
2833   CcTest::InitializeVM();
2834   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2835   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2836   v8::HandleScope scope(CcTest::isolate());
2837   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
2838   heap::SimulateFullSpace(CcTest::heap()->new_space());
2839   AlwaysAllocateScope always_allocate(CcTest::i_isolate());
2840   v8::Local<v8::Value> res = CompileRun(
2841       "function c(x) {"
2842       "  this.x = x;"
2843       "  for (var i = 0; i < 32; i++) {"
2844       "    this['x' + i] = x;"
2845       "  }"
2846       "}"
2847       "function f(x) { return new c(x); };"
2848       "f(1); f(2); f(3);"
2849       "%OptimizeFunctionOnNextCall(f);"
2850       "f(4);");
2851 
2852   CHECK_EQ(4, res.As<v8::Object>()
2853                   ->GetRealNamedProperty(ctx, v8_str("x"))
2854                   .ToLocalChecked()
2855                   ->Int32Value(ctx)
2856                   .FromJust());
2857 
2858   i::Handle<JSReceiver> o =
2859       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res));
2860 
2861   CHECK(CcTest::heap()->InNewSpace(*o));
2862 }
2863 
2864 
TEST(OptimizedPretenuringAllocationFolding)2865 TEST(OptimizedPretenuringAllocationFolding) {
2866   i::FLAG_allow_natives_syntax = true;
2867   i::FLAG_expose_gc = true;
2868   CcTest::InitializeVM();
2869   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2870   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2871   v8::HandleScope scope(CcTest::isolate());
2872   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
2873   // Grow new space unitl maximum capacity reached.
2874   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2875     CcTest::heap()->new_space()->Grow();
2876   }
2877 
2878   i::ScopedVector<char> source(1024);
2879   i::SNPrintF(
2880       source,
2881       "var number_elements = %d;"
2882       "var elements = new Array();"
2883       "function f() {"
2884       "  for (var i = 0; i < number_elements; i++) {"
2885       "    elements[i] = [[{}], [1.1]];"
2886       "  }"
2887       "  return elements[number_elements-1]"
2888       "};"
2889       "f(); gc();"
2890       "f(); f();"
2891       "%%OptimizeFunctionOnNextCall(f);"
2892       "f();",
2893       AllocationSite::kPretenureMinimumCreated);
2894 
2895   v8::Local<v8::Value> res = CompileRun(source.start());
2896 
2897   v8::Local<v8::Value> int_array =
2898       v8::Object::Cast(*res)->Get(ctx, v8_str("0")).ToLocalChecked();
2899   i::Handle<JSObject> int_array_handle = i::Handle<JSObject>::cast(
2900       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(int_array)));
2901   v8::Local<v8::Value> double_array =
2902       v8::Object::Cast(*res)->Get(ctx, v8_str("1")).ToLocalChecked();
2903   i::Handle<JSObject> double_array_handle = i::Handle<JSObject>::cast(
2904       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(double_array)));
2905 
2906   i::Handle<JSReceiver> o =
2907       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res));
2908   CHECK(CcTest::heap()->InOldSpace(*o));
2909   CHECK(CcTest::heap()->InOldSpace(*int_array_handle));
2910   CHECK(CcTest::heap()->InOldSpace(int_array_handle->elements()));
2911   CHECK(CcTest::heap()->InOldSpace(*double_array_handle));
2912   CHECK(CcTest::heap()->InOldSpace(double_array_handle->elements()));
2913 }
2914 
2915 
TEST(OptimizedPretenuringObjectArrayLiterals)2916 TEST(OptimizedPretenuringObjectArrayLiterals) {
2917   i::FLAG_allow_natives_syntax = true;
2918   i::FLAG_expose_gc = true;
2919   CcTest::InitializeVM();
2920   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2921   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2922   v8::HandleScope scope(CcTest::isolate());
2923 
2924   // Grow new space unitl maximum capacity reached.
2925   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2926     CcTest::heap()->new_space()->Grow();
2927   }
2928 
2929   i::ScopedVector<char> source(1024);
2930   i::SNPrintF(
2931       source,
2932       "var number_elements = %d;"
2933       "var elements = new Array(number_elements);"
2934       "function f() {"
2935       "  for (var i = 0; i < number_elements; i++) {"
2936       "    elements[i] = [{}, {}, {}];"
2937       "  }"
2938       "  return elements[number_elements - 1];"
2939       "};"
2940       "f(); gc();"
2941       "f(); f();"
2942       "%%OptimizeFunctionOnNextCall(f);"
2943       "f();",
2944       AllocationSite::kPretenureMinimumCreated);
2945 
2946   v8::Local<v8::Value> res = CompileRun(source.start());
2947 
2948   i::Handle<JSObject> o = Handle<JSObject>::cast(
2949       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
2950 
2951   CHECK(CcTest::heap()->InOldSpace(o->elements()));
2952   CHECK(CcTest::heap()->InOldSpace(*o));
2953 }
2954 
2955 
TEST(OptimizedPretenuringMixedInObjectProperties)2956 TEST(OptimizedPretenuringMixedInObjectProperties) {
2957   i::FLAG_allow_natives_syntax = true;
2958   i::FLAG_expose_gc = true;
2959   CcTest::InitializeVM();
2960   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
2961   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
2962   v8::HandleScope scope(CcTest::isolate());
2963 
2964   // Grow new space unitl maximum capacity reached.
2965   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
2966     CcTest::heap()->new_space()->Grow();
2967   }
2968 
2969 
2970   i::ScopedVector<char> source(1024);
2971   i::SNPrintF(
2972       source,
2973       "var number_elements = %d;"
2974       "var elements = new Array(number_elements);"
2975       "function f() {"
2976       "  for (var i = 0; i < number_elements; i++) {"
2977       "    elements[i] = {a: {c: 2.2, d: {}}, b: 1.1};"
2978       "  }"
2979       "  return elements[number_elements - 1];"
2980       "};"
2981       "f(); gc();"
2982       "f(); f();"
2983       "%%OptimizeFunctionOnNextCall(f);"
2984       "f();",
2985       AllocationSite::kPretenureMinimumCreated);
2986 
2987   v8::Local<v8::Value> res = CompileRun(source.start());
2988 
2989   i::Handle<JSObject> o = Handle<JSObject>::cast(
2990       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
2991 
2992   CHECK(CcTest::heap()->InOldSpace(*o));
2993   FieldIndex idx1 = FieldIndex::ForPropertyIndex(o->map(), 0);
2994   FieldIndex idx2 = FieldIndex::ForPropertyIndex(o->map(), 1);
2995   CHECK(CcTest::heap()->InOldSpace(o->RawFastPropertyAt(idx1)));
2996   if (!o->IsUnboxedDoubleField(idx2)) {
2997     CHECK(CcTest::heap()->InOldSpace(o->RawFastPropertyAt(idx2)));
2998   } else {
2999     CHECK_EQ(1.1, o->RawFastDoublePropertyAt(idx2));
3000   }
3001 
3002   JSObject* inner_object =
3003       reinterpret_cast<JSObject*>(o->RawFastPropertyAt(idx1));
3004   CHECK(CcTest::heap()->InOldSpace(inner_object));
3005   if (!inner_object->IsUnboxedDoubleField(idx1)) {
3006     CHECK(CcTest::heap()->InOldSpace(inner_object->RawFastPropertyAt(idx1)));
3007   } else {
3008     CHECK_EQ(2.2, inner_object->RawFastDoublePropertyAt(idx1));
3009   }
3010   CHECK(CcTest::heap()->InOldSpace(inner_object->RawFastPropertyAt(idx2)));
3011 }
3012 
3013 
TEST(OptimizedPretenuringDoubleArrayProperties)3014 TEST(OptimizedPretenuringDoubleArrayProperties) {
3015   i::FLAG_allow_natives_syntax = true;
3016   i::FLAG_expose_gc = true;
3017   CcTest::InitializeVM();
3018   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
3019   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
3020   v8::HandleScope scope(CcTest::isolate());
3021 
3022   // Grow new space unitl maximum capacity reached.
3023   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
3024     CcTest::heap()->new_space()->Grow();
3025   }
3026 
3027   i::ScopedVector<char> source(1024);
3028   i::SNPrintF(
3029       source,
3030       "var number_elements = %d;"
3031       "var elements = new Array(number_elements);"
3032       "function f() {"
3033       "  for (var i = 0; i < number_elements; i++) {"
3034       "    elements[i] = {a: 1.1, b: 2.2};"
3035       "  }"
3036       "  return elements[i - 1];"
3037       "};"
3038       "f(); gc();"
3039       "f(); f();"
3040       "%%OptimizeFunctionOnNextCall(f);"
3041       "f();",
3042       AllocationSite::kPretenureMinimumCreated);
3043 
3044   v8::Local<v8::Value> res = CompileRun(source.start());
3045 
3046   i::Handle<JSObject> o = Handle<JSObject>::cast(
3047       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
3048 
3049   CHECK(CcTest::heap()->InOldSpace(*o));
3050   CHECK(CcTest::heap()->InOldSpace(o->properties()));
3051 }
3052 
3053 
TEST(OptimizedPretenuringdoubleArrayLiterals)3054 TEST(OptimizedPretenuringdoubleArrayLiterals) {
3055   i::FLAG_allow_natives_syntax = true;
3056   i::FLAG_expose_gc = true;
3057   CcTest::InitializeVM();
3058   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
3059   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
3060   v8::HandleScope scope(CcTest::isolate());
3061 
3062   // Grow new space unitl maximum capacity reached.
3063   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
3064     CcTest::heap()->new_space()->Grow();
3065   }
3066 
3067   i::ScopedVector<char> source(1024);
3068   i::SNPrintF(
3069       source,
3070       "var number_elements = %d;"
3071       "var elements = new Array(number_elements);"
3072       "function f() {"
3073       "  for (var i = 0; i < number_elements; i++) {"
3074       "    elements[i] = [1.1, 2.2, 3.3];"
3075       "  }"
3076       "  return elements[number_elements - 1];"
3077       "};"
3078       "f(); gc();"
3079       "f(); f();"
3080       "%%OptimizeFunctionOnNextCall(f);"
3081       "f();",
3082       AllocationSite::kPretenureMinimumCreated);
3083 
3084   v8::Local<v8::Value> res = CompileRun(source.start());
3085 
3086   i::Handle<JSObject> o = Handle<JSObject>::cast(
3087       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
3088 
3089   CHECK(CcTest::heap()->InOldSpace(o->elements()));
3090   CHECK(CcTest::heap()->InOldSpace(*o));
3091 }
3092 
3093 
TEST(OptimizedPretenuringNestedMixedArrayLiterals)3094 TEST(OptimizedPretenuringNestedMixedArrayLiterals) {
3095   i::FLAG_allow_natives_syntax = true;
3096   i::FLAG_expose_gc = true;
3097   CcTest::InitializeVM();
3098   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
3099   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
3100   v8::HandleScope scope(CcTest::isolate());
3101   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3102   // Grow new space unitl maximum capacity reached.
3103   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
3104     CcTest::heap()->new_space()->Grow();
3105   }
3106 
3107   i::ScopedVector<char> source(1024);
3108   i::SNPrintF(
3109       source,
3110       "var number_elements = 100;"
3111       "var elements = new Array(number_elements);"
3112       "function f() {"
3113       "  for (var i = 0; i < number_elements; i++) {"
3114       "    elements[i] = [[{}, {}, {}], [1.1, 2.2, 3.3]];"
3115       "  }"
3116       "  return elements[number_elements - 1];"
3117       "};"
3118       "f(); gc();"
3119       "f(); f();"
3120       "%%OptimizeFunctionOnNextCall(f);"
3121       "f();");
3122 
3123   v8::Local<v8::Value> res = CompileRun(source.start());
3124 
3125   v8::Local<v8::Value> int_array =
3126       v8::Object::Cast(*res)->Get(ctx, v8_str("0")).ToLocalChecked();
3127   i::Handle<JSObject> int_array_handle = i::Handle<JSObject>::cast(
3128       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(int_array)));
3129   v8::Local<v8::Value> double_array =
3130       v8::Object::Cast(*res)->Get(ctx, v8_str("1")).ToLocalChecked();
3131   i::Handle<JSObject> double_array_handle = i::Handle<JSObject>::cast(
3132       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(double_array)));
3133 
3134   Handle<JSObject> o = Handle<JSObject>::cast(
3135       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
3136   CHECK(CcTest::heap()->InOldSpace(*o));
3137   CHECK(CcTest::heap()->InOldSpace(*int_array_handle));
3138   CHECK(CcTest::heap()->InOldSpace(int_array_handle->elements()));
3139   CHECK(CcTest::heap()->InOldSpace(*double_array_handle));
3140   CHECK(CcTest::heap()->InOldSpace(double_array_handle->elements()));
3141 }
3142 
3143 
TEST(OptimizedPretenuringNestedObjectLiterals)3144 TEST(OptimizedPretenuringNestedObjectLiterals) {
3145   i::FLAG_allow_natives_syntax = true;
3146   i::FLAG_expose_gc = true;
3147   CcTest::InitializeVM();
3148   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
3149   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
3150   v8::HandleScope scope(CcTest::isolate());
3151   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3152   // Grow new space unitl maximum capacity reached.
3153   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
3154     CcTest::heap()->new_space()->Grow();
3155   }
3156 
3157   i::ScopedVector<char> source(1024);
3158   i::SNPrintF(
3159       source,
3160       "var number_elements = %d;"
3161       "var elements = new Array(number_elements);"
3162       "function f() {"
3163       "  for (var i = 0; i < number_elements; i++) {"
3164       "    elements[i] = [[{}, {}, {}],[{}, {}, {}]];"
3165       "  }"
3166       "  return elements[number_elements - 1];"
3167       "};"
3168       "f(); gc();"
3169       "f(); f();"
3170       "%%OptimizeFunctionOnNextCall(f);"
3171       "f();",
3172       AllocationSite::kPretenureMinimumCreated);
3173 
3174   v8::Local<v8::Value> res = CompileRun(source.start());
3175 
3176   v8::Local<v8::Value> int_array_1 =
3177       v8::Object::Cast(*res)->Get(ctx, v8_str("0")).ToLocalChecked();
3178   Handle<JSObject> int_array_handle_1 = Handle<JSObject>::cast(
3179       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(int_array_1)));
3180   v8::Local<v8::Value> int_array_2 =
3181       v8::Object::Cast(*res)->Get(ctx, v8_str("1")).ToLocalChecked();
3182   Handle<JSObject> int_array_handle_2 = Handle<JSObject>::cast(
3183       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(int_array_2)));
3184 
3185   Handle<JSObject> o = Handle<JSObject>::cast(
3186       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
3187   CHECK(CcTest::heap()->InOldSpace(*o));
3188   CHECK(CcTest::heap()->InOldSpace(*int_array_handle_1));
3189   CHECK(CcTest::heap()->InOldSpace(int_array_handle_1->elements()));
3190   CHECK(CcTest::heap()->InOldSpace(*int_array_handle_2));
3191   CHECK(CcTest::heap()->InOldSpace(int_array_handle_2->elements()));
3192 }
3193 
3194 
TEST(OptimizedPretenuringNestedDoubleLiterals)3195 TEST(OptimizedPretenuringNestedDoubleLiterals) {
3196   i::FLAG_allow_natives_syntax = true;
3197   i::FLAG_expose_gc = true;
3198   CcTest::InitializeVM();
3199   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
3200   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
3201   v8::HandleScope scope(CcTest::isolate());
3202   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3203   // Grow new space unitl maximum capacity reached.
3204   while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) {
3205     CcTest::heap()->new_space()->Grow();
3206   }
3207 
3208   i::ScopedVector<char> source(1024);
3209   i::SNPrintF(
3210       source,
3211       "var number_elements = %d;"
3212       "var elements = new Array(number_elements);"
3213       "function f() {"
3214       "  for (var i = 0; i < number_elements; i++) {"
3215       "    elements[i] = [[1.1, 1.2, 1.3],[2.1, 2.2, 2.3]];"
3216       "  }"
3217       "  return elements[number_elements - 1];"
3218       "};"
3219       "f(); gc();"
3220       "f(); f();"
3221       "%%OptimizeFunctionOnNextCall(f);"
3222       "f();",
3223       AllocationSite::kPretenureMinimumCreated);
3224 
3225   v8::Local<v8::Value> res = CompileRun(source.start());
3226 
3227   v8::Local<v8::Value> double_array_1 =
3228       v8::Object::Cast(*res)->Get(ctx, v8_str("0")).ToLocalChecked();
3229   i::Handle<JSObject> double_array_handle_1 = i::Handle<JSObject>::cast(
3230       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(double_array_1)));
3231   v8::Local<v8::Value> double_array_2 =
3232       v8::Object::Cast(*res)->Get(ctx, v8_str("1")).ToLocalChecked();
3233   i::Handle<JSObject> double_array_handle_2 = Handle<JSObject>::cast(
3234       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(double_array_2)));
3235 
3236   i::Handle<JSObject> o = Handle<JSObject>::cast(
3237       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
3238   CHECK(CcTest::heap()->InOldSpace(*o));
3239   CHECK(CcTest::heap()->InOldSpace(*double_array_handle_1));
3240   CHECK(CcTest::heap()->InOldSpace(double_array_handle_1->elements()));
3241   CHECK(CcTest::heap()->InOldSpace(*double_array_handle_2));
3242   CHECK(CcTest::heap()->InOldSpace(double_array_handle_2->elements()));
3243 }
3244 
3245 
3246 // Test regular array literals allocation.
TEST(OptimizedAllocationArrayLiterals)3247 TEST(OptimizedAllocationArrayLiterals) {
3248   i::FLAG_allow_natives_syntax = true;
3249   CcTest::InitializeVM();
3250   if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return;
3251   if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
3252   v8::HandleScope scope(CcTest::isolate());
3253   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3254   v8::Local<v8::Value> res = CompileRun(
3255       "function f() {"
3256       "  var numbers = new Array(1, 2, 3);"
3257       "  numbers[0] = 3.14;"
3258       "  return numbers;"
3259       "};"
3260       "f(); f(); f();"
3261       "%OptimizeFunctionOnNextCall(f);"
3262       "f();");
3263   CHECK_EQ(static_cast<int>(3.14), v8::Object::Cast(*res)
3264                                        ->Get(ctx, v8_str("0"))
3265                                        .ToLocalChecked()
3266                                        ->Int32Value(ctx)
3267                                        .FromJust());
3268 
3269   i::Handle<JSObject> o = Handle<JSObject>::cast(
3270       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
3271 
3272   CHECK(CcTest::heap()->InNewSpace(o->elements()));
3273 }
3274 
3275 
CountMapTransitions(Map * map)3276 static int CountMapTransitions(Map* map) {
3277   return TransitionArray::NumberOfTransitions(map->raw_transitions());
3278 }
3279 
3280 
3281 // Test that map transitions are cleared and maps are collected with
3282 // incremental marking as well.
TEST(Regress1465)3283 TEST(Regress1465) {
3284   i::FLAG_stress_compaction = false;
3285   i::FLAG_allow_natives_syntax = true;
3286   i::FLAG_trace_incremental_marking = true;
3287   i::FLAG_retain_maps_for_n_gc = 0;
3288   CcTest::InitializeVM();
3289   v8::HandleScope scope(CcTest::isolate());
3290   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3291   static const int transitions_count = 256;
3292 
3293   CompileRun("function F() {}");
3294   {
3295     AlwaysAllocateScope always_allocate(CcTest::i_isolate());
3296     for (int i = 0; i < transitions_count; i++) {
3297       EmbeddedVector<char, 64> buffer;
3298       SNPrintF(buffer, "var o = new F; o.prop%d = %d;", i, i);
3299       CompileRun(buffer.start());
3300     }
3301     CompileRun("var root = new F;");
3302   }
3303 
3304   i::Handle<JSReceiver> root =
3305       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(
3306           CcTest::global()->Get(ctx, v8_str("root")).ToLocalChecked()));
3307 
3308   // Count number of live transitions before marking.
3309   int transitions_before = CountMapTransitions(root->map());
3310   CompileRun("%DebugPrint(root);");
3311   CHECK_EQ(transitions_count, transitions_before);
3312 
3313   heap::SimulateIncrementalMarking(CcTest::heap());
3314   CcTest::heap()->CollectAllGarbage();
3315 
3316   // Count number of live transitions after marking.  Note that one transition
3317   // is left, because 'o' still holds an instance of one transition target.
3318   int transitions_after = CountMapTransitions(root->map());
3319   CompileRun("%DebugPrint(root);");
3320   CHECK_EQ(1, transitions_after);
3321 }
3322 
3323 
3324 #ifdef DEBUG
AddTransitions(int transitions_count)3325 static void AddTransitions(int transitions_count) {
3326   AlwaysAllocateScope always_allocate(CcTest::i_isolate());
3327   for (int i = 0; i < transitions_count; i++) {
3328     EmbeddedVector<char, 64> buffer;
3329     SNPrintF(buffer, "var o = new F; o.prop%d = %d;", i, i);
3330     CompileRun(buffer.start());
3331   }
3332 }
3333 
3334 
GetByName(const char * name)3335 static i::Handle<JSObject> GetByName(const char* name) {
3336   return i::Handle<JSObject>::cast(
3337       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(
3338           CcTest::global()
3339               ->Get(CcTest::isolate()->GetCurrentContext(), v8_str(name))
3340               .ToLocalChecked())));
3341 }
3342 
3343 
AddPropertyTo(int gc_count,Handle<JSObject> object,const char * property_name)3344 static void AddPropertyTo(
3345     int gc_count, Handle<JSObject> object, const char* property_name) {
3346   Isolate* isolate = CcTest::i_isolate();
3347   Factory* factory = isolate->factory();
3348   Handle<String> prop_name = factory->InternalizeUtf8String(property_name);
3349   Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
3350   i::FLAG_gc_interval = gc_count;
3351   i::FLAG_gc_global = true;
3352   i::FLAG_retain_maps_for_n_gc = 0;
3353   CcTest::heap()->set_allocation_timeout(gc_count);
3354   JSReceiver::SetProperty(object, prop_name, twenty_three, SLOPPY).Check();
3355 }
3356 
3357 
TEST(TransitionArrayShrinksDuringAllocToZero)3358 TEST(TransitionArrayShrinksDuringAllocToZero) {
3359   i::FLAG_stress_compaction = false;
3360   i::FLAG_allow_natives_syntax = true;
3361   CcTest::InitializeVM();
3362   v8::HandleScope scope(CcTest::isolate());
3363   static const int transitions_count = 10;
3364   CompileRun("function F() { }");
3365   AddTransitions(transitions_count);
3366   CompileRun("var root = new F;");
3367   Handle<JSObject> root = GetByName("root");
3368 
3369   // Count number of live transitions before marking.
3370   int transitions_before = CountMapTransitions(root->map());
3371   CHECK_EQ(transitions_count, transitions_before);
3372 
3373   // Get rid of o
3374   CompileRun("o = new F;"
3375              "root = new F");
3376   root = GetByName("root");
3377   AddPropertyTo(2, root, "funny");
3378   CcTest::heap()->CollectGarbage(NEW_SPACE);
3379 
3380   // Count number of live transitions after marking.  Note that one transition
3381   // is left, because 'o' still holds an instance of one transition target.
3382   int transitions_after = CountMapTransitions(
3383       Map::cast(root->map()->GetBackPointer()));
3384   CHECK_EQ(1, transitions_after);
3385 }
3386 
3387 
TEST(TransitionArrayShrinksDuringAllocToOne)3388 TEST(TransitionArrayShrinksDuringAllocToOne) {
3389   i::FLAG_stress_compaction = false;
3390   i::FLAG_allow_natives_syntax = true;
3391   CcTest::InitializeVM();
3392   v8::HandleScope scope(CcTest::isolate());
3393   static const int transitions_count = 10;
3394   CompileRun("function F() {}");
3395   AddTransitions(transitions_count);
3396   CompileRun("var root = new F;");
3397   Handle<JSObject> root = GetByName("root");
3398 
3399   // Count number of live transitions before marking.
3400   int transitions_before = CountMapTransitions(root->map());
3401   CHECK_EQ(transitions_count, transitions_before);
3402 
3403   root = GetByName("root");
3404   AddPropertyTo(2, root, "funny");
3405   CcTest::heap()->CollectGarbage(NEW_SPACE);
3406 
3407   // Count number of live transitions after marking.  Note that one transition
3408   // is left, because 'o' still holds an instance of one transition target.
3409   int transitions_after = CountMapTransitions(
3410       Map::cast(root->map()->GetBackPointer()));
3411   CHECK_EQ(2, transitions_after);
3412 }
3413 
3414 
TEST(TransitionArrayShrinksDuringAllocToOnePropertyFound)3415 TEST(TransitionArrayShrinksDuringAllocToOnePropertyFound) {
3416   i::FLAG_stress_compaction = false;
3417   i::FLAG_allow_natives_syntax = true;
3418   CcTest::InitializeVM();
3419   v8::HandleScope scope(CcTest::isolate());
3420   static const int transitions_count = 10;
3421   CompileRun("function F() {}");
3422   AddTransitions(transitions_count);
3423   CompileRun("var root = new F;");
3424   Handle<JSObject> root = GetByName("root");
3425 
3426   // Count number of live transitions before marking.
3427   int transitions_before = CountMapTransitions(root->map());
3428   CHECK_EQ(transitions_count, transitions_before);
3429 
3430   root = GetByName("root");
3431   AddPropertyTo(0, root, "prop9");
3432   CcTest::i_isolate()->heap()->CollectGarbage(OLD_SPACE);
3433 
3434   // Count number of live transitions after marking.  Note that one transition
3435   // is left, because 'o' still holds an instance of one transition target.
3436   int transitions_after = CountMapTransitions(
3437       Map::cast(root->map()->GetBackPointer()));
3438   CHECK_EQ(1, transitions_after);
3439 }
3440 
3441 
TEST(TransitionArraySimpleToFull)3442 TEST(TransitionArraySimpleToFull) {
3443   i::FLAG_stress_compaction = false;
3444   i::FLAG_allow_natives_syntax = true;
3445   CcTest::InitializeVM();
3446   v8::HandleScope scope(CcTest::isolate());
3447   static const int transitions_count = 1;
3448   CompileRun("function F() {}");
3449   AddTransitions(transitions_count);
3450   CompileRun("var root = new F;");
3451   Handle<JSObject> root = GetByName("root");
3452 
3453   // Count number of live transitions before marking.
3454   int transitions_before = CountMapTransitions(root->map());
3455   CHECK_EQ(transitions_count, transitions_before);
3456 
3457   CompileRun("o = new F;"
3458              "root = new F");
3459   root = GetByName("root");
3460   CHECK(TransitionArray::IsSimpleTransition(root->map()->raw_transitions()));
3461   AddPropertyTo(2, root, "happy");
3462 
3463   // Count number of live transitions after marking.  Note that one transition
3464   // is left, because 'o' still holds an instance of one transition target.
3465   int transitions_after = CountMapTransitions(
3466       Map::cast(root->map()->GetBackPointer()));
3467   CHECK_EQ(1, transitions_after);
3468 }
3469 #endif  // DEBUG
3470 
3471 
TEST(Regress2143a)3472 TEST(Regress2143a) {
3473   i::FLAG_incremental_marking = true;
3474   CcTest::InitializeVM();
3475   v8::HandleScope scope(CcTest::isolate());
3476 
3477   // Prepare a map transition from the root object together with a yet
3478   // untransitioned root object.
3479   CompileRun("var root = new Object;"
3480              "root.foo = 0;"
3481              "root = new Object;");
3482 
3483   heap::SimulateIncrementalMarking(CcTest::heap());
3484 
3485   // Compile a StoreIC that performs the prepared map transition. This
3486   // will restart incremental marking and should make sure the root is
3487   // marked grey again.
3488   CompileRun("function f(o) {"
3489              "  o.foo = 0;"
3490              "}"
3491              "f(new Object);"
3492              "f(root);");
3493 
3494   // This bug only triggers with aggressive IC clearing.
3495   CcTest::heap()->AgeInlineCaches();
3496 
3497   // Explicitly request GC to perform final marking step and sweeping.
3498   CcTest::heap()->CollectAllGarbage();
3499 
3500   Handle<JSReceiver> root = v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(
3501       CcTest::global()
3502           ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("root"))
3503           .ToLocalChecked()));
3504 
3505   // The root object should be in a sane state.
3506   CHECK(root->IsJSObject());
3507   CHECK(root->map()->IsMap());
3508 }
3509 
3510 
TEST(Regress2143b)3511 TEST(Regress2143b) {
3512   i::FLAG_incremental_marking = true;
3513   i::FLAG_allow_natives_syntax = true;
3514   CcTest::InitializeVM();
3515   v8::HandleScope scope(CcTest::isolate());
3516 
3517   // Prepare a map transition from the root object together with a yet
3518   // untransitioned root object.
3519   CompileRun("var root = new Object;"
3520              "root.foo = 0;"
3521              "root = new Object;");
3522 
3523   heap::SimulateIncrementalMarking(CcTest::heap());
3524 
3525   // Compile an optimized LStoreNamedField that performs the prepared
3526   // map transition. This will restart incremental marking and should
3527   // make sure the root is marked grey again.
3528   CompileRun("function f(o) {"
3529              "  o.foo = 0;"
3530              "}"
3531              "f(new Object);"
3532              "f(new Object);"
3533              "%OptimizeFunctionOnNextCall(f);"
3534              "f(root);"
3535              "%DeoptimizeFunction(f);");
3536 
3537   // This bug only triggers with aggressive IC clearing.
3538   CcTest::heap()->AgeInlineCaches();
3539 
3540   // Explicitly request GC to perform final marking step and sweeping.
3541   CcTest::heap()->CollectAllGarbage();
3542 
3543   Handle<JSReceiver> root = v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(
3544       CcTest::global()
3545           ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("root"))
3546           .ToLocalChecked()));
3547 
3548   // The root object should be in a sane state.
3549   CHECK(root->IsJSObject());
3550   CHECK(root->map()->IsMap());
3551 }
3552 
3553 
TEST(ReleaseOverReservedPages)3554 TEST(ReleaseOverReservedPages) {
3555   if (FLAG_never_compact) return;
3556   i::FLAG_trace_gc = true;
3557   // The optimizer can allocate stuff, messing up the test.
3558   i::FLAG_crankshaft = false;
3559   i::FLAG_always_opt = false;
3560   // Parallel compaction increases fragmentation, depending on how existing
3561   // memory is distributed. Since this is non-deterministic because of
3562   // concurrent sweeping, we disable it for this test.
3563   i::FLAG_parallel_compaction = false;
3564   // Concurrent sweeping adds non determinism, depending on when memory is
3565   // available for further reuse.
3566   i::FLAG_concurrent_sweeping = false;
3567   // Fast evacuation of pages may result in a different page count in old space.
3568   i::FLAG_page_promotion = false;
3569   CcTest::InitializeVM();
3570   Isolate* isolate = CcTest::i_isolate();
3571   Factory* factory = isolate->factory();
3572   Heap* heap = isolate->heap();
3573   v8::HandleScope scope(CcTest::isolate());
3574   static const int number_of_test_pages = 20;
3575 
3576   // Prepare many pages with low live-bytes count.
3577   PagedSpace* old_space = heap->old_space();
3578   const int initial_page_count = old_space->CountTotalPages();
3579   const int overall_page_count = number_of_test_pages + initial_page_count;
3580   for (int i = 0; i < number_of_test_pages; i++) {
3581     AlwaysAllocateScope always_allocate(isolate);
3582     heap::SimulateFullSpace(old_space);
3583     factory->NewFixedArray(1, TENURED);
3584   }
3585   CHECK_EQ(overall_page_count, old_space->CountTotalPages());
3586 
3587   // Triggering one GC will cause a lot of garbage to be discovered but
3588   // even spread across all allocated pages.
3589   heap->CollectAllGarbage(Heap::kFinalizeIncrementalMarkingMask,
3590                           "triggered for preparation");
3591   CHECK_GE(overall_page_count, old_space->CountTotalPages());
3592 
3593   // Triggering subsequent GCs should cause at least half of the pages
3594   // to be released to the OS after at most two cycles.
3595   heap->CollectAllGarbage(Heap::kFinalizeIncrementalMarkingMask,
3596                           "triggered by test 1");
3597   CHECK_GE(overall_page_count, old_space->CountTotalPages());
3598   heap->CollectAllGarbage(Heap::kFinalizeIncrementalMarkingMask,
3599                           "triggered by test 2");
3600   CHECK_GE(overall_page_count, old_space->CountTotalPages() * 2);
3601 
3602   // Triggering a last-resort GC should cause all pages to be released to the
3603   // OS so that other processes can seize the memory.  If we get a failure here
3604   // where there are 2 pages left instead of 1, then we should increase the
3605   // size of the first page a little in SizeOfFirstPage in spaces.cc.  The
3606   // first page should be small in order to reduce memory used when the VM
3607   // boots, but if the 20 small arrays don't fit on the first page then that's
3608   // an indication that it is too small.
3609   heap->CollectAllAvailableGarbage("triggered really hard");
3610   CHECK_EQ(initial_page_count, old_space->CountTotalPages());
3611 }
3612 
3613 static int forced_gc_counter = 0;
3614 
MockUseCounterCallback(v8::Isolate * isolate,v8::Isolate::UseCounterFeature feature)3615 void MockUseCounterCallback(v8::Isolate* isolate,
3616                             v8::Isolate::UseCounterFeature feature) {
3617   isolate->GetCurrentContext();
3618   if (feature == v8::Isolate::kForcedGC) {
3619     forced_gc_counter++;
3620   }
3621 }
3622 
3623 
TEST(CountForcedGC)3624 TEST(CountForcedGC) {
3625   i::FLAG_expose_gc = true;
3626   CcTest::InitializeVM();
3627   Isolate* isolate = CcTest::i_isolate();
3628   v8::HandleScope scope(CcTest::isolate());
3629 
3630   isolate->SetUseCounterCallback(MockUseCounterCallback);
3631 
3632   forced_gc_counter = 0;
3633   const char* source = "gc();";
3634   CompileRun(source);
3635   CHECK_GT(forced_gc_counter, 0);
3636 }
3637 
3638 
3639 #ifdef OBJECT_PRINT
TEST(PrintSharedFunctionInfo)3640 TEST(PrintSharedFunctionInfo) {
3641   CcTest::InitializeVM();
3642   v8::HandleScope scope(CcTest::isolate());
3643   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3644   const char* source = "f = function() { return 987654321; }\n"
3645                        "g = function() { return 123456789; }\n";
3646   CompileRun(source);
3647   i::Handle<JSFunction> g = i::Handle<JSFunction>::cast(
3648       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
3649           CcTest::global()->Get(ctx, v8_str("g")).ToLocalChecked())));
3650 
3651   OFStream os(stdout);
3652   g->shared()->Print(os);
3653   os << std::endl;
3654 }
3655 #endif  // OBJECT_PRINT
3656 
3657 
TEST(IncrementalMarkingPreservesMonomorphicCallIC)3658 TEST(IncrementalMarkingPreservesMonomorphicCallIC) {
3659   if (i::FLAG_always_opt) return;
3660   CcTest::InitializeVM();
3661   v8::HandleScope scope(CcTest::isolate());
3662   v8::Local<v8::Value> fun1, fun2;
3663   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3664   {
3665     CompileRun("function fun() {};");
3666     fun1 = CcTest::global()->Get(ctx, v8_str("fun")).ToLocalChecked();
3667   }
3668 
3669   {
3670     CompileRun("function fun() {};");
3671     fun2 = CcTest::global()->Get(ctx, v8_str("fun")).ToLocalChecked();
3672   }
3673 
3674   // Prepare function f that contains type feedback for the two closures.
3675   CHECK(CcTest::global()->Set(ctx, v8_str("fun1"), fun1).FromJust());
3676   CHECK(CcTest::global()->Set(ctx, v8_str("fun2"), fun2).FromJust());
3677   CompileRun("function f(a, b) { a(); b(); } f(fun1, fun2);");
3678 
3679   Handle<JSFunction> f = Handle<JSFunction>::cast(
3680       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
3681           CcTest::global()->Get(ctx, v8_str("f")).ToLocalChecked())));
3682 
3683   Handle<TypeFeedbackVector> feedback_vector(f->feedback_vector());
3684   FeedbackVectorHelper feedback_helper(feedback_vector);
3685 
3686   int expected_slots = 2;
3687   CHECK_EQ(expected_slots, feedback_helper.slot_count());
3688   int slot1 = 0;
3689   int slot2 = 1;
3690   CHECK(feedback_vector->Get(feedback_helper.slot(slot1))->IsWeakCell());
3691   CHECK(feedback_vector->Get(feedback_helper.slot(slot2))->IsWeakCell());
3692 
3693   heap::SimulateIncrementalMarking(CcTest::heap());
3694   CcTest::heap()->CollectAllGarbage();
3695 
3696   CHECK(!WeakCell::cast(feedback_vector->Get(feedback_helper.slot(slot1)))
3697              ->cleared());
3698   CHECK(!WeakCell::cast(feedback_vector->Get(feedback_helper.slot(slot2)))
3699              ->cleared());
3700 }
3701 
3702 
FindFirstIC(Code * code,Code::Kind kind)3703 static Code* FindFirstIC(Code* code, Code::Kind kind) {
3704   int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
3705              RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
3706   for (RelocIterator it(code, mask); !it.done(); it.next()) {
3707     RelocInfo* info = it.rinfo();
3708     Code* target = Code::GetCodeFromTargetAddress(info->target_address());
3709     if (target->is_inline_cache_stub() && target->kind() == kind) {
3710       return target;
3711     }
3712   }
3713   return NULL;
3714 }
3715 
3716 
CheckVectorIC(Handle<JSFunction> f,int slot_index,InlineCacheState desired_state)3717 static void CheckVectorIC(Handle<JSFunction> f, int slot_index,
3718                           InlineCacheState desired_state) {
3719   Handle<TypeFeedbackVector> vector =
3720       Handle<TypeFeedbackVector>(f->feedback_vector());
3721   FeedbackVectorHelper helper(vector);
3722   FeedbackVectorSlot slot = helper.slot(slot_index);
3723   if (vector->GetKind(slot) == FeedbackVectorSlotKind::LOAD_IC) {
3724     LoadICNexus nexus(vector, slot);
3725     CHECK(nexus.StateFromFeedback() == desired_state);
3726   } else {
3727     CHECK_EQ(FeedbackVectorSlotKind::KEYED_LOAD_IC, vector->GetKind(slot));
3728     KeyedLoadICNexus nexus(vector, slot);
3729     CHECK(nexus.StateFromFeedback() == desired_state);
3730   }
3731 }
3732 
TEST(IncrementalMarkingPreservesMonomorphicConstructor)3733 TEST(IncrementalMarkingPreservesMonomorphicConstructor) {
3734   if (i::FLAG_always_opt) return;
3735   CcTest::InitializeVM();
3736   v8::HandleScope scope(CcTest::isolate());
3737   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3738   // Prepare function f that contains a monomorphic IC for object
3739   // originating from the same native context.
3740   CompileRun(
3741       "function fun() { this.x = 1; };"
3742       "function f(o) { return new o(); } f(fun); f(fun);");
3743   Handle<JSFunction> f = Handle<JSFunction>::cast(
3744       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
3745           CcTest::global()->Get(ctx, v8_str("f")).ToLocalChecked())));
3746 
3747   Handle<TypeFeedbackVector> vector(f->feedback_vector());
3748   CHECK(vector->Get(FeedbackVectorSlot(0))->IsWeakCell());
3749 
3750   heap::SimulateIncrementalMarking(CcTest::heap());
3751   CcTest::heap()->CollectAllGarbage();
3752 
3753   CHECK(vector->Get(FeedbackVectorSlot(0))->IsWeakCell());
3754 }
3755 
TEST(IncrementalMarkingPreservesMonomorphicIC)3756 TEST(IncrementalMarkingPreservesMonomorphicIC) {
3757   if (i::FLAG_always_opt) return;
3758   CcTest::InitializeVM();
3759   v8::HandleScope scope(CcTest::isolate());
3760   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3761   // Prepare function f that contains a monomorphic IC for object
3762   // originating from the same native context.
3763   CompileRun("function fun() { this.x = 1; }; var obj = new fun();"
3764              "function f(o) { return o.x; } f(obj); f(obj);");
3765   Handle<JSFunction> f = Handle<JSFunction>::cast(
3766       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
3767           CcTest::global()->Get(ctx, v8_str("f")).ToLocalChecked())));
3768 
3769   CheckVectorIC(f, 0, MONOMORPHIC);
3770 
3771   heap::SimulateIncrementalMarking(CcTest::heap());
3772   CcTest::heap()->CollectAllGarbage();
3773 
3774   CheckVectorIC(f, 0, MONOMORPHIC);
3775 }
3776 
TEST(IncrementalMarkingPreservesPolymorphicIC)3777 TEST(IncrementalMarkingPreservesPolymorphicIC) {
3778   if (i::FLAG_always_opt) return;
3779   CcTest::InitializeVM();
3780   v8::HandleScope scope(CcTest::isolate());
3781   v8::Local<v8::Value> obj1, obj2;
3782   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3783 
3784   {
3785     LocalContext env;
3786     CompileRun("function fun() { this.x = 1; }; var obj = new fun();");
3787     obj1 = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
3788   }
3789 
3790   {
3791     LocalContext env;
3792     CompileRun("function fun() { this.x = 2; }; var obj = new fun();");
3793     obj2 = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
3794   }
3795 
3796   // Prepare function f that contains a polymorphic IC for objects
3797   // originating from two different native contexts.
3798   CHECK(CcTest::global()->Set(ctx, v8_str("obj1"), obj1).FromJust());
3799   CHECK(CcTest::global()->Set(ctx, v8_str("obj2"), obj2).FromJust());
3800   CompileRun("function f(o) { return o.x; } f(obj1); f(obj1); f(obj2);");
3801   Handle<JSFunction> f = Handle<JSFunction>::cast(
3802       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
3803           CcTest::global()->Get(ctx, v8_str("f")).ToLocalChecked())));
3804 
3805   CheckVectorIC(f, 0, POLYMORPHIC);
3806 
3807   // Fire context dispose notification.
3808   heap::SimulateIncrementalMarking(CcTest::heap());
3809   CcTest::heap()->CollectAllGarbage();
3810 
3811   CheckVectorIC(f, 0, POLYMORPHIC);
3812 }
3813 
TEST(ContextDisposeDoesntClearPolymorphicIC)3814 TEST(ContextDisposeDoesntClearPolymorphicIC) {
3815   if (i::FLAG_always_opt) return;
3816   CcTest::InitializeVM();
3817   v8::HandleScope scope(CcTest::isolate());
3818   v8::Local<v8::Value> obj1, obj2;
3819   v8::Local<v8::Context> ctx = CcTest::isolate()->GetCurrentContext();
3820 
3821   {
3822     LocalContext env;
3823     CompileRun("function fun() { this.x = 1; }; var obj = new fun();");
3824     obj1 = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
3825   }
3826 
3827   {
3828     LocalContext env;
3829     CompileRun("function fun() { this.x = 2; }; var obj = new fun();");
3830     obj2 = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked();
3831   }
3832 
3833   // Prepare function f that contains a polymorphic IC for objects
3834   // originating from two different native contexts.
3835   CHECK(CcTest::global()->Set(ctx, v8_str("obj1"), obj1).FromJust());
3836   CHECK(CcTest::global()->Set(ctx, v8_str("obj2"), obj2).FromJust());
3837   CompileRun("function f(o) { return o.x; } f(obj1); f(obj1); f(obj2);");
3838   Handle<JSFunction> f = Handle<JSFunction>::cast(
3839       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
3840           CcTest::global()->Get(ctx, v8_str("f")).ToLocalChecked())));
3841 
3842   CheckVectorIC(f, 0, POLYMORPHIC);
3843 
3844   // Fire context dispose notification.
3845   CcTest::isolate()->ContextDisposedNotification();
3846   heap::SimulateIncrementalMarking(CcTest::heap());
3847   CcTest::heap()->CollectAllGarbage();
3848 
3849   CheckVectorIC(f, 0, POLYMORPHIC);
3850 }
3851 
3852 
3853 class SourceResource : public v8::String::ExternalOneByteStringResource {
3854  public:
SourceResource(const char * data)3855   explicit SourceResource(const char* data)
3856     : data_(data), length_(strlen(data)) { }
3857 
Dispose()3858   virtual void Dispose() {
3859     i::DeleteArray(data_);
3860     data_ = NULL;
3861   }
3862 
data() const3863   const char* data() const { return data_; }
3864 
length() const3865   size_t length() const { return length_; }
3866 
IsDisposed()3867   bool IsDisposed() { return data_ == NULL; }
3868 
3869  private:
3870   const char* data_;
3871   size_t length_;
3872 };
3873 
3874 
ReleaseStackTraceDataTest(v8::Isolate * isolate,const char * source,const char * accessor)3875 void ReleaseStackTraceDataTest(v8::Isolate* isolate, const char* source,
3876                                const char* accessor) {
3877   // Test that the data retained by the Error.stack accessor is released
3878   // after the first time the accessor is fired.  We use external string
3879   // to check whether the data is being released since the external string
3880   // resource's callback is fired when the external string is GC'ed.
3881   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3882   v8::HandleScope scope(isolate);
3883   SourceResource* resource = new SourceResource(i::StrDup(source));
3884   {
3885     v8::HandleScope scope(isolate);
3886     v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
3887     v8::Local<v8::String> source_string =
3888         v8::String::NewExternalOneByte(isolate, resource).ToLocalChecked();
3889     i_isolate->heap()->CollectAllAvailableGarbage();
3890     v8::Script::Compile(ctx, source_string)
3891         .ToLocalChecked()
3892         ->Run(ctx)
3893         .ToLocalChecked();
3894     CHECK(!resource->IsDisposed());
3895   }
3896   // i_isolate->heap()->CollectAllAvailableGarbage();
3897   CHECK(!resource->IsDisposed());
3898 
3899   CompileRun(accessor);
3900   i_isolate->heap()->CollectAllAvailableGarbage();
3901 
3902   // External source has been released.
3903   CHECK(resource->IsDisposed());
3904   delete resource;
3905 }
3906 
3907 
UNINITIALIZED_TEST(ReleaseStackTraceData)3908 UNINITIALIZED_TEST(ReleaseStackTraceData) {
3909   if (i::FLAG_always_opt) {
3910     // TODO(ulan): Remove this once the memory leak via code_next_link is fixed.
3911     // See: https://codereview.chromium.org/181833004/
3912     return;
3913   }
3914   FLAG_use_ic = false;  // ICs retain objects.
3915   FLAG_concurrent_recompilation = false;
3916   v8::Isolate::CreateParams create_params;
3917   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
3918   v8::Isolate* isolate = v8::Isolate::New(create_params);
3919   {
3920     v8::Isolate::Scope isolate_scope(isolate);
3921     v8::HandleScope handle_scope(isolate);
3922     v8::Context::New(isolate)->Enter();
3923     static const char* source1 = "var error = null;            "
3924     /* Normal Error */           "try {                        "
3925                                  "  throw new Error();         "
3926                                  "} catch (e) {                "
3927                                  "  error = e;                 "
3928                                  "}                            ";
3929     static const char* source2 = "var error = null;            "
3930     /* Stack overflow */         "try {                        "
3931                                  "  (function f() { f(); })(); "
3932                                  "} catch (e) {                "
3933                                  "  error = e;                 "
3934                                  "}                            ";
3935     static const char* source3 = "var error = null;            "
3936     /* Normal Error */           "try {                        "
3937     /* as prototype */           "  throw new Error();         "
3938                                  "} catch (e) {                "
3939                                  "  error = {};                "
3940                                  "  error.__proto__ = e;       "
3941                                  "}                            ";
3942     static const char* source4 = "var error = null;            "
3943     /* Stack overflow */         "try {                        "
3944     /* as prototype   */         "  (function f() { f(); })(); "
3945                                  "} catch (e) {                "
3946                                  "  error = {};                "
3947                                  "  error.__proto__ = e;       "
3948                                  "}                            ";
3949     static const char* getter = "error.stack";
3950     static const char* setter = "error.stack = 0";
3951 
3952     ReleaseStackTraceDataTest(isolate, source1, setter);
3953     ReleaseStackTraceDataTest(isolate, source2, setter);
3954     // We do not test source3 and source4 with setter, since the setter is
3955     // supposed to (untypically) write to the receiver, not the holder.  This is
3956     // to emulate the behavior of a data property.
3957 
3958     ReleaseStackTraceDataTest(isolate, source1, getter);
3959     ReleaseStackTraceDataTest(isolate, source2, getter);
3960     ReleaseStackTraceDataTest(isolate, source3, getter);
3961     ReleaseStackTraceDataTest(isolate, source4, getter);
3962   }
3963   isolate->Dispose();
3964 }
3965 
3966 
TEST(Regress159140)3967 TEST(Regress159140) {
3968   i::FLAG_allow_natives_syntax = true;
3969   CcTest::InitializeVM();
3970   Isolate* isolate = CcTest::i_isolate();
3971   LocalContext env;
3972   Heap* heap = isolate->heap();
3973   HandleScope scope(isolate);
3974 
3975   // Perform one initial GC to enable code flushing.
3976   heap->CollectAllGarbage();
3977 
3978   // Prepare several closures that are all eligible for code flushing
3979   // because all reachable ones are not optimized. Make sure that the
3980   // optimized code object is directly reachable through a handle so
3981   // that it is marked black during incremental marking.
3982   Handle<Code> code;
3983   {
3984     HandleScope inner_scope(isolate);
3985     CompileRun("function h(x) {}"
3986                "function mkClosure() {"
3987                "  return function(x) { return x + 1; };"
3988                "}"
3989                "var f = mkClosure();"
3990                "var g = mkClosure();"
3991                "f(1); f(2);"
3992                "g(1); g(2);"
3993                "h(1); h(2);"
3994                "%OptimizeFunctionOnNextCall(f); f(3);"
3995                "%OptimizeFunctionOnNextCall(h); h(3);");
3996 
3997     Handle<JSFunction> f = Handle<JSFunction>::cast(
3998         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
3999             CcTest::global()->Get(env.local(), v8_str("f")).ToLocalChecked())));
4000     CHECK(f->is_compiled());
4001     CompileRun("f = null;");
4002 
4003     Handle<JSFunction> g = Handle<JSFunction>::cast(
4004         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4005             CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked())));
4006     CHECK(g->is_compiled());
4007     const int kAgingThreshold = 6;
4008     for (int i = 0; i < kAgingThreshold; i++) {
4009       g->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
4010     }
4011 
4012     code = inner_scope.CloseAndEscape(Handle<Code>(f->code()));
4013   }
4014 
4015   // Simulate incremental marking so that the functions are enqueued as
4016   // code flushing candidates. Then optimize one function. Finally
4017   // finish the GC to complete code flushing.
4018   heap::SimulateIncrementalMarking(heap);
4019   CompileRun("%OptimizeFunctionOnNextCall(g); g(3);");
4020   heap->CollectAllGarbage();
4021 
4022   // Unoptimized code is missing and the deoptimizer will go ballistic.
4023   CompileRun("g('bozo');");
4024 }
4025 
4026 
TEST(Regress165495)4027 TEST(Regress165495) {
4028   i::FLAG_allow_natives_syntax = true;
4029   CcTest::InitializeVM();
4030   Isolate* isolate = CcTest::i_isolate();
4031   Heap* heap = isolate->heap();
4032   HandleScope scope(isolate);
4033 
4034   // Perform one initial GC to enable code flushing.
4035   heap->CollectAllGarbage();
4036 
4037   // Prepare an optimized closure that the optimized code map will get
4038   // populated. Then age the unoptimized code to trigger code flushing
4039   // but make sure the optimized code is unreachable.
4040   {
4041     HandleScope inner_scope(isolate);
4042     LocalContext env;
4043     CompileRun("function mkClosure() {"
4044                "  return function(x) { return x + 1; };"
4045                "}"
4046                "var f = mkClosure();"
4047                "f(1); f(2);"
4048                "%OptimizeFunctionOnNextCall(f); f(3);");
4049 
4050     Handle<JSFunction> f = Handle<JSFunction>::cast(
4051         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4052             CcTest::global()->Get(env.local(), v8_str("f")).ToLocalChecked())));
4053     CHECK(f->is_compiled());
4054     const int kAgingThreshold = 6;
4055     for (int i = 0; i < kAgingThreshold; i++) {
4056       f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
4057     }
4058 
4059     CompileRun("f = null;");
4060   }
4061 
4062   // Simulate incremental marking so that unoptimized code is flushed
4063   // even though it still is cached in the optimized code map.
4064   heap::SimulateIncrementalMarking(heap);
4065   heap->CollectAllGarbage();
4066 
4067   // Make a new closure that will get code installed from the code map.
4068   // Unoptimized code is missing and the deoptimizer will go ballistic.
4069   CompileRun("var g = mkClosure(); g('bozo');");
4070 }
4071 
4072 
TEST(Regress169209)4073 TEST(Regress169209) {
4074   i::FLAG_stress_compaction = false;
4075   i::FLAG_allow_natives_syntax = true;
4076 
4077   CcTest::InitializeVM();
4078   Isolate* isolate = CcTest::i_isolate();
4079   Heap* heap = isolate->heap();
4080   HandleScope scope(isolate);
4081 
4082   // Perform one initial GC to enable code flushing.
4083   heap->CollectAllGarbage();
4084 
4085   // Prepare a shared function info eligible for code flushing for which
4086   // the unoptimized code will be replaced during optimization.
4087   Handle<SharedFunctionInfo> shared1;
4088   {
4089     HandleScope inner_scope(isolate);
4090     LocalContext env;
4091     CompileRun("function f() { return 'foobar'; }"
4092                "function g(x) { if (x) f(); }"
4093                "f();"
4094                "g(false);"
4095                "g(false);");
4096 
4097     Handle<JSFunction> f = Handle<JSFunction>::cast(
4098         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4099             CcTest::global()->Get(env.local(), v8_str("f")).ToLocalChecked())));
4100     CHECK(f->is_compiled());
4101     const int kAgingThreshold = 6;
4102     for (int i = 0; i < kAgingThreshold; i++) {
4103       f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
4104     }
4105 
4106     shared1 = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
4107   }
4108 
4109   // Prepare a shared function info eligible for code flushing that will
4110   // represent the dangling tail of the candidate list.
4111   Handle<SharedFunctionInfo> shared2;
4112   {
4113     HandleScope inner_scope(isolate);
4114     LocalContext env;
4115     CompileRun("function flushMe() { return 0; }"
4116                "flushMe(1);");
4117 
4118     Handle<JSFunction> f = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
4119         *v8::Local<v8::Function>::Cast(CcTest::global()
4120                                            ->Get(env.local(), v8_str("flushMe"))
4121                                            .ToLocalChecked())));
4122     CHECK(f->is_compiled());
4123     const int kAgingThreshold = 6;
4124     for (int i = 0; i < kAgingThreshold; i++) {
4125       f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
4126     }
4127 
4128     shared2 = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
4129   }
4130 
4131   // Simulate incremental marking and collect code flushing candidates.
4132   heap::SimulateIncrementalMarking(heap);
4133   CHECK(shared1->code()->gc_metadata() != NULL);
4134 
4135   // Optimize function and make sure the unoptimized code is replaced.
4136   CompileRun("%OptimizeFunctionOnNextCall(g);"
4137              "g(false);");
4138 
4139   // Finish garbage collection cycle.
4140   heap->CollectAllGarbage();
4141   CHECK(shared1->code()->gc_metadata() == NULL);
4142 }
4143 
4144 
TEST(Regress169928)4145 TEST(Regress169928) {
4146   i::FLAG_allow_natives_syntax = true;
4147   i::FLAG_crankshaft = false;
4148   CcTest::InitializeVM();
4149   Isolate* isolate = CcTest::i_isolate();
4150   LocalContext env;
4151   Factory* factory = isolate->factory();
4152   v8::HandleScope scope(CcTest::isolate());
4153 
4154   // Some flags turn Scavenge collections into Mark-sweep collections
4155   // and hence are incompatible with this test case.
4156   if (FLAG_gc_global || FLAG_stress_compaction) return;
4157 
4158   // Prepare the environment
4159   CompileRun("function fastliteralcase(literal, value) {"
4160              "    literal[0] = value;"
4161              "    return literal;"
4162              "}"
4163              "function get_standard_literal() {"
4164              "    var literal = [1, 2, 3];"
4165              "    return literal;"
4166              "}"
4167              "obj = fastliteralcase(get_standard_literal(), 1);"
4168              "obj = fastliteralcase(get_standard_literal(), 1.5);"
4169              "obj = fastliteralcase(get_standard_literal(), 2);");
4170 
4171   // prepare the heap
4172   v8::Local<v8::String> mote_code_string =
4173       v8_str("fastliteralcase(mote, 2.5);");
4174 
4175   v8::Local<v8::String> array_name = v8_str("mote");
4176   CHECK(CcTest::global()
4177             ->Set(env.local(), array_name, v8::Int32::New(CcTest::isolate(), 0))
4178             .FromJust());
4179 
4180   // First make sure we flip spaces
4181   CcTest::heap()->CollectGarbage(NEW_SPACE);
4182 
4183   // Allocate the object.
4184   Handle<FixedArray> array_data = factory->NewFixedArray(2, NOT_TENURED);
4185   array_data->set(0, Smi::FromInt(1));
4186   array_data->set(1, Smi::FromInt(2));
4187 
4188   heap::AllocateAllButNBytes(
4189       CcTest::heap()->new_space(),
4190       JSArray::kSize + AllocationMemento::kSize + kPointerSize);
4191 
4192   Handle<JSArray> array =
4193       factory->NewJSArrayWithElements(array_data, FAST_SMI_ELEMENTS);
4194 
4195   CHECK_EQ(Smi::FromInt(2), array->length());
4196   CHECK(array->HasFastSmiOrObjectElements());
4197 
4198   // We need filler the size of AllocationMemento object, plus an extra
4199   // fill pointer value.
4200   HeapObject* obj = NULL;
4201   AllocationResult allocation =
4202       CcTest::heap()->new_space()->AllocateRawUnaligned(
4203           AllocationMemento::kSize + kPointerSize);
4204   CHECK(allocation.To(&obj));
4205   Address addr_obj = obj->address();
4206   CcTest::heap()->CreateFillerObjectAt(addr_obj,
4207                                        AllocationMemento::kSize + kPointerSize,
4208                                        ClearRecordedSlots::kNo);
4209 
4210   // Give the array a name, making sure not to allocate strings.
4211   v8::Local<v8::Object> array_obj = v8::Utils::ToLocal(array);
4212   CHECK(CcTest::global()->Set(env.local(), array_name, array_obj).FromJust());
4213 
4214   // This should crash with a protection violation if we are running a build
4215   // with the bug.
4216   AlwaysAllocateScope aa_scope(isolate);
4217   v8::Script::Compile(env.local(), mote_code_string)
4218       .ToLocalChecked()
4219       ->Run(env.local())
4220       .ToLocalChecked();
4221 }
4222 
4223 
4224 #ifdef DEBUG
TEST(Regress513507)4225 TEST(Regress513507) {
4226   i::FLAG_flush_optimized_code_cache = false;
4227   i::FLAG_allow_natives_syntax = true;
4228   i::FLAG_gc_global = true;
4229   CcTest::InitializeVM();
4230   Isolate* isolate = CcTest::i_isolate();
4231   LocalContext env;
4232   Heap* heap = isolate->heap();
4233   HandleScope scope(isolate);
4234 
4235   // Prepare function whose optimized code map we can use.
4236   Handle<SharedFunctionInfo> shared;
4237   {
4238     HandleScope inner_scope(isolate);
4239     CompileRun("function f() { return 1 }"
4240                "f(); %OptimizeFunctionOnNextCall(f); f();");
4241 
4242     Handle<JSFunction> f = Handle<JSFunction>::cast(
4243         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4244             CcTest::global()->Get(env.local(), v8_str("f")).ToLocalChecked())));
4245     shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
4246     CompileRun("f = null");
4247   }
4248 
4249   // Prepare optimized code that we can use.
4250   Handle<Code> code;
4251   {
4252     HandleScope inner_scope(isolate);
4253     CompileRun("function g() { return 2 }"
4254                "g(); %OptimizeFunctionOnNextCall(g); g();");
4255 
4256     Handle<JSFunction> g = Handle<JSFunction>::cast(
4257         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4258             CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked())));
4259     code = inner_scope.CloseAndEscape(handle(g->code(), isolate));
4260     if (!code->is_optimized_code()) return;
4261   }
4262 
4263   Handle<TypeFeedbackVector> vector =
4264       TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata()));
4265   Handle<LiteralsArray> lit =
4266       LiteralsArray::New(isolate, vector, shared->num_literals(), TENURED);
4267   Handle<Context> context(isolate->context());
4268 
4269   // Add the new code several times to the optimized code map and also set an
4270   // allocation timeout so that expanding the code map will trigger a GC.
4271   heap->set_allocation_timeout(5);
4272   FLAG_gc_interval = 1000;
4273   for (int i = 0; i < 10; ++i) {
4274     BailoutId id = BailoutId(i);
4275     SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, lit, id);
4276   }
4277 }
4278 #endif  // DEBUG
4279 
4280 
TEST(Regress514122)4281 TEST(Regress514122) {
4282   i::FLAG_flush_optimized_code_cache = false;
4283   i::FLAG_allow_natives_syntax = true;
4284   CcTest::InitializeVM();
4285   Isolate* isolate = CcTest::i_isolate();
4286   LocalContext env;
4287   Heap* heap = isolate->heap();
4288   HandleScope scope(isolate);
4289 
4290   // Perfrom one initial GC to enable code flushing.
4291   CcTest::heap()->CollectAllGarbage();
4292 
4293   // Prepare function whose optimized code map we can use.
4294   Handle<SharedFunctionInfo> shared;
4295   {
4296     HandleScope inner_scope(isolate);
4297     CompileRun("function f() { return 1 }"
4298                "f(); %OptimizeFunctionOnNextCall(f); f();");
4299 
4300     Handle<JSFunction> f = Handle<JSFunction>::cast(
4301         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4302             CcTest::global()->Get(env.local(), v8_str("f")).ToLocalChecked())));
4303     shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
4304     CompileRun("f = null");
4305   }
4306 
4307   // Prepare optimized code that we can use.
4308   Handle<Code> code;
4309   {
4310     HandleScope inner_scope(isolate);
4311     CompileRun("function g() { return 2 }"
4312                "g(); %OptimizeFunctionOnNextCall(g); g();");
4313 
4314     Handle<JSFunction> g = Handle<JSFunction>::cast(
4315         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4316             CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked())));
4317     code = inner_scope.CloseAndEscape(handle(g->code(), isolate));
4318     if (!code->is_optimized_code()) return;
4319   }
4320 
4321   Handle<TypeFeedbackVector> vector =
4322       TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata()));
4323   Handle<LiteralsArray> lit =
4324       LiteralsArray::New(isolate, vector, shared->num_literals(), TENURED);
4325   Handle<Context> context(isolate->context());
4326 
4327   // Add the code several times to the optimized code map.
4328   for (int i = 0; i < 3; ++i) {
4329     HandleScope inner_scope(isolate);
4330     BailoutId id = BailoutId(i);
4331     SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, lit, id);
4332   }
4333   shared->optimized_code_map()->Print();
4334 
4335   // Add the code with a literals array to be evacuated.
4336   Page* evac_page;
4337   {
4338     HandleScope inner_scope(isolate);
4339     AlwaysAllocateScope always_allocate(isolate);
4340     // Make sure literal is placed on an old-space evacuation candidate.
4341     heap::SimulateFullSpace(heap->old_space());
4342 
4343     // Make sure there the number of literals is > 0.
4344     Handle<LiteralsArray> lit =
4345         LiteralsArray::New(isolate, vector, 23, TENURED);
4346 
4347     evac_page = Page::FromAddress(lit->address());
4348     BailoutId id = BailoutId(100);
4349     SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, lit, id);
4350   }
4351 
4352   // Heap is ready, force {lit_page} to become an evacuation candidate and
4353   // simulate incremental marking to enqueue optimized code map.
4354   FLAG_manual_evacuation_candidates_selection = true;
4355   evac_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
4356   heap::SimulateIncrementalMarking(heap);
4357 
4358   // No matter whether reachable or not, {boomer} is doomed.
4359   Handle<Object> boomer(shared->optimized_code_map(), isolate);
4360 
4361   // Add the code several times to the optimized code map. This will leave old
4362   // copies of the optimized code map unreachable but still marked.
4363   for (int i = 3; i < 6; ++i) {
4364     HandleScope inner_scope(isolate);
4365     BailoutId id = BailoutId(i);
4366     SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, lit, id);
4367   }
4368 
4369   // Trigger a GC to flush out the bug.
4370   heap->CollectGarbage(i::OLD_SPACE, "fire in the hole");
4371   boomer->Print();
4372 }
4373 
4374 
TEST(OptimizedCodeMapReuseEntries)4375 TEST(OptimizedCodeMapReuseEntries) {
4376   i::FLAG_flush_optimized_code_cache = false;
4377   i::FLAG_allow_natives_syntax = true;
4378   // BUG(v8:4598): Since TurboFan doesn't treat maps in code weakly, we can't
4379   // run this test.
4380   if (i::FLAG_turbo) return;
4381   CcTest::InitializeVM();
4382   v8::Isolate* v8_isolate = CcTest::isolate();
4383   Isolate* isolate = CcTest::i_isolate();
4384   Heap* heap = isolate->heap();
4385   HandleScope scope(isolate);
4386 
4387   // Create 3 contexts, allow the 2nd one to be disposed, and verify that
4388   // a 4th context will re-use the weak slots in the optimized code map
4389   // to hold data, rather than expanding the map.
4390   v8::Local<v8::Context> c1 = v8::Context::New(v8_isolate);
4391   const char* source = "function foo(x) { var l = [1]; return x+l[0]; }";
4392   v8::ScriptCompiler::Source script_source(
4393       v8::String::NewFromUtf8(v8_isolate, source, v8::NewStringType::kNormal)
4394           .ToLocalChecked());
4395   v8::Local<v8::UnboundScript> indep =
4396       v8::ScriptCompiler::CompileUnboundScript(v8_isolate, &script_source)
4397           .ToLocalChecked();
4398   const char* toplevel = "foo(3); %OptimizeFunctionOnNextCall(foo); foo(3);";
4399   // Perfrom one initial GC to enable code flushing.
4400   heap->CollectAllGarbage();
4401 
4402   c1->Enter();
4403   indep->BindToCurrentContext()->Run(c1).ToLocalChecked();
4404   CompileRun(toplevel);
4405 
4406   Handle<SharedFunctionInfo> shared;
4407   Handle<JSFunction> foo = Handle<JSFunction>::cast(
4408       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4409           CcTest::global()->Get(c1, v8_str("foo")).ToLocalChecked())));
4410   CHECK(foo->shared()->is_compiled());
4411   shared = handle(foo->shared());
4412   c1->Exit();
4413 
4414   {
4415     HandleScope scope(isolate);
4416     v8::Local<v8::Context> c2 = v8::Context::New(v8_isolate);
4417     c2->Enter();
4418     indep->BindToCurrentContext()->Run(c2).ToLocalChecked();
4419     CompileRun(toplevel);
4420     c2->Exit();
4421   }
4422 
4423   {
4424     HandleScope scope(isolate);
4425     v8::Local<v8::Context> c3 = v8::Context::New(v8_isolate);
4426     c3->Enter();
4427     indep->BindToCurrentContext()->Run(c3).ToLocalChecked();
4428     CompileRun(toplevel);
4429     c3->Exit();
4430 
4431     // Now, collect garbage. Context c2 should have no roots to it, and it's
4432     // entry in the optimized code map should be free for a new context.
4433     for (int i = 0; i < 4; i++) {
4434       heap->CollectAllGarbage();
4435     }
4436 
4437     Handle<FixedArray> optimized_code_map =
4438         handle(shared->optimized_code_map());
4439     // There should be 3 entries in the map.
4440     CHECK_EQ(
4441         3, ((optimized_code_map->length() - SharedFunctionInfo::kEntriesStart) /
4442             SharedFunctionInfo::kEntryLength));
4443     // But one of them (formerly for c2) should be cleared.
4444     int cleared_count = 0;
4445     for (int i = SharedFunctionInfo::kEntriesStart;
4446          i < optimized_code_map->length();
4447          i += SharedFunctionInfo::kEntryLength) {
4448       cleared_count +=
4449           WeakCell::cast(
4450               optimized_code_map->get(i + SharedFunctionInfo::kContextOffset))
4451                   ->cleared()
4452               ? 1
4453               : 0;
4454     }
4455     CHECK_EQ(1, cleared_count);
4456 
4457     // Verify that a new context uses the cleared entry rather than creating a
4458     // new
4459     // optimized code map array.
4460     v8::Local<v8::Context> c4 = v8::Context::New(v8_isolate);
4461     c4->Enter();
4462     indep->BindToCurrentContext()->Run(c4).ToLocalChecked();
4463     CompileRun(toplevel);
4464     c4->Exit();
4465     CHECK_EQ(*optimized_code_map, shared->optimized_code_map());
4466 
4467     // Now each entry is in use.
4468     cleared_count = 0;
4469     for (int i = SharedFunctionInfo::kEntriesStart;
4470          i < optimized_code_map->length();
4471          i += SharedFunctionInfo::kEntryLength) {
4472       cleared_count +=
4473           WeakCell::cast(
4474               optimized_code_map->get(i + SharedFunctionInfo::kContextOffset))
4475                   ->cleared()
4476               ? 1
4477               : 0;
4478     }
4479     CHECK_EQ(0, cleared_count);
4480   }
4481 }
4482 
4483 
TEST(Regress513496)4484 TEST(Regress513496) {
4485   i::FLAG_flush_optimized_code_cache = false;
4486   i::FLAG_allow_natives_syntax = true;
4487   CcTest::InitializeVM();
4488   Isolate* isolate = CcTest::i_isolate();
4489   Heap* heap = isolate->heap();
4490   HandleScope scope(isolate);
4491 
4492   // Perfrom one initial GC to enable code flushing.
4493   CcTest::heap()->CollectAllGarbage();
4494 
4495   // Prepare an optimized closure with containing an inlined function. Then age
4496   // the inlined unoptimized code to trigger code flushing but make sure the
4497   // outer optimized code is kept in the optimized code map.
4498   Handle<SharedFunctionInfo> shared;
4499   {
4500     LocalContext context;
4501     HandleScope inner_scope(isolate);
4502     CompileRun(
4503         "function g(x) { return x + 1 }"
4504         "function mkClosure() {"
4505         "  return function(x) { return g(x); };"
4506         "}"
4507         "var f = mkClosure();"
4508         "f(1); f(2);"
4509         "%OptimizeFunctionOnNextCall(f); f(3);");
4510 
4511     Handle<JSFunction> g = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
4512         *v8::Local<v8::Function>::Cast(CcTest::global()
4513                                            ->Get(context.local(), v8_str("g"))
4514                                            .ToLocalChecked())));
4515     CHECK(g->shared()->is_compiled());
4516     const int kAgingThreshold = 6;
4517     for (int i = 0; i < kAgingThreshold; i++) {
4518       g->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
4519     }
4520 
4521     Handle<JSFunction> f = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
4522         *v8::Local<v8::Function>::Cast(CcTest::global()
4523                                            ->Get(context.local(), v8_str("f"))
4524                                            .ToLocalChecked())));
4525     CHECK(f->is_compiled());
4526     shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
4527     CompileRun("f = null");
4528   }
4529 
4530   // Lookup the optimized code and keep it alive.
4531   CodeAndLiterals result = shared->SearchOptimizedCodeMap(
4532       isolate->context()->native_context(), BailoutId::None());
4533   Handle<Code> optimized_code(result.code, isolate);
4534 
4535   // Finish a full GC cycle so that the unoptimized code of 'g' is flushed even
4536   // though the optimized code for 'f' is reachable via the optimized code map.
4537   heap->CollectAllGarbage();
4538 
4539   // Make a new closure that will get code installed from the code map.
4540   // Unoptimized code is missing and the deoptimizer will go ballistic.
4541   CompileRun("var h = mkClosure(); h('bozo');");
4542 }
4543 
4544 
TEST(LargeObjectSlotRecording)4545 TEST(LargeObjectSlotRecording) {
4546   FLAG_manual_evacuation_candidates_selection = true;
4547   CcTest::InitializeVM();
4548   Isolate* isolate = CcTest::i_isolate();
4549   Heap* heap = isolate->heap();
4550   HandleScope scope(isolate);
4551 
4552   // Create an object on an evacuation candidate.
4553   heap::SimulateFullSpace(heap->old_space());
4554   Handle<FixedArray> lit = isolate->factory()->NewFixedArray(4, TENURED);
4555   Page* evac_page = Page::FromAddress(lit->address());
4556   evac_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
4557   FixedArray* old_location = *lit;
4558 
4559   // Allocate a large object.
4560   int size = Max(1000000, Page::kMaxRegularHeapObjectSize + KB);
4561   CHECK(size > Page::kMaxRegularHeapObjectSize);
4562   Handle<FixedArray> lo = isolate->factory()->NewFixedArray(size, TENURED);
4563   CHECK(heap->lo_space()->Contains(*lo));
4564 
4565   // Start incremental marking to active write barrier.
4566   heap::SimulateIncrementalMarking(heap, false);
4567   heap->incremental_marking()->AdvanceIncrementalMarking(
4568       10000000, IncrementalMarking::IdleStepActions());
4569 
4570   // Create references from the large object to the object on the evacuation
4571   // candidate.
4572   const int kStep = size / 10;
4573   for (int i = 0; i < size; i += kStep) {
4574     lo->set(i, *lit);
4575     CHECK(lo->get(i) == old_location);
4576   }
4577 
4578   // Move the evaucation candidate object.
4579   CcTest::heap()->CollectAllGarbage();
4580 
4581   // Verify that the pointers in the large object got updated.
4582   for (int i = 0; i < size; i += kStep) {
4583     CHECK_EQ(lo->get(i), *lit);
4584     CHECK(lo->get(i) != old_location);
4585   }
4586 }
4587 
4588 
4589 class DummyVisitor : public ObjectVisitor {
4590  public:
VisitPointers(Object ** start,Object ** end)4591   void VisitPointers(Object** start, Object** end) override {}
4592 };
4593 
4594 
TEST(DeferredHandles)4595 TEST(DeferredHandles) {
4596   CcTest::InitializeVM();
4597   Isolate* isolate = CcTest::i_isolate();
4598   Heap* heap = isolate->heap();
4599   v8::HandleScope scope(reinterpret_cast<v8::Isolate*>(isolate));
4600   HandleScopeData* data = isolate->handle_scope_data();
4601   Handle<Object> init(heap->empty_string(), isolate);
4602   while (data->next < data->limit) {
4603     Handle<Object> obj(heap->empty_string(), isolate);
4604   }
4605   // An entire block of handles has been filled.
4606   // Next handle would require a new block.
4607   CHECK(data->next == data->limit);
4608 
4609   DeferredHandleScope deferred(isolate);
4610   DummyVisitor visitor;
4611   isolate->handle_scope_implementer()->Iterate(&visitor);
4612   delete deferred.Detach();
4613 }
4614 
4615 
TEST(IncrementalMarkingStepMakesBigProgressWithLargeObjects)4616 TEST(IncrementalMarkingStepMakesBigProgressWithLargeObjects) {
4617   CcTest::InitializeVM();
4618   v8::HandleScope scope(CcTest::isolate());
4619   CompileRun("function f(n) {"
4620              "    var a = new Array(n);"
4621              "    for (var i = 0; i < n; i += 100) a[i] = i;"
4622              "};"
4623              "f(10 * 1024 * 1024);");
4624   IncrementalMarking* marking = CcTest::heap()->incremental_marking();
4625   if (marking->IsStopped()) {
4626     CcTest::heap()->StartIncrementalMarking();
4627   }
4628   // This big step should be sufficient to mark the whole array.
4629   marking->Step(100 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
4630   CHECK(marking->IsComplete() ||
4631         marking->IsReadyToOverApproximateWeakClosure());
4632 }
4633 
4634 
TEST(DisableInlineAllocation)4635 TEST(DisableInlineAllocation) {
4636   i::FLAG_allow_natives_syntax = true;
4637   CcTest::InitializeVM();
4638   v8::HandleScope scope(CcTest::isolate());
4639   CompileRun("function test() {"
4640              "  var x = [];"
4641              "  for (var i = 0; i < 10; i++) {"
4642              "    x[i] = [ {}, [1,2,3], [1,x,3] ];"
4643              "  }"
4644              "}"
4645              "function run() {"
4646              "  %OptimizeFunctionOnNextCall(test);"
4647              "  test();"
4648              "  %DeoptimizeFunction(test);"
4649              "}");
4650 
4651   // Warm-up with inline allocation enabled.
4652   CompileRun("test(); test(); run();");
4653 
4654   // Run test with inline allocation disabled.
4655   CcTest::heap()->DisableInlineAllocation();
4656   CompileRun("run()");
4657 
4658   // Run test with inline allocation re-enabled.
4659   CcTest::heap()->EnableInlineAllocation();
4660   CompileRun("run()");
4661 }
4662 
4663 
AllocationSitesCount(Heap * heap)4664 static int AllocationSitesCount(Heap* heap) {
4665   int count = 0;
4666   for (Object* site = heap->allocation_sites_list();
4667        !(site->IsUndefined(heap->isolate()));
4668        site = AllocationSite::cast(site)->weak_next()) {
4669     count++;
4670   }
4671   return count;
4672 }
4673 
4674 
TEST(EnsureAllocationSiteDependentCodesProcessed)4675 TEST(EnsureAllocationSiteDependentCodesProcessed) {
4676   if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
4677   i::FLAG_allow_natives_syntax = true;
4678   CcTest::InitializeVM();
4679   Isolate* isolate = CcTest::i_isolate();
4680   v8::internal::Heap* heap = CcTest::heap();
4681   GlobalHandles* global_handles = isolate->global_handles();
4682 
4683   if (!isolate->use_crankshaft()) return;
4684 
4685   // The allocation site at the head of the list is ours.
4686   Handle<AllocationSite> site;
4687   {
4688     LocalContext context;
4689     v8::HandleScope scope(context->GetIsolate());
4690 
4691     int count = AllocationSitesCount(heap);
4692     CompileRun("var bar = function() { return (new Array()); };"
4693                "var a = bar();"
4694                "bar();"
4695                "bar();");
4696 
4697     // One allocation site should have been created.
4698     int new_count = AllocationSitesCount(heap);
4699     CHECK_EQ(new_count, (count + 1));
4700     site = Handle<AllocationSite>::cast(
4701         global_handles->Create(
4702             AllocationSite::cast(heap->allocation_sites_list())));
4703 
4704     CompileRun("%OptimizeFunctionOnNextCall(bar); bar();");
4705 
4706     CHECK_EQ(DependentCode::kAllocationSiteTransitionChangedGroup,
4707              site->dependent_code()->group());
4708     CHECK_EQ(1, site->dependent_code()->count());
4709     CHECK(site->dependent_code()->object_at(0)->IsWeakCell());
4710     Code* function_bar = Code::cast(
4711         WeakCell::cast(site->dependent_code()->object_at(0))->value());
4712     Handle<JSFunction> bar_handle = Handle<JSFunction>::cast(
4713         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4714             CcTest::global()
4715                 ->Get(context.local(), v8_str("bar"))
4716                 .ToLocalChecked())));
4717     CHECK_EQ(bar_handle->code(), function_bar);
4718   }
4719 
4720   // Now make sure that a gc should get rid of the function, even though we
4721   // still have the allocation site alive.
4722   for (int i = 0; i < 4; i++) {
4723     heap->CollectAllGarbage();
4724   }
4725 
4726   // The site still exists because of our global handle, but the code is no
4727   // longer referred to by dependent_code().
4728   CHECK(site->dependent_code()->object_at(0)->IsWeakCell() &&
4729         WeakCell::cast(site->dependent_code()->object_at(0))->cleared());
4730 }
4731 
4732 
TEST(CellsInOptimizedCodeAreWeak)4733 TEST(CellsInOptimizedCodeAreWeak) {
4734   if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
4735   i::FLAG_weak_embedded_objects_in_optimized_code = true;
4736   i::FLAG_allow_natives_syntax = true;
4737   CcTest::InitializeVM();
4738   Isolate* isolate = CcTest::i_isolate();
4739   v8::internal::Heap* heap = CcTest::heap();
4740 
4741   if (!isolate->use_crankshaft()) return;
4742   HandleScope outer_scope(heap->isolate());
4743   Handle<Code> code;
4744   {
4745     LocalContext context;
4746     HandleScope scope(heap->isolate());
4747 
4748     CompileRun(
4749         "bar = (function() {"
4750         "  function bar() {"
4751         "    return foo(1);"
4752         "  };"
4753         "  var foo = function(x) { with (x) { return 1 + x; } };"
4754         "  %NeverOptimizeFunction(foo);"
4755         "  bar(foo);"
4756         "  bar(foo);"
4757         "  bar(foo);"
4758         "  %OptimizeFunctionOnNextCall(bar);"
4759         "  bar(foo);"
4760         "  return bar;})();");
4761 
4762     Handle<JSFunction> bar = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
4763         *v8::Local<v8::Function>::Cast(CcTest::global()
4764                                            ->Get(context.local(), v8_str("bar"))
4765                                            .ToLocalChecked())));
4766     code = scope.CloseAndEscape(Handle<Code>(bar->code()));
4767   }
4768 
4769   // Now make sure that a gc should get rid of the function
4770   for (int i = 0; i < 4; i++) {
4771     heap->CollectAllGarbage();
4772   }
4773 
4774   CHECK(code->marked_for_deoptimization());
4775 }
4776 
4777 
TEST(ObjectsInOptimizedCodeAreWeak)4778 TEST(ObjectsInOptimizedCodeAreWeak) {
4779   if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
4780   i::FLAG_weak_embedded_objects_in_optimized_code = true;
4781   i::FLAG_allow_natives_syntax = true;
4782   CcTest::InitializeVM();
4783   Isolate* isolate = CcTest::i_isolate();
4784   v8::internal::Heap* heap = CcTest::heap();
4785 
4786   if (!isolate->use_crankshaft()) return;
4787   HandleScope outer_scope(heap->isolate());
4788   Handle<Code> code;
4789   {
4790     LocalContext context;
4791     HandleScope scope(heap->isolate());
4792 
4793     CompileRun(
4794         "function bar() {"
4795         "  return foo(1);"
4796         "};"
4797         "function foo(x) { with (x) { return 1 + x; } };"
4798         "%NeverOptimizeFunction(foo);"
4799         "bar();"
4800         "bar();"
4801         "bar();"
4802         "%OptimizeFunctionOnNextCall(bar);"
4803         "bar();");
4804 
4805     Handle<JSFunction> bar = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
4806         *v8::Local<v8::Function>::Cast(CcTest::global()
4807                                            ->Get(context.local(), v8_str("bar"))
4808                                            .ToLocalChecked())));
4809     code = scope.CloseAndEscape(Handle<Code>(bar->code()));
4810   }
4811 
4812   // Now make sure that a gc should get rid of the function
4813   for (int i = 0; i < 4; i++) {
4814     heap->CollectAllGarbage();
4815   }
4816 
4817   CHECK(code->marked_for_deoptimization());
4818 }
4819 
4820 
TEST(NoWeakHashTableLeakWithIncrementalMarking)4821 TEST(NoWeakHashTableLeakWithIncrementalMarking) {
4822   if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
4823   if (!i::FLAG_incremental_marking) return;
4824   i::FLAG_weak_embedded_objects_in_optimized_code = true;
4825   i::FLAG_allow_natives_syntax = true;
4826   i::FLAG_compilation_cache = false;
4827   i::FLAG_retain_maps_for_n_gc = 0;
4828   CcTest::InitializeVM();
4829   Isolate* isolate = CcTest::i_isolate();
4830 
4831   // Do not run for no-snap builds.
4832   if (!i::Snapshot::HaveASnapshotToStartFrom(isolate)) return;
4833 
4834   v8::internal::Heap* heap = CcTest::heap();
4835 
4836   // Get a clean slate regarding optimized functions on the heap.
4837   i::Deoptimizer::DeoptimizeAll(isolate);
4838   heap->CollectAllGarbage();
4839 
4840   if (!isolate->use_crankshaft()) return;
4841   HandleScope outer_scope(heap->isolate());
4842   for (int i = 0; i < 3; i++) {
4843     heap::SimulateIncrementalMarking(heap);
4844     {
4845       LocalContext context;
4846       HandleScope scope(heap->isolate());
4847       EmbeddedVector<char, 256> source;
4848       SNPrintF(source,
4849                "function bar%d() {"
4850                "  return foo%d(1);"
4851                "};"
4852                "function foo%d(x) { with (x) { return 1 + x; } };"
4853                "bar%d();"
4854                "bar%d();"
4855                "bar%d();"
4856                "%%OptimizeFunctionOnNextCall(bar%d);"
4857                "bar%d();",
4858                i, i, i, i, i, i, i, i);
4859       CompileRun(source.start());
4860     }
4861     // We have to abort incremental marking here to abandon black pages.
4862     heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
4863   }
4864   int elements = 0;
4865   if (heap->weak_object_to_code_table()->IsHashTable()) {
4866     WeakHashTable* t = WeakHashTable::cast(heap->weak_object_to_code_table());
4867     elements = t->NumberOfElements();
4868   }
4869   CHECK_EQ(0, elements);
4870 }
4871 
4872 
OptimizeDummyFunction(v8::Isolate * isolate,const char * name)4873 static Handle<JSFunction> OptimizeDummyFunction(v8::Isolate* isolate,
4874                                                 const char* name) {
4875   EmbeddedVector<char, 256> source;
4876   SNPrintF(source,
4877           "function %s() { return 0; }"
4878           "%s(); %s();"
4879           "%%OptimizeFunctionOnNextCall(%s);"
4880           "%s();", name, name, name, name, name);
4881   CompileRun(source.start());
4882   i::Handle<JSFunction> fun = Handle<JSFunction>::cast(
4883       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4884           CcTest::global()
4885               ->Get(isolate->GetCurrentContext(), v8_str(name))
4886               .ToLocalChecked())));
4887   return fun;
4888 }
4889 
4890 
GetCodeChainLength(Code * code)4891 static int GetCodeChainLength(Code* code) {
4892   int result = 0;
4893   while (code->next_code_link()->IsCode()) {
4894     result++;
4895     code = Code::cast(code->next_code_link());
4896   }
4897   return result;
4898 }
4899 
4900 
TEST(NextCodeLinkIsWeak)4901 TEST(NextCodeLinkIsWeak) {
4902   i::FLAG_always_opt = false;
4903   i::FLAG_allow_natives_syntax = true;
4904   CcTest::InitializeVM();
4905   Isolate* isolate = CcTest::i_isolate();
4906   v8::internal::Heap* heap = CcTest::heap();
4907 
4908   if (!isolate->use_crankshaft()) return;
4909   HandleScope outer_scope(heap->isolate());
4910   Handle<Code> code;
4911   heap->CollectAllAvailableGarbage();
4912   int code_chain_length_before, code_chain_length_after;
4913   {
4914     HandleScope scope(heap->isolate());
4915     Handle<JSFunction> mortal =
4916         OptimizeDummyFunction(CcTest::isolate(), "mortal");
4917     Handle<JSFunction> immortal =
4918         OptimizeDummyFunction(CcTest::isolate(), "immortal");
4919     CHECK_EQ(immortal->code()->next_code_link(), mortal->code());
4920     code_chain_length_before = GetCodeChainLength(immortal->code());
4921     // Keep the immortal code and let the mortal code die.
4922     code = scope.CloseAndEscape(Handle<Code>(immortal->code()));
4923     CompileRun("mortal = null; immortal = null;");
4924   }
4925   heap->CollectAllAvailableGarbage();
4926   // Now mortal code should be dead.
4927   code_chain_length_after = GetCodeChainLength(*code);
4928   CHECK_EQ(code_chain_length_before - 1, code_chain_length_after);
4929 }
4930 
4931 
DummyOptimizedCode(Isolate * isolate)4932 static Handle<Code> DummyOptimizedCode(Isolate* isolate) {
4933   i::byte buffer[i::Assembler::kMinimalBufferSize];
4934   MacroAssembler masm(isolate, buffer, sizeof(buffer),
4935                       v8::internal::CodeObjectRequired::kYes);
4936   CodeDesc desc;
4937   masm.Push(isolate->factory()->undefined_value());
4938   masm.Drop(1);
4939   masm.GetCode(&desc);
4940   Handle<Object> undefined(isolate->heap()->undefined_value(), isolate);
4941   Handle<Code> code = isolate->factory()->NewCode(
4942       desc, Code::ComputeFlags(Code::OPTIMIZED_FUNCTION), undefined);
4943   CHECK(code->IsCode());
4944   return code;
4945 }
4946 
4947 
TEST(NextCodeLinkIsWeak2)4948 TEST(NextCodeLinkIsWeak2) {
4949   i::FLAG_allow_natives_syntax = true;
4950   CcTest::InitializeVM();
4951   Isolate* isolate = CcTest::i_isolate();
4952   v8::internal::Heap* heap = CcTest::heap();
4953 
4954   if (!isolate->use_crankshaft()) return;
4955   HandleScope outer_scope(heap->isolate());
4956   heap->CollectAllAvailableGarbage();
4957   Handle<Context> context(Context::cast(heap->native_contexts_list()), isolate);
4958   Handle<Code> new_head;
4959   Handle<Object> old_head(context->get(Context::OPTIMIZED_CODE_LIST), isolate);
4960   {
4961     HandleScope scope(heap->isolate());
4962     Handle<Code> immortal = DummyOptimizedCode(isolate);
4963     Handle<Code> mortal = DummyOptimizedCode(isolate);
4964     mortal->set_next_code_link(*old_head);
4965     immortal->set_next_code_link(*mortal);
4966     context->set(Context::OPTIMIZED_CODE_LIST, *immortal);
4967     new_head = scope.CloseAndEscape(immortal);
4968   }
4969   heap->CollectAllAvailableGarbage();
4970   // Now mortal code should be dead.
4971   CHECK_EQ(*old_head, new_head->next_code_link());
4972 }
4973 
4974 
4975 static bool weak_ic_cleared = false;
4976 
ClearWeakIC(const v8::WeakCallbackInfo<v8::Persistent<v8::Object>> & data)4977 static void ClearWeakIC(
4978     const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
4979   printf("clear weak is called\n");
4980   weak_ic_cleared = true;
4981   data.GetParameter()->Reset();
4982 }
4983 
4984 
TEST(WeakFunctionInConstructor)4985 TEST(WeakFunctionInConstructor) {
4986   if (i::FLAG_always_opt) return;
4987   i::FLAG_stress_compaction = false;
4988   CcTest::InitializeVM();
4989   v8::Isolate* isolate = CcTest::isolate();
4990   LocalContext env;
4991   v8::HandleScope scope(isolate);
4992   CompileRun(
4993       "function createObj(obj) {"
4994       "  return new obj();"
4995       "}");
4996   i::Handle<JSFunction> createObj = Handle<JSFunction>::cast(
4997       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
4998           CcTest::global()
4999               ->Get(env.local(), v8_str("createObj"))
5000               .ToLocalChecked())));
5001 
5002   v8::Persistent<v8::Object> garbage;
5003   {
5004     v8::HandleScope scope(isolate);
5005     const char* source =
5006         " (function() {"
5007         "   function hat() { this.x = 5; }"
5008         "   createObj(hat);"
5009         "   createObj(hat);"
5010         "   return hat;"
5011         " })();";
5012     garbage.Reset(isolate, CompileRun(env.local(), source)
5013                                .ToLocalChecked()
5014                                ->ToObject(env.local())
5015                                .ToLocalChecked());
5016   }
5017   weak_ic_cleared = false;
5018   garbage.SetWeak(&garbage, &ClearWeakIC, v8::WeakCallbackType::kParameter);
5019   Heap* heap = CcTest::i_isolate()->heap();
5020   heap->CollectAllGarbage();
5021   CHECK(weak_ic_cleared);
5022 
5023   // We've determined the constructor in createObj has had it's weak cell
5024   // cleared. Now, verify that one additional call with a new function
5025   // allows monomorphicity.
5026   Handle<TypeFeedbackVector> feedback_vector = Handle<TypeFeedbackVector>(
5027       createObj->feedback_vector(), CcTest::i_isolate());
5028   for (int i = 0; i < 20; i++) {
5029     Object* slot_value = feedback_vector->Get(FeedbackVectorSlot(0));
5030     CHECK(slot_value->IsWeakCell());
5031     if (WeakCell::cast(slot_value)->cleared()) break;
5032     heap->CollectAllGarbage();
5033   }
5034 
5035   Object* slot_value = feedback_vector->Get(FeedbackVectorSlot(0));
5036   CHECK(slot_value->IsWeakCell() && WeakCell::cast(slot_value)->cleared());
5037   CompileRun(
5038       "function coat() { this.x = 6; }"
5039       "createObj(coat);");
5040   slot_value = feedback_vector->Get(FeedbackVectorSlot(0));
5041   CHECK(slot_value->IsWeakCell() && !WeakCell::cast(slot_value)->cleared());
5042 }
5043 
5044 
5045 // Checks that the value returned by execution of the source is weak.
CheckWeakness(const char * source)5046 void CheckWeakness(const char* source) {
5047   i::FLAG_stress_compaction = false;
5048   CcTest::InitializeVM();
5049   v8::Isolate* isolate = CcTest::isolate();
5050   LocalContext env;
5051   v8::HandleScope scope(isolate);
5052   v8::Persistent<v8::Object> garbage;
5053   {
5054     v8::HandleScope scope(isolate);
5055     garbage.Reset(isolate, CompileRun(env.local(), source)
5056                                .ToLocalChecked()
5057                                ->ToObject(env.local())
5058                                .ToLocalChecked());
5059   }
5060   weak_ic_cleared = false;
5061   garbage.SetWeak(&garbage, &ClearWeakIC, v8::WeakCallbackType::kParameter);
5062   Heap* heap = CcTest::i_isolate()->heap();
5063   heap->CollectAllGarbage();
5064   CHECK(weak_ic_cleared);
5065 }
5066 
5067 
5068 // Each of the following "weak IC" tests creates an IC that embeds a map with
5069 // the prototype pointing to _proto_ and checks that the _proto_ dies on GC.
TEST(WeakMapInMonomorphicLoadIC)5070 TEST(WeakMapInMonomorphicLoadIC) {
5071   CheckWeakness("function loadIC(obj) {"
5072                 "  return obj.name;"
5073                 "}"
5074                 " (function() {"
5075                 "   var proto = {'name' : 'weak'};"
5076                 "   var obj = Object.create(proto);"
5077                 "   loadIC(obj);"
5078                 "   loadIC(obj);"
5079                 "   loadIC(obj);"
5080                 "   return proto;"
5081                 " })();");
5082 }
5083 
5084 
TEST(WeakMapInPolymorphicLoadIC)5085 TEST(WeakMapInPolymorphicLoadIC) {
5086   CheckWeakness(
5087       "function loadIC(obj) {"
5088       "  return obj.name;"
5089       "}"
5090       " (function() {"
5091       "   var proto = {'name' : 'weak'};"
5092       "   var obj = Object.create(proto);"
5093       "   loadIC(obj);"
5094       "   loadIC(obj);"
5095       "   loadIC(obj);"
5096       "   var poly = Object.create(proto);"
5097       "   poly.x = true;"
5098       "   loadIC(poly);"
5099       "   return proto;"
5100       " })();");
5101 }
5102 
5103 
TEST(WeakMapInMonomorphicKeyedLoadIC)5104 TEST(WeakMapInMonomorphicKeyedLoadIC) {
5105   CheckWeakness("function keyedLoadIC(obj, field) {"
5106                 "  return obj[field];"
5107                 "}"
5108                 " (function() {"
5109                 "   var proto = {'name' : 'weak'};"
5110                 "   var obj = Object.create(proto);"
5111                 "   keyedLoadIC(obj, 'name');"
5112                 "   keyedLoadIC(obj, 'name');"
5113                 "   keyedLoadIC(obj, 'name');"
5114                 "   return proto;"
5115                 " })();");
5116 }
5117 
5118 
TEST(WeakMapInPolymorphicKeyedLoadIC)5119 TEST(WeakMapInPolymorphicKeyedLoadIC) {
5120   CheckWeakness(
5121       "function keyedLoadIC(obj, field) {"
5122       "  return obj[field];"
5123       "}"
5124       " (function() {"
5125       "   var proto = {'name' : 'weak'};"
5126       "   var obj = Object.create(proto);"
5127       "   keyedLoadIC(obj, 'name');"
5128       "   keyedLoadIC(obj, 'name');"
5129       "   keyedLoadIC(obj, 'name');"
5130       "   var poly = Object.create(proto);"
5131       "   poly.x = true;"
5132       "   keyedLoadIC(poly, 'name');"
5133       "   return proto;"
5134       " })();");
5135 }
5136 
5137 
TEST(WeakMapInMonomorphicStoreIC)5138 TEST(WeakMapInMonomorphicStoreIC) {
5139   CheckWeakness("function storeIC(obj, value) {"
5140                 "  obj.name = value;"
5141                 "}"
5142                 " (function() {"
5143                 "   var proto = {'name' : 'weak'};"
5144                 "   var obj = Object.create(proto);"
5145                 "   storeIC(obj, 'x');"
5146                 "   storeIC(obj, 'x');"
5147                 "   storeIC(obj, 'x');"
5148                 "   return proto;"
5149                 " })();");
5150 }
5151 
5152 
TEST(WeakMapInPolymorphicStoreIC)5153 TEST(WeakMapInPolymorphicStoreIC) {
5154   CheckWeakness(
5155       "function storeIC(obj, value) {"
5156       "  obj.name = value;"
5157       "}"
5158       " (function() {"
5159       "   var proto = {'name' : 'weak'};"
5160       "   var obj = Object.create(proto);"
5161       "   storeIC(obj, 'x');"
5162       "   storeIC(obj, 'x');"
5163       "   storeIC(obj, 'x');"
5164       "   var poly = Object.create(proto);"
5165       "   poly.x = true;"
5166       "   storeIC(poly, 'x');"
5167       "   return proto;"
5168       " })();");
5169 }
5170 
5171 
TEST(WeakMapInMonomorphicKeyedStoreIC)5172 TEST(WeakMapInMonomorphicKeyedStoreIC) {
5173   CheckWeakness("function keyedStoreIC(obj, field, value) {"
5174                 "  obj[field] = value;"
5175                 "}"
5176                 " (function() {"
5177                 "   var proto = {'name' : 'weak'};"
5178                 "   var obj = Object.create(proto);"
5179                 "   keyedStoreIC(obj, 'x');"
5180                 "   keyedStoreIC(obj, 'x');"
5181                 "   keyedStoreIC(obj, 'x');"
5182                 "   return proto;"
5183                 " })();");
5184 }
5185 
5186 
TEST(WeakMapInPolymorphicKeyedStoreIC)5187 TEST(WeakMapInPolymorphicKeyedStoreIC) {
5188   CheckWeakness(
5189       "function keyedStoreIC(obj, field, value) {"
5190       "  obj[field] = value;"
5191       "}"
5192       " (function() {"
5193       "   var proto = {'name' : 'weak'};"
5194       "   var obj = Object.create(proto);"
5195       "   keyedStoreIC(obj, 'x');"
5196       "   keyedStoreIC(obj, 'x');"
5197       "   keyedStoreIC(obj, 'x');"
5198       "   var poly = Object.create(proto);"
5199       "   poly.x = true;"
5200       "   keyedStoreIC(poly, 'x');"
5201       "   return proto;"
5202       " })();");
5203 }
5204 
5205 
TEST(WeakMapInMonomorphicCompareNilIC)5206 TEST(WeakMapInMonomorphicCompareNilIC) {
5207   CheckWeakness("function compareNilIC(obj) {"
5208                 "  return obj == null;"
5209                 "}"
5210                 " (function() {"
5211                 "   var proto = {'name' : 'weak'};"
5212                 "   var obj = Object.create(proto);"
5213                 "   compareNilIC(obj);"
5214                 "   compareNilIC(obj);"
5215                 "   compareNilIC(obj);"
5216                 "   return proto;"
5217                 " })();");
5218 }
5219 
5220 
GetFunctionByName(Isolate * isolate,const char * name)5221 Handle<JSFunction> GetFunctionByName(Isolate* isolate, const char* name) {
5222   Handle<String> str = isolate->factory()->InternalizeUtf8String(name);
5223   Handle<Object> obj =
5224       Object::GetProperty(isolate->global_object(), str).ToHandleChecked();
5225   return Handle<JSFunction>::cast(obj);
5226 }
5227 
CheckIC(Handle<JSFunction> function,Code::Kind kind,int slot_index,InlineCacheState state)5228 void CheckIC(Handle<JSFunction> function, Code::Kind kind, int slot_index,
5229              InlineCacheState state) {
5230   if (kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC ||
5231       kind == Code::CALL_IC) {
5232     TypeFeedbackVector* vector = function->feedback_vector();
5233     FeedbackVectorSlot slot(slot_index);
5234     if (kind == Code::LOAD_IC) {
5235       LoadICNexus nexus(vector, slot);
5236       CHECK_EQ(nexus.StateFromFeedback(), state);
5237     } else if (kind == Code::KEYED_LOAD_IC) {
5238       KeyedLoadICNexus nexus(vector, slot);
5239       CHECK_EQ(nexus.StateFromFeedback(), state);
5240     } else if (kind == Code::CALL_IC) {
5241       CallICNexus nexus(vector, slot);
5242       CHECK_EQ(nexus.StateFromFeedback(), state);
5243     }
5244   } else {
5245     Code* ic = FindFirstIC(function->code(), kind);
5246     CHECK(ic->is_inline_cache_stub());
5247     CHECK(!IC::ICUseVector(kind));
5248     CHECK_EQ(state, IC::StateFromCode(ic));
5249   }
5250 }
5251 
5252 
TEST(MonomorphicStaysMonomorphicAfterGC)5253 TEST(MonomorphicStaysMonomorphicAfterGC) {
5254   if (FLAG_always_opt) return;
5255   CcTest::InitializeVM();
5256   Isolate* isolate = CcTest::i_isolate();
5257   Heap* heap = isolate->heap();
5258   v8::HandleScope scope(CcTest::isolate());
5259   CompileRun(
5260       "function loadIC(obj) {"
5261       "  return obj.name;"
5262       "}"
5263       "function testIC() {"
5264       "  var proto = {'name' : 'weak'};"
5265       "  var obj = Object.create(proto);"
5266       "  loadIC(obj);"
5267       "  loadIC(obj);"
5268       "  loadIC(obj);"
5269       "  return proto;"
5270       "};");
5271   Handle<JSFunction> loadIC = GetFunctionByName(isolate, "loadIC");
5272   {
5273     v8::HandleScope scope(CcTest::isolate());
5274     CompileRun("(testIC())");
5275   }
5276   heap->CollectAllGarbage();
5277   CheckIC(loadIC, Code::LOAD_IC, 0, MONOMORPHIC);
5278   {
5279     v8::HandleScope scope(CcTest::isolate());
5280     CompileRun("(testIC())");
5281   }
5282   CheckIC(loadIC, Code::LOAD_IC, 0, MONOMORPHIC);
5283 }
5284 
5285 
TEST(PolymorphicStaysPolymorphicAfterGC)5286 TEST(PolymorphicStaysPolymorphicAfterGC) {
5287   if (FLAG_always_opt) return;
5288   CcTest::InitializeVM();
5289   Isolate* isolate = CcTest::i_isolate();
5290   Heap* heap = isolate->heap();
5291   v8::HandleScope scope(CcTest::isolate());
5292   CompileRun(
5293       "function loadIC(obj) {"
5294       "  return obj.name;"
5295       "}"
5296       "function testIC() {"
5297       "  var proto = {'name' : 'weak'};"
5298       "  var obj = Object.create(proto);"
5299       "  loadIC(obj);"
5300       "  loadIC(obj);"
5301       "  loadIC(obj);"
5302       "  var poly = Object.create(proto);"
5303       "  poly.x = true;"
5304       "  loadIC(poly);"
5305       "  return proto;"
5306       "};");
5307   Handle<JSFunction> loadIC = GetFunctionByName(isolate, "loadIC");
5308   {
5309     v8::HandleScope scope(CcTest::isolate());
5310     CompileRun("(testIC())");
5311   }
5312   heap->CollectAllGarbage();
5313   CheckIC(loadIC, Code::LOAD_IC, 0, POLYMORPHIC);
5314   {
5315     v8::HandleScope scope(CcTest::isolate());
5316     CompileRun("(testIC())");
5317   }
5318   CheckIC(loadIC, Code::LOAD_IC, 0, POLYMORPHIC);
5319 }
5320 
5321 
TEST(WeakCell)5322 TEST(WeakCell) {
5323   CcTest::InitializeVM();
5324   Isolate* isolate = CcTest::i_isolate();
5325   v8::internal::Heap* heap = CcTest::heap();
5326   v8::internal::Factory* factory = isolate->factory();
5327 
5328   HandleScope outer_scope(isolate);
5329   Handle<WeakCell> weak_cell1;
5330   {
5331     HandleScope inner_scope(isolate);
5332     Handle<HeapObject> value = factory->NewFixedArray(1, NOT_TENURED);
5333     weak_cell1 = inner_scope.CloseAndEscape(factory->NewWeakCell(value));
5334   }
5335 
5336   Handle<FixedArray> survivor = factory->NewFixedArray(1, NOT_TENURED);
5337   Handle<WeakCell> weak_cell2;
5338   {
5339     HandleScope inner_scope(isolate);
5340     weak_cell2 = inner_scope.CloseAndEscape(factory->NewWeakCell(survivor));
5341   }
5342   CHECK(weak_cell1->value()->IsFixedArray());
5343   CHECK_EQ(*survivor, weak_cell2->value());
5344   heap->CollectGarbage(NEW_SPACE);
5345   CHECK(weak_cell1->value()->IsFixedArray());
5346   CHECK_EQ(*survivor, weak_cell2->value());
5347   heap->CollectGarbage(NEW_SPACE);
5348   CHECK(weak_cell1->value()->IsFixedArray());
5349   CHECK_EQ(*survivor, weak_cell2->value());
5350   heap->CollectAllAvailableGarbage();
5351   CHECK(weak_cell1->cleared());
5352   CHECK_EQ(*survivor, weak_cell2->value());
5353 }
5354 
5355 
TEST(WeakCellsWithIncrementalMarking)5356 TEST(WeakCellsWithIncrementalMarking) {
5357   CcTest::InitializeVM();
5358   Isolate* isolate = CcTest::i_isolate();
5359   v8::internal::Heap* heap = CcTest::heap();
5360   v8::internal::Factory* factory = isolate->factory();
5361 
5362   const int N = 16;
5363   HandleScope outer_scope(isolate);
5364   Handle<FixedArray> survivor = factory->NewFixedArray(1, NOT_TENURED);
5365   Handle<WeakCell> weak_cells[N];
5366 
5367   for (int i = 0; i < N; i++) {
5368     HandleScope inner_scope(isolate);
5369     Handle<HeapObject> value =
5370         i == 0 ? survivor : factory->NewFixedArray(1, NOT_TENURED);
5371     Handle<WeakCell> weak_cell = factory->NewWeakCell(value);
5372     CHECK(weak_cell->value()->IsFixedArray());
5373     IncrementalMarking* marking = heap->incremental_marking();
5374     if (marking->IsStopped()) {
5375       heap->StartIncrementalMarking();
5376     }
5377     marking->Step(128, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
5378     heap->CollectGarbage(NEW_SPACE);
5379     CHECK(weak_cell->value()->IsFixedArray());
5380     weak_cells[i] = inner_scope.CloseAndEscape(weak_cell);
5381   }
5382   // Call collect all twice to make sure that we also cleared
5383   // weak cells that were allocated on black pages.
5384   heap->CollectAllGarbage();
5385   heap->CollectAllGarbage();
5386   CHECK_EQ(*survivor, weak_cells[0]->value());
5387   for (int i = 1; i < N; i++) {
5388     CHECK(weak_cells[i]->cleared());
5389   }
5390 }
5391 
5392 
5393 #ifdef DEBUG
TEST(AddInstructionChangesNewSpacePromotion)5394 TEST(AddInstructionChangesNewSpacePromotion) {
5395   i::FLAG_allow_natives_syntax = true;
5396   i::FLAG_expose_gc = true;
5397   i::FLAG_stress_compaction = true;
5398   i::FLAG_gc_interval = 1000;
5399   CcTest::InitializeVM();
5400   if (!i::FLAG_allocation_site_pretenuring) return;
5401   v8::HandleScope scope(CcTest::isolate());
5402   Isolate* isolate = CcTest::i_isolate();
5403   Heap* heap = isolate->heap();
5404   LocalContext env;
5405   CompileRun(
5406       "function add(a, b) {"
5407       "  return a + b;"
5408       "}"
5409       "add(1, 2);"
5410       "add(\"a\", \"b\");"
5411       "var oldSpaceObject;"
5412       "gc();"
5413       "function crash(x) {"
5414       "  var object = {a: null, b: null};"
5415       "  var result = add(1.5, x | 0);"
5416       "  object.a = result;"
5417       "  oldSpaceObject = object;"
5418       "  return object;"
5419       "}"
5420       "crash(1);"
5421       "crash(1);"
5422       "%OptimizeFunctionOnNextCall(crash);"
5423       "crash(1);");
5424 
5425   v8::Local<v8::Object> global = CcTest::global();
5426   v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
5427       global->Get(env.local(), v8_str("crash")).ToLocalChecked());
5428   v8::Local<v8::Value> args1[] = {v8_num(1)};
5429   heap->DisableInlineAllocation();
5430   heap->set_allocation_timeout(1);
5431   g->Call(env.local(), global, 1, args1).ToLocalChecked();
5432   heap->CollectAllGarbage();
5433 }
5434 
5435 
OnFatalErrorExpectOOM(const char * location,const char * message)5436 void OnFatalErrorExpectOOM(const char* location, const char* message) {
5437   // Exit with 0 if the location matches our expectation.
5438   exit(strcmp(location, "CALL_AND_RETRY_LAST"));
5439 }
5440 
5441 
TEST(CEntryStubOOM)5442 TEST(CEntryStubOOM) {
5443   i::FLAG_allow_natives_syntax = true;
5444   CcTest::InitializeVM();
5445   v8::HandleScope scope(CcTest::isolate());
5446   CcTest::isolate()->SetFatalErrorHandler(OnFatalErrorExpectOOM);
5447 
5448   v8::Local<v8::Value> result = CompileRun(
5449       "%SetFlags('--gc-interval=1');"
5450       "var a = [];"
5451       "a.__proto__ = [];"
5452       "a.unshift(1)");
5453 
5454   CHECK(result->IsNumber());
5455 }
5456 
5457 #endif  // DEBUG
5458 
5459 
InterruptCallback357137(v8::Isolate * isolate,void * data)5460 static void InterruptCallback357137(v8::Isolate* isolate, void* data) { }
5461 
5462 
RequestInterrupt(const v8::FunctionCallbackInfo<v8::Value> & args)5463 static void RequestInterrupt(const v8::FunctionCallbackInfo<v8::Value>& args) {
5464   CcTest::isolate()->RequestInterrupt(&InterruptCallback357137, NULL);
5465 }
5466 
5467 
UNINITIALIZED_TEST(Regress538257)5468 UNINITIALIZED_TEST(Regress538257) {
5469   i::FLAG_manual_evacuation_candidates_selection = true;
5470   v8::Isolate::CreateParams create_params;
5471   // Set heap limits.
5472   create_params.constraints.set_max_semi_space_size(1 * Page::kPageSize / MB);
5473   create_params.constraints.set_max_old_space_size(6 * Page::kPageSize / MB);
5474   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
5475   v8::Isolate* isolate = v8::Isolate::New(create_params);
5476   isolate->Enter();
5477   {
5478     i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
5479     Heap* heap = i_isolate->heap();
5480     HandleScope handle_scope(i_isolate);
5481     PagedSpace* old_space = heap->old_space();
5482     const int kMaxObjects = 10000;
5483     const int kFixedArrayLen = 512;
5484     Handle<FixedArray> objects[kMaxObjects];
5485     for (int i = 0; (i < kMaxObjects) &&
5486                     heap->CanExpandOldGeneration(old_space->AreaSize());
5487          i++) {
5488       objects[i] = i_isolate->factory()->NewFixedArray(kFixedArrayLen, TENURED);
5489       Page::FromAddress(objects[i]->address())
5490           ->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
5491     }
5492     heap::SimulateFullSpace(old_space);
5493     heap->CollectGarbage(OLD_SPACE);
5494     // If we get this far, we've successfully aborted compaction. Any further
5495     // allocations might trigger OOM.
5496   }
5497   isolate->Exit();
5498   isolate->Dispose();
5499 }
5500 
5501 
TEST(Regress357137)5502 TEST(Regress357137) {
5503   CcTest::InitializeVM();
5504   v8::Isolate* isolate = CcTest::isolate();
5505   v8::HandleScope hscope(isolate);
5506   v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
5507   global->Set(
5508       v8::String::NewFromUtf8(isolate, "interrupt", v8::NewStringType::kNormal)
5509           .ToLocalChecked(),
5510       v8::FunctionTemplate::New(isolate, RequestInterrupt));
5511   v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
5512   CHECK(!context.IsEmpty());
5513   v8::Context::Scope cscope(context);
5514 
5515   v8::Local<v8::Value> result = CompileRun(
5516       "var locals = '';"
5517       "for (var i = 0; i < 512; i++) locals += 'var v' + i + '= 42;';"
5518       "eval('function f() {' + locals + 'return function() { return v0; }; }');"
5519       "interrupt();"  // This triggers a fake stack overflow in f.
5520       "f()()");
5521   CHECK_EQ(42.0, result->ToNumber(context).ToLocalChecked()->Value());
5522 }
5523 
5524 
TEST(Regress507979)5525 TEST(Regress507979) {
5526   const int kFixedArrayLen = 10;
5527   CcTest::InitializeVM();
5528   Isolate* isolate = CcTest::i_isolate();
5529   Heap* heap = isolate->heap();
5530   HandleScope handle_scope(isolate);
5531 
5532   Handle<FixedArray> o1 = isolate->factory()->NewFixedArray(kFixedArrayLen);
5533   Handle<FixedArray> o2 = isolate->factory()->NewFixedArray(kFixedArrayLen);
5534   CHECK(heap->InNewSpace(*o1));
5535   CHECK(heap->InNewSpace(*o2));
5536 
5537   HeapIterator it(heap, i::HeapIterator::kFilterUnreachable);
5538 
5539   // Replace parts of an object placed before a live object with a filler. This
5540   // way the filler object shares the mark bits with the following live object.
5541   o1->Shrink(kFixedArrayLen - 1);
5542 
5543   for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
5544     // Let's not optimize the loop away.
5545     CHECK(obj->address() != nullptr);
5546   }
5547 }
5548 
5549 
UNINITIALIZED_TEST(PromotionQueue)5550 UNINITIALIZED_TEST(PromotionQueue) {
5551   i::FLAG_expose_gc = true;
5552   i::FLAG_max_semi_space_size = 2 * Page::kPageSize / MB;
5553   i::FLAG_min_semi_space_size = i::FLAG_max_semi_space_size;
5554   v8::Isolate::CreateParams create_params;
5555   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
5556   v8::Isolate* isolate = v8::Isolate::New(create_params);
5557   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
5558   {
5559     v8::Isolate::Scope isolate_scope(isolate);
5560     v8::HandleScope handle_scope(isolate);
5561     v8::Context::New(isolate)->Enter();
5562     Heap* heap = i_isolate->heap();
5563     NewSpace* new_space = heap->new_space();
5564 
5565     // In this test we will try to overwrite the promotion queue which is at the
5566     // end of to-space. To actually make that possible, we need at least two
5567     // semi-space pages and take advantage of fragmentation.
5568     // (1) Use a semi-space consisting of two pages.
5569     // (2) Create a few small long living objects and call the scavenger to
5570     // move them to the other semi-space.
5571     // (3) Create a huge object, i.e., remainder of first semi-space page and
5572     // create another huge object which should be of maximum allocatable memory
5573     // size of the second semi-space page.
5574     // (4) Call the scavenger again.
5575     // What will happen is: the scavenger will promote the objects created in
5576     // (2) and will create promotion queue entries at the end of the second
5577     // semi-space page during the next scavenge when it promotes the objects to
5578     // the old generation. The first allocation of (3) will fill up the first
5579     // semi-space page. The second allocation in (3) will not fit into the
5580     // first semi-space page, but it will overwrite the promotion queue which
5581     // are in the second semi-space page. If the right guards are in place, the
5582     // promotion queue will be evacuated in that case.
5583 
5584 
5585     CHECK(new_space->IsAtMaximumCapacity());
5586     CHECK(i::FLAG_min_semi_space_size * MB == new_space->TotalCapacity());
5587 
5588     // Call the scavenger two times to get an empty new space
5589     heap->CollectGarbage(NEW_SPACE);
5590     heap->CollectGarbage(NEW_SPACE);
5591 
5592     // First create a few objects which will survive a scavenge, and will get
5593     // promoted to the old generation later on. These objects will create
5594     // promotion queue entries at the end of the second semi-space page.
5595     const int number_handles = 12;
5596     Handle<FixedArray> handles[number_handles];
5597     for (int i = 0; i < number_handles; i++) {
5598       handles[i] = i_isolate->factory()->NewFixedArray(1, NOT_TENURED);
5599     }
5600 
5601     heap->CollectGarbage(NEW_SPACE);
5602     CHECK(i::FLAG_min_semi_space_size * MB == new_space->TotalCapacity());
5603 
5604     // Fill-up the first semi-space page.
5605     heap::FillUpOnePage(new_space);
5606 
5607     // Create a small object to initialize the bump pointer on the second
5608     // semi-space page.
5609     Handle<FixedArray> small =
5610         i_isolate->factory()->NewFixedArray(1, NOT_TENURED);
5611     CHECK(heap->InNewSpace(*small));
5612 
5613     // Fill-up the second semi-space page.
5614     heap::FillUpOnePage(new_space);
5615 
5616     // This scavenge will corrupt memory if the promotion queue is not
5617     // evacuated.
5618     heap->CollectGarbage(NEW_SPACE);
5619   }
5620   isolate->Dispose();
5621 }
5622 
5623 
TEST(Regress388880)5624 TEST(Regress388880) {
5625   i::FLAG_expose_gc = true;
5626   CcTest::InitializeVM();
5627   v8::HandleScope scope(CcTest::isolate());
5628   Isolate* isolate = CcTest::i_isolate();
5629   Factory* factory = isolate->factory();
5630   Heap* heap = isolate->heap();
5631 
5632   Handle<Map> map1 = Map::Create(isolate, 1);
5633   Handle<String> name = factory->NewStringFromStaticChars("foo");
5634   name = factory->InternalizeString(name);
5635   Handle<Map> map2 =
5636       Map::CopyWithField(map1, name, FieldType::Any(isolate), NONE,
5637                          Representation::Tagged(), OMIT_TRANSITION)
5638           .ToHandleChecked();
5639 
5640   int desired_offset = Page::kPageSize - map1->instance_size();
5641 
5642   // Allocate padding objects in old pointer space so, that object allocated
5643   // afterwards would end at the end of the page.
5644   heap::SimulateFullSpace(heap->old_space());
5645   int padding_size = desired_offset - Page::kObjectStartOffset;
5646   heap::CreatePadding(heap, padding_size, TENURED);
5647 
5648   Handle<JSObject> o = factory->NewJSObjectFromMap(map1, TENURED);
5649   o->set_properties(*factory->empty_fixed_array());
5650 
5651   // Ensure that the object allocated where we need it.
5652   Page* page = Page::FromAddress(o->address());
5653   CHECK_EQ(desired_offset, page->Offset(o->address()));
5654 
5655   // Now we have an object right at the end of the page.
5656 
5657   // Enable incremental marking to trigger actions in Heap::AdjustLiveBytes()
5658   // that would cause crash.
5659   IncrementalMarking* marking = CcTest::heap()->incremental_marking();
5660   marking->Stop();
5661   CcTest::heap()->StartIncrementalMarking();
5662   CHECK(marking->IsMarking());
5663 
5664   // Now everything is set up for crashing in JSObject::MigrateFastToFast()
5665   // when it calls heap->AdjustLiveBytes(...).
5666   JSObject::MigrateToMap(o, map2);
5667 }
5668 
5669 
TEST(Regress3631)5670 TEST(Regress3631) {
5671   i::FLAG_expose_gc = true;
5672   CcTest::InitializeVM();
5673   v8::HandleScope scope(CcTest::isolate());
5674   Isolate* isolate = CcTest::i_isolate();
5675   Heap* heap = isolate->heap();
5676   IncrementalMarking* marking = CcTest::heap()->incremental_marking();
5677   v8::Local<v8::Value> result = CompileRun(
5678       "var weak_map = new WeakMap();"
5679       "var future_keys = [];"
5680       "for (var i = 0; i < 50; i++) {"
5681       "  var key = {'k' : i + 0.1};"
5682       "  weak_map.set(key, 1);"
5683       "  future_keys.push({'x' : i + 0.2});"
5684       "}"
5685       "weak_map");
5686   if (marking->IsStopped()) {
5687     CcTest::heap()->StartIncrementalMarking();
5688   }
5689   // Incrementally mark the backing store.
5690   Handle<JSReceiver> obj =
5691       v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(result));
5692   Handle<JSWeakCollection> weak_map(reinterpret_cast<JSWeakCollection*>(*obj));
5693   while (!Marking::IsBlack(
5694              Marking::MarkBitFrom(HeapObject::cast(weak_map->table()))) &&
5695          !marking->IsStopped()) {
5696     marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
5697   }
5698   // Stash the backing store in a handle.
5699   Handle<Object> save(weak_map->table(), isolate);
5700   // The following line will update the backing store.
5701   CompileRun(
5702       "for (var i = 0; i < 50; i++) {"
5703       "  weak_map.set(future_keys[i], i);"
5704       "}");
5705   heap->incremental_marking()->set_should_hurry(true);
5706   heap->CollectGarbage(OLD_SPACE);
5707 }
5708 
5709 
TEST(Regress442710)5710 TEST(Regress442710) {
5711   CcTest::InitializeVM();
5712   Isolate* isolate = CcTest::i_isolate();
5713   Heap* heap = isolate->heap();
5714   Factory* factory = isolate->factory();
5715 
5716   HandleScope sc(isolate);
5717   Handle<JSGlobalObject> global(
5718       CcTest::i_isolate()->context()->global_object());
5719   Handle<JSArray> array = factory->NewJSArray(2);
5720 
5721   Handle<String> name = factory->InternalizeUtf8String("testArray");
5722   JSReceiver::SetProperty(global, name, array, SLOPPY).Check();
5723   CompileRun("testArray[0] = 1; testArray[1] = 2; testArray.shift();");
5724   heap->CollectGarbage(OLD_SPACE);
5725 }
5726 
5727 
HEAP_TEST(NumberStringCacheSize)5728 HEAP_TEST(NumberStringCacheSize) {
5729   // Test that the number-string cache has not been resized in the snapshot.
5730   CcTest::InitializeVM();
5731   Isolate* isolate = CcTest::i_isolate();
5732   if (!isolate->snapshot_available()) return;
5733   Heap* heap = isolate->heap();
5734   CHECK_EQ(Heap::kInitialNumberStringCacheSize * 2,
5735            heap->number_string_cache()->length());
5736 }
5737 
5738 
TEST(Regress3877)5739 TEST(Regress3877) {
5740   CcTest::InitializeVM();
5741   Isolate* isolate = CcTest::i_isolate();
5742   Heap* heap = isolate->heap();
5743   Factory* factory = isolate->factory();
5744   HandleScope scope(isolate);
5745   CompileRun("function cls() { this.x = 10; }");
5746   Handle<WeakCell> weak_prototype;
5747   {
5748     HandleScope inner_scope(isolate);
5749     v8::Local<v8::Value> result = CompileRun("cls.prototype");
5750     Handle<JSReceiver> proto =
5751         v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(result));
5752     weak_prototype = inner_scope.CloseAndEscape(factory->NewWeakCell(proto));
5753   }
5754   CHECK(!weak_prototype->cleared());
5755   CompileRun(
5756       "var a = { };"
5757       "a.x = new cls();"
5758       "cls.prototype = null;");
5759   for (int i = 0; i < 4; i++) {
5760     heap->CollectAllGarbage();
5761   }
5762   // The map of a.x keeps prototype alive
5763   CHECK(!weak_prototype->cleared());
5764   // Change the map of a.x and make the previous map garbage collectable.
5765   CompileRun("a.x.__proto__ = {};");
5766   for (int i = 0; i < 4; i++) {
5767     heap->CollectAllGarbage();
5768   }
5769   CHECK(weak_prototype->cleared());
5770 }
5771 
5772 
AddRetainedMap(Isolate * isolate,Heap * heap)5773 Handle<WeakCell> AddRetainedMap(Isolate* isolate, Heap* heap) {
5774     HandleScope inner_scope(isolate);
5775     Handle<Map> map = Map::Create(isolate, 1);
5776     v8::Local<v8::Value> result =
5777         CompileRun("(function () { return {x : 10}; })();");
5778     Handle<JSReceiver> proto =
5779         v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(result));
5780     Map::SetPrototype(map, proto);
5781     heap->AddRetainedMap(map);
5782     return inner_scope.CloseAndEscape(Map::WeakCellForMap(map));
5783 }
5784 
5785 
CheckMapRetainingFor(int n)5786 void CheckMapRetainingFor(int n) {
5787   FLAG_retain_maps_for_n_gc = n;
5788   Isolate* isolate = CcTest::i_isolate();
5789   Heap* heap = isolate->heap();
5790   Handle<WeakCell> weak_cell = AddRetainedMap(isolate, heap);
5791   CHECK(!weak_cell->cleared());
5792   for (int i = 0; i < n; i++) {
5793     heap::SimulateIncrementalMarking(heap);
5794     heap->CollectGarbage(OLD_SPACE);
5795   }
5796   CHECK(!weak_cell->cleared());
5797   heap::SimulateIncrementalMarking(heap);
5798   heap->CollectGarbage(OLD_SPACE);
5799   CHECK(weak_cell->cleared());
5800 }
5801 
5802 
TEST(MapRetaining)5803 TEST(MapRetaining) {
5804   CcTest::InitializeVM();
5805   v8::HandleScope scope(CcTest::isolate());
5806   CheckMapRetainingFor(FLAG_retain_maps_for_n_gc);
5807   CheckMapRetainingFor(0);
5808   CheckMapRetainingFor(1);
5809   CheckMapRetainingFor(7);
5810 }
5811 
5812 
TEST(RegressArrayListGC)5813 TEST(RegressArrayListGC) {
5814   FLAG_retain_maps_for_n_gc = 1;
5815   FLAG_incremental_marking = 0;
5816   FLAG_gc_global = true;
5817   CcTest::InitializeVM();
5818   v8::HandleScope scope(CcTest::isolate());
5819   Isolate* isolate = CcTest::i_isolate();
5820   Heap* heap = isolate->heap();
5821   AddRetainedMap(isolate, heap);
5822   Handle<Map> map = Map::Create(isolate, 1);
5823   heap->CollectGarbage(OLD_SPACE);
5824   // Force GC in old space on next addition of retained map.
5825   Map::WeakCellForMap(map);
5826   heap::SimulateFullSpace(CcTest::heap()->new_space());
5827   for (int i = 0; i < 10; i++) {
5828     heap->AddRetainedMap(map);
5829   }
5830   heap->CollectGarbage(OLD_SPACE);
5831 }
5832 
5833 
5834 #ifdef DEBUG
TEST(PathTracer)5835 TEST(PathTracer) {
5836   CcTest::InitializeVM();
5837   v8::HandleScope scope(CcTest::isolate());
5838 
5839   v8::Local<v8::Value> result = CompileRun("'abc'");
5840   Handle<Object> o = v8::Utils::OpenHandle(*result);
5841   CcTest::i_isolate()->heap()->TracePathToObject(*o);
5842 }
5843 #endif  // DEBUG
5844 
5845 
TEST(WritableVsImmortalRoots)5846 TEST(WritableVsImmortalRoots) {
5847   for (int i = 0; i < Heap::kStrongRootListLength; ++i) {
5848     Heap::RootListIndex root_index = static_cast<Heap::RootListIndex>(i);
5849     bool writable = Heap::RootCanBeWrittenAfterInitialization(root_index);
5850     bool immortal = Heap::RootIsImmortalImmovable(root_index);
5851     // A root value can be writable, immortal, or neither, but not both.
5852     CHECK(!immortal || !writable);
5853   }
5854 }
5855 
5856 
TestRightTrimFixedTypedArray(i::ExternalArrayType type,int initial_length,int elements_to_trim)5857 static void TestRightTrimFixedTypedArray(i::ExternalArrayType type,
5858                                          int initial_length,
5859                                          int elements_to_trim) {
5860   v8::HandleScope scope(CcTest::isolate());
5861   Isolate* isolate = CcTest::i_isolate();
5862   Factory* factory = isolate->factory();
5863   Heap* heap = isolate->heap();
5864 
5865   Handle<FixedTypedArrayBase> array =
5866       factory->NewFixedTypedArray(initial_length, type, true);
5867   int old_size = array->size();
5868   heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(*array,
5869                                                          elements_to_trim);
5870 
5871   // Check that free space filler is at the right place and did not smash the
5872   // array header.
5873   CHECK(array->IsFixedArrayBase());
5874   CHECK_EQ(initial_length - elements_to_trim, array->length());
5875   int new_size = array->size();
5876   if (new_size != old_size) {
5877     // Free space filler should be created in this case.
5878     Address next_obj_address = array->address() + array->size();
5879     CHECK(HeapObject::FromAddress(next_obj_address)->IsFiller());
5880   }
5881   heap->CollectAllAvailableGarbage();
5882 }
5883 
5884 
TEST(Regress472513)5885 TEST(Regress472513) {
5886   CcTest::InitializeVM();
5887   v8::HandleScope scope(CcTest::isolate());
5888 
5889   // The combination of type/initial_length/elements_to_trim triggered
5890   // typed array header smashing with free space filler (crbug/472513).
5891 
5892   // 64-bit cases.
5893   TestRightTrimFixedTypedArray(i::kExternalUint8Array, 32, 6);
5894   TestRightTrimFixedTypedArray(i::kExternalUint8Array, 32 - 7, 6);
5895   TestRightTrimFixedTypedArray(i::kExternalUint16Array, 16, 6);
5896   TestRightTrimFixedTypedArray(i::kExternalUint16Array, 16 - 3, 6);
5897   TestRightTrimFixedTypedArray(i::kExternalUint32Array, 8, 6);
5898   TestRightTrimFixedTypedArray(i::kExternalUint32Array, 8 - 1, 6);
5899 
5900   // 32-bit cases.
5901   TestRightTrimFixedTypedArray(i::kExternalUint8Array, 16, 3);
5902   TestRightTrimFixedTypedArray(i::kExternalUint8Array, 16 - 3, 3);
5903   TestRightTrimFixedTypedArray(i::kExternalUint16Array, 8, 3);
5904   TestRightTrimFixedTypedArray(i::kExternalUint16Array, 8 - 1, 3);
5905   TestRightTrimFixedTypedArray(i::kExternalUint32Array, 4, 3);
5906 }
5907 
5908 
TEST(WeakFixedArray)5909 TEST(WeakFixedArray) {
5910   CcTest::InitializeVM();
5911   v8::HandleScope scope(CcTest::isolate());
5912 
5913   Handle<HeapNumber> number = CcTest::i_isolate()->factory()->NewHeapNumber(1);
5914   Handle<WeakFixedArray> array = WeakFixedArray::Add(Handle<Object>(), number);
5915   array->Remove(number);
5916   array->Compact<WeakFixedArray::NullCallback>();
5917   WeakFixedArray::Add(array, number);
5918 }
5919 
5920 
TEST(PreprocessStackTrace)5921 TEST(PreprocessStackTrace) {
5922   // Do not automatically trigger early GC.
5923   FLAG_gc_interval = -1;
5924   CcTest::InitializeVM();
5925   v8::HandleScope scope(CcTest::isolate());
5926   v8::TryCatch try_catch(CcTest::isolate());
5927   CompileRun("throw new Error();");
5928   CHECK(try_catch.HasCaught());
5929   Isolate* isolate = CcTest::i_isolate();
5930   Handle<Object> exception = v8::Utils::OpenHandle(*try_catch.Exception());
5931   Handle<Name> key = isolate->factory()->stack_trace_symbol();
5932   Handle<Object> stack_trace =
5933       Object::GetProperty(exception, key).ToHandleChecked();
5934   Handle<Object> code =
5935       Object::GetElement(isolate, stack_trace, 3).ToHandleChecked();
5936   CHECK(code->IsCode());
5937 
5938   isolate->heap()->CollectAllAvailableGarbage("stack trace preprocessing");
5939 
5940   Handle<Object> pos =
5941       Object::GetElement(isolate, stack_trace, 3).ToHandleChecked();
5942   CHECK(pos->IsSmi());
5943 
5944   Handle<JSArray> stack_trace_array = Handle<JSArray>::cast(stack_trace);
5945   int array_length = Smi::cast(stack_trace_array->length())->value();
5946   for (int i = 0; i < array_length; i++) {
5947     Handle<Object> element =
5948         Object::GetElement(isolate, stack_trace, i).ToHandleChecked();
5949     CHECK(!element->IsCode());
5950   }
5951 }
5952 
5953 
5954 static bool utils_has_been_collected = false;
5955 
UtilsHasBeenCollected(const v8::WeakCallbackInfo<v8::Persistent<v8::Object>> & data)5956 static void UtilsHasBeenCollected(
5957     const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) {
5958   utils_has_been_collected = true;
5959   data.GetParameter()->Reset();
5960 }
5961 
5962 
TEST(BootstrappingExports)5963 TEST(BootstrappingExports) {
5964   // Expose utils object and delete it to observe that it is indeed
5965   // being garbage-collected.
5966   FLAG_expose_natives_as = "utils";
5967   CcTest::InitializeVM();
5968   v8::Isolate* isolate = CcTest::isolate();
5969   LocalContext env;
5970 
5971   if (Snapshot::HaveASnapshotToStartFrom(CcTest::i_isolate())) return;
5972 
5973   utils_has_been_collected = false;
5974 
5975   v8::Persistent<v8::Object> utils;
5976 
5977   {
5978     v8::HandleScope scope(isolate);
5979     v8::Local<v8::String> name = v8_str("utils");
5980     utils.Reset(isolate, CcTest::global()
5981                              ->Get(env.local(), name)
5982                              .ToLocalChecked()
5983                              ->ToObject(env.local())
5984                              .ToLocalChecked());
5985     CHECK(CcTest::global()->Delete(env.local(), name).FromJust());
5986   }
5987 
5988   utils.SetWeak(&utils, UtilsHasBeenCollected,
5989                 v8::WeakCallbackType::kParameter);
5990 
5991   CcTest::heap()->CollectAllAvailableGarbage("fire weak callbacks");
5992 
5993   CHECK(utils_has_been_collected);
5994 }
5995 
5996 
TEST(Regress1878)5997 TEST(Regress1878) {
5998   FLAG_allow_natives_syntax = true;
5999   CcTest::InitializeVM();
6000   v8::Isolate* isolate = CcTest::isolate();
6001   v8::HandleScope scope(isolate);
6002   v8::Local<v8::Function> constructor = v8::Utils::CallableToLocal(
6003       CcTest::i_isolate()->internal_array_function());
6004   LocalContext env;
6005   CHECK(CcTest::global()
6006             ->Set(env.local(), v8_str("InternalArray"), constructor)
6007             .FromJust());
6008 
6009   v8::TryCatch try_catch(isolate);
6010 
6011   CompileRun(
6012       "var a = Array();"
6013       "for (var i = 0; i < 1000; i++) {"
6014       "  var ai = new InternalArray(10000);"
6015       "  if (%HaveSameMap(ai, a)) throw Error();"
6016       "  if (!%HasFastObjectElements(ai)) throw Error();"
6017       "}"
6018       "for (var i = 0; i < 1000; i++) {"
6019       "  var ai = new InternalArray(10000);"
6020       "  if (%HaveSameMap(ai, a)) throw Error();"
6021       "  if (!%HasFastObjectElements(ai)) throw Error();"
6022       "}");
6023 
6024   CHECK(!try_catch.HasCaught());
6025 }
6026 
6027 
AllocateInSpace(Isolate * isolate,size_t bytes,AllocationSpace space)6028 void AllocateInSpace(Isolate* isolate, size_t bytes, AllocationSpace space) {
6029   CHECK(bytes >= FixedArray::kHeaderSize);
6030   CHECK(bytes % kPointerSize == 0);
6031   Factory* factory = isolate->factory();
6032   HandleScope scope(isolate);
6033   AlwaysAllocateScope always_allocate(isolate);
6034   int elements =
6035       static_cast<int>((bytes - FixedArray::kHeaderSize) / kPointerSize);
6036   Handle<FixedArray> array = factory->NewFixedArray(
6037       elements, space == NEW_SPACE ? NOT_TENURED : TENURED);
6038   CHECK((space == NEW_SPACE) == isolate->heap()->InNewSpace(*array));
6039   CHECK_EQ(bytes, static_cast<size_t>(array->Size()));
6040 }
6041 
6042 
TEST(NewSpaceAllocationCounter)6043 TEST(NewSpaceAllocationCounter) {
6044   CcTest::InitializeVM();
6045   v8::HandleScope scope(CcTest::isolate());
6046   Isolate* isolate = CcTest::i_isolate();
6047   Heap* heap = isolate->heap();
6048   size_t counter1 = heap->NewSpaceAllocationCounter();
6049   heap->CollectGarbage(NEW_SPACE);
6050   const size_t kSize = 1024;
6051   AllocateInSpace(isolate, kSize, NEW_SPACE);
6052   size_t counter2 = heap->NewSpaceAllocationCounter();
6053   CHECK_EQ(kSize, counter2 - counter1);
6054   heap->CollectGarbage(NEW_SPACE);
6055   size_t counter3 = heap->NewSpaceAllocationCounter();
6056   CHECK_EQ(0U, counter3 - counter2);
6057   // Test counter overflow.
6058   size_t max_counter = -1;
6059   heap->set_new_space_allocation_counter(max_counter - 10 * kSize);
6060   size_t start = heap->NewSpaceAllocationCounter();
6061   for (int i = 0; i < 20; i++) {
6062     AllocateInSpace(isolate, kSize, NEW_SPACE);
6063     size_t counter = heap->NewSpaceAllocationCounter();
6064     CHECK_EQ(kSize, counter - start);
6065     start = counter;
6066   }
6067 }
6068 
6069 
TEST(OldSpaceAllocationCounter)6070 TEST(OldSpaceAllocationCounter) {
6071   CcTest::InitializeVM();
6072   v8::HandleScope scope(CcTest::isolate());
6073   Isolate* isolate = CcTest::i_isolate();
6074   Heap* heap = isolate->heap();
6075   size_t counter1 = heap->OldGenerationAllocationCounter();
6076   heap->CollectGarbage(NEW_SPACE);
6077   heap->CollectGarbage(NEW_SPACE);
6078   const size_t kSize = 1024;
6079   AllocateInSpace(isolate, kSize, OLD_SPACE);
6080   size_t counter2 = heap->OldGenerationAllocationCounter();
6081   // TODO(ulan): replace all CHECK_LE with CHECK_EQ after v8:4148 is fixed.
6082   CHECK_LE(kSize, counter2 - counter1);
6083   heap->CollectGarbage(NEW_SPACE);
6084   size_t counter3 = heap->OldGenerationAllocationCounter();
6085   CHECK_EQ(0u, counter3 - counter2);
6086   AllocateInSpace(isolate, kSize, OLD_SPACE);
6087   heap->CollectGarbage(OLD_SPACE);
6088   size_t counter4 = heap->OldGenerationAllocationCounter();
6089   CHECK_LE(kSize, counter4 - counter3);
6090   // Test counter overflow.
6091   size_t max_counter = -1;
6092   heap->set_old_generation_allocation_counter(max_counter - 10 * kSize);
6093   size_t start = heap->OldGenerationAllocationCounter();
6094   for (int i = 0; i < 20; i++) {
6095     AllocateInSpace(isolate, kSize, OLD_SPACE);
6096     size_t counter = heap->OldGenerationAllocationCounter();
6097     CHECK_LE(kSize, counter - start);
6098     start = counter;
6099   }
6100 }
6101 
6102 
TEST(NewSpaceAllocationThroughput)6103 TEST(NewSpaceAllocationThroughput) {
6104   CcTest::InitializeVM();
6105   v8::HandleScope scope(CcTest::isolate());
6106   Isolate* isolate = CcTest::i_isolate();
6107   Heap* heap = isolate->heap();
6108   GCTracer* tracer = heap->tracer();
6109   tracer->ResetForTesting();
6110   int time1 = 100;
6111   size_t counter1 = 1000;
6112   tracer->SampleAllocation(time1, counter1, 0);
6113   int time2 = 200;
6114   size_t counter2 = 2000;
6115   tracer->SampleAllocation(time2, counter2, 0);
6116   size_t throughput =
6117       tracer->NewSpaceAllocationThroughputInBytesPerMillisecond();
6118   CHECK_EQ((counter2 - counter1) / (time2 - time1), throughput);
6119   int time3 = 1000;
6120   size_t counter3 = 30000;
6121   tracer->SampleAllocation(time3, counter3, 0);
6122   throughput = tracer->NewSpaceAllocationThroughputInBytesPerMillisecond();
6123   CHECK_EQ((counter3 - counter1) / (time3 - time1), throughput);
6124 }
6125 
6126 
TEST(NewSpaceAllocationThroughput2)6127 TEST(NewSpaceAllocationThroughput2) {
6128   CcTest::InitializeVM();
6129   v8::HandleScope scope(CcTest::isolate());
6130   Isolate* isolate = CcTest::i_isolate();
6131   Heap* heap = isolate->heap();
6132   GCTracer* tracer = heap->tracer();
6133   tracer->ResetForTesting();
6134   int time1 = 100;
6135   size_t counter1 = 1000;
6136   tracer->SampleAllocation(time1, counter1, 0);
6137   int time2 = 200;
6138   size_t counter2 = 2000;
6139   tracer->SampleAllocation(time2, counter2, 0);
6140   size_t throughput =
6141       tracer->NewSpaceAllocationThroughputInBytesPerMillisecond(100);
6142   CHECK_EQ((counter2 - counter1) / (time2 - time1), throughput);
6143   int time3 = 1000;
6144   size_t counter3 = 30000;
6145   tracer->SampleAllocation(time3, counter3, 0);
6146   throughput = tracer->NewSpaceAllocationThroughputInBytesPerMillisecond(100);
6147   CHECK_EQ((counter3 - counter1) / (time3 - time1), throughput);
6148 }
6149 
6150 
CheckLeak(const v8::FunctionCallbackInfo<v8::Value> & args)6151 static void CheckLeak(const v8::FunctionCallbackInfo<v8::Value>& args) {
6152   Isolate* isolate = CcTest::i_isolate();
6153   Object* message =
6154       *reinterpret_cast<Object**>(isolate->pending_message_obj_address());
6155   CHECK(message->IsTheHole(isolate));
6156 }
6157 
6158 
TEST(MessageObjectLeak)6159 TEST(MessageObjectLeak) {
6160   CcTest::InitializeVM();
6161   v8::Isolate* isolate = CcTest::isolate();
6162   v8::HandleScope scope(isolate);
6163   v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
6164   global->Set(
6165       v8::String::NewFromUtf8(isolate, "check", v8::NewStringType::kNormal)
6166           .ToLocalChecked(),
6167       v8::FunctionTemplate::New(isolate, CheckLeak));
6168   v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
6169   v8::Context::Scope cscope(context);
6170 
6171   const char* test =
6172       "try {"
6173       "  throw 'message 1';"
6174       "} catch (e) {"
6175       "}"
6176       "check();"
6177       "L: try {"
6178       "  throw 'message 2';"
6179       "} finally {"
6180       "  break L;"
6181       "}"
6182       "check();";
6183   CompileRun(test);
6184 
6185   const char* flag = "--turbo-filter=*";
6186   FlagList::SetFlagsFromString(flag, StrLength(flag));
6187   FLAG_always_opt = true;
6188 
6189   CompileRun(test);
6190 }
6191 
6192 
CheckEqualSharedFunctionInfos(const v8::FunctionCallbackInfo<v8::Value> & args)6193 static void CheckEqualSharedFunctionInfos(
6194     const v8::FunctionCallbackInfo<v8::Value>& args) {
6195   Handle<Object> obj1 = v8::Utils::OpenHandle(*args[0]);
6196   Handle<Object> obj2 = v8::Utils::OpenHandle(*args[1]);
6197   Handle<JSFunction> fun1 = Handle<JSFunction>::cast(obj1);
6198   Handle<JSFunction> fun2 = Handle<JSFunction>::cast(obj2);
6199   CHECK(fun1->shared() == fun2->shared());
6200 }
6201 
6202 
RemoveCodeAndGC(const v8::FunctionCallbackInfo<v8::Value> & args)6203 static void RemoveCodeAndGC(const v8::FunctionCallbackInfo<v8::Value>& args) {
6204   Isolate* isolate = CcTest::i_isolate();
6205   Handle<Object> obj = v8::Utils::OpenHandle(*args[0]);
6206   Handle<JSFunction> fun = Handle<JSFunction>::cast(obj);
6207   fun->ReplaceCode(*isolate->builtins()->CompileLazy());
6208   fun->shared()->ReplaceCode(*isolate->builtins()->CompileLazy());
6209   fun->shared()->ClearBytecodeArray();  // Bytecode is code too.
6210   isolate->heap()->CollectAllAvailableGarbage("remove code and gc");
6211 }
6212 
6213 
TEST(CanonicalSharedFunctionInfo)6214 TEST(CanonicalSharedFunctionInfo) {
6215   CcTest::InitializeVM();
6216   v8::Isolate* isolate = CcTest::isolate();
6217   v8::HandleScope scope(isolate);
6218   v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
6219   global->Set(isolate, "check", v8::FunctionTemplate::New(
6220                                     isolate, CheckEqualSharedFunctionInfos));
6221   global->Set(isolate, "remove",
6222               v8::FunctionTemplate::New(isolate, RemoveCodeAndGC));
6223   v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
6224   v8::Context::Scope cscope(context);
6225   CompileRun(
6226       "function f() { return function g() {}; }"
6227       "var g1 = f();"
6228       "remove(f);"
6229       "var g2 = f();"
6230       "check(g1, g2);");
6231 
6232   CompileRun(
6233       "function f() { return (function() { return function g() {}; })(); }"
6234       "var g1 = f();"
6235       "remove(f);"
6236       "var g2 = f();"
6237       "check(g1, g2);");
6238 }
6239 
TEST(RemoveCodeFromSharedFunctionInfoButNotFromClosure)6240 TEST(RemoveCodeFromSharedFunctionInfoButNotFromClosure) {
6241   CcTest::InitializeVM();
6242   v8::Isolate* isolate = CcTest::isolate();
6243   v8::HandleScope scope(isolate);
6244   v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
6245   global->Set(isolate, "check", v8::FunctionTemplate::New(
6246                                     isolate, CheckEqualSharedFunctionInfos));
6247   global->Set(isolate, "remove",
6248               v8::FunctionTemplate::New(isolate, RemoveCodeAndGC));
6249   v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
6250   v8::Context::Scope cscope(context);
6251   CompileRun(
6252       "function f() { return function g() {}; }"
6253       "var g1 = f();"
6254       "var g2 = f();"
6255       "check(g1, g2);"
6256       "g1();"
6257       "g2();"
6258       "remove(g1);"
6259       "g2();"
6260       "check(g1, g2);");
6261 }
6262 
TEST(OldGenerationAllocationThroughput)6263 TEST(OldGenerationAllocationThroughput) {
6264   CcTest::InitializeVM();
6265   v8::HandleScope scope(CcTest::isolate());
6266   Isolate* isolate = CcTest::i_isolate();
6267   Heap* heap = isolate->heap();
6268   GCTracer* tracer = heap->tracer();
6269   tracer->ResetForTesting();
6270   int time1 = 100;
6271   size_t counter1 = 1000;
6272   tracer->SampleAllocation(time1, 0, counter1);
6273   int time2 = 200;
6274   size_t counter2 = 2000;
6275   tracer->SampleAllocation(time2, 0, counter2);
6276   size_t throughput = static_cast<size_t>(
6277       tracer->OldGenerationAllocationThroughputInBytesPerMillisecond(100));
6278   CHECK_EQ((counter2 - counter1) / (time2 - time1), throughput);
6279   int time3 = 1000;
6280   size_t counter3 = 30000;
6281   tracer->SampleAllocation(time3, 0, counter3);
6282   throughput = static_cast<size_t>(
6283       tracer->OldGenerationAllocationThroughputInBytesPerMillisecond(100));
6284   CHECK_EQ((counter3 - counter1) / (time3 - time1), throughput);
6285 }
6286 
6287 
TEST(AllocationThroughput)6288 TEST(AllocationThroughput) {
6289   CcTest::InitializeVM();
6290   v8::HandleScope scope(CcTest::isolate());
6291   Isolate* isolate = CcTest::i_isolate();
6292   Heap* heap = isolate->heap();
6293   GCTracer* tracer = heap->tracer();
6294   tracer->ResetForTesting();
6295   int time1 = 100;
6296   size_t counter1 = 1000;
6297   tracer->SampleAllocation(time1, counter1, counter1);
6298   int time2 = 200;
6299   size_t counter2 = 2000;
6300   tracer->SampleAllocation(time2, counter2, counter2);
6301   size_t throughput = static_cast<size_t>(
6302       tracer->AllocationThroughputInBytesPerMillisecond(100));
6303   CHECK_EQ(2 * (counter2 - counter1) / (time2 - time1), throughput);
6304   int time3 = 1000;
6305   size_t counter3 = 30000;
6306   tracer->SampleAllocation(time3, counter3, counter3);
6307   throughput = tracer->AllocationThroughputInBytesPerMillisecond(100);
6308   CHECK_EQ(2 * (counter3 - counter1) / (time3 - time1), throughput);
6309 }
6310 
6311 
TEST(ContextMeasure)6312 TEST(ContextMeasure) {
6313   CcTest::InitializeVM();
6314   v8::HandleScope scope(CcTest::isolate());
6315   Isolate* isolate = CcTest::i_isolate();
6316   LocalContext context;
6317 
6318   int size_upper_limit = 0;
6319   int count_upper_limit = 0;
6320   HeapIterator it(CcTest::heap());
6321   for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
6322     size_upper_limit += obj->Size();
6323     count_upper_limit++;
6324   }
6325 
6326   ContextMeasure measure(*isolate->native_context());
6327 
6328   PrintF("Context size        : %d bytes\n", measure.Size());
6329   PrintF("Context object count: %d\n", measure.Count());
6330 
6331   CHECK_LE(1000, measure.Count());
6332   CHECK_LE(50000, measure.Size());
6333 
6334   CHECK_LE(measure.Count(), count_upper_limit);
6335   CHECK_LE(measure.Size(), size_upper_limit);
6336 }
6337 
6338 
TEST(ScriptIterator)6339 TEST(ScriptIterator) {
6340   CcTest::InitializeVM();
6341   v8::HandleScope scope(CcTest::isolate());
6342   Isolate* isolate = CcTest::i_isolate();
6343   Heap* heap = CcTest::heap();
6344   LocalContext context;
6345 
6346   heap->CollectAllGarbage();
6347 
6348   int script_count = 0;
6349   {
6350     HeapIterator it(heap);
6351     for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
6352       if (obj->IsScript()) script_count++;
6353     }
6354   }
6355 
6356   {
6357     Script::Iterator iterator(isolate);
6358     while (iterator.Next()) script_count--;
6359   }
6360 
6361   CHECK_EQ(0, script_count);
6362 }
6363 
6364 
TEST(SharedFunctionInfoIterator)6365 TEST(SharedFunctionInfoIterator) {
6366   CcTest::InitializeVM();
6367   v8::HandleScope scope(CcTest::isolate());
6368   Isolate* isolate = CcTest::i_isolate();
6369   Heap* heap = CcTest::heap();
6370   LocalContext context;
6371 
6372   heap->CollectAllGarbage();
6373   heap->CollectAllGarbage();
6374 
6375   int sfi_count = 0;
6376   {
6377     HeapIterator it(heap);
6378     for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
6379       if (!obj->IsSharedFunctionInfo()) continue;
6380       sfi_count++;
6381     }
6382   }
6383 
6384   {
6385     SharedFunctionInfo::Iterator iterator(isolate);
6386     while (iterator.Next()) sfi_count--;
6387   }
6388 
6389   CHECK_EQ(0, sfi_count);
6390 }
6391 
6392 
6393 template <typename T>
MakeUniqueId(const Persistent<T> & p)6394 static UniqueId MakeUniqueId(const Persistent<T>& p) {
6395   return UniqueId(reinterpret_cast<uintptr_t>(*v8::Utils::OpenPersistent(p)));
6396 }
6397 
6398 
TEST(Regress519319)6399 TEST(Regress519319) {
6400   CcTest::InitializeVM();
6401   v8::Isolate* isolate = CcTest::isolate();
6402   v8::HandleScope scope(isolate);
6403   Heap* heap = CcTest::heap();
6404   LocalContext context;
6405 
6406   v8::Persistent<Value> parent;
6407   v8::Persistent<Value> child;
6408 
6409   parent.Reset(isolate, v8::Object::New(isolate));
6410   child.Reset(isolate, v8::Object::New(isolate));
6411 
6412   heap::SimulateFullSpace(heap->old_space());
6413   heap->CollectGarbage(OLD_SPACE);
6414   {
6415     UniqueId id = MakeUniqueId(parent);
6416     isolate->SetObjectGroupId(parent, id);
6417     isolate->SetReferenceFromGroup(id, child);
6418   }
6419   // The CollectGarbage call above starts sweeper threads.
6420   // The crash will happen if the following two functions
6421   // are called before sweeping finishes.
6422   heap->StartIncrementalMarking();
6423   heap->FinalizeIncrementalMarkingIfComplete("test");
6424 }
6425 
6426 
HEAP_TEST(TestMemoryReducerSampleJsCalls)6427 HEAP_TEST(TestMemoryReducerSampleJsCalls) {
6428   CcTest::InitializeVM();
6429   v8::HandleScope scope(CcTest::isolate());
6430   Heap* heap = CcTest::heap();
6431   Isolate* isolate = CcTest::i_isolate();
6432   MemoryReducer* memory_reducer = heap->memory_reducer_;
6433   memory_reducer->SampleAndGetJsCallsPerMs(0);
6434   isolate->IncrementJsCallsFromApiCounter();
6435   isolate->IncrementJsCallsFromApiCounter();
6436   isolate->IncrementJsCallsFromApiCounter();
6437   double calls_per_ms = memory_reducer->SampleAndGetJsCallsPerMs(1);
6438   CheckDoubleEquals(3, calls_per_ms);
6439 
6440   calls_per_ms = memory_reducer->SampleAndGetJsCallsPerMs(2);
6441   CheckDoubleEquals(0, calls_per_ms);
6442 
6443   isolate->IncrementJsCallsFromApiCounter();
6444   isolate->IncrementJsCallsFromApiCounter();
6445   isolate->IncrementJsCallsFromApiCounter();
6446   isolate->IncrementJsCallsFromApiCounter();
6447   calls_per_ms = memory_reducer->SampleAndGetJsCallsPerMs(4);
6448   CheckDoubleEquals(2, calls_per_ms);
6449 }
6450 
HEAP_TEST(Regress587004)6451 HEAP_TEST(Regress587004) {
6452   FLAG_concurrent_sweeping = false;
6453 #ifdef VERIFY_HEAP
6454   FLAG_verify_heap = false;
6455 #endif
6456   CcTest::InitializeVM();
6457   v8::HandleScope scope(CcTest::isolate());
6458   Heap* heap = CcTest::heap();
6459   Isolate* isolate = CcTest::i_isolate();
6460   Factory* factory = isolate->factory();
6461   const int N = (Page::kMaxRegularHeapObjectSize - FixedArray::kHeaderSize) /
6462                 kPointerSize;
6463   Handle<FixedArray> array = factory->NewFixedArray(N, TENURED);
6464   CHECK(heap->old_space()->Contains(*array));
6465   Handle<Object> number = factory->NewHeapNumber(1.0);
6466   CHECK(heap->InNewSpace(*number));
6467   for (int i = 0; i < N; i++) {
6468     array->set(i, *number);
6469   }
6470   heap->CollectGarbage(OLD_SPACE);
6471   heap::SimulateFullSpace(heap->old_space());
6472   heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(*array, N - 1);
6473   heap->mark_compact_collector()->EnsureSweepingCompleted();
6474   ByteArray* byte_array;
6475   const int M = 256;
6476   // Don't allow old space expansion. The test works without this flag too,
6477   // but becomes very slow.
6478   heap->set_force_oom(true);
6479   while (heap->AllocateByteArray(M, TENURED).To(&byte_array)) {
6480     for (int j = 0; j < M; j++) {
6481       byte_array->set(j, 0x31);
6482     }
6483   }
6484   // Re-enable old space expansion to avoid OOM crash.
6485   heap->set_force_oom(false);
6486   heap->CollectGarbage(NEW_SPACE);
6487 }
6488 
HEAP_TEST(Regress589413)6489 HEAP_TEST(Regress589413) {
6490   FLAG_stress_compaction = true;
6491   FLAG_manual_evacuation_candidates_selection = true;
6492   FLAG_parallel_compaction = false;
6493   FLAG_concurrent_sweeping = false;
6494   CcTest::InitializeVM();
6495   v8::HandleScope scope(CcTest::isolate());
6496   Heap* heap = CcTest::heap();
6497   // Get the heap in clean state.
6498   heap->CollectGarbage(OLD_SPACE);
6499   heap->CollectGarbage(OLD_SPACE);
6500   Isolate* isolate = CcTest::i_isolate();
6501   Factory* factory = isolate->factory();
6502   // Fill the new space with byte arrays with elements looking like pointers.
6503   const int M = 256;
6504   ByteArray* byte_array;
6505   while (heap->AllocateByteArray(M).To(&byte_array)) {
6506     for (int j = 0; j < M; j++) {
6507       byte_array->set(j, 0x31);
6508     }
6509     // Add the array in root set.
6510     handle(byte_array);
6511   }
6512   // Make sure the byte arrays will be promoted on the next GC.
6513   heap->CollectGarbage(NEW_SPACE);
6514   // This number is close to large free list category threshold.
6515   const int N = 0x3eee;
6516   {
6517     std::vector<FixedArray*> arrays;
6518     std::set<Page*> pages;
6519     FixedArray* array;
6520     // Fill all pages with fixed arrays.
6521     heap->set_force_oom(true);
6522     while (heap->AllocateFixedArray(N, TENURED).To(&array)) {
6523       arrays.push_back(array);
6524       pages.insert(Page::FromAddress(array->address()));
6525       // Add the array in root set.
6526       handle(array);
6527     }
6528     // Expand and full one complete page with fixed arrays.
6529     heap->set_force_oom(false);
6530     while (heap->AllocateFixedArray(N, TENURED).To(&array)) {
6531       arrays.push_back(array);
6532       pages.insert(Page::FromAddress(array->address()));
6533       // Add the array in root set.
6534       handle(array);
6535       // Do not expand anymore.
6536       heap->set_force_oom(true);
6537     }
6538     // Expand and mark the new page as evacuation candidate.
6539     heap->set_force_oom(false);
6540     {
6541       AlwaysAllocateScope always_allocate(isolate);
6542       Handle<HeapObject> ec_obj = factory->NewFixedArray(5000, TENURED);
6543       Page* ec_page = Page::FromAddress(ec_obj->address());
6544       ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
6545       // Make all arrays point to evacuation candidate so that
6546       // slots are recorded for them.
6547       for (size_t j = 0; j < arrays.size(); j++) {
6548         array = arrays[j];
6549         for (int i = 0; i < N; i++) {
6550           array->set(i, *ec_obj);
6551         }
6552       }
6553     }
6554     heap::SimulateIncrementalMarking(heap);
6555     for (size_t j = 0; j < arrays.size(); j++) {
6556       heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(arrays[j], N - 1);
6557     }
6558   }
6559   // Force allocation from the free list.
6560   heap->set_force_oom(true);
6561   heap->CollectGarbage(OLD_SPACE);
6562 }
6563 
TEST(Regress598319)6564 TEST(Regress598319) {
6565   // This test ensures that no white objects can cross the progress bar of large
6566   // objects during incremental marking. It checks this by using Shift() during
6567   // incremental marking.
6568   CcTest::InitializeVM();
6569   v8::HandleScope scope(CcTest::isolate());
6570   Heap* heap = CcTest::heap();
6571   Isolate* isolate = heap->isolate();
6572 
6573   const int kNumberOfObjects = Page::kMaxRegularHeapObjectSize / kPointerSize;
6574 
6575   struct Arr {
6576     Arr(Isolate* isolate, int number_of_objects) {
6577       root = isolate->factory()->NewFixedArray(1, TENURED);
6578       {
6579         // Temporary scope to avoid getting any other objects into the root set.
6580         v8::HandleScope scope(CcTest::isolate());
6581         Handle<FixedArray> tmp =
6582             isolate->factory()->NewFixedArray(number_of_objects);
6583         root->set(0, *tmp);
6584         for (int i = 0; i < get()->length(); i++) {
6585           tmp = isolate->factory()->NewFixedArray(100, TENURED);
6586           get()->set(i, *tmp);
6587         }
6588       }
6589     }
6590 
6591     FixedArray* get() { return FixedArray::cast(root->get(0)); }
6592 
6593     Handle<FixedArray> root;
6594   } arr(isolate, kNumberOfObjects);
6595 
6596   CHECK_EQ(arr.get()->length(), kNumberOfObjects);
6597   CHECK(heap->lo_space()->Contains(arr.get()));
6598   LargePage* page = heap->lo_space()->FindPage(arr.get()->address());
6599   CHECK_NOT_NULL(page);
6600 
6601   // GC to cleanup state
6602   heap->CollectGarbage(OLD_SPACE);
6603   MarkCompactCollector* collector = heap->mark_compact_collector();
6604   if (collector->sweeping_in_progress()) {
6605     collector->EnsureSweepingCompleted();
6606   }
6607 
6608   CHECK(heap->lo_space()->Contains(arr.get()));
6609   CHECK(Marking::IsWhite(Marking::MarkBitFrom(arr.get())));
6610   for (int i = 0; i < arr.get()->length(); i++) {
6611     CHECK(Marking::IsWhite(
6612         Marking::MarkBitFrom(HeapObject::cast(arr.get()->get(i)))));
6613   }
6614 
6615   // Start incremental marking.
6616   IncrementalMarking* marking = heap->incremental_marking();
6617   CHECK(marking->IsMarking() || marking->IsStopped());
6618   if (marking->IsStopped()) {
6619     heap->StartIncrementalMarking();
6620   }
6621   CHECK(marking->IsMarking());
6622 
6623   // Check that we have not marked the interesting array during root scanning.
6624   for (int i = 0; i < arr.get()->length(); i++) {
6625     CHECK(Marking::IsWhite(
6626         Marking::MarkBitFrom(HeapObject::cast(arr.get()->get(i)))));
6627   }
6628 
6629   // Now we search for a state where we are in incremental marking and have
6630   // only partially marked the large object.
6631   while (!marking->IsComplete()) {
6632     marking->Step(i::KB, i::IncrementalMarking::NO_GC_VIA_STACK_GUARD);
6633     if (page->IsFlagSet(Page::HAS_PROGRESS_BAR) && page->progress_bar() > 0) {
6634       CHECK_NE(page->progress_bar(), arr.get()->Size());
6635       {
6636         // Shift by 1, effectively moving one white object across the progress
6637         // bar, meaning that we will miss marking it.
6638         v8::HandleScope scope(CcTest::isolate());
6639         Handle<JSArray> js_array = isolate->factory()->NewJSArrayWithElements(
6640             Handle<FixedArray>(arr.get()));
6641         js_array->GetElementsAccessor()->Shift(js_array);
6642       }
6643       break;
6644     }
6645   }
6646 
6647   // Finish marking with bigger steps to speed up test.
6648   while (!marking->IsComplete()) {
6649     marking->Step(10 * i::MB, i::IncrementalMarking::NO_GC_VIA_STACK_GUARD);
6650     if (marking->IsReadyToOverApproximateWeakClosure()) {
6651       marking->FinalizeIncrementally();
6652     }
6653   }
6654   CHECK(marking->IsComplete());
6655 
6656   // All objects need to be black after marking. If a white object crossed the
6657   // progress bar, we would fail here.
6658   for (int i = 0; i < arr.get()->length(); i++) {
6659     CHECK(Marking::IsBlack(
6660         Marking::MarkBitFrom(HeapObject::cast(arr.get()->get(i)))));
6661   }
6662 }
6663 
TEST(Regress609761)6664 TEST(Regress609761) {
6665   CcTest::InitializeVM();
6666   v8::HandleScope scope(CcTest::isolate());
6667   Heap* heap = CcTest::heap();
6668   Isolate* isolate = heap->isolate();
6669 
6670   intptr_t size_before = heap->SizeOfObjects();
6671   Handle<FixedArray> array = isolate->factory()->NewFixedArray(200000);
6672   array->Shrink(1);
6673   intptr_t size_after = heap->SizeOfObjects();
6674   CHECK_EQ(size_after, size_before + array->Size());
6675 }
6676 
TEST(Regress615489)6677 TEST(Regress615489) {
6678   FLAG_black_allocation = true;
6679   CcTest::InitializeVM();
6680   v8::HandleScope scope(CcTest::isolate());
6681   Heap* heap = CcTest::heap();
6682   Isolate* isolate = heap->isolate();
6683   heap->CollectAllGarbage();
6684 
6685   i::MarkCompactCollector* collector = heap->mark_compact_collector();
6686   i::IncrementalMarking* marking = heap->incremental_marking();
6687   if (collector->sweeping_in_progress()) {
6688     collector->EnsureSweepingCompleted();
6689   }
6690   CHECK(marking->IsMarking() || marking->IsStopped());
6691   if (marking->IsStopped()) {
6692     heap->StartIncrementalMarking();
6693   }
6694   CHECK(marking->IsMarking());
6695   marking->StartBlackAllocationForTesting();
6696   {
6697     AlwaysAllocateScope always_allocate(CcTest::i_isolate());
6698     v8::HandleScope inner(CcTest::isolate());
6699     isolate->factory()->NewFixedArray(500, TENURED)->Size();
6700   }
6701   while (!marking->IsComplete()) {
6702     marking->Step(i::MB, i::IncrementalMarking::NO_GC_VIA_STACK_GUARD);
6703     if (marking->IsReadyToOverApproximateWeakClosure()) {
6704       marking->FinalizeIncrementally();
6705     }
6706   }
6707   CHECK(marking->IsComplete());
6708   intptr_t size_before = heap->SizeOfObjects();
6709   CcTest::heap()->CollectAllGarbage();
6710   intptr_t size_after = heap->SizeOfObjects();
6711   // Live size does not increase after garbage collection.
6712   CHECK_LE(size_after, size_before);
6713 }
6714 
TEST(Regress618958)6715 TEST(Regress618958) {
6716   CcTest::InitializeVM();
6717   v8::HandleScope scope(CcTest::isolate());
6718   Heap* heap = CcTest::heap();
6719   bool isolate_is_locked = true;
6720   heap->update_external_memory(100 * MB);
6721   int mark_sweep_count_before = heap->ms_count();
6722   heap->MemoryPressureNotification(MemoryPressureLevel::kCritical,
6723                                    isolate_is_locked);
6724   int mark_sweep_count_after = heap->ms_count();
6725   int mark_sweeps_performed = mark_sweep_count_after - mark_sweep_count_before;
6726   // The memory pressuer handler either performed two GCs or performed one and
6727   // started incremental marking.
6728   CHECK(mark_sweeps_performed == 2 ||
6729         (mark_sweeps_performed == 1 &&
6730          !heap->incremental_marking()->IsStopped()));
6731 }
6732 
6733 }  // namespace internal
6734 }  // namespace v8
6735