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