1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include <stdlib.h> 29 #include <utility> 30 31 #include "src/v8.h" 32 33 #include "src/compilation-cache.h" 34 #include "src/execution.h" 35 #include "src/factory.h" 36 #include "src/global-handles.h" 37 #include "src/macro-assembler.h" 38 #include "src/stub-cache.h" 39 #include "test/cctest/cctest.h" 40 41 using namespace v8::internal; 42 43 // Go through all incremental marking steps in one swoop. 44 static void SimulateIncrementalMarking() { 45 MarkCompactCollector* collector = CcTest::heap()->mark_compact_collector(); 46 IncrementalMarking* marking = CcTest::heap()->incremental_marking(); 47 if (collector->IsConcurrentSweepingInProgress()) { 48 collector->WaitUntilSweepingCompleted(); 49 } 50 CHECK(marking->IsMarking() || marking->IsStopped()); 51 if (marking->IsStopped()) { 52 marking->Start(); 53 } 54 CHECK(marking->IsMarking()); 55 while (!marking->IsComplete()) { 56 marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); 57 } 58 CHECK(marking->IsComplete()); 59 } 60 61 62 static void CheckMap(Map* map, int type, int instance_size) { 63 CHECK(map->IsHeapObject()); 64 #ifdef DEBUG 65 CHECK(CcTest::heap()->Contains(map)); 66 #endif 67 CHECK_EQ(CcTest::heap()->meta_map(), map->map()); 68 CHECK_EQ(type, map->instance_type()); 69 CHECK_EQ(instance_size, map->instance_size()); 70 } 71 72 73 TEST(HeapMaps) { 74 CcTest::InitializeVM(); 75 Heap* heap = CcTest::heap(); 76 CheckMap(heap->meta_map(), MAP_TYPE, Map::kSize); 77 CheckMap(heap->heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize); 78 CheckMap(heap->fixed_array_map(), FIXED_ARRAY_TYPE, kVariableSizeSentinel); 79 CheckMap(heap->string_map(), STRING_TYPE, kVariableSizeSentinel); 80 } 81 82 83 static void CheckOddball(Isolate* isolate, Object* obj, const char* string) { 84 CHECK(obj->IsOddball()); 85 Handle<Object> handle(obj, isolate); 86 Object* print_string = 87 *Execution::ToString(isolate, handle).ToHandleChecked(); 88 CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string))); 89 } 90 91 92 static void CheckSmi(Isolate* isolate, int value, const char* string) { 93 Handle<Object> handle(Smi::FromInt(value), isolate); 94 Object* print_string = 95 *Execution::ToString(isolate, handle).ToHandleChecked(); 96 CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string))); 97 } 98 99 100 static void CheckNumber(Isolate* isolate, double value, const char* string) { 101 Handle<Object> number = isolate->factory()->NewNumber(value); 102 CHECK(number->IsNumber()); 103 Handle<Object> print_string = 104 Execution::ToString(isolate, number).ToHandleChecked(); 105 CHECK(String::cast(*print_string)->IsUtf8EqualTo(CStrVector(string))); 106 } 107 108 109 static void CheckFindCodeObject(Isolate* isolate) { 110 // Test FindCodeObject 111 #define __ assm. 112 113 Assembler assm(isolate, NULL, 0); 114 115 __ nop(); // supported on all architectures 116 117 CodeDesc desc; 118 assm.GetCode(&desc); 119 Handle<Code> code = isolate->factory()->NewCode( 120 desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); 121 CHECK(code->IsCode()); 122 123 HeapObject* obj = HeapObject::cast(*code); 124 Address obj_addr = obj->address(); 125 126 for (int i = 0; i < obj->Size(); i += kPointerSize) { 127 Object* found = isolate->FindCodeObject(obj_addr + i); 128 CHECK_EQ(*code, found); 129 } 130 131 Handle<Code> copy = isolate->factory()->NewCode( 132 desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); 133 HeapObject* obj_copy = HeapObject::cast(*copy); 134 Object* not_right = isolate->FindCodeObject(obj_copy->address() + 135 obj_copy->Size() / 2); 136 CHECK(not_right != *code); 137 } 138 139 140 TEST(HandleNull) { 141 CcTest::InitializeVM(); 142 Isolate* isolate = CcTest::i_isolate(); 143 HandleScope outer_scope(isolate); 144 LocalContext context; 145 Handle<Object> n(reinterpret_cast<Object*>(NULL), isolate); 146 CHECK(!n.is_null()); 147 } 148 149 150 TEST(HeapObjects) { 151 CcTest::InitializeVM(); 152 Isolate* isolate = CcTest::i_isolate(); 153 Factory* factory = isolate->factory(); 154 Heap* heap = isolate->heap(); 155 156 HandleScope sc(isolate); 157 Handle<Object> value = factory->NewNumber(1.000123); 158 CHECK(value->IsHeapNumber()); 159 CHECK(value->IsNumber()); 160 CHECK_EQ(1.000123, value->Number()); 161 162 value = factory->NewNumber(1.0); 163 CHECK(value->IsSmi()); 164 CHECK(value->IsNumber()); 165 CHECK_EQ(1.0, value->Number()); 166 167 value = factory->NewNumberFromInt(1024); 168 CHECK(value->IsSmi()); 169 CHECK(value->IsNumber()); 170 CHECK_EQ(1024.0, value->Number()); 171 172 value = factory->NewNumberFromInt(Smi::kMinValue); 173 CHECK(value->IsSmi()); 174 CHECK(value->IsNumber()); 175 CHECK_EQ(Smi::kMinValue, Handle<Smi>::cast(value)->value()); 176 177 value = factory->NewNumberFromInt(Smi::kMaxValue); 178 CHECK(value->IsSmi()); 179 CHECK(value->IsNumber()); 180 CHECK_EQ(Smi::kMaxValue, Handle<Smi>::cast(value)->value()); 181 182 #if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_ARM64) 183 // TODO(lrn): We need a NumberFromIntptr function in order to test this. 184 value = factory->NewNumberFromInt(Smi::kMinValue - 1); 185 CHECK(value->IsHeapNumber()); 186 CHECK(value->IsNumber()); 187 CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number()); 188 #endif 189 190 value = factory->NewNumberFromUint(static_cast<uint32_t>(Smi::kMaxValue) + 1); 191 CHECK(value->IsHeapNumber()); 192 CHECK(value->IsNumber()); 193 CHECK_EQ(static_cast<double>(static_cast<uint32_t>(Smi::kMaxValue) + 1), 194 value->Number()); 195 196 value = factory->NewNumberFromUint(static_cast<uint32_t>(1) << 31); 197 CHECK(value->IsHeapNumber()); 198 CHECK(value->IsNumber()); 199 CHECK_EQ(static_cast<double>(static_cast<uint32_t>(1) << 31), 200 value->Number()); 201 202 // nan oddball checks 203 CHECK(factory->nan_value()->IsNumber()); 204 CHECK(std::isnan(factory->nan_value()->Number())); 205 206 Handle<String> s = factory->NewStringFromStaticAscii("fisk hest "); 207 CHECK(s->IsString()); 208 CHECK_EQ(10, s->length()); 209 210 Handle<String> object_string = Handle<String>::cast(factory->Object_string()); 211 Handle<GlobalObject> global(CcTest::i_isolate()->context()->global_object()); 212 CHECK(JSReceiver::HasOwnProperty(global, object_string)); 213 214 // Check ToString for oddballs 215 CheckOddball(isolate, heap->true_value(), "true"); 216 CheckOddball(isolate, heap->false_value(), "false"); 217 CheckOddball(isolate, heap->null_value(), "null"); 218 CheckOddball(isolate, heap->undefined_value(), "undefined"); 219 220 // Check ToString for Smis 221 CheckSmi(isolate, 0, "0"); 222 CheckSmi(isolate, 42, "42"); 223 CheckSmi(isolate, -42, "-42"); 224 225 // Check ToString for Numbers 226 CheckNumber(isolate, 1.1, "1.1"); 227 228 CheckFindCodeObject(isolate); 229 } 230 231 232 TEST(Tagging) { 233 CcTest::InitializeVM(); 234 int request = 24; 235 CHECK_EQ(request, static_cast<int>(OBJECT_POINTER_ALIGN(request))); 236 CHECK(Smi::FromInt(42)->IsSmi()); 237 CHECK(Smi::FromInt(Smi::kMinValue)->IsSmi()); 238 CHECK(Smi::FromInt(Smi::kMaxValue)->IsSmi()); 239 } 240 241 242 TEST(GarbageCollection) { 243 CcTest::InitializeVM(); 244 Isolate* isolate = CcTest::i_isolate(); 245 Heap* heap = isolate->heap(); 246 Factory* factory = isolate->factory(); 247 248 HandleScope sc(isolate); 249 // Check GC. 250 heap->CollectGarbage(NEW_SPACE); 251 252 Handle<GlobalObject> global(CcTest::i_isolate()->context()->global_object()); 253 Handle<String> name = factory->InternalizeUtf8String("theFunction"); 254 Handle<String> prop_name = factory->InternalizeUtf8String("theSlot"); 255 Handle<String> prop_namex = factory->InternalizeUtf8String("theSlotx"); 256 Handle<String> obj_name = factory->InternalizeUtf8String("theObject"); 257 Handle<Smi> twenty_three(Smi::FromInt(23), isolate); 258 Handle<Smi> twenty_four(Smi::FromInt(24), isolate); 259 260 { 261 HandleScope inner_scope(isolate); 262 // Allocate a function and keep it in global object's property. 263 Handle<JSFunction> function = factory->NewFunction(name); 264 JSReceiver::SetProperty(global, name, function, NONE, SLOPPY).Check(); 265 // Allocate an object. Unrooted after leaving the scope. 266 Handle<JSObject> obj = factory->NewJSObject(function); 267 JSReceiver::SetProperty( 268 obj, prop_name, twenty_three, NONE, SLOPPY).Check(); 269 JSReceiver::SetProperty( 270 obj, prop_namex, twenty_four, NONE, SLOPPY).Check(); 271 272 CHECK_EQ(Smi::FromInt(23), 273 *Object::GetProperty(obj, prop_name).ToHandleChecked()); 274 CHECK_EQ(Smi::FromInt(24), 275 *Object::GetProperty(obj, prop_namex).ToHandleChecked()); 276 } 277 278 heap->CollectGarbage(NEW_SPACE); 279 280 // Function should be alive. 281 CHECK(JSReceiver::HasOwnProperty(global, name)); 282 // Check function is retained. 283 Handle<Object> func_value = 284 Object::GetProperty(global, name).ToHandleChecked(); 285 CHECK(func_value->IsJSFunction()); 286 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value); 287 288 { 289 HandleScope inner_scope(isolate); 290 // Allocate another object, make it reachable from global. 291 Handle<JSObject> obj = factory->NewJSObject(function); 292 JSReceiver::SetProperty(global, obj_name, obj, NONE, SLOPPY).Check(); 293 JSReceiver::SetProperty( 294 obj, prop_name, twenty_three, NONE, SLOPPY).Check(); 295 } 296 297 // After gc, it should survive. 298 heap->CollectGarbage(NEW_SPACE); 299 300 CHECK(JSReceiver::HasOwnProperty(global, obj_name)); 301 Handle<Object> obj = 302 Object::GetProperty(global, obj_name).ToHandleChecked(); 303 CHECK(obj->IsJSObject()); 304 CHECK_EQ(Smi::FromInt(23), 305 *Object::GetProperty(obj, prop_name).ToHandleChecked()); 306 } 307 308 309 static void VerifyStringAllocation(Isolate* isolate, const char* string) { 310 HandleScope scope(isolate); 311 Handle<String> s = isolate->factory()->NewStringFromUtf8( 312 CStrVector(string)).ToHandleChecked(); 313 CHECK_EQ(StrLength(string), s->length()); 314 for (int index = 0; index < s->length(); index++) { 315 CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index)); 316 } 317 } 318 319 320 TEST(String) { 321 CcTest::InitializeVM(); 322 Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate()); 323 324 VerifyStringAllocation(isolate, "a"); 325 VerifyStringAllocation(isolate, "ab"); 326 VerifyStringAllocation(isolate, "abc"); 327 VerifyStringAllocation(isolate, "abcd"); 328 VerifyStringAllocation(isolate, "fiskerdrengen er paa havet"); 329 } 330 331 332 TEST(LocalHandles) { 333 CcTest::InitializeVM(); 334 Isolate* isolate = CcTest::i_isolate(); 335 Factory* factory = isolate->factory(); 336 337 v8::HandleScope scope(CcTest::isolate()); 338 const char* name = "Kasper the spunky"; 339 Handle<String> string = factory->NewStringFromAsciiChecked(name); 340 CHECK_EQ(StrLength(name), string->length()); 341 } 342 343 344 TEST(GlobalHandles) { 345 CcTest::InitializeVM(); 346 Isolate* isolate = CcTest::i_isolate(); 347 Heap* heap = isolate->heap(); 348 Factory* factory = isolate->factory(); 349 GlobalHandles* global_handles = isolate->global_handles(); 350 351 Handle<Object> h1; 352 Handle<Object> h2; 353 Handle<Object> h3; 354 Handle<Object> h4; 355 356 { 357 HandleScope scope(isolate); 358 359 Handle<Object> i = factory->NewStringFromStaticAscii("fisk"); 360 Handle<Object> u = factory->NewNumber(1.12344); 361 362 h1 = global_handles->Create(*i); 363 h2 = global_handles->Create(*u); 364 h3 = global_handles->Create(*i); 365 h4 = global_handles->Create(*u); 366 } 367 368 // after gc, it should survive 369 heap->CollectGarbage(NEW_SPACE); 370 371 CHECK((*h1)->IsString()); 372 CHECK((*h2)->IsHeapNumber()); 373 CHECK((*h3)->IsString()); 374 CHECK((*h4)->IsHeapNumber()); 375 376 CHECK_EQ(*h3, *h1); 377 GlobalHandles::Destroy(h1.location()); 378 GlobalHandles::Destroy(h3.location()); 379 380 CHECK_EQ(*h4, *h2); 381 GlobalHandles::Destroy(h2.location()); 382 GlobalHandles::Destroy(h4.location()); 383 } 384 385 386 static bool WeakPointerCleared = false; 387 388 static void TestWeakGlobalHandleCallback( 389 const v8::WeakCallbackData<v8::Value, void>& data) { 390 std::pair<v8::Persistent<v8::Value>*, int>* p = 391 reinterpret_cast<std::pair<v8::Persistent<v8::Value>*, int>*>( 392 data.GetParameter()); 393 if (p->second == 1234) WeakPointerCleared = true; 394 p->first->Reset(); 395 } 396 397 398 TEST(WeakGlobalHandlesScavenge) { 399 i::FLAG_stress_compaction = false; 400 CcTest::InitializeVM(); 401 Isolate* isolate = CcTest::i_isolate(); 402 Heap* heap = isolate->heap(); 403 Factory* factory = isolate->factory(); 404 GlobalHandles* global_handles = isolate->global_handles(); 405 406 WeakPointerCleared = false; 407 408 Handle<Object> h1; 409 Handle<Object> h2; 410 411 { 412 HandleScope scope(isolate); 413 414 Handle<Object> i = factory->NewStringFromStaticAscii("fisk"); 415 Handle<Object> u = factory->NewNumber(1.12344); 416 417 h1 = global_handles->Create(*i); 418 h2 = global_handles->Create(*u); 419 } 420 421 std::pair<Handle<Object>*, int> handle_and_id(&h2, 1234); 422 GlobalHandles::MakeWeak(h2.location(), 423 reinterpret_cast<void*>(&handle_and_id), 424 &TestWeakGlobalHandleCallback); 425 426 // Scavenge treats weak pointers as normal roots. 427 heap->CollectGarbage(NEW_SPACE); 428 429 CHECK((*h1)->IsString()); 430 CHECK((*h2)->IsHeapNumber()); 431 432 CHECK(!WeakPointerCleared); 433 CHECK(!global_handles->IsNearDeath(h2.location())); 434 CHECK(!global_handles->IsNearDeath(h1.location())); 435 436 GlobalHandles::Destroy(h1.location()); 437 GlobalHandles::Destroy(h2.location()); 438 } 439 440 441 TEST(WeakGlobalHandlesMark) { 442 CcTest::InitializeVM(); 443 Isolate* isolate = CcTest::i_isolate(); 444 Heap* heap = isolate->heap(); 445 Factory* factory = isolate->factory(); 446 GlobalHandles* global_handles = isolate->global_handles(); 447 448 WeakPointerCleared = false; 449 450 Handle<Object> h1; 451 Handle<Object> h2; 452 453 { 454 HandleScope scope(isolate); 455 456 Handle<Object> i = factory->NewStringFromStaticAscii("fisk"); 457 Handle<Object> u = factory->NewNumber(1.12344); 458 459 h1 = global_handles->Create(*i); 460 h2 = global_handles->Create(*u); 461 } 462 463 // Make sure the objects are promoted. 464 heap->CollectGarbage(OLD_POINTER_SPACE); 465 heap->CollectGarbage(NEW_SPACE); 466 CHECK(!heap->InNewSpace(*h1) && !heap->InNewSpace(*h2)); 467 468 std::pair<Handle<Object>*, int> handle_and_id(&h2, 1234); 469 GlobalHandles::MakeWeak(h2.location(), 470 reinterpret_cast<void*>(&handle_and_id), 471 &TestWeakGlobalHandleCallback); 472 CHECK(!GlobalHandles::IsNearDeath(h1.location())); 473 CHECK(!GlobalHandles::IsNearDeath(h2.location())); 474 475 // Incremental marking potentially marked handles before they turned weak. 476 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 477 478 CHECK((*h1)->IsString()); 479 480 CHECK(WeakPointerCleared); 481 CHECK(!GlobalHandles::IsNearDeath(h1.location())); 482 483 GlobalHandles::Destroy(h1.location()); 484 } 485 486 487 TEST(DeleteWeakGlobalHandle) { 488 i::FLAG_stress_compaction = false; 489 CcTest::InitializeVM(); 490 Isolate* isolate = CcTest::i_isolate(); 491 Heap* heap = isolate->heap(); 492 Factory* factory = isolate->factory(); 493 GlobalHandles* global_handles = isolate->global_handles(); 494 495 WeakPointerCleared = false; 496 497 Handle<Object> h; 498 499 { 500 HandleScope scope(isolate); 501 502 Handle<Object> i = factory->NewStringFromStaticAscii("fisk"); 503 h = global_handles->Create(*i); 504 } 505 506 std::pair<Handle<Object>*, int> handle_and_id(&h, 1234); 507 GlobalHandles::MakeWeak(h.location(), 508 reinterpret_cast<void*>(&handle_and_id), 509 &TestWeakGlobalHandleCallback); 510 511 // Scanvenge does not recognize weak reference. 512 heap->CollectGarbage(NEW_SPACE); 513 514 CHECK(!WeakPointerCleared); 515 516 // Mark-compact treats weak reference properly. 517 heap->CollectGarbage(OLD_POINTER_SPACE); 518 519 CHECK(WeakPointerCleared); 520 } 521 522 523 static const char* not_so_random_string_table[] = { 524 "abstract", 525 "boolean", 526 "break", 527 "byte", 528 "case", 529 "catch", 530 "char", 531 "class", 532 "const", 533 "continue", 534 "debugger", 535 "default", 536 "delete", 537 "do", 538 "double", 539 "else", 540 "enum", 541 "export", 542 "extends", 543 "false", 544 "final", 545 "finally", 546 "float", 547 "for", 548 "function", 549 "goto", 550 "if", 551 "implements", 552 "import", 553 "in", 554 "instanceof", 555 "int", 556 "interface", 557 "long", 558 "native", 559 "new", 560 "null", 561 "package", 562 "private", 563 "protected", 564 "public", 565 "return", 566 "short", 567 "static", 568 "super", 569 "switch", 570 "synchronized", 571 "this", 572 "throw", 573 "throws", 574 "transient", 575 "true", 576 "try", 577 "typeof", 578 "var", 579 "void", 580 "volatile", 581 "while", 582 "with", 583 0 584 }; 585 586 587 static void CheckInternalizedStrings(const char** strings) { 588 Isolate* isolate = CcTest::i_isolate(); 589 Factory* factory = isolate->factory(); 590 for (const char* string = *strings; *strings != 0; string = *strings++) { 591 HandleScope scope(isolate); 592 Handle<String> a = 593 isolate->factory()->InternalizeUtf8String(CStrVector(string)); 594 // InternalizeUtf8String may return a failure if a GC is needed. 595 CHECK(a->IsInternalizedString()); 596 Handle<String> b = factory->InternalizeUtf8String(string); 597 CHECK_EQ(*b, *a); 598 CHECK(b->IsUtf8EqualTo(CStrVector(string))); 599 b = isolate->factory()->InternalizeUtf8String(CStrVector(string)); 600 CHECK_EQ(*b, *a); 601 CHECK(b->IsUtf8EqualTo(CStrVector(string))); 602 } 603 } 604 605 606 TEST(StringTable) { 607 CcTest::InitializeVM(); 608 609 v8::HandleScope sc(CcTest::isolate()); 610 CheckInternalizedStrings(not_so_random_string_table); 611 CheckInternalizedStrings(not_so_random_string_table); 612 } 613 614 615 TEST(FunctionAllocation) { 616 CcTest::InitializeVM(); 617 Isolate* isolate = CcTest::i_isolate(); 618 Factory* factory = isolate->factory(); 619 620 v8::HandleScope sc(CcTest::isolate()); 621 Handle<String> name = factory->InternalizeUtf8String("theFunction"); 622 Handle<JSFunction> function = factory->NewFunction(name); 623 624 Handle<Smi> twenty_three(Smi::FromInt(23), isolate); 625 Handle<Smi> twenty_four(Smi::FromInt(24), isolate); 626 627 Handle<String> prop_name = factory->InternalizeUtf8String("theSlot"); 628 Handle<JSObject> obj = factory->NewJSObject(function); 629 JSReceiver::SetProperty(obj, prop_name, twenty_three, NONE, SLOPPY).Check(); 630 CHECK_EQ(Smi::FromInt(23), 631 *Object::GetProperty(obj, prop_name).ToHandleChecked()); 632 // Check that we can add properties to function objects. 633 JSReceiver::SetProperty( 634 function, prop_name, twenty_four, NONE, SLOPPY).Check(); 635 CHECK_EQ(Smi::FromInt(24), 636 *Object::GetProperty(function, prop_name).ToHandleChecked()); 637 } 638 639 640 TEST(ObjectProperties) { 641 CcTest::InitializeVM(); 642 Isolate* isolate = CcTest::i_isolate(); 643 Factory* factory = isolate->factory(); 644 645 v8::HandleScope sc(CcTest::isolate()); 646 Handle<String> object_string(String::cast(CcTest::heap()->Object_string())); 647 Handle<Object> object = Object::GetProperty( 648 CcTest::i_isolate()->global_object(), object_string).ToHandleChecked(); 649 Handle<JSFunction> constructor = Handle<JSFunction>::cast(object); 650 Handle<JSObject> obj = factory->NewJSObject(constructor); 651 Handle<String> first = factory->InternalizeUtf8String("first"); 652 Handle<String> second = factory->InternalizeUtf8String("second"); 653 654 Handle<Smi> one(Smi::FromInt(1), isolate); 655 Handle<Smi> two(Smi::FromInt(2), isolate); 656 657 // check for empty 658 CHECK(!JSReceiver::HasOwnProperty(obj, first)); 659 660 // add first 661 JSReceiver::SetProperty(obj, first, one, NONE, SLOPPY).Check(); 662 CHECK(JSReceiver::HasOwnProperty(obj, first)); 663 664 // delete first 665 JSReceiver::DeleteProperty(obj, first, JSReceiver::NORMAL_DELETION).Check(); 666 CHECK(!JSReceiver::HasOwnProperty(obj, first)); 667 668 // add first and then second 669 JSReceiver::SetProperty(obj, first, one, NONE, SLOPPY).Check(); 670 JSReceiver::SetProperty(obj, second, two, NONE, SLOPPY).Check(); 671 CHECK(JSReceiver::HasOwnProperty(obj, first)); 672 CHECK(JSReceiver::HasOwnProperty(obj, second)); 673 674 // delete first and then second 675 JSReceiver::DeleteProperty(obj, first, JSReceiver::NORMAL_DELETION).Check(); 676 CHECK(JSReceiver::HasOwnProperty(obj, second)); 677 JSReceiver::DeleteProperty(obj, second, JSReceiver::NORMAL_DELETION).Check(); 678 CHECK(!JSReceiver::HasOwnProperty(obj, first)); 679 CHECK(!JSReceiver::HasOwnProperty(obj, second)); 680 681 // add first and then second 682 JSReceiver::SetProperty(obj, first, one, NONE, SLOPPY).Check(); 683 JSReceiver::SetProperty(obj, second, two, NONE, SLOPPY).Check(); 684 CHECK(JSReceiver::HasOwnProperty(obj, first)); 685 CHECK(JSReceiver::HasOwnProperty(obj, second)); 686 687 // delete second and then first 688 JSReceiver::DeleteProperty(obj, second, JSReceiver::NORMAL_DELETION).Check(); 689 CHECK(JSReceiver::HasOwnProperty(obj, first)); 690 JSReceiver::DeleteProperty(obj, first, JSReceiver::NORMAL_DELETION).Check(); 691 CHECK(!JSReceiver::HasOwnProperty(obj, first)); 692 CHECK(!JSReceiver::HasOwnProperty(obj, second)); 693 694 // check string and internalized string match 695 const char* string1 = "fisk"; 696 Handle<String> s1 = factory->NewStringFromAsciiChecked(string1); 697 JSReceiver::SetProperty(obj, s1, one, NONE, SLOPPY).Check(); 698 Handle<String> s1_string = factory->InternalizeUtf8String(string1); 699 CHECK(JSReceiver::HasOwnProperty(obj, s1_string)); 700 701 // check internalized string and string match 702 const char* string2 = "fugl"; 703 Handle<String> s2_string = factory->InternalizeUtf8String(string2); 704 JSReceiver::SetProperty(obj, s2_string, one, NONE, SLOPPY).Check(); 705 Handle<String> s2 = factory->NewStringFromAsciiChecked(string2); 706 CHECK(JSReceiver::HasOwnProperty(obj, s2)); 707 } 708 709 710 TEST(JSObjectMaps) { 711 CcTest::InitializeVM(); 712 Isolate* isolate = CcTest::i_isolate(); 713 Factory* factory = isolate->factory(); 714 715 v8::HandleScope sc(CcTest::isolate()); 716 Handle<String> name = factory->InternalizeUtf8String("theFunction"); 717 Handle<JSFunction> function = factory->NewFunction(name); 718 719 Handle<String> prop_name = factory->InternalizeUtf8String("theSlot"); 720 Handle<JSObject> obj = factory->NewJSObject(function); 721 Handle<Map> initial_map(function->initial_map()); 722 723 // Set a propery 724 Handle<Smi> twenty_three(Smi::FromInt(23), isolate); 725 JSReceiver::SetProperty(obj, prop_name, twenty_three, NONE, SLOPPY).Check(); 726 CHECK_EQ(Smi::FromInt(23), 727 *Object::GetProperty(obj, prop_name).ToHandleChecked()); 728 729 // Check the map has changed 730 CHECK(*initial_map != obj->map()); 731 } 732 733 734 TEST(JSArray) { 735 CcTest::InitializeVM(); 736 Isolate* isolate = CcTest::i_isolate(); 737 Factory* factory = isolate->factory(); 738 739 v8::HandleScope sc(CcTest::isolate()); 740 Handle<String> name = factory->InternalizeUtf8String("Array"); 741 Handle<Object> fun_obj = Object::GetProperty( 742 CcTest::i_isolate()->global_object(), name).ToHandleChecked(); 743 Handle<JSFunction> function = Handle<JSFunction>::cast(fun_obj); 744 745 // Allocate the object. 746 Handle<Object> element; 747 Handle<JSObject> object = factory->NewJSObject(function); 748 Handle<JSArray> array = Handle<JSArray>::cast(object); 749 // We just initialized the VM, no heap allocation failure yet. 750 JSArray::Initialize(array, 0); 751 752 // Set array length to 0. 753 JSArray::SetElementsLength(array, handle(Smi::FromInt(0), isolate)).Check(); 754 CHECK_EQ(Smi::FromInt(0), array->length()); 755 // Must be in fast mode. 756 CHECK(array->HasFastSmiOrObjectElements()); 757 758 // array[length] = name. 759 JSReceiver::SetElement(array, 0, name, NONE, SLOPPY).Check(); 760 CHECK_EQ(Smi::FromInt(1), array->length()); 761 element = i::Object::GetElement(isolate, array, 0).ToHandleChecked(); 762 CHECK_EQ(*element, *name); 763 764 // Set array length with larger than smi value. 765 Handle<Object> length = 766 factory->NewNumberFromUint(static_cast<uint32_t>(Smi::kMaxValue) + 1); 767 JSArray::SetElementsLength(array, length).Check(); 768 769 uint32_t int_length = 0; 770 CHECK(length->ToArrayIndex(&int_length)); 771 CHECK_EQ(*length, array->length()); 772 CHECK(array->HasDictionaryElements()); // Must be in slow mode. 773 774 // array[length] = name. 775 JSReceiver::SetElement(array, int_length, name, NONE, SLOPPY).Check(); 776 uint32_t new_int_length = 0; 777 CHECK(array->length()->ToArrayIndex(&new_int_length)); 778 CHECK_EQ(static_cast<double>(int_length), new_int_length - 1); 779 element = Object::GetElement(isolate, array, int_length).ToHandleChecked(); 780 CHECK_EQ(*element, *name); 781 element = Object::GetElement(isolate, array, 0).ToHandleChecked(); 782 CHECK_EQ(*element, *name); 783 } 784 785 786 TEST(JSObjectCopy) { 787 CcTest::InitializeVM(); 788 Isolate* isolate = CcTest::i_isolate(); 789 Factory* factory = isolate->factory(); 790 791 v8::HandleScope sc(CcTest::isolate()); 792 Handle<String> object_string(String::cast(CcTest::heap()->Object_string())); 793 Handle<Object> object = Object::GetProperty( 794 CcTest::i_isolate()->global_object(), object_string).ToHandleChecked(); 795 Handle<JSFunction> constructor = Handle<JSFunction>::cast(object); 796 Handle<JSObject> obj = factory->NewJSObject(constructor); 797 Handle<String> first = factory->InternalizeUtf8String("first"); 798 Handle<String> second = factory->InternalizeUtf8String("second"); 799 800 Handle<Smi> one(Smi::FromInt(1), isolate); 801 Handle<Smi> two(Smi::FromInt(2), isolate); 802 803 JSReceiver::SetProperty(obj, first, one, NONE, SLOPPY).Check(); 804 JSReceiver::SetProperty(obj, second, two, NONE, SLOPPY).Check(); 805 806 JSReceiver::SetElement(obj, 0, first, NONE, SLOPPY).Check(); 807 JSReceiver::SetElement(obj, 1, second, NONE, SLOPPY).Check(); 808 809 // Make the clone. 810 Handle<Object> value1, value2; 811 Handle<JSObject> clone = factory->CopyJSObject(obj); 812 CHECK(!clone.is_identical_to(obj)); 813 814 value1 = Object::GetElement(isolate, obj, 0).ToHandleChecked(); 815 value2 = Object::GetElement(isolate, clone, 0).ToHandleChecked(); 816 CHECK_EQ(*value1, *value2); 817 value1 = Object::GetElement(isolate, obj, 1).ToHandleChecked(); 818 value2 = Object::GetElement(isolate, clone, 1).ToHandleChecked(); 819 CHECK_EQ(*value1, *value2); 820 821 value1 = Object::GetProperty(obj, first).ToHandleChecked(); 822 value2 = Object::GetProperty(clone, first).ToHandleChecked(); 823 CHECK_EQ(*value1, *value2); 824 value1 = Object::GetProperty(obj, second).ToHandleChecked(); 825 value2 = Object::GetProperty(clone, second).ToHandleChecked(); 826 CHECK_EQ(*value1, *value2); 827 828 // Flip the values. 829 JSReceiver::SetProperty(clone, first, two, NONE, SLOPPY).Check(); 830 JSReceiver::SetProperty(clone, second, one, NONE, SLOPPY).Check(); 831 832 JSReceiver::SetElement(clone, 0, second, NONE, SLOPPY).Check(); 833 JSReceiver::SetElement(clone, 1, first, NONE, SLOPPY).Check(); 834 835 value1 = Object::GetElement(isolate, obj, 1).ToHandleChecked(); 836 value2 = Object::GetElement(isolate, clone, 0).ToHandleChecked(); 837 CHECK_EQ(*value1, *value2); 838 value1 = Object::GetElement(isolate, obj, 0).ToHandleChecked(); 839 value2 = Object::GetElement(isolate, clone, 1).ToHandleChecked(); 840 CHECK_EQ(*value1, *value2); 841 842 value1 = Object::GetProperty(obj, second).ToHandleChecked(); 843 value2 = Object::GetProperty(clone, first).ToHandleChecked(); 844 CHECK_EQ(*value1, *value2); 845 value1 = Object::GetProperty(obj, first).ToHandleChecked(); 846 value2 = Object::GetProperty(clone, second).ToHandleChecked(); 847 CHECK_EQ(*value1, *value2); 848 } 849 850 851 TEST(StringAllocation) { 852 CcTest::InitializeVM(); 853 Isolate* isolate = CcTest::i_isolate(); 854 Factory* factory = isolate->factory(); 855 856 const unsigned char chars[] = { 0xe5, 0xa4, 0xa7 }; 857 for (int length = 0; length < 100; length++) { 858 v8::HandleScope scope(CcTest::isolate()); 859 char* non_ascii = NewArray<char>(3 * length + 1); 860 char* ascii = NewArray<char>(length + 1); 861 non_ascii[3 * length] = 0; 862 ascii[length] = 0; 863 for (int i = 0; i < length; i++) { 864 ascii[i] = 'a'; 865 non_ascii[3 * i] = chars[0]; 866 non_ascii[3 * i + 1] = chars[1]; 867 non_ascii[3 * i + 2] = chars[2]; 868 } 869 Handle<String> non_ascii_sym = 870 factory->InternalizeUtf8String( 871 Vector<const char>(non_ascii, 3 * length)); 872 CHECK_EQ(length, non_ascii_sym->length()); 873 Handle<String> ascii_sym = 874 factory->InternalizeOneByteString(OneByteVector(ascii, length)); 875 CHECK_EQ(length, ascii_sym->length()); 876 Handle<String> non_ascii_str = factory->NewStringFromUtf8( 877 Vector<const char>(non_ascii, 3 * length)).ToHandleChecked(); 878 non_ascii_str->Hash(); 879 CHECK_EQ(length, non_ascii_str->length()); 880 Handle<String> ascii_str = factory->NewStringFromUtf8( 881 Vector<const char>(ascii, length)).ToHandleChecked(); 882 ascii_str->Hash(); 883 CHECK_EQ(length, ascii_str->length()); 884 DeleteArray(non_ascii); 885 DeleteArray(ascii); 886 } 887 } 888 889 890 static int ObjectsFoundInHeap(Heap* heap, Handle<Object> objs[], int size) { 891 // Count the number of objects found in the heap. 892 int found_count = 0; 893 HeapIterator iterator(heap); 894 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 895 for (int i = 0; i < size; i++) { 896 if (*objs[i] == obj) { 897 found_count++; 898 } 899 } 900 } 901 return found_count; 902 } 903 904 905 TEST(Iteration) { 906 CcTest::InitializeVM(); 907 Isolate* isolate = CcTest::i_isolate(); 908 Factory* factory = isolate->factory(); 909 v8::HandleScope scope(CcTest::isolate()); 910 911 // Array of objects to scan haep for. 912 const int objs_count = 6; 913 Handle<Object> objs[objs_count]; 914 int next_objs_index = 0; 915 916 // Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE 917 objs[next_objs_index++] = factory->NewJSArray(10); 918 objs[next_objs_index++] = factory->NewJSArray(10, 919 FAST_HOLEY_ELEMENTS, 920 TENURED); 921 922 // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE 923 objs[next_objs_index++] = 924 factory->NewStringFromStaticAscii("abcdefghij"); 925 objs[next_objs_index++] = 926 factory->NewStringFromStaticAscii("abcdefghij", TENURED); 927 928 // Allocate a large string (for large object space). 929 int large_size = Page::kMaxRegularHeapObjectSize + 1; 930 char* str = new char[large_size]; 931 for (int i = 0; i < large_size - 1; ++i) str[i] = 'a'; 932 str[large_size - 1] = '\0'; 933 objs[next_objs_index++] = factory->NewStringFromAsciiChecked(str, TENURED); 934 delete[] str; 935 936 // Add a Map object to look for. 937 objs[next_objs_index++] = Handle<Map>(HeapObject::cast(*objs[0])->map()); 938 939 CHECK_EQ(objs_count, next_objs_index); 940 CHECK_EQ(objs_count, ObjectsFoundInHeap(CcTest::heap(), objs, objs_count)); 941 } 942 943 944 TEST(EmptyHandleEscapeFrom) { 945 CcTest::InitializeVM(); 946 947 v8::HandleScope scope(CcTest::isolate()); 948 Handle<JSObject> runaway; 949 950 { 951 v8::EscapableHandleScope nested(CcTest::isolate()); 952 Handle<JSObject> empty; 953 runaway = empty.EscapeFrom(&nested); 954 } 955 956 CHECK(runaway.is_null()); 957 } 958 959 960 static int LenFromSize(int size) { 961 return (size - FixedArray::kHeaderSize) / kPointerSize; 962 } 963 964 965 TEST(Regression39128) { 966 // Test case for crbug.com/39128. 967 CcTest::InitializeVM(); 968 Isolate* isolate = CcTest::i_isolate(); 969 TestHeap* heap = CcTest::test_heap(); 970 971 // Increase the chance of 'bump-the-pointer' allocation in old space. 972 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 973 974 v8::HandleScope scope(CcTest::isolate()); 975 976 // The plan: create JSObject which references objects in new space. 977 // Then clone this object (forcing it to go into old space) and check 978 // that region dirty marks are updated correctly. 979 980 // Step 1: prepare a map for the object. We add 1 inobject property to it. 981 Handle<JSFunction> object_ctor( 982 CcTest::i_isolate()->native_context()->object_function()); 983 CHECK(object_ctor->has_initial_map()); 984 // Create a map with single inobject property. 985 Handle<Map> my_map = Map::Create(object_ctor, 1); 986 int n_properties = my_map->inobject_properties(); 987 CHECK_GT(n_properties, 0); 988 989 int object_size = my_map->instance_size(); 990 991 // Step 2: allocate a lot of objects so to almost fill new space: we need 992 // just enough room to allocate JSObject and thus fill the newspace. 993 994 int allocation_amount = Min(FixedArray::kMaxSize, 995 Page::kMaxRegularHeapObjectSize + kPointerSize); 996 int allocation_len = LenFromSize(allocation_amount); 997 NewSpace* new_space = heap->new_space(); 998 Address* top_addr = new_space->allocation_top_address(); 999 Address* limit_addr = new_space->allocation_limit_address(); 1000 while ((*limit_addr - *top_addr) > allocation_amount) { 1001 CHECK(!heap->always_allocate()); 1002 Object* array = heap->AllocateFixedArray(allocation_len).ToObjectChecked(); 1003 CHECK(new_space->Contains(array)); 1004 } 1005 1006 // Step 3: now allocate fixed array and JSObject to fill the whole new space. 1007 int to_fill = static_cast<int>(*limit_addr - *top_addr - object_size); 1008 int fixed_array_len = LenFromSize(to_fill); 1009 CHECK(fixed_array_len < FixedArray::kMaxLength); 1010 1011 CHECK(!heap->always_allocate()); 1012 Object* array = heap->AllocateFixedArray(fixed_array_len).ToObjectChecked(); 1013 CHECK(new_space->Contains(array)); 1014 1015 Object* object = heap->AllocateJSObjectFromMap(*my_map).ToObjectChecked(); 1016 CHECK(new_space->Contains(object)); 1017 JSObject* jsobject = JSObject::cast(object); 1018 CHECK_EQ(0, FixedArray::cast(jsobject->elements())->length()); 1019 CHECK_EQ(0, jsobject->properties()->length()); 1020 // Create a reference to object in new space in jsobject. 1021 FieldIndex index = FieldIndex::ForInObjectOffset( 1022 JSObject::kHeaderSize - kPointerSize); 1023 jsobject->FastPropertyAtPut(index, array); 1024 1025 CHECK_EQ(0, static_cast<int>(*limit_addr - *top_addr)); 1026 1027 // Step 4: clone jsobject, but force always allocate first to create a clone 1028 // in old pointer space. 1029 Address old_pointer_space_top = heap->old_pointer_space()->top(); 1030 AlwaysAllocateScope aa_scope(isolate); 1031 Object* clone_obj = heap->CopyJSObject(jsobject).ToObjectChecked(); 1032 JSObject* clone = JSObject::cast(clone_obj); 1033 if (clone->address() != old_pointer_space_top) { 1034 // Alas, got allocated from free list, we cannot do checks. 1035 return; 1036 } 1037 CHECK(heap->old_pointer_space()->Contains(clone->address())); 1038 } 1039 1040 1041 TEST(TestCodeFlushing) { 1042 // If we do not flush code this test is invalid. 1043 if (!FLAG_flush_code) return; 1044 i::FLAG_allow_natives_syntax = true; 1045 i::FLAG_optimize_for_size = false; 1046 CcTest::InitializeVM(); 1047 Isolate* isolate = CcTest::i_isolate(); 1048 Factory* factory = isolate->factory(); 1049 v8::HandleScope scope(CcTest::isolate()); 1050 const char* source = "function foo() {" 1051 " var x = 42;" 1052 " var y = 42;" 1053 " var z = x + y;" 1054 "};" 1055 "foo()"; 1056 Handle<String> foo_name = factory->InternalizeUtf8String("foo"); 1057 1058 // This compile will add the code to the compilation cache. 1059 { v8::HandleScope scope(CcTest::isolate()); 1060 CompileRun(source); 1061 } 1062 1063 // Check function is compiled. 1064 Handle<Object> func_value = Object::GetProperty( 1065 CcTest::i_isolate()->global_object(), foo_name).ToHandleChecked(); 1066 CHECK(func_value->IsJSFunction()); 1067 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value); 1068 CHECK(function->shared()->is_compiled()); 1069 1070 // The code will survive at least two GCs. 1071 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1072 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1073 CHECK(function->shared()->is_compiled()); 1074 1075 // Simulate several GCs that use full marking. 1076 const int kAgingThreshold = 6; 1077 for (int i = 0; i < kAgingThreshold; i++) { 1078 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1079 } 1080 1081 // foo should no longer be in the compilation cache 1082 CHECK(!function->shared()->is_compiled() || function->IsOptimized()); 1083 CHECK(!function->is_compiled() || function->IsOptimized()); 1084 // Call foo to get it recompiled. 1085 CompileRun("foo()"); 1086 CHECK(function->shared()->is_compiled()); 1087 CHECK(function->is_compiled()); 1088 } 1089 1090 1091 TEST(TestCodeFlushingPreAged) { 1092 // If we do not flush code this test is invalid. 1093 if (!FLAG_flush_code) return; 1094 i::FLAG_allow_natives_syntax = true; 1095 i::FLAG_optimize_for_size = true; 1096 CcTest::InitializeVM(); 1097 Isolate* isolate = CcTest::i_isolate(); 1098 Factory* factory = isolate->factory(); 1099 v8::HandleScope scope(CcTest::isolate()); 1100 const char* source = "function foo() {" 1101 " var x = 42;" 1102 " var y = 42;" 1103 " var z = x + y;" 1104 "};" 1105 "foo()"; 1106 Handle<String> foo_name = factory->InternalizeUtf8String("foo"); 1107 1108 // Compile foo, but don't run it. 1109 { v8::HandleScope scope(CcTest::isolate()); 1110 CompileRun(source); 1111 } 1112 1113 // Check function is compiled. 1114 Handle<Object> func_value = 1115 Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked(); 1116 CHECK(func_value->IsJSFunction()); 1117 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value); 1118 CHECK(function->shared()->is_compiled()); 1119 1120 // The code has been run so will survive at least one GC. 1121 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1122 CHECK(function->shared()->is_compiled()); 1123 1124 // The code was only run once, so it should be pre-aged and collected on the 1125 // next GC. 1126 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1127 CHECK(!function->shared()->is_compiled() || function->IsOptimized()); 1128 1129 // Execute the function again twice, and ensure it is reset to the young age. 1130 { v8::HandleScope scope(CcTest::isolate()); 1131 CompileRun("foo();" 1132 "foo();"); 1133 } 1134 1135 // The code will survive at least two GC now that it is young again. 1136 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1137 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1138 CHECK(function->shared()->is_compiled()); 1139 1140 // Simulate several GCs that use full marking. 1141 const int kAgingThreshold = 6; 1142 for (int i = 0; i < kAgingThreshold; i++) { 1143 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1144 } 1145 1146 // foo should no longer be in the compilation cache 1147 CHECK(!function->shared()->is_compiled() || function->IsOptimized()); 1148 CHECK(!function->is_compiled() || function->IsOptimized()); 1149 // Call foo to get it recompiled. 1150 CompileRun("foo()"); 1151 CHECK(function->shared()->is_compiled()); 1152 CHECK(function->is_compiled()); 1153 } 1154 1155 1156 TEST(TestCodeFlushingIncremental) { 1157 // If we do not flush code this test is invalid. 1158 if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; 1159 i::FLAG_allow_natives_syntax = true; 1160 i::FLAG_optimize_for_size = false; 1161 CcTest::InitializeVM(); 1162 Isolate* isolate = CcTest::i_isolate(); 1163 Factory* factory = isolate->factory(); 1164 v8::HandleScope scope(CcTest::isolate()); 1165 const char* source = "function foo() {" 1166 " var x = 42;" 1167 " var y = 42;" 1168 " var z = x + y;" 1169 "};" 1170 "foo()"; 1171 Handle<String> foo_name = factory->InternalizeUtf8String("foo"); 1172 1173 // This compile will add the code to the compilation cache. 1174 { v8::HandleScope scope(CcTest::isolate()); 1175 CompileRun(source); 1176 } 1177 1178 // Check function is compiled. 1179 Handle<Object> func_value = 1180 Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked(); 1181 CHECK(func_value->IsJSFunction()); 1182 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value); 1183 CHECK(function->shared()->is_compiled()); 1184 1185 // The code will survive at least two GCs. 1186 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1187 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1188 CHECK(function->shared()->is_compiled()); 1189 1190 // Simulate several GCs that use incremental marking. 1191 const int kAgingThreshold = 6; 1192 for (int i = 0; i < kAgingThreshold; i++) { 1193 SimulateIncrementalMarking(); 1194 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1195 } 1196 CHECK(!function->shared()->is_compiled() || function->IsOptimized()); 1197 CHECK(!function->is_compiled() || function->IsOptimized()); 1198 1199 // This compile will compile the function again. 1200 { v8::HandleScope scope(CcTest::isolate()); 1201 CompileRun("foo();"); 1202 } 1203 1204 // Simulate several GCs that use incremental marking but make sure 1205 // the loop breaks once the function is enqueued as a candidate. 1206 for (int i = 0; i < kAgingThreshold; i++) { 1207 SimulateIncrementalMarking(); 1208 if (!function->next_function_link()->IsUndefined()) break; 1209 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1210 } 1211 1212 // Force optimization while incremental marking is active and while 1213 // the function is enqueued as a candidate. 1214 { v8::HandleScope scope(CcTest::isolate()); 1215 CompileRun("%OptimizeFunctionOnNextCall(foo); foo();"); 1216 } 1217 1218 // Simulate one final GC to make sure the candidate queue is sane. 1219 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1220 CHECK(function->shared()->is_compiled() || !function->IsOptimized()); 1221 CHECK(function->is_compiled() || !function->IsOptimized()); 1222 } 1223 1224 1225 TEST(TestCodeFlushingIncrementalScavenge) { 1226 // If we do not flush code this test is invalid. 1227 if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; 1228 i::FLAG_allow_natives_syntax = true; 1229 i::FLAG_optimize_for_size = false; 1230 CcTest::InitializeVM(); 1231 Isolate* isolate = CcTest::i_isolate(); 1232 Factory* factory = isolate->factory(); 1233 v8::HandleScope scope(CcTest::isolate()); 1234 const char* source = "var foo = function() {" 1235 " var x = 42;" 1236 " var y = 42;" 1237 " var z = x + y;" 1238 "};" 1239 "foo();" 1240 "var bar = function() {" 1241 " var x = 23;" 1242 "};" 1243 "bar();"; 1244 Handle<String> foo_name = factory->InternalizeUtf8String("foo"); 1245 Handle<String> bar_name = factory->InternalizeUtf8String("bar"); 1246 1247 // Perfrom one initial GC to enable code flushing. 1248 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1249 1250 // This compile will add the code to the compilation cache. 1251 { v8::HandleScope scope(CcTest::isolate()); 1252 CompileRun(source); 1253 } 1254 1255 // Check functions are compiled. 1256 Handle<Object> func_value = 1257 Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked(); 1258 CHECK(func_value->IsJSFunction()); 1259 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value); 1260 CHECK(function->shared()->is_compiled()); 1261 Handle<Object> func_value2 = 1262 Object::GetProperty(isolate->global_object(), bar_name).ToHandleChecked(); 1263 CHECK(func_value2->IsJSFunction()); 1264 Handle<JSFunction> function2 = Handle<JSFunction>::cast(func_value2); 1265 CHECK(function2->shared()->is_compiled()); 1266 1267 // Clear references to functions so that one of them can die. 1268 { v8::HandleScope scope(CcTest::isolate()); 1269 CompileRun("foo = 0; bar = 0;"); 1270 } 1271 1272 // Bump the code age so that flushing is triggered while the function 1273 // object is still located in new-space. 1274 const int kAgingThreshold = 6; 1275 for (int i = 0; i < kAgingThreshold; i++) { 1276 function->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 1277 function2->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 1278 } 1279 1280 // Simulate incremental marking so that the functions are enqueued as 1281 // code flushing candidates. Then kill one of the functions. Finally 1282 // perform a scavenge while incremental marking is still running. 1283 SimulateIncrementalMarking(); 1284 *function2.location() = NULL; 1285 CcTest::heap()->CollectGarbage(NEW_SPACE, "test scavenge while marking"); 1286 1287 // Simulate one final GC to make sure the candidate queue is sane. 1288 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1289 CHECK(!function->shared()->is_compiled() || function->IsOptimized()); 1290 CHECK(!function->is_compiled() || function->IsOptimized()); 1291 } 1292 1293 1294 TEST(TestCodeFlushingIncrementalAbort) { 1295 // If we do not flush code this test is invalid. 1296 if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; 1297 i::FLAG_allow_natives_syntax = true; 1298 i::FLAG_optimize_for_size = false; 1299 CcTest::InitializeVM(); 1300 Isolate* isolate = CcTest::i_isolate(); 1301 Factory* factory = isolate->factory(); 1302 Heap* heap = isolate->heap(); 1303 v8::HandleScope scope(CcTest::isolate()); 1304 const char* source = "function foo() {" 1305 " var x = 42;" 1306 " var y = 42;" 1307 " var z = x + y;" 1308 "};" 1309 "foo()"; 1310 Handle<String> foo_name = factory->InternalizeUtf8String("foo"); 1311 1312 // This compile will add the code to the compilation cache. 1313 { v8::HandleScope scope(CcTest::isolate()); 1314 CompileRun(source); 1315 } 1316 1317 // Check function is compiled. 1318 Handle<Object> func_value = 1319 Object::GetProperty(isolate->global_object(), foo_name).ToHandleChecked(); 1320 CHECK(func_value->IsJSFunction()); 1321 Handle<JSFunction> function = Handle<JSFunction>::cast(func_value); 1322 CHECK(function->shared()->is_compiled()); 1323 1324 // The code will survive at least two GCs. 1325 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1326 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 1327 CHECK(function->shared()->is_compiled()); 1328 1329 // Bump the code age so that flushing is triggered. 1330 const int kAgingThreshold = 6; 1331 for (int i = 0; i < kAgingThreshold; i++) { 1332 function->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 1333 } 1334 1335 // Simulate incremental marking so that the function is enqueued as 1336 // code flushing candidate. 1337 SimulateIncrementalMarking(); 1338 1339 // Enable the debugger and add a breakpoint while incremental marking 1340 // is running so that incremental marking aborts and code flushing is 1341 // disabled. 1342 int position = 0; 1343 Handle<Object> breakpoint_object(Smi::FromInt(0), isolate); 1344 isolate->debug()->SetBreakPoint(function, breakpoint_object, &position); 1345 isolate->debug()->ClearAllBreakPoints(); 1346 1347 // Force optimization now that code flushing is disabled. 1348 { v8::HandleScope scope(CcTest::isolate()); 1349 CompileRun("%OptimizeFunctionOnNextCall(foo); foo();"); 1350 } 1351 1352 // Simulate one final GC to make sure the candidate queue is sane. 1353 heap->CollectAllGarbage(Heap::kNoGCFlags); 1354 CHECK(function->shared()->is_compiled() || !function->IsOptimized()); 1355 CHECK(function->is_compiled() || !function->IsOptimized()); 1356 } 1357 1358 1359 // Count the number of native contexts in the weak list of native contexts. 1360 int CountNativeContexts() { 1361 int count = 0; 1362 Object* object = CcTest::heap()->native_contexts_list(); 1363 while (!object->IsUndefined()) { 1364 count++; 1365 object = Context::cast(object)->get(Context::NEXT_CONTEXT_LINK); 1366 } 1367 return count; 1368 } 1369 1370 1371 // Count the number of user functions in the weak list of optimized 1372 // functions attached to a native context. 1373 static int CountOptimizedUserFunctions(v8::Handle<v8::Context> context) { 1374 int count = 0; 1375 Handle<Context> icontext = v8::Utils::OpenHandle(*context); 1376 Object* object = icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST); 1377 while (object->IsJSFunction() && !JSFunction::cast(object)->IsBuiltin()) { 1378 count++; 1379 object = JSFunction::cast(object)->next_function_link(); 1380 } 1381 return count; 1382 } 1383 1384 1385 TEST(TestInternalWeakLists) { 1386 v8::V8::Initialize(); 1387 1388 // Some flags turn Scavenge collections into Mark-sweep collections 1389 // and hence are incompatible with this test case. 1390 if (FLAG_gc_global || FLAG_stress_compaction) return; 1391 1392 static const int kNumTestContexts = 10; 1393 1394 Isolate* isolate = CcTest::i_isolate(); 1395 Heap* heap = isolate->heap(); 1396 HandleScope scope(isolate); 1397 v8::Handle<v8::Context> ctx[kNumTestContexts]; 1398 1399 CHECK_EQ(0, CountNativeContexts()); 1400 1401 // Create a number of global contests which gets linked together. 1402 for (int i = 0; i < kNumTestContexts; i++) { 1403 ctx[i] = v8::Context::New(CcTest::isolate()); 1404 1405 // Collect garbage that might have been created by one of the 1406 // installed extensions. 1407 isolate->compilation_cache()->Clear(); 1408 heap->CollectAllGarbage(Heap::kNoGCFlags); 1409 1410 bool opt = (FLAG_always_opt && isolate->use_crankshaft()); 1411 1412 CHECK_EQ(i + 1, CountNativeContexts()); 1413 1414 ctx[i]->Enter(); 1415 1416 // Create a handle scope so no function objects get stuch in the outer 1417 // handle scope 1418 HandleScope scope(isolate); 1419 const char* source = "function f1() { };" 1420 "function f2() { };" 1421 "function f3() { };" 1422 "function f4() { };" 1423 "function f5() { };"; 1424 CompileRun(source); 1425 CHECK_EQ(0, CountOptimizedUserFunctions(ctx[i])); 1426 CompileRun("f1()"); 1427 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctions(ctx[i])); 1428 CompileRun("f2()"); 1429 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i])); 1430 CompileRun("f3()"); 1431 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i])); 1432 CompileRun("f4()"); 1433 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); 1434 CompileRun("f5()"); 1435 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[i])); 1436 1437 // Remove function f1, and 1438 CompileRun("f1=null"); 1439 1440 // Scavenge treats these references as strong. 1441 for (int j = 0; j < 10; j++) { 1442 CcTest::heap()->CollectGarbage(NEW_SPACE); 1443 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[i])); 1444 } 1445 1446 // Mark compact handles the weak references. 1447 isolate->compilation_cache()->Clear(); 1448 heap->CollectAllGarbage(Heap::kNoGCFlags); 1449 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); 1450 1451 // Get rid of f3 and f5 in the same way. 1452 CompileRun("f3=null"); 1453 for (int j = 0; j < 10; j++) { 1454 CcTest::heap()->CollectGarbage(NEW_SPACE); 1455 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); 1456 } 1457 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1458 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i])); 1459 CompileRun("f5=null"); 1460 for (int j = 0; j < 10; j++) { 1461 CcTest::heap()->CollectGarbage(NEW_SPACE); 1462 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i])); 1463 } 1464 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1465 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i])); 1466 1467 ctx[i]->Exit(); 1468 } 1469 1470 // Force compilation cache cleanup. 1471 CcTest::heap()->NotifyContextDisposed(); 1472 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1473 1474 // Dispose the native contexts one by one. 1475 for (int i = 0; i < kNumTestContexts; i++) { 1476 // TODO(dcarney): is there a better way to do this? 1477 i::Object** unsafe = reinterpret_cast<i::Object**>(*ctx[i]); 1478 *unsafe = CcTest::heap()->undefined_value(); 1479 ctx[i].Clear(); 1480 1481 // Scavenge treats these references as strong. 1482 for (int j = 0; j < 10; j++) { 1483 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 1484 CHECK_EQ(kNumTestContexts - i, CountNativeContexts()); 1485 } 1486 1487 // Mark compact handles the weak references. 1488 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1489 CHECK_EQ(kNumTestContexts - i - 1, CountNativeContexts()); 1490 } 1491 1492 CHECK_EQ(0, CountNativeContexts()); 1493 } 1494 1495 1496 // Count the number of native contexts in the weak list of native contexts 1497 // causing a GC after the specified number of elements. 1498 static int CountNativeContextsWithGC(Isolate* isolate, int n) { 1499 Heap* heap = isolate->heap(); 1500 int count = 0; 1501 Handle<Object> object(heap->native_contexts_list(), isolate); 1502 while (!object->IsUndefined()) { 1503 count++; 1504 if (count == n) heap->CollectAllGarbage(Heap::kNoGCFlags); 1505 object = 1506 Handle<Object>(Context::cast(*object)->get(Context::NEXT_CONTEXT_LINK), 1507 isolate); 1508 } 1509 return count; 1510 } 1511 1512 1513 // Count the number of user functions in the weak list of optimized 1514 // functions attached to a native context causing a GC after the 1515 // specified number of elements. 1516 static int CountOptimizedUserFunctionsWithGC(v8::Handle<v8::Context> context, 1517 int n) { 1518 int count = 0; 1519 Handle<Context> icontext = v8::Utils::OpenHandle(*context); 1520 Isolate* isolate = icontext->GetIsolate(); 1521 Handle<Object> object(icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST), 1522 isolate); 1523 while (object->IsJSFunction() && 1524 !Handle<JSFunction>::cast(object)->IsBuiltin()) { 1525 count++; 1526 if (count == n) isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); 1527 object = Handle<Object>( 1528 Object::cast(JSFunction::cast(*object)->next_function_link()), 1529 isolate); 1530 } 1531 return count; 1532 } 1533 1534 1535 TEST(TestInternalWeakListsTraverseWithGC) { 1536 v8::V8::Initialize(); 1537 Isolate* isolate = CcTest::i_isolate(); 1538 1539 static const int kNumTestContexts = 10; 1540 1541 HandleScope scope(isolate); 1542 v8::Handle<v8::Context> ctx[kNumTestContexts]; 1543 1544 CHECK_EQ(0, CountNativeContexts()); 1545 1546 // Create an number of contexts and check the length of the weak list both 1547 // with and without GCs while iterating the list. 1548 for (int i = 0; i < kNumTestContexts; i++) { 1549 ctx[i] = v8::Context::New(CcTest::isolate()); 1550 CHECK_EQ(i + 1, CountNativeContexts()); 1551 CHECK_EQ(i + 1, CountNativeContextsWithGC(isolate, i / 2 + 1)); 1552 } 1553 1554 bool opt = (FLAG_always_opt && isolate->use_crankshaft()); 1555 1556 // Compile a number of functions the length of the weak list of optimized 1557 // functions both with and without GCs while iterating the list. 1558 ctx[0]->Enter(); 1559 const char* source = "function f1() { };" 1560 "function f2() { };" 1561 "function f3() { };" 1562 "function f4() { };" 1563 "function f5() { };"; 1564 CompileRun(source); 1565 CHECK_EQ(0, CountOptimizedUserFunctions(ctx[0])); 1566 CompileRun("f1()"); 1567 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctions(ctx[0])); 1568 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1)); 1569 CompileRun("f2()"); 1570 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[0])); 1571 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1)); 1572 CompileRun("f3()"); 1573 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[0])); 1574 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1)); 1575 CompileRun("f4()"); 1576 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[0])); 1577 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 2)); 1578 CompileRun("f5()"); 1579 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[0])); 1580 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 4)); 1581 1582 ctx[0]->Exit(); 1583 } 1584 1585 1586 TEST(TestSizeOfObjects) { 1587 v8::V8::Initialize(); 1588 1589 // Get initial heap size after several full GCs, which will stabilize 1590 // the heap size and return with sweeping finished completely. 1591 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1592 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1593 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1594 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1595 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1596 MarkCompactCollector* collector = CcTest::heap()->mark_compact_collector(); 1597 if (collector->IsConcurrentSweepingInProgress()) { 1598 collector->WaitUntilSweepingCompleted(); 1599 } 1600 int initial_size = static_cast<int>(CcTest::heap()->SizeOfObjects()); 1601 1602 { 1603 // Allocate objects on several different old-space pages so that 1604 // concurrent sweeper threads will be busy sweeping the old space on 1605 // subsequent GC runs. 1606 AlwaysAllocateScope always_allocate(CcTest::i_isolate()); 1607 int filler_size = static_cast<int>(FixedArray::SizeFor(8192)); 1608 for (int i = 1; i <= 100; i++) { 1609 CcTest::test_heap()->AllocateFixedArray(8192, TENURED).ToObjectChecked(); 1610 CHECK_EQ(initial_size + i * filler_size, 1611 static_cast<int>(CcTest::heap()->SizeOfObjects())); 1612 } 1613 } 1614 1615 // The heap size should go back to initial size after a full GC, even 1616 // though sweeping didn't finish yet. 1617 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1618 1619 // Normally sweeping would not be complete here, but no guarantees. 1620 1621 CHECK_EQ(initial_size, static_cast<int>(CcTest::heap()->SizeOfObjects())); 1622 1623 // Waiting for sweeper threads should not change heap size. 1624 if (collector->IsConcurrentSweepingInProgress()) { 1625 collector->WaitUntilSweepingCompleted(); 1626 } 1627 CHECK_EQ(initial_size, static_cast<int>(CcTest::heap()->SizeOfObjects())); 1628 } 1629 1630 1631 TEST(TestSizeOfObjectsVsHeapIteratorPrecision) { 1632 CcTest::InitializeVM(); 1633 HeapIterator iterator(CcTest::heap()); 1634 intptr_t size_of_objects_1 = CcTest::heap()->SizeOfObjects(); 1635 intptr_t size_of_objects_2 = 0; 1636 for (HeapObject* obj = iterator.next(); 1637 obj != NULL; 1638 obj = iterator.next()) { 1639 if (!obj->IsFreeSpace()) { 1640 size_of_objects_2 += obj->Size(); 1641 } 1642 } 1643 // Delta must be within 5% of the larger result. 1644 // TODO(gc): Tighten this up by distinguishing between byte 1645 // arrays that are real and those that merely mark free space 1646 // on the heap. 1647 if (size_of_objects_1 > size_of_objects_2) { 1648 intptr_t delta = size_of_objects_1 - size_of_objects_2; 1649 PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, " 1650 "Iterator: %" V8_PTR_PREFIX "d, " 1651 "delta: %" V8_PTR_PREFIX "d\n", 1652 size_of_objects_1, size_of_objects_2, delta); 1653 CHECK_GT(size_of_objects_1 / 20, delta); 1654 } else { 1655 intptr_t delta = size_of_objects_2 - size_of_objects_1; 1656 PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, " 1657 "Iterator: %" V8_PTR_PREFIX "d, " 1658 "delta: %" V8_PTR_PREFIX "d\n", 1659 size_of_objects_1, size_of_objects_2, delta); 1660 CHECK_GT(size_of_objects_2 / 20, delta); 1661 } 1662 } 1663 1664 1665 static void FillUpNewSpace(NewSpace* new_space) { 1666 // Fill up new space to the point that it is completely full. Make sure 1667 // that the scavenger does not undo the filling. 1668 Heap* heap = new_space->heap(); 1669 Isolate* isolate = heap->isolate(); 1670 Factory* factory = isolate->factory(); 1671 HandleScope scope(isolate); 1672 AlwaysAllocateScope always_allocate(isolate); 1673 intptr_t available = new_space->EffectiveCapacity() - new_space->Size(); 1674 intptr_t number_of_fillers = (available / FixedArray::SizeFor(32)) - 1; 1675 for (intptr_t i = 0; i < number_of_fillers; i++) { 1676 CHECK(heap->InNewSpace(*factory->NewFixedArray(32, NOT_TENURED))); 1677 } 1678 } 1679 1680 1681 TEST(GrowAndShrinkNewSpace) { 1682 CcTest::InitializeVM(); 1683 Heap* heap = CcTest::heap(); 1684 NewSpace* new_space = heap->new_space(); 1685 1686 if (heap->ReservedSemiSpaceSize() == heap->InitialSemiSpaceSize() || 1687 heap->MaxSemiSpaceSize() == heap->InitialSemiSpaceSize()) { 1688 // The max size cannot exceed the reserved size, since semispaces must be 1689 // always within the reserved space. We can't test new space growing and 1690 // shrinking if the reserved size is the same as the minimum (initial) size. 1691 return; 1692 } 1693 1694 // Explicitly growing should double the space capacity. 1695 intptr_t old_capacity, new_capacity; 1696 old_capacity = new_space->Capacity(); 1697 new_space->Grow(); 1698 new_capacity = new_space->Capacity(); 1699 CHECK(2 * old_capacity == new_capacity); 1700 1701 old_capacity = new_space->Capacity(); 1702 FillUpNewSpace(new_space); 1703 new_capacity = new_space->Capacity(); 1704 CHECK(old_capacity == new_capacity); 1705 1706 // Explicitly shrinking should not affect space capacity. 1707 old_capacity = new_space->Capacity(); 1708 new_space->Shrink(); 1709 new_capacity = new_space->Capacity(); 1710 CHECK(old_capacity == new_capacity); 1711 1712 // Let the scavenger empty the new space. 1713 heap->CollectGarbage(NEW_SPACE); 1714 CHECK_LE(new_space->Size(), old_capacity); 1715 1716 // Explicitly shrinking should halve the space capacity. 1717 old_capacity = new_space->Capacity(); 1718 new_space->Shrink(); 1719 new_capacity = new_space->Capacity(); 1720 CHECK(old_capacity == 2 * new_capacity); 1721 1722 // Consecutive shrinking should not affect space capacity. 1723 old_capacity = new_space->Capacity(); 1724 new_space->Shrink(); 1725 new_space->Shrink(); 1726 new_space->Shrink(); 1727 new_capacity = new_space->Capacity(); 1728 CHECK(old_capacity == new_capacity); 1729 } 1730 1731 1732 TEST(CollectingAllAvailableGarbageShrinksNewSpace) { 1733 CcTest::InitializeVM(); 1734 Heap* heap = CcTest::heap(); 1735 if (heap->ReservedSemiSpaceSize() == heap->InitialSemiSpaceSize() || 1736 heap->MaxSemiSpaceSize() == heap->InitialSemiSpaceSize()) { 1737 // The max size cannot exceed the reserved size, since semispaces must be 1738 // always within the reserved space. We can't test new space growing and 1739 // shrinking if the reserved size is the same as the minimum (initial) size. 1740 return; 1741 } 1742 1743 v8::HandleScope scope(CcTest::isolate()); 1744 NewSpace* new_space = heap->new_space(); 1745 intptr_t old_capacity, new_capacity; 1746 old_capacity = new_space->Capacity(); 1747 new_space->Grow(); 1748 new_capacity = new_space->Capacity(); 1749 CHECK(2 * old_capacity == new_capacity); 1750 FillUpNewSpace(new_space); 1751 heap->CollectAllAvailableGarbage(); 1752 new_capacity = new_space->Capacity(); 1753 CHECK(old_capacity == new_capacity); 1754 } 1755 1756 1757 static int NumberOfGlobalObjects() { 1758 int count = 0; 1759 HeapIterator iterator(CcTest::heap()); 1760 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 1761 if (obj->IsGlobalObject()) count++; 1762 } 1763 return count; 1764 } 1765 1766 1767 // Test that we don't embed maps from foreign contexts into 1768 // optimized code. 1769 TEST(LeakNativeContextViaMap) { 1770 i::FLAG_allow_natives_syntax = true; 1771 v8::Isolate* isolate = CcTest::isolate(); 1772 v8::HandleScope outer_scope(isolate); 1773 v8::Persistent<v8::Context> ctx1p; 1774 v8::Persistent<v8::Context> ctx2p; 1775 { 1776 v8::HandleScope scope(isolate); 1777 ctx1p.Reset(isolate, v8::Context::New(isolate)); 1778 ctx2p.Reset(isolate, v8::Context::New(isolate)); 1779 v8::Local<v8::Context>::New(isolate, ctx1p)->Enter(); 1780 } 1781 1782 CcTest::heap()->CollectAllAvailableGarbage(); 1783 CHECK_EQ(4, NumberOfGlobalObjects()); 1784 1785 { 1786 v8::HandleScope inner_scope(isolate); 1787 CompileRun("var v = {x: 42}"); 1788 v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p); 1789 v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p); 1790 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); 1791 ctx2->Enter(); 1792 ctx2->Global()->Set(v8_str("o"), v); 1793 v8::Local<v8::Value> res = CompileRun( 1794 "function f() { return o.x; }" 1795 "for (var i = 0; i < 10; ++i) f();" 1796 "%OptimizeFunctionOnNextCall(f);" 1797 "f();"); 1798 CHECK_EQ(42, res->Int32Value()); 1799 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(isolate, 0)); 1800 ctx2->Exit(); 1801 v8::Local<v8::Context>::New(isolate, ctx1)->Exit(); 1802 ctx1p.Reset(); 1803 v8::V8::ContextDisposedNotification(); 1804 } 1805 CcTest::heap()->CollectAllAvailableGarbage(); 1806 CHECK_EQ(2, NumberOfGlobalObjects()); 1807 ctx2p.Reset(); 1808 CcTest::heap()->CollectAllAvailableGarbage(); 1809 CHECK_EQ(0, NumberOfGlobalObjects()); 1810 } 1811 1812 1813 // Test that we don't embed functions from foreign contexts into 1814 // optimized code. 1815 TEST(LeakNativeContextViaFunction) { 1816 i::FLAG_allow_natives_syntax = true; 1817 v8::Isolate* isolate = CcTest::isolate(); 1818 v8::HandleScope outer_scope(isolate); 1819 v8::Persistent<v8::Context> ctx1p; 1820 v8::Persistent<v8::Context> ctx2p; 1821 { 1822 v8::HandleScope scope(isolate); 1823 ctx1p.Reset(isolate, v8::Context::New(isolate)); 1824 ctx2p.Reset(isolate, v8::Context::New(isolate)); 1825 v8::Local<v8::Context>::New(isolate, ctx1p)->Enter(); 1826 } 1827 1828 CcTest::heap()->CollectAllAvailableGarbage(); 1829 CHECK_EQ(4, NumberOfGlobalObjects()); 1830 1831 { 1832 v8::HandleScope inner_scope(isolate); 1833 CompileRun("var v = function() { return 42; }"); 1834 v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p); 1835 v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p); 1836 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); 1837 ctx2->Enter(); 1838 ctx2->Global()->Set(v8_str("o"), v); 1839 v8::Local<v8::Value> res = CompileRun( 1840 "function f(x) { return x(); }" 1841 "for (var i = 0; i < 10; ++i) f(o);" 1842 "%OptimizeFunctionOnNextCall(f);" 1843 "f(o);"); 1844 CHECK_EQ(42, res->Int32Value()); 1845 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(isolate, 0)); 1846 ctx2->Exit(); 1847 ctx1->Exit(); 1848 ctx1p.Reset(); 1849 v8::V8::ContextDisposedNotification(); 1850 } 1851 CcTest::heap()->CollectAllAvailableGarbage(); 1852 CHECK_EQ(2, NumberOfGlobalObjects()); 1853 ctx2p.Reset(); 1854 CcTest::heap()->CollectAllAvailableGarbage(); 1855 CHECK_EQ(0, NumberOfGlobalObjects()); 1856 } 1857 1858 1859 TEST(LeakNativeContextViaMapKeyed) { 1860 i::FLAG_allow_natives_syntax = true; 1861 v8::Isolate* isolate = CcTest::isolate(); 1862 v8::HandleScope outer_scope(isolate); 1863 v8::Persistent<v8::Context> ctx1p; 1864 v8::Persistent<v8::Context> ctx2p; 1865 { 1866 v8::HandleScope scope(isolate); 1867 ctx1p.Reset(isolate, v8::Context::New(isolate)); 1868 ctx2p.Reset(isolate, v8::Context::New(isolate)); 1869 v8::Local<v8::Context>::New(isolate, ctx1p)->Enter(); 1870 } 1871 1872 CcTest::heap()->CollectAllAvailableGarbage(); 1873 CHECK_EQ(4, NumberOfGlobalObjects()); 1874 1875 { 1876 v8::HandleScope inner_scope(isolate); 1877 CompileRun("var v = [42, 43]"); 1878 v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p); 1879 v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p); 1880 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); 1881 ctx2->Enter(); 1882 ctx2->Global()->Set(v8_str("o"), v); 1883 v8::Local<v8::Value> res = CompileRun( 1884 "function f() { return o[0]; }" 1885 "for (var i = 0; i < 10; ++i) f();" 1886 "%OptimizeFunctionOnNextCall(f);" 1887 "f();"); 1888 CHECK_EQ(42, res->Int32Value()); 1889 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(isolate, 0)); 1890 ctx2->Exit(); 1891 ctx1->Exit(); 1892 ctx1p.Reset(); 1893 v8::V8::ContextDisposedNotification(); 1894 } 1895 CcTest::heap()->CollectAllAvailableGarbage(); 1896 CHECK_EQ(2, NumberOfGlobalObjects()); 1897 ctx2p.Reset(); 1898 CcTest::heap()->CollectAllAvailableGarbage(); 1899 CHECK_EQ(0, NumberOfGlobalObjects()); 1900 } 1901 1902 1903 TEST(LeakNativeContextViaMapProto) { 1904 i::FLAG_allow_natives_syntax = true; 1905 v8::Isolate* isolate = CcTest::isolate(); 1906 v8::HandleScope outer_scope(isolate); 1907 v8::Persistent<v8::Context> ctx1p; 1908 v8::Persistent<v8::Context> ctx2p; 1909 { 1910 v8::HandleScope scope(isolate); 1911 ctx1p.Reset(isolate, v8::Context::New(isolate)); 1912 ctx2p.Reset(isolate, v8::Context::New(isolate)); 1913 v8::Local<v8::Context>::New(isolate, ctx1p)->Enter(); 1914 } 1915 1916 CcTest::heap()->CollectAllAvailableGarbage(); 1917 CHECK_EQ(4, NumberOfGlobalObjects()); 1918 1919 { 1920 v8::HandleScope inner_scope(isolate); 1921 CompileRun("var v = { y: 42}"); 1922 v8::Local<v8::Context> ctx1 = v8::Local<v8::Context>::New(isolate, ctx1p); 1923 v8::Local<v8::Context> ctx2 = v8::Local<v8::Context>::New(isolate, ctx2p); 1924 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); 1925 ctx2->Enter(); 1926 ctx2->Global()->Set(v8_str("o"), v); 1927 v8::Local<v8::Value> res = CompileRun( 1928 "function f() {" 1929 " var p = {x: 42};" 1930 " p.__proto__ = o;" 1931 " return p.x;" 1932 "}" 1933 "for (var i = 0; i < 10; ++i) f();" 1934 "%OptimizeFunctionOnNextCall(f);" 1935 "f();"); 1936 CHECK_EQ(42, res->Int32Value()); 1937 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(isolate, 0)); 1938 ctx2->Exit(); 1939 ctx1->Exit(); 1940 ctx1p.Reset(); 1941 v8::V8::ContextDisposedNotification(); 1942 } 1943 CcTest::heap()->CollectAllAvailableGarbage(); 1944 CHECK_EQ(2, NumberOfGlobalObjects()); 1945 ctx2p.Reset(); 1946 CcTest::heap()->CollectAllAvailableGarbage(); 1947 CHECK_EQ(0, NumberOfGlobalObjects()); 1948 } 1949 1950 1951 TEST(InstanceOfStubWriteBarrier) { 1952 i::FLAG_allow_natives_syntax = true; 1953 #ifdef VERIFY_HEAP 1954 i::FLAG_verify_heap = true; 1955 #endif 1956 1957 CcTest::InitializeVM(); 1958 if (!CcTest::i_isolate()->use_crankshaft()) return; 1959 if (i::FLAG_force_marking_deque_overflows) return; 1960 v8::HandleScope outer_scope(CcTest::isolate()); 1961 1962 { 1963 v8::HandleScope scope(CcTest::isolate()); 1964 CompileRun( 1965 "function foo () { }" 1966 "function mkbar () { return new (new Function(\"\")) (); }" 1967 "function f (x) { return (x instanceof foo); }" 1968 "function g () { f(mkbar()); }" 1969 "f(new foo()); f(new foo());" 1970 "%OptimizeFunctionOnNextCall(f);" 1971 "f(new foo()); g();"); 1972 } 1973 1974 IncrementalMarking* marking = CcTest::heap()->incremental_marking(); 1975 marking->Abort(); 1976 marking->Start(); 1977 1978 Handle<JSFunction> f = 1979 v8::Utils::OpenHandle( 1980 *v8::Handle<v8::Function>::Cast( 1981 CcTest::global()->Get(v8_str("f")))); 1982 1983 CHECK(f->IsOptimized()); 1984 1985 while (!Marking::IsBlack(Marking::MarkBitFrom(f->code())) && 1986 !marking->IsStopped()) { 1987 // Discard any pending GC requests otherwise we will get GC when we enter 1988 // code below. 1989 marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); 1990 } 1991 1992 CHECK(marking->IsMarking()); 1993 1994 { 1995 v8::HandleScope scope(CcTest::isolate()); 1996 v8::Handle<v8::Object> global = CcTest::global(); 1997 v8::Handle<v8::Function> g = 1998 v8::Handle<v8::Function>::Cast(global->Get(v8_str("g"))); 1999 g->Call(global, 0, NULL); 2000 } 2001 2002 CcTest::heap()->incremental_marking()->set_should_hurry(true); 2003 CcTest::heap()->CollectGarbage(OLD_POINTER_SPACE); 2004 } 2005 2006 2007 TEST(PrototypeTransitionClearing) { 2008 if (FLAG_never_compact) return; 2009 CcTest::InitializeVM(); 2010 Isolate* isolate = CcTest::i_isolate(); 2011 Factory* factory = isolate->factory(); 2012 v8::HandleScope scope(CcTest::isolate()); 2013 2014 CompileRun("var base = {};"); 2015 Handle<JSObject> baseObject = 2016 v8::Utils::OpenHandle( 2017 *v8::Handle<v8::Object>::Cast( 2018 CcTest::global()->Get(v8_str("base")))); 2019 int initialTransitions = baseObject->map()->NumberOfProtoTransitions(); 2020 2021 CompileRun( 2022 "var live = [];" 2023 "for (var i = 0; i < 10; i++) {" 2024 " var object = {};" 2025 " var prototype = {};" 2026 " object.__proto__ = prototype;" 2027 " if (i >= 3) live.push(object, prototype);" 2028 "}"); 2029 2030 // Verify that only dead prototype transitions are cleared. 2031 CHECK_EQ(initialTransitions + 10, 2032 baseObject->map()->NumberOfProtoTransitions()); 2033 CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 2034 const int transitions = 10 - 3; 2035 CHECK_EQ(initialTransitions + transitions, 2036 baseObject->map()->NumberOfProtoTransitions()); 2037 2038 // Verify that prototype transitions array was compacted. 2039 FixedArray* trans = baseObject->map()->GetPrototypeTransitions(); 2040 for (int i = initialTransitions; i < initialTransitions + transitions; i++) { 2041 int j = Map::kProtoTransitionHeaderSize + 2042 i * Map::kProtoTransitionElementsPerEntry; 2043 CHECK(trans->get(j + Map::kProtoTransitionMapOffset)->IsMap()); 2044 Object* proto = trans->get(j + Map::kProtoTransitionPrototypeOffset); 2045 CHECK(proto->IsJSObject()); 2046 } 2047 2048 // Make sure next prototype is placed on an old-space evacuation candidate. 2049 Handle<JSObject> prototype; 2050 PagedSpace* space = CcTest::heap()->old_pointer_space(); 2051 { 2052 AlwaysAllocateScope always_allocate(isolate); 2053 SimulateFullSpace(space); 2054 prototype = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); 2055 } 2056 2057 // Add a prototype on an evacuation candidate and verify that transition 2058 // clearing correctly records slots in prototype transition array. 2059 i::FLAG_always_compact = true; 2060 Handle<Map> map(baseObject->map()); 2061 CHECK(!space->LastPage()->Contains( 2062 map->GetPrototypeTransitions()->address())); 2063 CHECK(space->LastPage()->Contains(prototype->address())); 2064 } 2065 2066 2067 TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) { 2068 i::FLAG_stress_compaction = false; 2069 i::FLAG_allow_natives_syntax = true; 2070 #ifdef VERIFY_HEAP 2071 i::FLAG_verify_heap = true; 2072 #endif 2073 2074 CcTest::InitializeVM(); 2075 if (!CcTest::i_isolate()->use_crankshaft()) return; 2076 v8::HandleScope outer_scope(CcTest::isolate()); 2077 2078 { 2079 v8::HandleScope scope(CcTest::isolate()); 2080 CompileRun( 2081 "function f () {" 2082 " var s = 0;" 2083 " for (var i = 0; i < 100; i++) s += i;" 2084 " return s;" 2085 "}" 2086 "f(); f();" 2087 "%OptimizeFunctionOnNextCall(f);" 2088 "f();"); 2089 } 2090 Handle<JSFunction> f = 2091 v8::Utils::OpenHandle( 2092 *v8::Handle<v8::Function>::Cast( 2093 CcTest::global()->Get(v8_str("f")))); 2094 CHECK(f->IsOptimized()); 2095 2096 IncrementalMarking* marking = CcTest::heap()->incremental_marking(); 2097 marking->Abort(); 2098 marking->Start(); 2099 2100 // The following two calls will increment CcTest::heap()->global_ic_age(). 2101 const int kLongIdlePauseInMs = 1000; 2102 v8::V8::ContextDisposedNotification(); 2103 v8::V8::IdleNotification(kLongIdlePauseInMs); 2104 2105 while (!marking->IsStopped() && !marking->IsComplete()) { 2106 marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); 2107 } 2108 if (!marking->IsStopped() || marking->should_hurry()) { 2109 // We don't normally finish a GC via Step(), we normally finish by 2110 // setting the stack guard and then do the final steps in the stack 2111 // guard interrupt. But here we didn't ask for that, and there is no 2112 // JS code running to trigger the interrupt, so we explicitly finalize 2113 // here. 2114 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags, 2115 "Test finalizing incremental mark-sweep"); 2116 } 2117 2118 CHECK_EQ(CcTest::heap()->global_ic_age(), f->shared()->ic_age()); 2119 CHECK_EQ(0, f->shared()->opt_count()); 2120 CHECK_EQ(0, f->shared()->code()->profiler_ticks()); 2121 } 2122 2123 2124 TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) { 2125 i::FLAG_stress_compaction = false; 2126 i::FLAG_allow_natives_syntax = true; 2127 #ifdef VERIFY_HEAP 2128 i::FLAG_verify_heap = true; 2129 #endif 2130 2131 CcTest::InitializeVM(); 2132 if (!CcTest::i_isolate()->use_crankshaft()) return; 2133 v8::HandleScope outer_scope(CcTest::isolate()); 2134 2135 { 2136 v8::HandleScope scope(CcTest::isolate()); 2137 CompileRun( 2138 "function f () {" 2139 " var s = 0;" 2140 " for (var i = 0; i < 100; i++) s += i;" 2141 " return s;" 2142 "}" 2143 "f(); f();" 2144 "%OptimizeFunctionOnNextCall(f);" 2145 "f();"); 2146 } 2147 Handle<JSFunction> f = 2148 v8::Utils::OpenHandle( 2149 *v8::Handle<v8::Function>::Cast( 2150 CcTest::global()->Get(v8_str("f")))); 2151 CHECK(f->IsOptimized()); 2152 2153 CcTest::heap()->incremental_marking()->Abort(); 2154 2155 // The following two calls will increment CcTest::heap()->global_ic_age(). 2156 // Since incremental marking is off, IdleNotification will do full GC. 2157 const int kLongIdlePauseInMs = 1000; 2158 v8::V8::ContextDisposedNotification(); 2159 v8::V8::IdleNotification(kLongIdlePauseInMs); 2160 2161 CHECK_EQ(CcTest::heap()->global_ic_age(), f->shared()->ic_age()); 2162 CHECK_EQ(0, f->shared()->opt_count()); 2163 CHECK_EQ(0, f->shared()->code()->profiler_ticks()); 2164 } 2165 2166 2167 // Test that HAllocateObject will always return an object in new-space. 2168 TEST(OptimizedAllocationAlwaysInNewSpace) { 2169 i::FLAG_allow_natives_syntax = true; 2170 CcTest::InitializeVM(); 2171 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2172 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2173 v8::HandleScope scope(CcTest::isolate()); 2174 2175 SimulateFullSpace(CcTest::heap()->new_space()); 2176 AlwaysAllocateScope always_allocate(CcTest::i_isolate()); 2177 v8::Local<v8::Value> res = CompileRun( 2178 "function c(x) {" 2179 " this.x = x;" 2180 " for (var i = 0; i < 32; i++) {" 2181 " this['x' + i] = x;" 2182 " }" 2183 "}" 2184 "function f(x) { return new c(x); };" 2185 "f(1); f(2); f(3);" 2186 "%OptimizeFunctionOnNextCall(f);" 2187 "f(4);"); 2188 CHECK_EQ(4, res->ToObject()->GetRealNamedProperty(v8_str("x"))->Int32Value()); 2189 2190 Handle<JSObject> o = 2191 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2192 2193 CHECK(CcTest::heap()->InNewSpace(*o)); 2194 } 2195 2196 2197 TEST(OptimizedPretenuringAllocationFolding) { 2198 i::FLAG_allow_natives_syntax = true; 2199 i::FLAG_expose_gc = true; 2200 CcTest::InitializeVM(); 2201 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2202 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2203 v8::HandleScope scope(CcTest::isolate()); 2204 2205 // Grow new space unitl maximum capacity reached. 2206 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2207 CcTest::heap()->new_space()->Grow(); 2208 } 2209 2210 i::ScopedVector<char> source(1024); 2211 i::SNPrintF( 2212 source, 2213 "var number_elements = %d;" 2214 "var elements = new Array();" 2215 "function f() {" 2216 " for (var i = 0; i < number_elements; i++) {" 2217 " elements[i] = [[{}], [1.1]];" 2218 " }" 2219 " return elements[number_elements-1]" 2220 "};" 2221 "f(); gc();" 2222 "f(); f();" 2223 "%%OptimizeFunctionOnNextCall(f);" 2224 "f();", 2225 AllocationSite::kPretenureMinimumCreated); 2226 2227 v8::Local<v8::Value> res = CompileRun(source.start()); 2228 2229 v8::Local<v8::Value> int_array = v8::Object::Cast(*res)->Get(v8_str("0")); 2230 Handle<JSObject> int_array_handle = 2231 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(int_array)); 2232 v8::Local<v8::Value> double_array = v8::Object::Cast(*res)->Get(v8_str("1")); 2233 Handle<JSObject> double_array_handle = 2234 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(double_array)); 2235 2236 Handle<JSObject> o = 2237 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2238 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2239 CHECK(CcTest::heap()->InOldPointerSpace(*int_array_handle)); 2240 CHECK(CcTest::heap()->InOldPointerSpace(int_array_handle->elements())); 2241 CHECK(CcTest::heap()->InOldPointerSpace(*double_array_handle)); 2242 CHECK(CcTest::heap()->InOldDataSpace(double_array_handle->elements())); 2243 } 2244 2245 2246 TEST(OptimizedPretenuringObjectArrayLiterals) { 2247 i::FLAG_allow_natives_syntax = true; 2248 i::FLAG_expose_gc = true; 2249 CcTest::InitializeVM(); 2250 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2251 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2252 v8::HandleScope scope(CcTest::isolate()); 2253 2254 // Grow new space unitl maximum capacity reached. 2255 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2256 CcTest::heap()->new_space()->Grow(); 2257 } 2258 2259 i::ScopedVector<char> source(1024); 2260 i::SNPrintF( 2261 source, 2262 "var number_elements = %d;" 2263 "var elements = new Array(number_elements);" 2264 "function f() {" 2265 " for (var i = 0; i < number_elements; i++) {" 2266 " elements[i] = [{}, {}, {}];" 2267 " }" 2268 " return elements[number_elements - 1];" 2269 "};" 2270 "f(); gc();" 2271 "f(); f();" 2272 "%%OptimizeFunctionOnNextCall(f);" 2273 "f();", 2274 AllocationSite::kPretenureMinimumCreated); 2275 2276 v8::Local<v8::Value> res = CompileRun(source.start()); 2277 2278 Handle<JSObject> o = 2279 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2280 2281 CHECK(CcTest::heap()->InOldPointerSpace(o->elements())); 2282 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2283 } 2284 2285 2286 TEST(OptimizedPretenuringMixedInObjectProperties) { 2287 i::FLAG_allow_natives_syntax = true; 2288 i::FLAG_expose_gc = true; 2289 CcTest::InitializeVM(); 2290 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2291 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2292 v8::HandleScope scope(CcTest::isolate()); 2293 2294 // Grow new space unitl maximum capacity reached. 2295 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2296 CcTest::heap()->new_space()->Grow(); 2297 } 2298 2299 2300 i::ScopedVector<char> source(1024); 2301 i::SNPrintF( 2302 source, 2303 "var number_elements = %d;" 2304 "var elements = new Array(number_elements);" 2305 "function f() {" 2306 " for (var i = 0; i < number_elements; i++) {" 2307 " elements[i] = {a: {c: 2.2, d: {}}, b: 1.1};" 2308 " }" 2309 " return elements[number_elements - 1];" 2310 "};" 2311 "f(); gc();" 2312 "f(); f();" 2313 "%%OptimizeFunctionOnNextCall(f);" 2314 "f();", 2315 AllocationSite::kPretenureMinimumCreated); 2316 2317 v8::Local<v8::Value> res = CompileRun(source.start()); 2318 2319 Handle<JSObject> o = 2320 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2321 2322 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2323 FieldIndex idx1 = FieldIndex::ForPropertyIndex(o->map(), 0); 2324 FieldIndex idx2 = FieldIndex::ForPropertyIndex(o->map(), 1); 2325 CHECK(CcTest::heap()->InOldPointerSpace(o->RawFastPropertyAt(idx1))); 2326 CHECK(CcTest::heap()->InOldDataSpace(o->RawFastPropertyAt(idx2))); 2327 2328 JSObject* inner_object = 2329 reinterpret_cast<JSObject*>(o->RawFastPropertyAt(idx1)); 2330 CHECK(CcTest::heap()->InOldPointerSpace(inner_object)); 2331 CHECK(CcTest::heap()->InOldDataSpace(inner_object->RawFastPropertyAt(idx1))); 2332 CHECK(CcTest::heap()->InOldPointerSpace( 2333 inner_object->RawFastPropertyAt(idx2))); 2334 } 2335 2336 2337 TEST(OptimizedPretenuringDoubleArrayProperties) { 2338 i::FLAG_allow_natives_syntax = true; 2339 i::FLAG_expose_gc = true; 2340 CcTest::InitializeVM(); 2341 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2342 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2343 v8::HandleScope scope(CcTest::isolate()); 2344 2345 // Grow new space unitl maximum capacity reached. 2346 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2347 CcTest::heap()->new_space()->Grow(); 2348 } 2349 2350 i::ScopedVector<char> source(1024); 2351 i::SNPrintF( 2352 source, 2353 "var number_elements = %d;" 2354 "var elements = new Array(number_elements);" 2355 "function f() {" 2356 " for (var i = 0; i < number_elements; i++) {" 2357 " elements[i] = {a: 1.1, b: 2.2};" 2358 " }" 2359 " return elements[i - 1];" 2360 "};" 2361 "f(); gc();" 2362 "f(); f();" 2363 "%%OptimizeFunctionOnNextCall(f);" 2364 "f();", 2365 AllocationSite::kPretenureMinimumCreated); 2366 2367 v8::Local<v8::Value> res = CompileRun(source.start()); 2368 2369 Handle<JSObject> o = 2370 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2371 2372 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2373 CHECK(CcTest::heap()->InOldDataSpace(o->properties())); 2374 } 2375 2376 2377 TEST(OptimizedPretenuringdoubleArrayLiterals) { 2378 i::FLAG_allow_natives_syntax = true; 2379 i::FLAG_expose_gc = true; 2380 CcTest::InitializeVM(); 2381 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2382 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2383 v8::HandleScope scope(CcTest::isolate()); 2384 2385 // Grow new space unitl maximum capacity reached. 2386 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2387 CcTest::heap()->new_space()->Grow(); 2388 } 2389 2390 i::ScopedVector<char> source(1024); 2391 i::SNPrintF( 2392 source, 2393 "var number_elements = %d;" 2394 "var elements = new Array(number_elements);" 2395 "function f() {" 2396 " for (var i = 0; i < number_elements; i++) {" 2397 " elements[i] = [1.1, 2.2, 3.3];" 2398 " }" 2399 " return elements[number_elements - 1];" 2400 "};" 2401 "f(); gc();" 2402 "f(); f();" 2403 "%%OptimizeFunctionOnNextCall(f);" 2404 "f();", 2405 AllocationSite::kPretenureMinimumCreated); 2406 2407 v8::Local<v8::Value> res = CompileRun(source.start()); 2408 2409 Handle<JSObject> o = 2410 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2411 2412 CHECK(CcTest::heap()->InOldDataSpace(o->elements())); 2413 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2414 } 2415 2416 2417 TEST(OptimizedPretenuringNestedMixedArrayLiterals) { 2418 i::FLAG_allow_natives_syntax = true; 2419 i::FLAG_expose_gc = true; 2420 CcTest::InitializeVM(); 2421 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2422 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2423 v8::HandleScope scope(CcTest::isolate()); 2424 2425 // Grow new space unitl maximum capacity reached. 2426 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2427 CcTest::heap()->new_space()->Grow(); 2428 } 2429 2430 i::ScopedVector<char> source(1024); 2431 i::SNPrintF( 2432 source, 2433 "var number_elements = 100;" 2434 "var elements = new Array(number_elements);" 2435 "function f() {" 2436 " for (var i = 0; i < number_elements; i++) {" 2437 " elements[i] = [[{}, {}, {}], [1.1, 2.2, 3.3]];" 2438 " }" 2439 " return elements[number_elements - 1];" 2440 "};" 2441 "f(); gc();" 2442 "f(); f();" 2443 "%%OptimizeFunctionOnNextCall(f);" 2444 "f();"); 2445 2446 v8::Local<v8::Value> res = CompileRun(source.start()); 2447 2448 v8::Local<v8::Value> int_array = v8::Object::Cast(*res)->Get(v8_str("0")); 2449 Handle<JSObject> int_array_handle = 2450 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(int_array)); 2451 v8::Local<v8::Value> double_array = v8::Object::Cast(*res)->Get(v8_str("1")); 2452 Handle<JSObject> double_array_handle = 2453 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(double_array)); 2454 2455 Handle<JSObject> o = 2456 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2457 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2458 CHECK(CcTest::heap()->InOldPointerSpace(*int_array_handle)); 2459 CHECK(CcTest::heap()->InOldPointerSpace(int_array_handle->elements())); 2460 CHECK(CcTest::heap()->InOldPointerSpace(*double_array_handle)); 2461 CHECK(CcTest::heap()->InOldDataSpace(double_array_handle->elements())); 2462 } 2463 2464 2465 TEST(OptimizedPretenuringNestedObjectLiterals) { 2466 i::FLAG_allow_natives_syntax = true; 2467 i::FLAG_expose_gc = true; 2468 CcTest::InitializeVM(); 2469 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2470 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2471 v8::HandleScope scope(CcTest::isolate()); 2472 2473 // Grow new space unitl maximum capacity reached. 2474 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2475 CcTest::heap()->new_space()->Grow(); 2476 } 2477 2478 i::ScopedVector<char> source(1024); 2479 i::SNPrintF( 2480 source, 2481 "var number_elements = %d;" 2482 "var elements = new Array(number_elements);" 2483 "function f() {" 2484 " for (var i = 0; i < number_elements; i++) {" 2485 " elements[i] = [[{}, {}, {}],[{}, {}, {}]];" 2486 " }" 2487 " return elements[number_elements - 1];" 2488 "};" 2489 "f(); gc();" 2490 "f(); f();" 2491 "%%OptimizeFunctionOnNextCall(f);" 2492 "f();", 2493 AllocationSite::kPretenureMinimumCreated); 2494 2495 v8::Local<v8::Value> res = CompileRun(source.start()); 2496 2497 v8::Local<v8::Value> int_array_1 = v8::Object::Cast(*res)->Get(v8_str("0")); 2498 Handle<JSObject> int_array_handle_1 = 2499 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(int_array_1)); 2500 v8::Local<v8::Value> int_array_2 = v8::Object::Cast(*res)->Get(v8_str("1")); 2501 Handle<JSObject> int_array_handle_2 = 2502 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(int_array_2)); 2503 2504 Handle<JSObject> o = 2505 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2506 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2507 CHECK(CcTest::heap()->InOldPointerSpace(*int_array_handle_1)); 2508 CHECK(CcTest::heap()->InOldPointerSpace(int_array_handle_1->elements())); 2509 CHECK(CcTest::heap()->InOldPointerSpace(*int_array_handle_2)); 2510 CHECK(CcTest::heap()->InOldPointerSpace(int_array_handle_2->elements())); 2511 } 2512 2513 2514 TEST(OptimizedPretenuringNestedDoubleLiterals) { 2515 i::FLAG_allow_natives_syntax = true; 2516 i::FLAG_expose_gc = true; 2517 CcTest::InitializeVM(); 2518 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2519 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2520 v8::HandleScope scope(CcTest::isolate()); 2521 2522 // Grow new space unitl maximum capacity reached. 2523 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2524 CcTest::heap()->new_space()->Grow(); 2525 } 2526 2527 i::ScopedVector<char> source(1024); 2528 i::SNPrintF( 2529 source, 2530 "var number_elements = %d;" 2531 "var elements = new Array(number_elements);" 2532 "function f() {" 2533 " for (var i = 0; i < number_elements; i++) {" 2534 " elements[i] = [[1.1, 1.2, 1.3],[2.1, 2.2, 2.3]];" 2535 " }" 2536 " return elements[number_elements - 1];" 2537 "};" 2538 "f(); gc();" 2539 "f(); f();" 2540 "%%OptimizeFunctionOnNextCall(f);" 2541 "f();", 2542 AllocationSite::kPretenureMinimumCreated); 2543 2544 v8::Local<v8::Value> res = CompileRun(source.start()); 2545 2546 v8::Local<v8::Value> double_array_1 = 2547 v8::Object::Cast(*res)->Get(v8_str("0")); 2548 Handle<JSObject> double_array_handle_1 = 2549 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(double_array_1)); 2550 v8::Local<v8::Value> double_array_2 = 2551 v8::Object::Cast(*res)->Get(v8_str("1")); 2552 Handle<JSObject> double_array_handle_2 = 2553 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(double_array_2)); 2554 2555 Handle<JSObject> o = 2556 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2557 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2558 CHECK(CcTest::heap()->InOldPointerSpace(*double_array_handle_1)); 2559 CHECK(CcTest::heap()->InOldDataSpace(double_array_handle_1->elements())); 2560 CHECK(CcTest::heap()->InOldPointerSpace(*double_array_handle_2)); 2561 CHECK(CcTest::heap()->InOldDataSpace(double_array_handle_2->elements())); 2562 } 2563 2564 2565 // Make sure pretenuring feedback is gathered for constructed objects as well 2566 // as for literals. 2567 TEST(OptimizedPretenuringConstructorCalls) { 2568 if (!i::FLAG_pretenuring_call_new) { 2569 // FLAG_pretenuring_call_new needs to be synced with the snapshot. 2570 return; 2571 } 2572 i::FLAG_allow_natives_syntax = true; 2573 i::FLAG_expose_gc = true; 2574 CcTest::InitializeVM(); 2575 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2576 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2577 v8::HandleScope scope(CcTest::isolate()); 2578 2579 // Grow new space unitl maximum capacity reached. 2580 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2581 CcTest::heap()->new_space()->Grow(); 2582 } 2583 2584 i::ScopedVector<char> source(1024); 2585 // Call new is doing slack tracking for the first 2586 // JSFunction::kGenerousAllocationCount allocations, and we can't find 2587 // mementos during that time. 2588 i::SNPrintF( 2589 source, 2590 "var number_elements = %d;" 2591 "var elements = new Array(number_elements);" 2592 "function foo() {" 2593 " this.a = 3;" 2594 " this.b = {};" 2595 "}" 2596 "function f() {" 2597 " for (var i = 0; i < number_elements; i++) {" 2598 " elements[i] = new foo();" 2599 " }" 2600 " return elements[number_elements - 1];" 2601 "};" 2602 "f(); gc();" 2603 "f(); f();" 2604 "%%OptimizeFunctionOnNextCall(f);" 2605 "f();", 2606 AllocationSite::kPretenureMinimumCreated + 2607 JSFunction::kGenerousAllocationCount); 2608 2609 v8::Local<v8::Value> res = CompileRun(source.start()); 2610 2611 Handle<JSObject> o = 2612 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2613 2614 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2615 } 2616 2617 2618 TEST(OptimizedPretenuringCallNew) { 2619 if (!i::FLAG_pretenuring_call_new) { 2620 // FLAG_pretenuring_call_new needs to be synced with the snapshot. 2621 return; 2622 } 2623 i::FLAG_allow_natives_syntax = true; 2624 i::FLAG_expose_gc = true; 2625 CcTest::InitializeVM(); 2626 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2627 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2628 v8::HandleScope scope(CcTest::isolate()); 2629 2630 // Grow new space unitl maximum capacity reached. 2631 while (!CcTest::heap()->new_space()->IsAtMaximumCapacity()) { 2632 CcTest::heap()->new_space()->Grow(); 2633 } 2634 2635 i::ScopedVector<char> source(1024); 2636 // Call new is doing slack tracking for the first 2637 // JSFunction::kGenerousAllocationCount allocations, and we can't find 2638 // mementos during that time. 2639 i::SNPrintF( 2640 source, 2641 "var number_elements = %d;" 2642 "var elements = new Array(number_elements);" 2643 "function g() { this.a = 0; }" 2644 "function f() {" 2645 " for (var i = 0; i < number_elements; i++) {" 2646 " elements[i] = new g();" 2647 " }" 2648 " return elements[number_elements - 1];" 2649 "};" 2650 "f(); gc();" 2651 "f(); f();" 2652 "%%OptimizeFunctionOnNextCall(f);" 2653 "f();", 2654 AllocationSite::kPretenureMinimumCreated + 2655 JSFunction::kGenerousAllocationCount); 2656 2657 v8::Local<v8::Value> res = CompileRun(source.start()); 2658 2659 Handle<JSObject> o = 2660 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2661 CHECK(CcTest::heap()->InOldPointerSpace(*o)); 2662 } 2663 2664 2665 // Test regular array literals allocation. 2666 TEST(OptimizedAllocationArrayLiterals) { 2667 i::FLAG_allow_natives_syntax = true; 2668 CcTest::InitializeVM(); 2669 if (!CcTest::i_isolate()->use_crankshaft() || i::FLAG_always_opt) return; 2670 if (i::FLAG_gc_global || i::FLAG_stress_compaction) return; 2671 v8::HandleScope scope(CcTest::isolate()); 2672 2673 v8::Local<v8::Value> res = CompileRun( 2674 "function f() {" 2675 " var numbers = new Array(1, 2, 3);" 2676 " numbers[0] = 3.14;" 2677 " return numbers;" 2678 "};" 2679 "f(); f(); f();" 2680 "%OptimizeFunctionOnNextCall(f);" 2681 "f();"); 2682 CHECK_EQ(static_cast<int>(3.14), 2683 v8::Object::Cast(*res)->Get(v8_str("0"))->Int32Value()); 2684 2685 Handle<JSObject> o = 2686 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res)); 2687 2688 CHECK(CcTest::heap()->InNewSpace(o->elements())); 2689 } 2690 2691 2692 static int CountMapTransitions(Map* map) { 2693 return map->transitions()->number_of_transitions(); 2694 } 2695 2696 2697 // Test that map transitions are cleared and maps are collected with 2698 // incremental marking as well. 2699 TEST(Regress1465) { 2700 i::FLAG_stress_compaction = false; 2701 i::FLAG_allow_natives_syntax = true; 2702 i::FLAG_trace_incremental_marking = true; 2703 CcTest::InitializeVM(); 2704 v8::HandleScope scope(CcTest::isolate()); 2705 static const int transitions_count = 256; 2706 2707 CompileRun("function F() {}"); 2708 { 2709 AlwaysAllocateScope always_allocate(CcTest::i_isolate()); 2710 for (int i = 0; i < transitions_count; i++) { 2711 EmbeddedVector<char, 64> buffer; 2712 SNPrintF(buffer, "var o = new F; o.prop%d = %d;", i, i); 2713 CompileRun(buffer.start()); 2714 } 2715 CompileRun("var root = new F;"); 2716 } 2717 2718 Handle<JSObject> root = 2719 v8::Utils::OpenHandle( 2720 *v8::Handle<v8::Object>::Cast( 2721 CcTest::global()->Get(v8_str("root")))); 2722 2723 // Count number of live transitions before marking. 2724 int transitions_before = CountMapTransitions(root->map()); 2725 CompileRun("%DebugPrint(root);"); 2726 CHECK_EQ(transitions_count, transitions_before); 2727 2728 SimulateIncrementalMarking(); 2729 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 2730 2731 // Count number of live transitions after marking. Note that one transition 2732 // is left, because 'o' still holds an instance of one transition target. 2733 int transitions_after = CountMapTransitions(root->map()); 2734 CompileRun("%DebugPrint(root);"); 2735 CHECK_EQ(1, transitions_after); 2736 } 2737 2738 2739 #ifdef DEBUG 2740 static void AddTransitions(int transitions_count) { 2741 AlwaysAllocateScope always_allocate(CcTest::i_isolate()); 2742 for (int i = 0; i < transitions_count; i++) { 2743 EmbeddedVector<char, 64> buffer; 2744 SNPrintF(buffer, "var o = new F; o.prop%d = %d;", i, i); 2745 CompileRun(buffer.start()); 2746 } 2747 } 2748 2749 2750 static Handle<JSObject> GetByName(const char* name) { 2751 return v8::Utils::OpenHandle( 2752 *v8::Handle<v8::Object>::Cast( 2753 CcTest::global()->Get(v8_str(name)))); 2754 } 2755 2756 2757 static void AddPropertyTo( 2758 int gc_count, Handle<JSObject> object, const char* property_name) { 2759 Isolate* isolate = CcTest::i_isolate(); 2760 Factory* factory = isolate->factory(); 2761 Handle<String> prop_name = factory->InternalizeUtf8String(property_name); 2762 Handle<Smi> twenty_three(Smi::FromInt(23), isolate); 2763 i::FLAG_gc_interval = gc_count; 2764 i::FLAG_gc_global = true; 2765 CcTest::heap()->set_allocation_timeout(gc_count); 2766 JSReceiver::SetProperty( 2767 object, prop_name, twenty_three, NONE, SLOPPY).Check(); 2768 } 2769 2770 2771 TEST(TransitionArrayShrinksDuringAllocToZero) { 2772 i::FLAG_stress_compaction = false; 2773 i::FLAG_allow_natives_syntax = true; 2774 CcTest::InitializeVM(); 2775 v8::HandleScope scope(CcTest::isolate()); 2776 static const int transitions_count = 10; 2777 CompileRun("function F() { }"); 2778 AddTransitions(transitions_count); 2779 CompileRun("var root = new F;"); 2780 Handle<JSObject> root = GetByName("root"); 2781 2782 // Count number of live transitions before marking. 2783 int transitions_before = CountMapTransitions(root->map()); 2784 CHECK_EQ(transitions_count, transitions_before); 2785 2786 // Get rid of o 2787 CompileRun("o = new F;" 2788 "root = new F"); 2789 root = GetByName("root"); 2790 AddPropertyTo(2, root, "funny"); 2791 2792 // Count number of live transitions after marking. Note that one transition 2793 // is left, because 'o' still holds an instance of one transition target. 2794 int transitions_after = CountMapTransitions( 2795 Map::cast(root->map()->GetBackPointer())); 2796 CHECK_EQ(1, transitions_after); 2797 } 2798 2799 2800 TEST(TransitionArrayShrinksDuringAllocToOne) { 2801 i::FLAG_stress_compaction = false; 2802 i::FLAG_allow_natives_syntax = true; 2803 CcTest::InitializeVM(); 2804 v8::HandleScope scope(CcTest::isolate()); 2805 static const int transitions_count = 10; 2806 CompileRun("function F() {}"); 2807 AddTransitions(transitions_count); 2808 CompileRun("var root = new F;"); 2809 Handle<JSObject> root = GetByName("root"); 2810 2811 // Count number of live transitions before marking. 2812 int transitions_before = CountMapTransitions(root->map()); 2813 CHECK_EQ(transitions_count, transitions_before); 2814 2815 root = GetByName("root"); 2816 AddPropertyTo(2, root, "funny"); 2817 2818 // Count number of live transitions after marking. Note that one transition 2819 // is left, because 'o' still holds an instance of one transition target. 2820 int transitions_after = CountMapTransitions( 2821 Map::cast(root->map()->GetBackPointer())); 2822 CHECK_EQ(2, transitions_after); 2823 } 2824 2825 2826 TEST(TransitionArrayShrinksDuringAllocToOnePropertyFound) { 2827 i::FLAG_stress_compaction = false; 2828 i::FLAG_allow_natives_syntax = true; 2829 CcTest::InitializeVM(); 2830 v8::HandleScope scope(CcTest::isolate()); 2831 static const int transitions_count = 10; 2832 CompileRun("function F() {}"); 2833 AddTransitions(transitions_count); 2834 CompileRun("var root = new F;"); 2835 Handle<JSObject> root = GetByName("root"); 2836 2837 // Count number of live transitions before marking. 2838 int transitions_before = CountMapTransitions(root->map()); 2839 CHECK_EQ(transitions_count, transitions_before); 2840 2841 root = GetByName("root"); 2842 AddPropertyTo(0, root, "prop9"); 2843 2844 // Count number of live transitions after marking. Note that one transition 2845 // is left, because 'o' still holds an instance of one transition target. 2846 int transitions_after = CountMapTransitions( 2847 Map::cast(root->map()->GetBackPointer())); 2848 CHECK_EQ(1, transitions_after); 2849 } 2850 2851 2852 TEST(TransitionArraySimpleToFull) { 2853 i::FLAG_stress_compaction = false; 2854 i::FLAG_allow_natives_syntax = true; 2855 CcTest::InitializeVM(); 2856 v8::HandleScope scope(CcTest::isolate()); 2857 static const int transitions_count = 1; 2858 CompileRun("function F() {}"); 2859 AddTransitions(transitions_count); 2860 CompileRun("var root = new F;"); 2861 Handle<JSObject> root = GetByName("root"); 2862 2863 // Count number of live transitions before marking. 2864 int transitions_before = CountMapTransitions(root->map()); 2865 CHECK_EQ(transitions_count, transitions_before); 2866 2867 CompileRun("o = new F;" 2868 "root = new F"); 2869 root = GetByName("root"); 2870 ASSERT(root->map()->transitions()->IsSimpleTransition()); 2871 AddPropertyTo(2, root, "happy"); 2872 2873 // Count number of live transitions after marking. Note that one transition 2874 // is left, because 'o' still holds an instance of one transition target. 2875 int transitions_after = CountMapTransitions( 2876 Map::cast(root->map()->GetBackPointer())); 2877 CHECK_EQ(1, transitions_after); 2878 } 2879 #endif // DEBUG 2880 2881 2882 TEST(Regress2143a) { 2883 i::FLAG_collect_maps = true; 2884 i::FLAG_incremental_marking = true; 2885 CcTest::InitializeVM(); 2886 v8::HandleScope scope(CcTest::isolate()); 2887 2888 // Prepare a map transition from the root object together with a yet 2889 // untransitioned root object. 2890 CompileRun("var root = new Object;" 2891 "root.foo = 0;" 2892 "root = new Object;"); 2893 2894 SimulateIncrementalMarking(); 2895 2896 // Compile a StoreIC that performs the prepared map transition. This 2897 // will restart incremental marking and should make sure the root is 2898 // marked grey again. 2899 CompileRun("function f(o) {" 2900 " o.foo = 0;" 2901 "}" 2902 "f(new Object);" 2903 "f(root);"); 2904 2905 // This bug only triggers with aggressive IC clearing. 2906 CcTest::heap()->AgeInlineCaches(); 2907 2908 // Explicitly request GC to perform final marking step and sweeping. 2909 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 2910 2911 Handle<JSObject> root = 2912 v8::Utils::OpenHandle( 2913 *v8::Handle<v8::Object>::Cast( 2914 CcTest::global()->Get(v8_str("root")))); 2915 2916 // The root object should be in a sane state. 2917 CHECK(root->IsJSObject()); 2918 CHECK(root->map()->IsMap()); 2919 } 2920 2921 2922 TEST(Regress2143b) { 2923 i::FLAG_collect_maps = true; 2924 i::FLAG_incremental_marking = true; 2925 i::FLAG_allow_natives_syntax = true; 2926 CcTest::InitializeVM(); 2927 v8::HandleScope scope(CcTest::isolate()); 2928 2929 // Prepare a map transition from the root object together with a yet 2930 // untransitioned root object. 2931 CompileRun("var root = new Object;" 2932 "root.foo = 0;" 2933 "root = new Object;"); 2934 2935 SimulateIncrementalMarking(); 2936 2937 // Compile an optimized LStoreNamedField that performs the prepared 2938 // map transition. This will restart incremental marking and should 2939 // make sure the root is marked grey again. 2940 CompileRun("function f(o) {" 2941 " o.foo = 0;" 2942 "}" 2943 "f(new Object);" 2944 "f(new Object);" 2945 "%OptimizeFunctionOnNextCall(f);" 2946 "f(root);" 2947 "%DeoptimizeFunction(f);"); 2948 2949 // This bug only triggers with aggressive IC clearing. 2950 CcTest::heap()->AgeInlineCaches(); 2951 2952 // Explicitly request GC to perform final marking step and sweeping. 2953 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 2954 2955 Handle<JSObject> root = 2956 v8::Utils::OpenHandle( 2957 *v8::Handle<v8::Object>::Cast( 2958 CcTest::global()->Get(v8_str("root")))); 2959 2960 // The root object should be in a sane state. 2961 CHECK(root->IsJSObject()); 2962 CHECK(root->map()->IsMap()); 2963 } 2964 2965 2966 TEST(ReleaseOverReservedPages) { 2967 if (FLAG_never_compact) return; 2968 i::FLAG_trace_gc = true; 2969 // The optimizer can allocate stuff, messing up the test. 2970 i::FLAG_crankshaft = false; 2971 i::FLAG_always_opt = false; 2972 CcTest::InitializeVM(); 2973 Isolate* isolate = CcTest::i_isolate(); 2974 Factory* factory = isolate->factory(); 2975 Heap* heap = isolate->heap(); 2976 v8::HandleScope scope(CcTest::isolate()); 2977 static const int number_of_test_pages = 20; 2978 2979 // Prepare many pages with low live-bytes count. 2980 PagedSpace* old_pointer_space = heap->old_pointer_space(); 2981 CHECK_EQ(1, old_pointer_space->CountTotalPages()); 2982 for (int i = 0; i < number_of_test_pages; i++) { 2983 AlwaysAllocateScope always_allocate(isolate); 2984 SimulateFullSpace(old_pointer_space); 2985 factory->NewFixedArray(1, TENURED); 2986 } 2987 CHECK_EQ(number_of_test_pages + 1, old_pointer_space->CountTotalPages()); 2988 2989 // Triggering one GC will cause a lot of garbage to be discovered but 2990 // even spread across all allocated pages. 2991 heap->CollectAllGarbage(Heap::kNoGCFlags, "triggered for preparation"); 2992 CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages()); 2993 2994 // Triggering subsequent GCs should cause at least half of the pages 2995 // to be released to the OS after at most two cycles. 2996 heap->CollectAllGarbage(Heap::kNoGCFlags, "triggered by test 1"); 2997 CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages()); 2998 heap->CollectAllGarbage(Heap::kNoGCFlags, "triggered by test 2"); 2999 CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages() * 2); 3000 3001 // Triggering a last-resort GC should cause all pages to be released to the 3002 // OS so that other processes can seize the memory. If we get a failure here 3003 // where there are 2 pages left instead of 1, then we should increase the 3004 // size of the first page a little in SizeOfFirstPage in spaces.cc. The 3005 // first page should be small in order to reduce memory used when the VM 3006 // boots, but if the 20 small arrays don't fit on the first page then that's 3007 // an indication that it is too small. 3008 heap->CollectAllAvailableGarbage("triggered really hard"); 3009 CHECK_EQ(1, old_pointer_space->CountTotalPages()); 3010 } 3011 3012 3013 TEST(Regress2237) { 3014 i::FLAG_stress_compaction = false; 3015 CcTest::InitializeVM(); 3016 Isolate* isolate = CcTest::i_isolate(); 3017 Factory* factory = isolate->factory(); 3018 v8::HandleScope scope(CcTest::isolate()); 3019 Handle<String> slice(CcTest::heap()->empty_string()); 3020 3021 { 3022 // Generate a parent that lives in new-space. 3023 v8::HandleScope inner_scope(CcTest::isolate()); 3024 const char* c = "This text is long enough to trigger sliced strings."; 3025 Handle<String> s = factory->NewStringFromAsciiChecked(c); 3026 CHECK(s->IsSeqOneByteString()); 3027 CHECK(CcTest::heap()->InNewSpace(*s)); 3028 3029 // Generate a sliced string that is based on the above parent and 3030 // lives in old-space. 3031 SimulateFullSpace(CcTest::heap()->new_space()); 3032 AlwaysAllocateScope always_allocate(isolate); 3033 Handle<String> t = factory->NewProperSubString(s, 5, 35); 3034 CHECK(t->IsSlicedString()); 3035 CHECK(!CcTest::heap()->InNewSpace(*t)); 3036 *slice.location() = *t.location(); 3037 } 3038 3039 CHECK(SlicedString::cast(*slice)->parent()->IsSeqOneByteString()); 3040 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 3041 CHECK(SlicedString::cast(*slice)->parent()->IsSeqOneByteString()); 3042 } 3043 3044 3045 #ifdef OBJECT_PRINT 3046 TEST(PrintSharedFunctionInfo) { 3047 CcTest::InitializeVM(); 3048 v8::HandleScope scope(CcTest::isolate()); 3049 const char* source = "f = function() { return 987654321; }\n" 3050 "g = function() { return 123456789; }\n"; 3051 CompileRun(source); 3052 Handle<JSFunction> g = 3053 v8::Utils::OpenHandle( 3054 *v8::Handle<v8::Function>::Cast( 3055 CcTest::global()->Get(v8_str("g")))); 3056 3057 DisallowHeapAllocation no_allocation; 3058 g->shared()->PrintLn(); 3059 } 3060 #endif // OBJECT_PRINT 3061 3062 3063 TEST(Regress2211) { 3064 CcTest::InitializeVM(); 3065 v8::HandleScope scope(CcTest::isolate()); 3066 3067 v8::Handle<v8::String> value = v8_str("val string"); 3068 Smi* hash = Smi::FromInt(321); 3069 Factory* factory = CcTest::i_isolate()->factory(); 3070 3071 for (int i = 0; i < 2; i++) { 3072 // Store identity hash first and common hidden property second. 3073 v8::Handle<v8::Object> obj = v8::Object::New(CcTest::isolate()); 3074 Handle<JSObject> internal_obj = v8::Utils::OpenHandle(*obj); 3075 CHECK(internal_obj->HasFastProperties()); 3076 3077 // In the first iteration, set hidden value first and identity hash second. 3078 // In the second iteration, reverse the order. 3079 if (i == 0) obj->SetHiddenValue(v8_str("key string"), value); 3080 JSObject::SetIdentityHash(internal_obj, handle(hash, CcTest::i_isolate())); 3081 if (i == 1) obj->SetHiddenValue(v8_str("key string"), value); 3082 3083 // Check values. 3084 CHECK_EQ(hash, 3085 internal_obj->GetHiddenProperty(factory->identity_hash_string())); 3086 CHECK(value->Equals(obj->GetHiddenValue(v8_str("key string")))); 3087 3088 // Check size. 3089 FieldIndex index = FieldIndex::ForDescriptor(internal_obj->map(), 0); 3090 ObjectHashTable* hashtable = ObjectHashTable::cast( 3091 internal_obj->RawFastPropertyAt(index)); 3092 // HashTable header (5) and 4 initial entries (8). 3093 CHECK_LE(hashtable->SizeFor(hashtable->length()), 13 * kPointerSize); 3094 } 3095 } 3096 3097 3098 TEST(IncrementalMarkingClearsTypeFeedbackInfo) { 3099 if (i::FLAG_always_opt) return; 3100 CcTest::InitializeVM(); 3101 v8::HandleScope scope(CcTest::isolate()); 3102 v8::Local<v8::Value> fun1, fun2; 3103 3104 { 3105 LocalContext env; 3106 CompileRun("function fun() {};"); 3107 fun1 = env->Global()->Get(v8_str("fun")); 3108 } 3109 3110 { 3111 LocalContext env; 3112 CompileRun("function fun() {};"); 3113 fun2 = env->Global()->Get(v8_str("fun")); 3114 } 3115 3116 // Prepare function f that contains type feedback for closures 3117 // originating from two different native contexts. 3118 CcTest::global()->Set(v8_str("fun1"), fun1); 3119 CcTest::global()->Set(v8_str("fun2"), fun2); 3120 CompileRun("function f(a, b) { a(); b(); } f(fun1, fun2);"); 3121 3122 Handle<JSFunction> f = 3123 v8::Utils::OpenHandle( 3124 *v8::Handle<v8::Function>::Cast( 3125 CcTest::global()->Get(v8_str("f")))); 3126 3127 Handle<FixedArray> feedback_vector(f->shared()->feedback_vector()); 3128 3129 CHECK_EQ(2, feedback_vector->length()); 3130 CHECK(feedback_vector->get(0)->IsJSFunction()); 3131 CHECK(feedback_vector->get(1)->IsJSFunction()); 3132 3133 SimulateIncrementalMarking(); 3134 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 3135 3136 CHECK_EQ(2, feedback_vector->length()); 3137 CHECK_EQ(feedback_vector->get(0), 3138 *TypeFeedbackInfo::UninitializedSentinel(CcTest::i_isolate())); 3139 CHECK_EQ(feedback_vector->get(1), 3140 *TypeFeedbackInfo::UninitializedSentinel(CcTest::i_isolate())); 3141 } 3142 3143 3144 static Code* FindFirstIC(Code* code, Code::Kind kind) { 3145 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | 3146 RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) | 3147 RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID); 3148 for (RelocIterator it(code, mask); !it.done(); it.next()) { 3149 RelocInfo* info = it.rinfo(); 3150 Code* target = Code::GetCodeFromTargetAddress(info->target_address()); 3151 if (target->is_inline_cache_stub() && target->kind() == kind) { 3152 return target; 3153 } 3154 } 3155 return NULL; 3156 } 3157 3158 3159 TEST(IncrementalMarkingPreservesMonomorphicIC) { 3160 if (i::FLAG_always_opt) return; 3161 CcTest::InitializeVM(); 3162 v8::HandleScope scope(CcTest::isolate()); 3163 3164 // Prepare function f that contains a monomorphic IC for object 3165 // originating from the same native context. 3166 CompileRun("function fun() { this.x = 1; }; var obj = new fun();" 3167 "function f(o) { return o.x; } f(obj); f(obj);"); 3168 Handle<JSFunction> f = 3169 v8::Utils::OpenHandle( 3170 *v8::Handle<v8::Function>::Cast( 3171 CcTest::global()->Get(v8_str("f")))); 3172 3173 Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC); 3174 CHECK(ic_before->ic_state() == MONOMORPHIC); 3175 3176 SimulateIncrementalMarking(); 3177 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 3178 3179 Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC); 3180 CHECK(ic_after->ic_state() == MONOMORPHIC); 3181 } 3182 3183 3184 TEST(IncrementalMarkingClearsMonomorphicIC) { 3185 if (i::FLAG_always_opt) return; 3186 CcTest::InitializeVM(); 3187 v8::HandleScope scope(CcTest::isolate()); 3188 v8::Local<v8::Value> obj1; 3189 3190 { 3191 LocalContext env; 3192 CompileRun("function fun() { this.x = 1; }; var obj = new fun();"); 3193 obj1 = env->Global()->Get(v8_str("obj")); 3194 } 3195 3196 // Prepare function f that contains a monomorphic IC for object 3197 // originating from a different native context. 3198 CcTest::global()->Set(v8_str("obj1"), obj1); 3199 CompileRun("function f(o) { return o.x; } f(obj1); f(obj1);"); 3200 Handle<JSFunction> f = 3201 v8::Utils::OpenHandle( 3202 *v8::Handle<v8::Function>::Cast( 3203 CcTest::global()->Get(v8_str("f")))); 3204 3205 Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC); 3206 CHECK(ic_before->ic_state() == MONOMORPHIC); 3207 3208 // Fire context dispose notification. 3209 v8::V8::ContextDisposedNotification(); 3210 SimulateIncrementalMarking(); 3211 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 3212 3213 Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC); 3214 CHECK(IC::IsCleared(ic_after)); 3215 } 3216 3217 3218 TEST(IncrementalMarkingClearsPolymorphicIC) { 3219 if (i::FLAG_always_opt) return; 3220 CcTest::InitializeVM(); 3221 v8::HandleScope scope(CcTest::isolate()); 3222 v8::Local<v8::Value> obj1, obj2; 3223 3224 { 3225 LocalContext env; 3226 CompileRun("function fun() { this.x = 1; }; var obj = new fun();"); 3227 obj1 = env->Global()->Get(v8_str("obj")); 3228 } 3229 3230 { 3231 LocalContext env; 3232 CompileRun("function fun() { this.x = 2; }; var obj = new fun();"); 3233 obj2 = env->Global()->Get(v8_str("obj")); 3234 } 3235 3236 // Prepare function f that contains a polymorphic IC for objects 3237 // originating from two different native contexts. 3238 CcTest::global()->Set(v8_str("obj1"), obj1); 3239 CcTest::global()->Set(v8_str("obj2"), obj2); 3240 CompileRun("function f(o) { return o.x; } f(obj1); f(obj1); f(obj2);"); 3241 Handle<JSFunction> f = 3242 v8::Utils::OpenHandle( 3243 *v8::Handle<v8::Function>::Cast( 3244 CcTest::global()->Get(v8_str("f")))); 3245 3246 Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC); 3247 CHECK(ic_before->ic_state() == POLYMORPHIC); 3248 3249 // Fire context dispose notification. 3250 v8::V8::ContextDisposedNotification(); 3251 SimulateIncrementalMarking(); 3252 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 3253 3254 Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC); 3255 CHECK(IC::IsCleared(ic_after)); 3256 } 3257 3258 3259 class SourceResource: public v8::String::ExternalAsciiStringResource { 3260 public: 3261 explicit SourceResource(const char* data) 3262 : data_(data), length_(strlen(data)) { } 3263 3264 virtual void Dispose() { 3265 i::DeleteArray(data_); 3266 data_ = NULL; 3267 } 3268 3269 const char* data() const { return data_; } 3270 3271 size_t length() const { return length_; } 3272 3273 bool IsDisposed() { return data_ == NULL; } 3274 3275 private: 3276 const char* data_; 3277 size_t length_; 3278 }; 3279 3280 3281 void ReleaseStackTraceDataTest(const char* source, const char* accessor) { 3282 // Test that the data retained by the Error.stack accessor is released 3283 // after the first time the accessor is fired. We use external string 3284 // to check whether the data is being released since the external string 3285 // resource's callback is fired when the external string is GC'ed. 3286 v8::HandleScope scope(CcTest::isolate()); 3287 SourceResource* resource = new SourceResource(i::StrDup(source)); 3288 { 3289 v8::HandleScope scope(CcTest::isolate()); 3290 v8::Handle<v8::String> source_string = 3291 v8::String::NewExternal(CcTest::isolate(), resource); 3292 CcTest::heap()->CollectAllAvailableGarbage(); 3293 v8::Script::Compile(source_string)->Run(); 3294 CHECK(!resource->IsDisposed()); 3295 } 3296 // CcTest::heap()->CollectAllAvailableGarbage(); 3297 CHECK(!resource->IsDisposed()); 3298 3299 CompileRun(accessor); 3300 CcTest::heap()->CollectAllAvailableGarbage(); 3301 3302 // External source has been released. 3303 CHECK(resource->IsDisposed()); 3304 delete resource; 3305 } 3306 3307 3308 TEST(ReleaseStackTraceData) { 3309 if (i::FLAG_always_opt) { 3310 // TODO(ulan): Remove this once the memory leak via code_next_link is fixed. 3311 // See: https://codereview.chromium.org/181833004/ 3312 return; 3313 } 3314 FLAG_use_ic = false; // ICs retain objects. 3315 FLAG_concurrent_recompilation = false; 3316 CcTest::InitializeVM(); 3317 static const char* source1 = "var error = null; " 3318 /* Normal Error */ "try { " 3319 " throw new Error(); " 3320 "} catch (e) { " 3321 " error = e; " 3322 "} "; 3323 static const char* source2 = "var error = null; " 3324 /* Stack overflow */ "try { " 3325 " (function f() { f(); })(); " 3326 "} catch (e) { " 3327 " error = e; " 3328 "} "; 3329 static const char* source3 = "var error = null; " 3330 /* Normal Error */ "try { " 3331 /* as prototype */ " throw new Error(); " 3332 "} catch (e) { " 3333 " error = {}; " 3334 " error.__proto__ = e; " 3335 "} "; 3336 static const char* source4 = "var error = null; " 3337 /* Stack overflow */ "try { " 3338 /* as prototype */ " (function f() { f(); })(); " 3339 "} catch (e) { " 3340 " error = {}; " 3341 " error.__proto__ = e; " 3342 "} "; 3343 static const char* getter = "error.stack"; 3344 static const char* setter = "error.stack = 0"; 3345 3346 ReleaseStackTraceDataTest(source1, setter); 3347 ReleaseStackTraceDataTest(source2, setter); 3348 // We do not test source3 and source4 with setter, since the setter is 3349 // supposed to (untypically) write to the receiver, not the holder. This is 3350 // to emulate the behavior of a data property. 3351 3352 ReleaseStackTraceDataTest(source1, getter); 3353 ReleaseStackTraceDataTest(source2, getter); 3354 ReleaseStackTraceDataTest(source3, getter); 3355 ReleaseStackTraceDataTest(source4, getter); 3356 } 3357 3358 3359 TEST(Regress159140) { 3360 i::FLAG_allow_natives_syntax = true; 3361 i::FLAG_flush_code_incrementally = true; 3362 CcTest::InitializeVM(); 3363 Isolate* isolate = CcTest::i_isolate(); 3364 Heap* heap = isolate->heap(); 3365 HandleScope scope(isolate); 3366 3367 // Perform one initial GC to enable code flushing. 3368 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 3369 3370 // Prepare several closures that are all eligible for code flushing 3371 // because all reachable ones are not optimized. Make sure that the 3372 // optimized code object is directly reachable through a handle so 3373 // that it is marked black during incremental marking. 3374 Handle<Code> code; 3375 { 3376 HandleScope inner_scope(isolate); 3377 CompileRun("function h(x) {}" 3378 "function mkClosure() {" 3379 " return function(x) { return x + 1; };" 3380 "}" 3381 "var f = mkClosure();" 3382 "var g = mkClosure();" 3383 "f(1); f(2);" 3384 "g(1); g(2);" 3385 "h(1); h(2);" 3386 "%OptimizeFunctionOnNextCall(f); f(3);" 3387 "%OptimizeFunctionOnNextCall(h); h(3);"); 3388 3389 Handle<JSFunction> f = 3390 v8::Utils::OpenHandle( 3391 *v8::Handle<v8::Function>::Cast( 3392 CcTest::global()->Get(v8_str("f")))); 3393 CHECK(f->is_compiled()); 3394 CompileRun("f = null;"); 3395 3396 Handle<JSFunction> g = 3397 v8::Utils::OpenHandle( 3398 *v8::Handle<v8::Function>::Cast( 3399 CcTest::global()->Get(v8_str("g")))); 3400 CHECK(g->is_compiled()); 3401 const int kAgingThreshold = 6; 3402 for (int i = 0; i < kAgingThreshold; i++) { 3403 g->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 3404 } 3405 3406 code = inner_scope.CloseAndEscape(Handle<Code>(f->code())); 3407 } 3408 3409 // Simulate incremental marking so that the functions are enqueued as 3410 // code flushing candidates. Then optimize one function. Finally 3411 // finish the GC to complete code flushing. 3412 SimulateIncrementalMarking(); 3413 CompileRun("%OptimizeFunctionOnNextCall(g); g(3);"); 3414 heap->CollectAllGarbage(Heap::kNoGCFlags); 3415 3416 // Unoptimized code is missing and the deoptimizer will go ballistic. 3417 CompileRun("g('bozo');"); 3418 } 3419 3420 3421 TEST(Regress165495) { 3422 i::FLAG_allow_natives_syntax = true; 3423 i::FLAG_flush_code_incrementally = true; 3424 CcTest::InitializeVM(); 3425 Isolate* isolate = CcTest::i_isolate(); 3426 Heap* heap = isolate->heap(); 3427 HandleScope scope(isolate); 3428 3429 // Perform one initial GC to enable code flushing. 3430 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 3431 3432 // Prepare an optimized closure that the optimized code map will get 3433 // populated. Then age the unoptimized code to trigger code flushing 3434 // but make sure the optimized code is unreachable. 3435 { 3436 HandleScope inner_scope(isolate); 3437 CompileRun("function mkClosure() {" 3438 " return function(x) { return x + 1; };" 3439 "}" 3440 "var f = mkClosure();" 3441 "f(1); f(2);" 3442 "%OptimizeFunctionOnNextCall(f); f(3);"); 3443 3444 Handle<JSFunction> f = 3445 v8::Utils::OpenHandle( 3446 *v8::Handle<v8::Function>::Cast( 3447 CcTest::global()->Get(v8_str("f")))); 3448 CHECK(f->is_compiled()); 3449 const int kAgingThreshold = 6; 3450 for (int i = 0; i < kAgingThreshold; i++) { 3451 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 3452 } 3453 3454 CompileRun("f = null;"); 3455 } 3456 3457 // Simulate incremental marking so that unoptimized code is flushed 3458 // even though it still is cached in the optimized code map. 3459 SimulateIncrementalMarking(); 3460 heap->CollectAllGarbage(Heap::kNoGCFlags); 3461 3462 // Make a new closure that will get code installed from the code map. 3463 // Unoptimized code is missing and the deoptimizer will go ballistic. 3464 CompileRun("var g = mkClosure(); g('bozo');"); 3465 } 3466 3467 3468 TEST(Regress169209) { 3469 i::FLAG_stress_compaction = false; 3470 i::FLAG_allow_natives_syntax = true; 3471 i::FLAG_flush_code_incrementally = true; 3472 3473 CcTest::InitializeVM(); 3474 Isolate* isolate = CcTest::i_isolate(); 3475 Heap* heap = isolate->heap(); 3476 HandleScope scope(isolate); 3477 3478 // Perform one initial GC to enable code flushing. 3479 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 3480 3481 // Prepare a shared function info eligible for code flushing for which 3482 // the unoptimized code will be replaced during optimization. 3483 Handle<SharedFunctionInfo> shared1; 3484 { 3485 HandleScope inner_scope(isolate); 3486 CompileRun("function f() { return 'foobar'; }" 3487 "function g(x) { if (x) f(); }" 3488 "f();" 3489 "g(false);" 3490 "g(false);"); 3491 3492 Handle<JSFunction> f = 3493 v8::Utils::OpenHandle( 3494 *v8::Handle<v8::Function>::Cast( 3495 CcTest::global()->Get(v8_str("f")))); 3496 CHECK(f->is_compiled()); 3497 const int kAgingThreshold = 6; 3498 for (int i = 0; i < kAgingThreshold; i++) { 3499 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 3500 } 3501 3502 shared1 = inner_scope.CloseAndEscape(handle(f->shared(), isolate)); 3503 } 3504 3505 // Prepare a shared function info eligible for code flushing that will 3506 // represent the dangling tail of the candidate list. 3507 Handle<SharedFunctionInfo> shared2; 3508 { 3509 HandleScope inner_scope(isolate); 3510 CompileRun("function flushMe() { return 0; }" 3511 "flushMe(1);"); 3512 3513 Handle<JSFunction> f = 3514 v8::Utils::OpenHandle( 3515 *v8::Handle<v8::Function>::Cast( 3516 CcTest::global()->Get(v8_str("flushMe")))); 3517 CHECK(f->is_compiled()); 3518 const int kAgingThreshold = 6; 3519 for (int i = 0; i < kAgingThreshold; i++) { 3520 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 3521 } 3522 3523 shared2 = inner_scope.CloseAndEscape(handle(f->shared(), isolate)); 3524 } 3525 3526 // Simulate incremental marking and collect code flushing candidates. 3527 SimulateIncrementalMarking(); 3528 CHECK(shared1->code()->gc_metadata() != NULL); 3529 3530 // Optimize function and make sure the unoptimized code is replaced. 3531 #ifdef DEBUG 3532 FLAG_stop_at = "f"; 3533 #endif 3534 CompileRun("%OptimizeFunctionOnNextCall(g);" 3535 "g(false);"); 3536 3537 // Finish garbage collection cycle. 3538 heap->CollectAllGarbage(Heap::kNoGCFlags); 3539 CHECK(shared1->code()->gc_metadata() == NULL); 3540 } 3541 3542 3543 // Helper function that simulates a fill new-space in the heap. 3544 static inline void AllocateAllButNBytes(v8::internal::NewSpace* space, 3545 int extra_bytes) { 3546 int space_remaining = static_cast<int>( 3547 *space->allocation_limit_address() - *space->allocation_top_address()); 3548 CHECK(space_remaining >= extra_bytes); 3549 int new_linear_size = space_remaining - extra_bytes; 3550 v8::internal::AllocationResult allocation = 3551 space->AllocateRaw(new_linear_size); 3552 v8::internal::FreeListNode* node = 3553 v8::internal::FreeListNode::cast(allocation.ToObjectChecked()); 3554 node->set_size(space->heap(), new_linear_size); 3555 } 3556 3557 3558 TEST(Regress169928) { 3559 i::FLAG_allow_natives_syntax = true; 3560 i::FLAG_crankshaft = false; 3561 CcTest::InitializeVM(); 3562 Isolate* isolate = CcTest::i_isolate(); 3563 Factory* factory = isolate->factory(); 3564 v8::HandleScope scope(CcTest::isolate()); 3565 3566 // Some flags turn Scavenge collections into Mark-sweep collections 3567 // and hence are incompatible with this test case. 3568 if (FLAG_gc_global || FLAG_stress_compaction) return; 3569 3570 // Prepare the environment 3571 CompileRun("function fastliteralcase(literal, value) {" 3572 " literal[0] = value;" 3573 " return literal;" 3574 "}" 3575 "function get_standard_literal() {" 3576 " var literal = [1, 2, 3];" 3577 " return literal;" 3578 "}" 3579 "obj = fastliteralcase(get_standard_literal(), 1);" 3580 "obj = fastliteralcase(get_standard_literal(), 1.5);" 3581 "obj = fastliteralcase(get_standard_literal(), 2);"); 3582 3583 // prepare the heap 3584 v8::Local<v8::String> mote_code_string = 3585 v8_str("fastliteralcase(mote, 2.5);"); 3586 3587 v8::Local<v8::String> array_name = v8_str("mote"); 3588 CcTest::global()->Set(array_name, v8::Int32::New(CcTest::isolate(), 0)); 3589 3590 // First make sure we flip spaces 3591 CcTest::heap()->CollectGarbage(NEW_SPACE); 3592 3593 // Allocate the object. 3594 Handle<FixedArray> array_data = factory->NewFixedArray(2, NOT_TENURED); 3595 array_data->set(0, Smi::FromInt(1)); 3596 array_data->set(1, Smi::FromInt(2)); 3597 3598 AllocateAllButNBytes(CcTest::heap()->new_space(), 3599 JSArray::kSize + AllocationMemento::kSize + 3600 kPointerSize); 3601 3602 Handle<JSArray> array = factory->NewJSArrayWithElements(array_data, 3603 FAST_SMI_ELEMENTS, 3604 NOT_TENURED); 3605 3606 CHECK_EQ(Smi::FromInt(2), array->length()); 3607 CHECK(array->HasFastSmiOrObjectElements()); 3608 3609 // We need filler the size of AllocationMemento object, plus an extra 3610 // fill pointer value. 3611 HeapObject* obj = NULL; 3612 AllocationResult allocation = CcTest::heap()->new_space()->AllocateRaw( 3613 AllocationMemento::kSize + kPointerSize); 3614 CHECK(allocation.To(&obj)); 3615 Address addr_obj = obj->address(); 3616 CcTest::heap()->CreateFillerObjectAt( 3617 addr_obj, AllocationMemento::kSize + kPointerSize); 3618 3619 // Give the array a name, making sure not to allocate strings. 3620 v8::Handle<v8::Object> array_obj = v8::Utils::ToLocal(array); 3621 CcTest::global()->Set(array_name, array_obj); 3622 3623 // This should crash with a protection violation if we are running a build 3624 // with the bug. 3625 AlwaysAllocateScope aa_scope(isolate); 3626 v8::Script::Compile(mote_code_string)->Run(); 3627 } 3628 3629 3630 TEST(Regress168801) { 3631 if (i::FLAG_never_compact) return; 3632 i::FLAG_always_compact = true; 3633 i::FLAG_cache_optimized_code = false; 3634 i::FLAG_allow_natives_syntax = true; 3635 i::FLAG_flush_code_incrementally = true; 3636 CcTest::InitializeVM(); 3637 Isolate* isolate = CcTest::i_isolate(); 3638 Heap* heap = isolate->heap(); 3639 HandleScope scope(isolate); 3640 3641 // Perform one initial GC to enable code flushing. 3642 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 3643 3644 // Ensure the code ends up on an evacuation candidate. 3645 SimulateFullSpace(heap->code_space()); 3646 3647 // Prepare an unoptimized function that is eligible for code flushing. 3648 Handle<JSFunction> function; 3649 { 3650 HandleScope inner_scope(isolate); 3651 CompileRun("function mkClosure() {" 3652 " return function(x) { return x + 1; };" 3653 "}" 3654 "var f = mkClosure();" 3655 "f(1); f(2);"); 3656 3657 Handle<JSFunction> f = 3658 v8::Utils::OpenHandle( 3659 *v8::Handle<v8::Function>::Cast( 3660 CcTest::global()->Get(v8_str("f")))); 3661 CHECK(f->is_compiled()); 3662 const int kAgingThreshold = 6; 3663 for (int i = 0; i < kAgingThreshold; i++) { 3664 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 3665 } 3666 3667 function = inner_scope.CloseAndEscape(handle(*f, isolate)); 3668 } 3669 3670 // Simulate incremental marking so that unoptimized function is enqueued as a 3671 // candidate for code flushing. The shared function info however will not be 3672 // explicitly enqueued. 3673 SimulateIncrementalMarking(); 3674 3675 // Now optimize the function so that it is taken off the candidate list. 3676 { 3677 HandleScope inner_scope(isolate); 3678 CompileRun("%OptimizeFunctionOnNextCall(f); f(3);"); 3679 } 3680 3681 // This cycle will bust the heap and subsequent cycles will go ballistic. 3682 heap->CollectAllGarbage(Heap::kNoGCFlags); 3683 heap->CollectAllGarbage(Heap::kNoGCFlags); 3684 } 3685 3686 3687 TEST(Regress173458) { 3688 if (i::FLAG_never_compact) return; 3689 i::FLAG_always_compact = true; 3690 i::FLAG_cache_optimized_code = false; 3691 i::FLAG_allow_natives_syntax = true; 3692 i::FLAG_flush_code_incrementally = true; 3693 CcTest::InitializeVM(); 3694 Isolate* isolate = CcTest::i_isolate(); 3695 Heap* heap = isolate->heap(); 3696 HandleScope scope(isolate); 3697 3698 // Perform one initial GC to enable code flushing. 3699 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 3700 3701 // Ensure the code ends up on an evacuation candidate. 3702 SimulateFullSpace(heap->code_space()); 3703 3704 // Prepare an unoptimized function that is eligible for code flushing. 3705 Handle<JSFunction> function; 3706 { 3707 HandleScope inner_scope(isolate); 3708 CompileRun("function mkClosure() {" 3709 " return function(x) { return x + 1; };" 3710 "}" 3711 "var f = mkClosure();" 3712 "f(1); f(2);"); 3713 3714 Handle<JSFunction> f = 3715 v8::Utils::OpenHandle( 3716 *v8::Handle<v8::Function>::Cast( 3717 CcTest::global()->Get(v8_str("f")))); 3718 CHECK(f->is_compiled()); 3719 const int kAgingThreshold = 6; 3720 for (int i = 0; i < kAgingThreshold; i++) { 3721 f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2)); 3722 } 3723 3724 function = inner_scope.CloseAndEscape(handle(*f, isolate)); 3725 } 3726 3727 // Simulate incremental marking so that unoptimized function is enqueued as a 3728 // candidate for code flushing. The shared function info however will not be 3729 // explicitly enqueued. 3730 SimulateIncrementalMarking(); 3731 3732 // Now enable the debugger which in turn will disable code flushing. 3733 CHECK(isolate->debug()->Load()); 3734 3735 // This cycle will bust the heap and subsequent cycles will go ballistic. 3736 heap->CollectAllGarbage(Heap::kNoGCFlags); 3737 heap->CollectAllGarbage(Heap::kNoGCFlags); 3738 } 3739 3740 3741 class DummyVisitor : public ObjectVisitor { 3742 public: 3743 void VisitPointers(Object** start, Object** end) { } 3744 }; 3745 3746 3747 TEST(DeferredHandles) { 3748 CcTest::InitializeVM(); 3749 Isolate* isolate = CcTest::i_isolate(); 3750 Heap* heap = isolate->heap(); 3751 v8::HandleScope scope(reinterpret_cast<v8::Isolate*>(isolate)); 3752 HandleScopeData* data = isolate->handle_scope_data(); 3753 Handle<Object> init(heap->empty_string(), isolate); 3754 while (data->next < data->limit) { 3755 Handle<Object> obj(heap->empty_string(), isolate); 3756 } 3757 // An entire block of handles has been filled. 3758 // Next handle would require a new block. 3759 ASSERT(data->next == data->limit); 3760 3761 DeferredHandleScope deferred(isolate); 3762 DummyVisitor visitor; 3763 isolate->handle_scope_implementer()->Iterate(&visitor); 3764 delete deferred.Detach(); 3765 } 3766 3767 3768 TEST(IncrementalMarkingStepMakesBigProgressWithLargeObjects) { 3769 CcTest::InitializeVM(); 3770 v8::HandleScope scope(CcTest::isolate()); 3771 CompileRun("function f(n) {" 3772 " var a = new Array(n);" 3773 " for (var i = 0; i < n; i += 100) a[i] = i;" 3774 "};" 3775 "f(10 * 1024 * 1024);"); 3776 IncrementalMarking* marking = CcTest::heap()->incremental_marking(); 3777 if (marking->IsStopped()) marking->Start(); 3778 // This big step should be sufficient to mark the whole array. 3779 marking->Step(100 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); 3780 ASSERT(marking->IsComplete()); 3781 } 3782 3783 3784 TEST(DisableInlineAllocation) { 3785 i::FLAG_allow_natives_syntax = true; 3786 CcTest::InitializeVM(); 3787 v8::HandleScope scope(CcTest::isolate()); 3788 CompileRun("function test() {" 3789 " var x = [];" 3790 " for (var i = 0; i < 10; i++) {" 3791 " x[i] = [ {}, [1,2,3], [1,x,3] ];" 3792 " }" 3793 "}" 3794 "function run() {" 3795 " %OptimizeFunctionOnNextCall(test);" 3796 " test();" 3797 " %DeoptimizeFunction(test);" 3798 "}"); 3799 3800 // Warm-up with inline allocation enabled. 3801 CompileRun("test(); test(); run();"); 3802 3803 // Run test with inline allocation disabled. 3804 CcTest::heap()->DisableInlineAllocation(); 3805 CompileRun("run()"); 3806 3807 // Run test with inline allocation re-enabled. 3808 CcTest::heap()->EnableInlineAllocation(); 3809 CompileRun("run()"); 3810 } 3811 3812 3813 static int AllocationSitesCount(Heap* heap) { 3814 int count = 0; 3815 for (Object* site = heap->allocation_sites_list(); 3816 !(site->IsUndefined()); 3817 site = AllocationSite::cast(site)->weak_next()) { 3818 count++; 3819 } 3820 return count; 3821 } 3822 3823 3824 TEST(EnsureAllocationSiteDependentCodesProcessed) { 3825 if (i::FLAG_always_opt || !i::FLAG_crankshaft) return; 3826 i::FLAG_allow_natives_syntax = true; 3827 CcTest::InitializeVM(); 3828 Isolate* isolate = CcTest::i_isolate(); 3829 v8::internal::Heap* heap = CcTest::heap(); 3830 GlobalHandles* global_handles = isolate->global_handles(); 3831 3832 if (!isolate->use_crankshaft()) return; 3833 3834 // The allocation site at the head of the list is ours. 3835 Handle<AllocationSite> site; 3836 { 3837 LocalContext context; 3838 v8::HandleScope scope(context->GetIsolate()); 3839 3840 int count = AllocationSitesCount(heap); 3841 CompileRun("var bar = function() { return (new Array()); };" 3842 "var a = bar();" 3843 "bar();" 3844 "bar();"); 3845 3846 // One allocation site should have been created. 3847 int new_count = AllocationSitesCount(heap); 3848 CHECK_EQ(new_count, (count + 1)); 3849 site = Handle<AllocationSite>::cast( 3850 global_handles->Create( 3851 AllocationSite::cast(heap->allocation_sites_list()))); 3852 3853 CompileRun("%OptimizeFunctionOnNextCall(bar); bar();"); 3854 3855 DependentCode::GroupStartIndexes starts(site->dependent_code()); 3856 CHECK_GE(starts.number_of_entries(), 1); 3857 int index = starts.at(DependentCode::kAllocationSiteTransitionChangedGroup); 3858 CHECK(site->dependent_code()->is_code_at(index)); 3859 Code* function_bar = site->dependent_code()->code_at(index); 3860 Handle<JSFunction> bar_handle = 3861 v8::Utils::OpenHandle( 3862 *v8::Handle<v8::Function>::Cast( 3863 CcTest::global()->Get(v8_str("bar")))); 3864 CHECK_EQ(bar_handle->code(), function_bar); 3865 } 3866 3867 // Now make sure that a gc should get rid of the function, even though we 3868 // still have the allocation site alive. 3869 for (int i = 0; i < 4; i++) { 3870 heap->CollectAllGarbage(Heap::kNoGCFlags); 3871 } 3872 3873 // The site still exists because of our global handle, but the code is no 3874 // longer referred to by dependent_code(). 3875 DependentCode::GroupStartIndexes starts(site->dependent_code()); 3876 int index = starts.at(DependentCode::kAllocationSiteTransitionChangedGroup); 3877 CHECK(!(site->dependent_code()->is_code_at(index))); 3878 } 3879 3880 3881 TEST(CellsInOptimizedCodeAreWeak) { 3882 if (i::FLAG_always_opt || !i::FLAG_crankshaft) return; 3883 i::FLAG_weak_embedded_objects_in_optimized_code = true; 3884 i::FLAG_allow_natives_syntax = true; 3885 CcTest::InitializeVM(); 3886 Isolate* isolate = CcTest::i_isolate(); 3887 v8::internal::Heap* heap = CcTest::heap(); 3888 3889 if (!isolate->use_crankshaft()) return; 3890 HandleScope outer_scope(heap->isolate()); 3891 Handle<Code> code; 3892 { 3893 LocalContext context; 3894 HandleScope scope(heap->isolate()); 3895 3896 CompileRun("bar = (function() {" 3897 " function bar() {" 3898 " return foo(1);" 3899 " };" 3900 " var foo = function(x) { with (x) { return 1 + x; } };" 3901 " bar(foo);" 3902 " bar(foo);" 3903 " bar(foo);" 3904 " %OptimizeFunctionOnNextCall(bar);" 3905 " bar(foo);" 3906 " return bar;})();"); 3907 3908 Handle<JSFunction> bar = 3909 v8::Utils::OpenHandle( 3910 *v8::Handle<v8::Function>::Cast( 3911 CcTest::global()->Get(v8_str("bar")))); 3912 code = scope.CloseAndEscape(Handle<Code>(bar->code())); 3913 } 3914 3915 // Now make sure that a gc should get rid of the function 3916 for (int i = 0; i < 4; i++) { 3917 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 3918 } 3919 3920 ASSERT(code->marked_for_deoptimization()); 3921 } 3922 3923 3924 TEST(ObjectsInOptimizedCodeAreWeak) { 3925 if (i::FLAG_always_opt || !i::FLAG_crankshaft) return; 3926 i::FLAG_weak_embedded_objects_in_optimized_code = true; 3927 i::FLAG_allow_natives_syntax = true; 3928 CcTest::InitializeVM(); 3929 Isolate* isolate = CcTest::i_isolate(); 3930 v8::internal::Heap* heap = CcTest::heap(); 3931 3932 if (!isolate->use_crankshaft()) return; 3933 HandleScope outer_scope(heap->isolate()); 3934 Handle<Code> code; 3935 { 3936 LocalContext context; 3937 HandleScope scope(heap->isolate()); 3938 3939 CompileRun("function bar() {" 3940 " return foo(1);" 3941 "};" 3942 "function foo(x) { with (x) { return 1 + x; } };" 3943 "bar();" 3944 "bar();" 3945 "bar();" 3946 "%OptimizeFunctionOnNextCall(bar);" 3947 "bar();"); 3948 3949 Handle<JSFunction> bar = 3950 v8::Utils::OpenHandle( 3951 *v8::Handle<v8::Function>::Cast( 3952 CcTest::global()->Get(v8_str("bar")))); 3953 code = scope.CloseAndEscape(Handle<Code>(bar->code())); 3954 } 3955 3956 // Now make sure that a gc should get rid of the function 3957 for (int i = 0; i < 4; i++) { 3958 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 3959 } 3960 3961 ASSERT(code->marked_for_deoptimization()); 3962 } 3963 3964 3965 TEST(NoWeakHashTableLeakWithIncrementalMarking) { 3966 if (i::FLAG_always_opt || !i::FLAG_crankshaft) return; 3967 if (!i::FLAG_incremental_marking) return; 3968 i::FLAG_weak_embedded_objects_in_optimized_code = true; 3969 i::FLAG_allow_natives_syntax = true; 3970 i::FLAG_compilation_cache = false; 3971 CcTest::InitializeVM(); 3972 Isolate* isolate = CcTest::i_isolate(); 3973 v8::internal::Heap* heap = CcTest::heap(); 3974 3975 if (!isolate->use_crankshaft()) return; 3976 HandleScope outer_scope(heap->isolate()); 3977 for (int i = 0; i < 3; i++) { 3978 SimulateIncrementalMarking(); 3979 { 3980 LocalContext context; 3981 HandleScope scope(heap->isolate()); 3982 EmbeddedVector<char, 256> source; 3983 SNPrintF(source, 3984 "function bar%d() {" 3985 " return foo%d(1);" 3986 "};" 3987 "function foo%d(x) { with (x) { return 1 + x; } };" 3988 "bar%d();" 3989 "bar%d();" 3990 "bar%d();" 3991 "%%OptimizeFunctionOnNextCall(bar%d);" 3992 "bar%d();", i, i, i, i, i, i, i, i); 3993 CompileRun(source.start()); 3994 } 3995 heap->CollectAllGarbage(i::Heap::kNoGCFlags); 3996 } 3997 int elements = 0; 3998 if (heap->weak_object_to_code_table()->IsHashTable()) { 3999 WeakHashTable* t = WeakHashTable::cast(heap->weak_object_to_code_table()); 4000 elements = t->NumberOfElements(); 4001 } 4002 CHECK_EQ(0, elements); 4003 } 4004 4005 4006 static Handle<JSFunction> OptimizeDummyFunction(const char* name) { 4007 EmbeddedVector<char, 256> source; 4008 SNPrintF(source, 4009 "function %s() { return 0; }" 4010 "%s(); %s();" 4011 "%%OptimizeFunctionOnNextCall(%s);" 4012 "%s();", name, name, name, name, name); 4013 CompileRun(source.start()); 4014 Handle<JSFunction> fun = 4015 v8::Utils::OpenHandle( 4016 *v8::Handle<v8::Function>::Cast( 4017 CcTest::global()->Get(v8_str(name)))); 4018 return fun; 4019 } 4020 4021 4022 static int GetCodeChainLength(Code* code) { 4023 int result = 0; 4024 while (code->next_code_link()->IsCode()) { 4025 result++; 4026 code = Code::cast(code->next_code_link()); 4027 } 4028 return result; 4029 } 4030 4031 4032 TEST(NextCodeLinkIsWeak) { 4033 i::FLAG_allow_natives_syntax = true; 4034 CcTest::InitializeVM(); 4035 Isolate* isolate = CcTest::i_isolate(); 4036 v8::internal::Heap* heap = CcTest::heap(); 4037 4038 if (!isolate->use_crankshaft()) return; 4039 HandleScope outer_scope(heap->isolate()); 4040 Handle<Code> code; 4041 heap->CollectAllAvailableGarbage(); 4042 int code_chain_length_before, code_chain_length_after; 4043 { 4044 HandleScope scope(heap->isolate()); 4045 Handle<JSFunction> mortal = OptimizeDummyFunction("mortal"); 4046 Handle<JSFunction> immortal = OptimizeDummyFunction("immortal"); 4047 CHECK_EQ(immortal->code()->next_code_link(), mortal->code()); 4048 code_chain_length_before = GetCodeChainLength(immortal->code()); 4049 // Keep the immortal code and let the mortal code die. 4050 code = scope.CloseAndEscape(Handle<Code>(immortal->code())); 4051 CompileRun("mortal = null; immortal = null;"); 4052 } 4053 heap->CollectAllAvailableGarbage(); 4054 // Now mortal code should be dead. 4055 code_chain_length_after = GetCodeChainLength(*code); 4056 CHECK_EQ(code_chain_length_before - 1, code_chain_length_after); 4057 } 4058 4059 4060 static Handle<Code> DummyOptimizedCode(Isolate* isolate) { 4061 i::byte buffer[i::Assembler::kMinimalBufferSize]; 4062 MacroAssembler masm(isolate, buffer, sizeof(buffer)); 4063 CodeDesc desc; 4064 masm.Push(isolate->factory()->undefined_value()); 4065 masm.Drop(1); 4066 masm.GetCode(&desc); 4067 Handle<Object> undefined(isolate->heap()->undefined_value(), isolate); 4068 Handle<Code> code = isolate->factory()->NewCode( 4069 desc, Code::ComputeFlags(Code::OPTIMIZED_FUNCTION), undefined); 4070 CHECK(code->IsCode()); 4071 return code; 4072 } 4073 4074 4075 TEST(NextCodeLinkIsWeak2) { 4076 i::FLAG_allow_natives_syntax = true; 4077 CcTest::InitializeVM(); 4078 Isolate* isolate = CcTest::i_isolate(); 4079 v8::internal::Heap* heap = CcTest::heap(); 4080 4081 if (!isolate->use_crankshaft()) return; 4082 HandleScope outer_scope(heap->isolate()); 4083 heap->CollectAllAvailableGarbage(); 4084 Handle<Context> context(Context::cast(heap->native_contexts_list()), isolate); 4085 Handle<Code> new_head; 4086 Handle<Object> old_head(context->get(Context::OPTIMIZED_CODE_LIST), isolate); 4087 { 4088 HandleScope scope(heap->isolate()); 4089 Handle<Code> immortal = DummyOptimizedCode(isolate); 4090 Handle<Code> mortal = DummyOptimizedCode(isolate); 4091 mortal->set_next_code_link(*old_head); 4092 immortal->set_next_code_link(*mortal); 4093 context->set(Context::OPTIMIZED_CODE_LIST, *immortal); 4094 new_head = scope.CloseAndEscape(immortal); 4095 } 4096 heap->CollectAllAvailableGarbage(); 4097 // Now mortal code should be dead. 4098 CHECK_EQ(*old_head, new_head->next_code_link()); 4099 } 4100 4101 4102 static bool weak_ic_cleared = false; 4103 4104 static void ClearWeakIC(const v8::WeakCallbackData<v8::Object, void>& data) { 4105 printf("clear weak is called\n"); 4106 weak_ic_cleared = true; 4107 v8::Persistent<v8::Value>* p = 4108 reinterpret_cast<v8::Persistent<v8::Value>*>(data.GetParameter()); 4109 CHECK(p->IsNearDeath()); 4110 p->Reset(); 4111 } 4112 4113 4114 // Checks that the value returned by execution of the source is weak. 4115 void CheckWeakness(const char* source) { 4116 i::FLAG_stress_compaction = false; 4117 CcTest::InitializeVM(); 4118 v8::Isolate* isolate = CcTest::isolate(); 4119 v8::HandleScope scope(isolate); 4120 v8::Persistent<v8::Object> garbage; 4121 { 4122 v8::HandleScope scope(isolate); 4123 garbage.Reset(isolate, CompileRun(source)->ToObject()); 4124 } 4125 weak_ic_cleared = false; 4126 garbage.SetWeak(static_cast<void*>(&garbage), &ClearWeakIC); 4127 Heap* heap = CcTest::i_isolate()->heap(); 4128 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 4129 CHECK(weak_ic_cleared); 4130 } 4131 4132 4133 // Each of the following "weak IC" tests creates an IC that embeds a map with 4134 // the prototype pointing to _proto_ and checks that the _proto_ dies on GC. 4135 TEST(WeakMapInMonomorphicLoadIC) { 4136 CheckWeakness("function loadIC(obj) {" 4137 " return obj.name;" 4138 "}" 4139 " (function() {" 4140 " var proto = {'name' : 'weak'};" 4141 " var obj = Object.create(proto);" 4142 " loadIC(obj);" 4143 " loadIC(obj);" 4144 " loadIC(obj);" 4145 " return proto;" 4146 " })();"); 4147 } 4148 4149 4150 TEST(WeakMapInMonomorphicKeyedLoadIC) { 4151 CheckWeakness("function keyedLoadIC(obj, field) {" 4152 " return obj[field];" 4153 "}" 4154 " (function() {" 4155 " var proto = {'name' : 'weak'};" 4156 " var obj = Object.create(proto);" 4157 " keyedLoadIC(obj, 'name');" 4158 " keyedLoadIC(obj, 'name');" 4159 " keyedLoadIC(obj, 'name');" 4160 " return proto;" 4161 " })();"); 4162 } 4163 4164 4165 TEST(WeakMapInMonomorphicStoreIC) { 4166 CheckWeakness("function storeIC(obj, value) {" 4167 " obj.name = value;" 4168 "}" 4169 " (function() {" 4170 " var proto = {'name' : 'weak'};" 4171 " var obj = Object.create(proto);" 4172 " storeIC(obj, 'x');" 4173 " storeIC(obj, 'x');" 4174 " storeIC(obj, 'x');" 4175 " return proto;" 4176 " })();"); 4177 } 4178 4179 4180 TEST(WeakMapInMonomorphicKeyedStoreIC) { 4181 CheckWeakness("function keyedStoreIC(obj, field, value) {" 4182 " obj[field] = value;" 4183 "}" 4184 " (function() {" 4185 " var proto = {'name' : 'weak'};" 4186 " var obj = Object.create(proto);" 4187 " keyedStoreIC(obj, 'x');" 4188 " keyedStoreIC(obj, 'x');" 4189 " keyedStoreIC(obj, 'x');" 4190 " return proto;" 4191 " })();"); 4192 } 4193 4194 4195 TEST(WeakMapInMonomorphicCompareNilIC) { 4196 CheckWeakness("function compareNilIC(obj) {" 4197 " return obj == null;" 4198 "}" 4199 " (function() {" 4200 " var proto = {'name' : 'weak'};" 4201 " var obj = Object.create(proto);" 4202 " compareNilIC(obj);" 4203 " compareNilIC(obj);" 4204 " compareNilIC(obj);" 4205 " return proto;" 4206 " })();"); 4207 } 4208 4209 4210 #ifdef DEBUG 4211 TEST(AddInstructionChangesNewSpacePromotion) { 4212 i::FLAG_allow_natives_syntax = true; 4213 i::FLAG_expose_gc = true; 4214 i::FLAG_stress_compaction = true; 4215 i::FLAG_gc_interval = 1000; 4216 CcTest::InitializeVM(); 4217 if (!i::FLAG_allocation_site_pretenuring) return; 4218 v8::HandleScope scope(CcTest::isolate()); 4219 Isolate* isolate = CcTest::i_isolate(); 4220 Heap* heap = isolate->heap(); 4221 4222 CompileRun( 4223 "function add(a, b) {" 4224 " return a + b;" 4225 "}" 4226 "add(1, 2);" 4227 "add(\"a\", \"b\");" 4228 "var oldSpaceObject;" 4229 "gc();" 4230 "function crash(x) {" 4231 " var object = {a: null, b: null};" 4232 " var result = add(1.5, x | 0);" 4233 " object.a = result;" 4234 " oldSpaceObject = object;" 4235 " return object;" 4236 "}" 4237 "crash(1);" 4238 "crash(1);" 4239 "%OptimizeFunctionOnNextCall(crash);" 4240 "crash(1);"); 4241 4242 v8::Handle<v8::Object> global = CcTest::global(); 4243 v8::Handle<v8::Function> g = 4244 v8::Handle<v8::Function>::Cast(global->Get(v8_str("crash"))); 4245 v8::Handle<v8::Value> args1[] = { v8_num(1) }; 4246 heap->DisableInlineAllocation(); 4247 heap->set_allocation_timeout(1); 4248 g->Call(global, 1, args1); 4249 heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 4250 } 4251 4252 4253 void OnFatalErrorExpectOOM(const char* location, const char* message) { 4254 // Exit with 0 if the location matches our expectation. 4255 exit(strcmp(location, "CALL_AND_RETRY_LAST")); 4256 } 4257 4258 4259 TEST(CEntryStubOOM) { 4260 i::FLAG_allow_natives_syntax = true; 4261 CcTest::InitializeVM(); 4262 v8::HandleScope scope(CcTest::isolate()); 4263 v8::V8::SetFatalErrorHandler(OnFatalErrorExpectOOM); 4264 4265 v8::Handle<v8::Value> result = CompileRun( 4266 "%SetFlags('--gc-interval=1');" 4267 "var a = [];" 4268 "a.__proto__ = [];" 4269 "a.unshift(1)"); 4270 4271 CHECK(result->IsNumber()); 4272 } 4273 4274 #endif // DEBUG 4275 4276 4277 static void InterruptCallback357137(v8::Isolate* isolate, void* data) { } 4278 4279 4280 static void RequestInterrupt(const v8::FunctionCallbackInfo<v8::Value>& args) { 4281 CcTest::isolate()->RequestInterrupt(&InterruptCallback357137, NULL); 4282 } 4283 4284 4285 TEST(Regress357137) { 4286 CcTest::InitializeVM(); 4287 v8::Isolate* isolate = CcTest::isolate(); 4288 v8::HandleScope hscope(isolate); 4289 v8::Handle<v8::ObjectTemplate> global =v8::ObjectTemplate::New(isolate); 4290 global->Set(v8::String::NewFromUtf8(isolate, "interrupt"), 4291 v8::FunctionTemplate::New(isolate, RequestInterrupt)); 4292 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); 4293 ASSERT(!context.IsEmpty()); 4294 v8::Context::Scope cscope(context); 4295 4296 v8::Local<v8::Value> result = CompileRun( 4297 "var locals = '';" 4298 "for (var i = 0; i < 512; i++) locals += 'var v' + i + '= 42;';" 4299 "eval('function f() {' + locals + 'return function() { return v0; }; }');" 4300 "interrupt();" // This triggers a fake stack overflow in f. 4301 "f()()"); 4302 CHECK_EQ(42.0, result->ToNumber()->Value()); 4303 } 4304 4305 4306 TEST(ArrayShiftSweeping) { 4307 i::FLAG_expose_gc = true; 4308 CcTest::InitializeVM(); 4309 v8::HandleScope scope(CcTest::isolate()); 4310 Isolate* isolate = CcTest::i_isolate(); 4311 Heap* heap = isolate->heap(); 4312 4313 v8::Local<v8::Value> result = CompileRun( 4314 "var array = new Array(40000);" 4315 "var tmp = new Array(100000);" 4316 "array[0] = 10;" 4317 "gc();" 4318 "array.shift();" 4319 "array;"); 4320 4321 Handle<JSObject> o = 4322 v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(result)); 4323 CHECK(heap->InOldPointerSpace(o->elements())); 4324 CHECK(heap->InOldPointerSpace(*o)); 4325 Page* page = Page::FromAddress(o->elements()->address()); 4326 CHECK(page->WasSwept() || 4327 Marking::IsBlack(Marking::MarkBitFrom(o->elements()))); 4328 } 4329 4330 4331 #ifdef DEBUG 4332 TEST(PathTracer) { 4333 CcTest::InitializeVM(); 4334 v8::HandleScope scope(CcTest::isolate()); 4335 4336 v8::Local<v8::Value> result = CompileRun("'abc'"); 4337 Handle<Object> o = v8::Utils::OpenHandle(*result); 4338 CcTest::i_isolate()->heap()->TracePathToObject(*o); 4339 } 4340 #endif // DEBUG 4341