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