1 // Copyright 2012 the V8 project authors. All rights reserved. 2 3 #include <stdlib.h> 4 5 #include "v8.h" 6 7 #include "execution.h" 8 #include "factory.h" 9 #include "macro-assembler.h" 10 #include "global-handles.h" 11 #include "cctest.h" 12 13 using namespace v8::internal; 14 15 static v8::Persistent<v8::Context> env; 16 17 static void InitializeVM() { 18 if (env.IsEmpty()) env = v8::Context::New(); 19 v8::HandleScope scope; 20 env->Enter(); 21 } 22 23 24 static void CheckMap(Map* map, int type, int instance_size) { 25 CHECK(map->IsHeapObject()); 26 #ifdef DEBUG 27 CHECK(HEAP->Contains(map)); 28 #endif 29 CHECK_EQ(HEAP->meta_map(), map->map()); 30 CHECK_EQ(type, map->instance_type()); 31 CHECK_EQ(instance_size, map->instance_size()); 32 } 33 34 35 TEST(HeapMaps) { 36 InitializeVM(); 37 CheckMap(HEAP->meta_map(), MAP_TYPE, Map::kSize); 38 CheckMap(HEAP->heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize); 39 CheckMap(HEAP->fixed_array_map(), FIXED_ARRAY_TYPE, kVariableSizeSentinel); 40 CheckMap(HEAP->string_map(), STRING_TYPE, kVariableSizeSentinel); 41 } 42 43 44 static void CheckOddball(Object* obj, const char* string) { 45 CHECK(obj->IsOddball()); 46 bool exc; 47 Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc); 48 CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string))); 49 } 50 51 52 static void CheckSmi(int value, const char* string) { 53 bool exc; 54 Object* print_string = 55 *Execution::ToString(Handle<Object>(Smi::FromInt(value)), &exc); 56 CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string))); 57 } 58 59 60 static void CheckNumber(double value, const char* string) { 61 Object* obj = HEAP->NumberFromDouble(value)->ToObjectChecked(); 62 CHECK(obj->IsNumber()); 63 bool exc; 64 Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc); 65 CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string))); 66 } 67 68 69 static void CheckFindCodeObject() { 70 // Test FindCodeObject 71 #define __ assm. 72 73 Assembler assm(Isolate::Current(), NULL, 0); 74 75 __ nop(); // supported on all architectures 76 77 CodeDesc desc; 78 assm.GetCode(&desc); 79 Object* code = HEAP->CreateCode( 80 desc, 81 Code::ComputeFlags(Code::STUB), 82 Handle<Object>(HEAP->undefined_value()))->ToObjectChecked(); 83 CHECK(code->IsCode()); 84 85 HeapObject* obj = HeapObject::cast(code); 86 Address obj_addr = obj->address(); 87 88 for (int i = 0; i < obj->Size(); i += kPointerSize) { 89 Object* found = HEAP->FindCodeObject(obj_addr + i); 90 CHECK_EQ(code, found); 91 } 92 93 Object* copy = HEAP->CreateCode( 94 desc, 95 Code::ComputeFlags(Code::STUB), 96 Handle<Object>(HEAP->undefined_value()))->ToObjectChecked(); 97 CHECK(copy->IsCode()); 98 HeapObject* obj_copy = HeapObject::cast(copy); 99 Object* not_right = HEAP->FindCodeObject(obj_copy->address() + 100 obj_copy->Size() / 2); 101 CHECK(not_right != code); 102 } 103 104 105 TEST(HeapObjects) { 106 InitializeVM(); 107 108 v8::HandleScope sc; 109 Object* value = HEAP->NumberFromDouble(1.000123)->ToObjectChecked(); 110 CHECK(value->IsHeapNumber()); 111 CHECK(value->IsNumber()); 112 CHECK_EQ(1.000123, value->Number()); 113 114 value = HEAP->NumberFromDouble(1.0)->ToObjectChecked(); 115 CHECK(value->IsSmi()); 116 CHECK(value->IsNumber()); 117 CHECK_EQ(1.0, value->Number()); 118 119 value = HEAP->NumberFromInt32(1024)->ToObjectChecked(); 120 CHECK(value->IsSmi()); 121 CHECK(value->IsNumber()); 122 CHECK_EQ(1024.0, value->Number()); 123 124 value = HEAP->NumberFromInt32(Smi::kMinValue)->ToObjectChecked(); 125 CHECK(value->IsSmi()); 126 CHECK(value->IsNumber()); 127 CHECK_EQ(Smi::kMinValue, Smi::cast(value)->value()); 128 129 value = HEAP->NumberFromInt32(Smi::kMaxValue)->ToObjectChecked(); 130 CHECK(value->IsSmi()); 131 CHECK(value->IsNumber()); 132 CHECK_EQ(Smi::kMaxValue, Smi::cast(value)->value()); 133 134 #ifndef V8_TARGET_ARCH_X64 135 // TODO(lrn): We need a NumberFromIntptr function in order to test this. 136 value = HEAP->NumberFromInt32(Smi::kMinValue - 1)->ToObjectChecked(); 137 CHECK(value->IsHeapNumber()); 138 CHECK(value->IsNumber()); 139 CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number()); 140 #endif 141 142 MaybeObject* maybe_value = 143 HEAP->NumberFromUint32(static_cast<uint32_t>(Smi::kMaxValue) + 1); 144 value = maybe_value->ToObjectChecked(); 145 CHECK(value->IsHeapNumber()); 146 CHECK(value->IsNumber()); 147 CHECK_EQ(static_cast<double>(static_cast<uint32_t>(Smi::kMaxValue) + 1), 148 value->Number()); 149 150 // nan oddball checks 151 CHECK(HEAP->nan_value()->IsNumber()); 152 CHECK(isnan(HEAP->nan_value()->Number())); 153 154 Handle<String> s = FACTORY->NewStringFromAscii(CStrVector("fisk hest ")); 155 CHECK(s->IsString()); 156 CHECK_EQ(10, s->length()); 157 158 String* object_symbol = String::cast(HEAP->Object_symbol()); 159 CHECK( 160 Isolate::Current()->context()->global()->HasLocalProperty(object_symbol)); 161 162 // Check ToString for oddballs 163 CheckOddball(HEAP->true_value(), "true"); 164 CheckOddball(HEAP->false_value(), "false"); 165 CheckOddball(HEAP->null_value(), "null"); 166 CheckOddball(HEAP->undefined_value(), "undefined"); 167 168 // Check ToString for Smis 169 CheckSmi(0, "0"); 170 CheckSmi(42, "42"); 171 CheckSmi(-42, "-42"); 172 173 // Check ToString for Numbers 174 CheckNumber(1.1, "1.1"); 175 176 CheckFindCodeObject(); 177 } 178 179 180 TEST(Tagging) { 181 InitializeVM(); 182 int request = 24; 183 CHECK_EQ(request, static_cast<int>(OBJECT_POINTER_ALIGN(request))); 184 CHECK(Smi::FromInt(42)->IsSmi()); 185 CHECK(Failure::RetryAfterGC(NEW_SPACE)->IsFailure()); 186 CHECK_EQ(NEW_SPACE, 187 Failure::RetryAfterGC(NEW_SPACE)->allocation_space()); 188 CHECK_EQ(OLD_POINTER_SPACE, 189 Failure::RetryAfterGC(OLD_POINTER_SPACE)->allocation_space()); 190 CHECK(Failure::Exception()->IsFailure()); 191 CHECK(Smi::FromInt(Smi::kMinValue)->IsSmi()); 192 CHECK(Smi::FromInt(Smi::kMaxValue)->IsSmi()); 193 } 194 195 196 TEST(GarbageCollection) { 197 InitializeVM(); 198 199 v8::HandleScope sc; 200 // Check GC. 201 HEAP->CollectGarbage(NEW_SPACE); 202 203 Handle<String> name = FACTORY->LookupAsciiSymbol("theFunction"); 204 Handle<String> prop_name = FACTORY->LookupAsciiSymbol("theSlot"); 205 Handle<String> prop_namex = FACTORY->LookupAsciiSymbol("theSlotx"); 206 Handle<String> obj_name = FACTORY->LookupAsciiSymbol("theObject"); 207 208 { 209 v8::HandleScope inner_scope; 210 // Allocate a function and keep it in global object's property. 211 Handle<JSFunction> function = 212 FACTORY->NewFunction(name, FACTORY->undefined_value()); 213 Handle<Map> initial_map = 214 FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); 215 function->set_initial_map(*initial_map); 216 Isolate::Current()->context()->global()->SetProperty( 217 *name, *function, NONE, kNonStrictMode)->ToObjectChecked(); 218 // Allocate an object. Unrooted after leaving the scope. 219 Handle<JSObject> obj = FACTORY->NewJSObject(function); 220 obj->SetProperty( 221 *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); 222 obj->SetProperty( 223 *prop_namex, Smi::FromInt(24), NONE, kNonStrictMode)->ToObjectChecked(); 224 225 CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name)); 226 CHECK_EQ(Smi::FromInt(24), obj->GetProperty(*prop_namex)); 227 } 228 229 HEAP->CollectGarbage(NEW_SPACE); 230 231 // Function should be alive. 232 CHECK(Isolate::Current()->context()->global()->HasLocalProperty(*name)); 233 // Check function is retained. 234 Object* func_value = Isolate::Current()->context()->global()-> 235 GetProperty(*name)->ToObjectChecked(); 236 CHECK(func_value->IsJSFunction()); 237 Handle<JSFunction> function(JSFunction::cast(func_value)); 238 239 { 240 HandleScope inner_scope; 241 // Allocate another object, make it reachable from global. 242 Handle<JSObject> obj = FACTORY->NewJSObject(function); 243 Isolate::Current()->context()->global()->SetProperty( 244 *obj_name, *obj, NONE, kNonStrictMode)->ToObjectChecked(); 245 obj->SetProperty( 246 *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); 247 } 248 249 // After gc, it should survive. 250 HEAP->CollectGarbage(NEW_SPACE); 251 252 CHECK(Isolate::Current()->context()->global()->HasLocalProperty(*obj_name)); 253 CHECK(Isolate::Current()->context()->global()-> 254 GetProperty(*obj_name)->ToObjectChecked()->IsJSObject()); 255 Object* obj = Isolate::Current()->context()->global()-> 256 GetProperty(*obj_name)->ToObjectChecked(); 257 JSObject* js_obj = JSObject::cast(obj); 258 CHECK_EQ(Smi::FromInt(23), js_obj->GetProperty(*prop_name)); 259 } 260 261 262 static void VerifyStringAllocation(const char* string) { 263 v8::HandleScope scope; 264 Handle<String> s = FACTORY->NewStringFromUtf8(CStrVector(string)); 265 CHECK_EQ(StrLength(string), s->length()); 266 for (int index = 0; index < s->length(); index++) { 267 CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index)); 268 } 269 } 270 271 272 TEST(String) { 273 InitializeVM(); 274 275 VerifyStringAllocation("a"); 276 VerifyStringAllocation("ab"); 277 VerifyStringAllocation("abc"); 278 VerifyStringAllocation("abcd"); 279 VerifyStringAllocation("fiskerdrengen er paa havet"); 280 } 281 282 283 TEST(LocalHandles) { 284 InitializeVM(); 285 286 v8::HandleScope scope; 287 const char* name = "Kasper the spunky"; 288 Handle<String> string = FACTORY->NewStringFromAscii(CStrVector(name)); 289 CHECK_EQ(StrLength(name), string->length()); 290 } 291 292 293 TEST(GlobalHandles) { 294 InitializeVM(); 295 GlobalHandles* global_handles = Isolate::Current()->global_handles(); 296 297 Handle<Object> h1; 298 Handle<Object> h2; 299 Handle<Object> h3; 300 Handle<Object> h4; 301 302 { 303 HandleScope scope; 304 305 Handle<Object> i = FACTORY->NewStringFromAscii(CStrVector("fisk")); 306 Handle<Object> u = FACTORY->NewNumber(1.12344); 307 308 h1 = global_handles->Create(*i); 309 h2 = global_handles->Create(*u); 310 h3 = global_handles->Create(*i); 311 h4 = global_handles->Create(*u); 312 } 313 314 // after gc, it should survive 315 HEAP->CollectGarbage(NEW_SPACE); 316 317 CHECK((*h1)->IsString()); 318 CHECK((*h2)->IsHeapNumber()); 319 CHECK((*h3)->IsString()); 320 CHECK((*h4)->IsHeapNumber()); 321 322 CHECK_EQ(*h3, *h1); 323 global_handles->Destroy(h1.location()); 324 global_handles->Destroy(h3.location()); 325 326 CHECK_EQ(*h4, *h2); 327 global_handles->Destroy(h2.location()); 328 global_handles->Destroy(h4.location()); 329 } 330 331 332 static bool WeakPointerCleared = false; 333 334 static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Value> handle, 335 void* id) { 336 if (1234 == reinterpret_cast<intptr_t>(id)) WeakPointerCleared = true; 337 handle.Dispose(); 338 } 339 340 341 TEST(WeakGlobalHandlesScavenge) { 342 InitializeVM(); 343 GlobalHandles* global_handles = Isolate::Current()->global_handles(); 344 345 WeakPointerCleared = false; 346 347 Handle<Object> h1; 348 Handle<Object> h2; 349 350 { 351 HandleScope scope; 352 353 Handle<Object> i = FACTORY->NewStringFromAscii(CStrVector("fisk")); 354 Handle<Object> u = FACTORY->NewNumber(1.12344); 355 356 h1 = global_handles->Create(*i); 357 h2 = global_handles->Create(*u); 358 } 359 360 global_handles->MakeWeak(h2.location(), 361 reinterpret_cast<void*>(1234), 362 &TestWeakGlobalHandleCallback); 363 364 // Scavenge treats weak pointers as normal roots. 365 HEAP->PerformScavenge(); 366 367 CHECK((*h1)->IsString()); 368 CHECK((*h2)->IsHeapNumber()); 369 370 CHECK(!WeakPointerCleared); 371 CHECK(!global_handles->IsNearDeath(h2.location())); 372 CHECK(!global_handles->IsNearDeath(h1.location())); 373 374 global_handles->Destroy(h1.location()); 375 global_handles->Destroy(h2.location()); 376 } 377 378 379 TEST(WeakGlobalHandlesMark) { 380 InitializeVM(); 381 GlobalHandles* global_handles = Isolate::Current()->global_handles(); 382 383 WeakPointerCleared = false; 384 385 Handle<Object> h1; 386 Handle<Object> h2; 387 388 { 389 HandleScope scope; 390 391 Handle<Object> i = FACTORY->NewStringFromAscii(CStrVector("fisk")); 392 Handle<Object> u = FACTORY->NewNumber(1.12344); 393 394 h1 = global_handles->Create(*i); 395 h2 = global_handles->Create(*u); 396 } 397 398 HEAP->CollectGarbage(OLD_POINTER_SPACE); 399 HEAP->CollectGarbage(NEW_SPACE); 400 // Make sure the object is promoted. 401 402 global_handles->MakeWeak(h2.location(), 403 reinterpret_cast<void*>(1234), 404 &TestWeakGlobalHandleCallback); 405 CHECK(!GlobalHandles::IsNearDeath(h1.location())); 406 CHECK(!GlobalHandles::IsNearDeath(h2.location())); 407 408 HEAP->CollectGarbage(OLD_POINTER_SPACE); 409 410 CHECK((*h1)->IsString()); 411 412 CHECK(WeakPointerCleared); 413 CHECK(!GlobalHandles::IsNearDeath(h1.location())); 414 415 global_handles->Destroy(h1.location()); 416 } 417 418 TEST(DeleteWeakGlobalHandle) { 419 InitializeVM(); 420 GlobalHandles* global_handles = Isolate::Current()->global_handles(); 421 422 WeakPointerCleared = false; 423 424 Handle<Object> h; 425 426 { 427 HandleScope scope; 428 429 Handle<Object> i = FACTORY->NewStringFromAscii(CStrVector("fisk")); 430 h = global_handles->Create(*i); 431 } 432 433 global_handles->MakeWeak(h.location(), 434 reinterpret_cast<void*>(1234), 435 &TestWeakGlobalHandleCallback); 436 437 // Scanvenge does not recognize weak reference. 438 HEAP->PerformScavenge(); 439 440 CHECK(!WeakPointerCleared); 441 442 // Mark-compact treats weak reference properly. 443 HEAP->CollectGarbage(OLD_POINTER_SPACE); 444 445 CHECK(WeakPointerCleared); 446 } 447 448 static const char* not_so_random_string_table[] = { 449 "abstract", 450 "boolean", 451 "break", 452 "byte", 453 "case", 454 "catch", 455 "char", 456 "class", 457 "const", 458 "continue", 459 "debugger", 460 "default", 461 "delete", 462 "do", 463 "double", 464 "else", 465 "enum", 466 "export", 467 "extends", 468 "false", 469 "final", 470 "finally", 471 "float", 472 "for", 473 "function", 474 "goto", 475 "if", 476 "implements", 477 "import", 478 "in", 479 "instanceof", 480 "int", 481 "interface", 482 "long", 483 "native", 484 "new", 485 "null", 486 "package", 487 "private", 488 "protected", 489 "public", 490 "return", 491 "short", 492 "static", 493 "super", 494 "switch", 495 "synchronized", 496 "this", 497 "throw", 498 "throws", 499 "transient", 500 "true", 501 "try", 502 "typeof", 503 "var", 504 "void", 505 "volatile", 506 "while", 507 "with", 508 0 509 }; 510 511 512 static void CheckSymbols(const char** strings) { 513 for (const char* string = *strings; *strings != 0; string = *strings++) { 514 Object* a; 515 MaybeObject* maybe_a = HEAP->LookupAsciiSymbol(string); 516 // LookupAsciiSymbol may return a failure if a GC is needed. 517 if (!maybe_a->ToObject(&a)) continue; 518 CHECK(a->IsSymbol()); 519 Object* b; 520 MaybeObject* maybe_b = HEAP->LookupAsciiSymbol(string); 521 if (!maybe_b->ToObject(&b)) continue; 522 CHECK_EQ(b, a); 523 CHECK(String::cast(b)->IsEqualTo(CStrVector(string))); 524 } 525 } 526 527 528 TEST(SymbolTable) { 529 InitializeVM(); 530 531 CheckSymbols(not_so_random_string_table); 532 CheckSymbols(not_so_random_string_table); 533 } 534 535 536 TEST(FunctionAllocation) { 537 InitializeVM(); 538 539 v8::HandleScope sc; 540 Handle<String> name = FACTORY->LookupAsciiSymbol("theFunction"); 541 Handle<JSFunction> function = 542 FACTORY->NewFunction(name, FACTORY->undefined_value()); 543 Handle<Map> initial_map = 544 FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); 545 function->set_initial_map(*initial_map); 546 547 Handle<String> prop_name = FACTORY->LookupAsciiSymbol("theSlot"); 548 Handle<JSObject> obj = FACTORY->NewJSObject(function); 549 obj->SetProperty( 550 *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); 551 CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name)); 552 // Check that we can add properties to function objects. 553 function->SetProperty( 554 *prop_name, Smi::FromInt(24), NONE, kNonStrictMode)->ToObjectChecked(); 555 CHECK_EQ(Smi::FromInt(24), function->GetProperty(*prop_name)); 556 } 557 558 559 TEST(ObjectProperties) { 560 InitializeVM(); 561 562 v8::HandleScope sc; 563 String* object_symbol = String::cast(HEAP->Object_symbol()); 564 Object* raw_object = Isolate::Current()->context()->global()-> 565 GetProperty(object_symbol)->ToObjectChecked(); 566 JSFunction* object_function = JSFunction::cast(raw_object); 567 Handle<JSFunction> constructor(object_function); 568 Handle<JSObject> obj = FACTORY->NewJSObject(constructor); 569 Handle<String> first = FACTORY->LookupAsciiSymbol("first"); 570 Handle<String> second = FACTORY->LookupAsciiSymbol("second"); 571 572 // check for empty 573 CHECK(!obj->HasLocalProperty(*first)); 574 575 // add first 576 obj->SetProperty( 577 *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); 578 CHECK(obj->HasLocalProperty(*first)); 579 580 // delete first 581 CHECK(obj->DeleteProperty(*first, JSObject::NORMAL_DELETION)); 582 CHECK(!obj->HasLocalProperty(*first)); 583 584 // add first and then second 585 obj->SetProperty( 586 *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); 587 obj->SetProperty( 588 *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); 589 CHECK(obj->HasLocalProperty(*first)); 590 CHECK(obj->HasLocalProperty(*second)); 591 592 // delete first and then second 593 CHECK(obj->DeleteProperty(*first, JSObject::NORMAL_DELETION)); 594 CHECK(obj->HasLocalProperty(*second)); 595 CHECK(obj->DeleteProperty(*second, JSObject::NORMAL_DELETION)); 596 CHECK(!obj->HasLocalProperty(*first)); 597 CHECK(!obj->HasLocalProperty(*second)); 598 599 // add first and then second 600 obj->SetProperty( 601 *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); 602 obj->SetProperty( 603 *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); 604 CHECK(obj->HasLocalProperty(*first)); 605 CHECK(obj->HasLocalProperty(*second)); 606 607 // delete second and then first 608 CHECK(obj->DeleteProperty(*second, JSObject::NORMAL_DELETION)); 609 CHECK(obj->HasLocalProperty(*first)); 610 CHECK(obj->DeleteProperty(*first, JSObject::NORMAL_DELETION)); 611 CHECK(!obj->HasLocalProperty(*first)); 612 CHECK(!obj->HasLocalProperty(*second)); 613 614 // check string and symbol match 615 const char* string1 = "fisk"; 616 Handle<String> s1 = FACTORY->NewStringFromAscii(CStrVector(string1)); 617 obj->SetProperty( 618 *s1, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); 619 Handle<String> s1_symbol = FACTORY->LookupAsciiSymbol(string1); 620 CHECK(obj->HasLocalProperty(*s1_symbol)); 621 622 // check symbol and string match 623 const char* string2 = "fugl"; 624 Handle<String> s2_symbol = FACTORY->LookupAsciiSymbol(string2); 625 obj->SetProperty( 626 *s2_symbol, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); 627 Handle<String> s2 = FACTORY->NewStringFromAscii(CStrVector(string2)); 628 CHECK(obj->HasLocalProperty(*s2)); 629 } 630 631 632 TEST(JSObjectMaps) { 633 InitializeVM(); 634 635 v8::HandleScope sc; 636 Handle<String> name = FACTORY->LookupAsciiSymbol("theFunction"); 637 Handle<JSFunction> function = 638 FACTORY->NewFunction(name, FACTORY->undefined_value()); 639 Handle<Map> initial_map = 640 FACTORY->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); 641 function->set_initial_map(*initial_map); 642 643 Handle<String> prop_name = FACTORY->LookupAsciiSymbol("theSlot"); 644 Handle<JSObject> obj = FACTORY->NewJSObject(function); 645 646 // Set a propery 647 obj->SetProperty( 648 *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); 649 CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name)); 650 651 // Check the map has changed 652 CHECK(*initial_map != obj->map()); 653 } 654 655 656 TEST(JSArray) { 657 InitializeVM(); 658 659 v8::HandleScope sc; 660 Handle<String> name = FACTORY->LookupAsciiSymbol("Array"); 661 Object* raw_object = Isolate::Current()->context()->global()-> 662 GetProperty(*name)->ToObjectChecked(); 663 Handle<JSFunction> function = Handle<JSFunction>( 664 JSFunction::cast(raw_object)); 665 666 // Allocate the object. 667 Handle<JSObject> object = FACTORY->NewJSObject(function); 668 Handle<JSArray> array = Handle<JSArray>::cast(object); 669 // We just initialized the VM, no heap allocation failure yet. 670 array->Initialize(0)->ToObjectChecked(); 671 672 // Set array length to 0. 673 array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked(); 674 CHECK_EQ(Smi::FromInt(0), array->length()); 675 // Must be in fast mode. 676 CHECK(array->HasFastTypeElements()); 677 678 // array[length] = name. 679 array->SetElement(0, *name, NONE, kNonStrictMode)->ToObjectChecked(); 680 CHECK_EQ(Smi::FromInt(1), array->length()); 681 CHECK_EQ(array->GetElement(0), *name); 682 683 // Set array length with larger than smi value. 684 Handle<Object> length = 685 FACTORY->NewNumberFromUint(static_cast<uint32_t>(Smi::kMaxValue) + 1); 686 array->SetElementsLength(*length)->ToObjectChecked(); 687 688 uint32_t int_length = 0; 689 CHECK(length->ToArrayIndex(&int_length)); 690 CHECK_EQ(*length, array->length()); 691 CHECK(array->HasDictionaryElements()); // Must be in slow mode. 692 693 // array[length] = name. 694 array->SetElement(int_length, *name, NONE, kNonStrictMode)->ToObjectChecked(); 695 uint32_t new_int_length = 0; 696 CHECK(array->length()->ToArrayIndex(&new_int_length)); 697 CHECK_EQ(static_cast<double>(int_length), new_int_length - 1); 698 CHECK_EQ(array->GetElement(int_length), *name); 699 CHECK_EQ(array->GetElement(0), *name); 700 } 701 702 703 TEST(JSObjectCopy) { 704 InitializeVM(); 705 706 v8::HandleScope sc; 707 String* object_symbol = String::cast(HEAP->Object_symbol()); 708 Object* raw_object = Isolate::Current()->context()->global()-> 709 GetProperty(object_symbol)->ToObjectChecked(); 710 JSFunction* object_function = JSFunction::cast(raw_object); 711 Handle<JSFunction> constructor(object_function); 712 Handle<JSObject> obj = FACTORY->NewJSObject(constructor); 713 Handle<String> first = FACTORY->LookupAsciiSymbol("first"); 714 Handle<String> second = FACTORY->LookupAsciiSymbol("second"); 715 716 obj->SetProperty( 717 *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); 718 obj->SetProperty( 719 *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); 720 721 obj->SetElement(0, *first, NONE, kNonStrictMode)->ToObjectChecked(); 722 obj->SetElement(1, *second, NONE, kNonStrictMode)->ToObjectChecked(); 723 724 // Make the clone. 725 Handle<JSObject> clone = Copy(obj); 726 CHECK(!clone.is_identical_to(obj)); 727 728 CHECK_EQ(obj->GetElement(0), clone->GetElement(0)); 729 CHECK_EQ(obj->GetElement(1), clone->GetElement(1)); 730 731 CHECK_EQ(obj->GetProperty(*first), clone->GetProperty(*first)); 732 CHECK_EQ(obj->GetProperty(*second), clone->GetProperty(*second)); 733 734 // Flip the values. 735 clone->SetProperty( 736 *first, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); 737 clone->SetProperty( 738 *second, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); 739 740 clone->SetElement(0, *second, NONE, kNonStrictMode)->ToObjectChecked(); 741 clone->SetElement(1, *first, NONE, kNonStrictMode)->ToObjectChecked(); 742 743 CHECK_EQ(obj->GetElement(1), clone->GetElement(0)); 744 CHECK_EQ(obj->GetElement(0), clone->GetElement(1)); 745 746 CHECK_EQ(obj->GetProperty(*second), clone->GetProperty(*first)); 747 CHECK_EQ(obj->GetProperty(*first), clone->GetProperty(*second)); 748 } 749 750 751 TEST(StringAllocation) { 752 InitializeVM(); 753 754 755 const unsigned char chars[] = { 0xe5, 0xa4, 0xa7 }; 756 for (int length = 0; length < 100; length++) { 757 v8::HandleScope scope; 758 char* non_ascii = NewArray<char>(3 * length + 1); 759 char* ascii = NewArray<char>(length + 1); 760 non_ascii[3 * length] = 0; 761 ascii[length] = 0; 762 for (int i = 0; i < length; i++) { 763 ascii[i] = 'a'; 764 non_ascii[3 * i] = chars[0]; 765 non_ascii[3 * i + 1] = chars[1]; 766 non_ascii[3 * i + 2] = chars[2]; 767 } 768 Handle<String> non_ascii_sym = 769 FACTORY->LookupSymbol(Vector<const char>(non_ascii, 3 * length)); 770 CHECK_EQ(length, non_ascii_sym->length()); 771 Handle<String> ascii_sym = 772 FACTORY->LookupSymbol(Vector<const char>(ascii, length)); 773 CHECK_EQ(length, ascii_sym->length()); 774 Handle<String> non_ascii_str = 775 FACTORY->NewStringFromUtf8(Vector<const char>(non_ascii, 3 * length)); 776 non_ascii_str->Hash(); 777 CHECK_EQ(length, non_ascii_str->length()); 778 Handle<String> ascii_str = 779 FACTORY->NewStringFromUtf8(Vector<const char>(ascii, length)); 780 ascii_str->Hash(); 781 CHECK_EQ(length, ascii_str->length()); 782 DeleteArray(non_ascii); 783 DeleteArray(ascii); 784 } 785 } 786 787 788 static int ObjectsFoundInHeap(Handle<Object> objs[], int size) { 789 // Count the number of objects found in the heap. 790 int found_count = 0; 791 HeapIterator iterator; 792 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 793 for (int i = 0; i < size; i++) { 794 if (*objs[i] == obj) { 795 found_count++; 796 } 797 } 798 } 799 return found_count; 800 } 801 802 803 TEST(Iteration) { 804 InitializeVM(); 805 v8::HandleScope scope; 806 807 // Array of objects to scan haep for. 808 const int objs_count = 6; 809 Handle<Object> objs[objs_count]; 810 int next_objs_index = 0; 811 812 // Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE 813 objs[next_objs_index++] = FACTORY->NewJSArray(10); 814 objs[next_objs_index++] = FACTORY->NewJSArray(10, FAST_ELEMENTS, TENURED); 815 816 // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE 817 objs[next_objs_index++] = 818 FACTORY->NewStringFromAscii(CStrVector("abcdefghij")); 819 objs[next_objs_index++] = 820 FACTORY->NewStringFromAscii(CStrVector("abcdefghij"), TENURED); 821 822 // Allocate a large string (for large object space). 823 int large_size = Page::kMaxNonCodeHeapObjectSize + 1; 824 char* str = new char[large_size]; 825 for (int i = 0; i < large_size - 1; ++i) str[i] = 'a'; 826 str[large_size - 1] = '\0'; 827 objs[next_objs_index++] = 828 FACTORY->NewStringFromAscii(CStrVector(str), TENURED); 829 delete[] str; 830 831 // Add a Map object to look for. 832 objs[next_objs_index++] = Handle<Map>(HeapObject::cast(*objs[0])->map()); 833 834 CHECK_EQ(objs_count, next_objs_index); 835 CHECK_EQ(objs_count, ObjectsFoundInHeap(objs, objs_count)); 836 } 837 838 839 TEST(EmptyHandleEscapeFrom) { 840 InitializeVM(); 841 842 v8::HandleScope scope; 843 Handle<JSObject> runaway; 844 845 { 846 v8::HandleScope nested; 847 Handle<JSObject> empty; 848 runaway = empty.EscapeFrom(&nested); 849 } 850 851 CHECK(runaway.is_null()); 852 } 853 854 855 static int LenFromSize(int size) { 856 return (size - FixedArray::kHeaderSize) / kPointerSize; 857 } 858 859 860 TEST(Regression39128) { 861 // Test case for crbug.com/39128. 862 InitializeVM(); 863 864 // Increase the chance of 'bump-the-pointer' allocation in old space. 865 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 866 867 v8::HandleScope scope; 868 869 // The plan: create JSObject which references objects in new space. 870 // Then clone this object (forcing it to go into old space) and check 871 // that region dirty marks are updated correctly. 872 873 // Step 1: prepare a map for the object. We add 1 inobject property to it. 874 Handle<JSFunction> object_ctor( 875 Isolate::Current()->global_context()->object_function()); 876 CHECK(object_ctor->has_initial_map()); 877 Handle<Map> object_map(object_ctor->initial_map()); 878 // Create a map with single inobject property. 879 Handle<Map> my_map = FACTORY->CopyMap(object_map, 1); 880 int n_properties = my_map->inobject_properties(); 881 CHECK_GT(n_properties, 0); 882 883 int object_size = my_map->instance_size(); 884 885 // Step 2: allocate a lot of objects so to almost fill new space: we need 886 // just enough room to allocate JSObject and thus fill the newspace. 887 888 int allocation_amount = Min(FixedArray::kMaxSize, 889 HEAP->MaxObjectSizeInNewSpace()); 890 int allocation_len = LenFromSize(allocation_amount); 891 NewSpace* new_space = HEAP->new_space(); 892 Address* top_addr = new_space->allocation_top_address(); 893 Address* limit_addr = new_space->allocation_limit_address(); 894 while ((*limit_addr - *top_addr) > allocation_amount) { 895 CHECK(!HEAP->always_allocate()); 896 Object* array = HEAP->AllocateFixedArray(allocation_len)->ToObjectChecked(); 897 CHECK(!array->IsFailure()); 898 CHECK(new_space->Contains(array)); 899 } 900 901 // Step 3: now allocate fixed array and JSObject to fill the whole new space. 902 int to_fill = static_cast<int>(*limit_addr - *top_addr - object_size); 903 int fixed_array_len = LenFromSize(to_fill); 904 CHECK(fixed_array_len < FixedArray::kMaxLength); 905 906 CHECK(!HEAP->always_allocate()); 907 Object* array = HEAP->AllocateFixedArray(fixed_array_len)->ToObjectChecked(); 908 CHECK(!array->IsFailure()); 909 CHECK(new_space->Contains(array)); 910 911 Object* object = HEAP->AllocateJSObjectFromMap(*my_map)->ToObjectChecked(); 912 CHECK(new_space->Contains(object)); 913 JSObject* jsobject = JSObject::cast(object); 914 CHECK_EQ(0, FixedArray::cast(jsobject->elements())->length()); 915 CHECK_EQ(0, jsobject->properties()->length()); 916 // Create a reference to object in new space in jsobject. 917 jsobject->FastPropertyAtPut(-1, array); 918 919 CHECK_EQ(0, static_cast<int>(*limit_addr - *top_addr)); 920 921 // Step 4: clone jsobject, but force always allocate first to create a clone 922 // in old pointer space. 923 Address old_pointer_space_top = HEAP->old_pointer_space()->top(); 924 AlwaysAllocateScope aa_scope; 925 Object* clone_obj = HEAP->CopyJSObject(jsobject)->ToObjectChecked(); 926 JSObject* clone = JSObject::cast(clone_obj); 927 if (clone->address() != old_pointer_space_top) { 928 // Alas, got allocated from free list, we cannot do checks. 929 return; 930 } 931 CHECK(HEAP->old_pointer_space()->Contains(clone->address())); 932 } 933 934 935 TEST(TestCodeFlushing) { 936 i::FLAG_allow_natives_syntax = true; 937 // If we do not flush code this test is invalid. 938 if (!FLAG_flush_code) return; 939 InitializeVM(); 940 v8::HandleScope scope; 941 const char* source = "function foo() {" 942 " var x = 42;" 943 " var y = 42;" 944 " var z = x + y;" 945 "};" 946 "foo()"; 947 Handle<String> foo_name = FACTORY->LookupAsciiSymbol("foo"); 948 949 // This compile will add the code to the compilation cache. 950 { v8::HandleScope scope; 951 CompileRun(source); 952 } 953 954 // Check function is compiled. 955 Object* func_value = Isolate::Current()->context()->global()-> 956 GetProperty(*foo_name)->ToObjectChecked(); 957 CHECK(func_value->IsJSFunction()); 958 Handle<JSFunction> function(JSFunction::cast(func_value)); 959 CHECK(function->shared()->is_compiled()); 960 961 // TODO(1609) Currently incremental marker does not support code flushing. 962 HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 963 HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 964 965 CHECK(function->shared()->is_compiled()); 966 967 HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 968 HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 969 HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 970 HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 971 HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 972 HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); 973 974 // foo should no longer be in the compilation cache 975 CHECK(!function->shared()->is_compiled() || function->IsOptimized()); 976 CHECK(!function->is_compiled() || function->IsOptimized()); 977 // Call foo to get it recompiled. 978 CompileRun("foo()"); 979 CHECK(function->shared()->is_compiled()); 980 CHECK(function->is_compiled()); 981 } 982 983 984 // Count the number of global contexts in the weak list of global contexts. 985 static int CountGlobalContexts() { 986 int count = 0; 987 Object* object = HEAP->global_contexts_list(); 988 while (!object->IsUndefined()) { 989 count++; 990 object = Context::cast(object)->get(Context::NEXT_CONTEXT_LINK); 991 } 992 return count; 993 } 994 995 996 // Count the number of user functions in the weak list of optimized 997 // functions attached to a global context. 998 static int CountOptimizedUserFunctions(v8::Handle<v8::Context> context) { 999 int count = 0; 1000 Handle<Context> icontext = v8::Utils::OpenHandle(*context); 1001 Object* object = icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST); 1002 while (object->IsJSFunction() && !JSFunction::cast(object)->IsBuiltin()) { 1003 count++; 1004 object = JSFunction::cast(object)->next_function_link(); 1005 } 1006 return count; 1007 } 1008 1009 1010 TEST(TestInternalWeakLists) { 1011 v8::V8::Initialize(); 1012 1013 static const int kNumTestContexts = 10; 1014 1015 v8::HandleScope scope; 1016 v8::Persistent<v8::Context> ctx[kNumTestContexts]; 1017 1018 CHECK_EQ(0, CountGlobalContexts()); 1019 1020 // Create a number of global contests which gets linked together. 1021 for (int i = 0; i < kNumTestContexts; i++) { 1022 ctx[i] = v8::Context::New(); 1023 1024 bool opt = (FLAG_always_opt && i::V8::UseCrankshaft()); 1025 1026 CHECK_EQ(i + 1, CountGlobalContexts()); 1027 1028 ctx[i]->Enter(); 1029 1030 // Create a handle scope so no function objects get stuch in the outer 1031 // handle scope 1032 v8::HandleScope scope; 1033 const char* source = "function f1() { };" 1034 "function f2() { };" 1035 "function f3() { };" 1036 "function f4() { };" 1037 "function f5() { };"; 1038 CompileRun(source); 1039 CHECK_EQ(0, CountOptimizedUserFunctions(ctx[i])); 1040 CompileRun("f1()"); 1041 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctions(ctx[i])); 1042 CompileRun("f2()"); 1043 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i])); 1044 CompileRun("f3()"); 1045 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i])); 1046 CompileRun("f4()"); 1047 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); 1048 CompileRun("f5()"); 1049 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[i])); 1050 1051 // Remove function f1, and 1052 CompileRun("f1=null"); 1053 1054 // Scavenge treats these references as strong. 1055 for (int j = 0; j < 10; j++) { 1056 HEAP->PerformScavenge(); 1057 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[i])); 1058 } 1059 1060 // Mark compact handles the weak references. 1061 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1062 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); 1063 1064 // Get rid of f3 and f5 in the same way. 1065 CompileRun("f3=null"); 1066 for (int j = 0; j < 10; j++) { 1067 HEAP->PerformScavenge(); 1068 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[i])); 1069 } 1070 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1071 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i])); 1072 CompileRun("f5=null"); 1073 for (int j = 0; j < 10; j++) { 1074 HEAP->PerformScavenge(); 1075 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[i])); 1076 } 1077 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1078 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[i])); 1079 1080 ctx[i]->Exit(); 1081 } 1082 1083 // Force compilation cache cleanup. 1084 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1085 1086 // Dispose the global contexts one by one. 1087 for (int i = 0; i < kNumTestContexts; i++) { 1088 ctx[i].Dispose(); 1089 ctx[i].Clear(); 1090 1091 // Scavenge treats these references as strong. 1092 for (int j = 0; j < 10; j++) { 1093 HEAP->PerformScavenge(); 1094 CHECK_EQ(kNumTestContexts - i, CountGlobalContexts()); 1095 } 1096 1097 // Mark compact handles the weak references. 1098 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1099 CHECK_EQ(kNumTestContexts - i - 1, CountGlobalContexts()); 1100 } 1101 1102 CHECK_EQ(0, CountGlobalContexts()); 1103 } 1104 1105 1106 // Count the number of global contexts in the weak list of global contexts 1107 // causing a GC after the specified number of elements. 1108 static int CountGlobalContextsWithGC(int n) { 1109 int count = 0; 1110 Handle<Object> object(HEAP->global_contexts_list()); 1111 while (!object->IsUndefined()) { 1112 count++; 1113 if (count == n) HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1114 object = 1115 Handle<Object>(Context::cast(*object)->get(Context::NEXT_CONTEXT_LINK)); 1116 } 1117 return count; 1118 } 1119 1120 1121 // Count the number of user functions in the weak list of optimized 1122 // functions attached to a global context causing a GC after the 1123 // specified number of elements. 1124 static int CountOptimizedUserFunctionsWithGC(v8::Handle<v8::Context> context, 1125 int n) { 1126 int count = 0; 1127 Handle<Context> icontext = v8::Utils::OpenHandle(*context); 1128 Handle<Object> object(icontext->get(Context::OPTIMIZED_FUNCTIONS_LIST)); 1129 while (object->IsJSFunction() && 1130 !Handle<JSFunction>::cast(object)->IsBuiltin()) { 1131 count++; 1132 if (count == n) HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1133 object = Handle<Object>( 1134 Object::cast(JSFunction::cast(*object)->next_function_link())); 1135 } 1136 return count; 1137 } 1138 1139 1140 TEST(TestInternalWeakListsTraverseWithGC) { 1141 v8::V8::Initialize(); 1142 1143 static const int kNumTestContexts = 10; 1144 1145 v8::HandleScope scope; 1146 v8::Persistent<v8::Context> ctx[kNumTestContexts]; 1147 1148 CHECK_EQ(0, CountGlobalContexts()); 1149 1150 // Create an number of contexts and check the length of the weak list both 1151 // with and without GCs while iterating the list. 1152 for (int i = 0; i < kNumTestContexts; i++) { 1153 ctx[i] = v8::Context::New(); 1154 CHECK_EQ(i + 1, CountGlobalContexts()); 1155 CHECK_EQ(i + 1, CountGlobalContextsWithGC(i / 2 + 1)); 1156 } 1157 1158 bool opt = (FLAG_always_opt && i::V8::UseCrankshaft()); 1159 1160 // Compile a number of functions the length of the weak list of optimized 1161 // functions both with and without GCs while iterating the list. 1162 ctx[0]->Enter(); 1163 const char* source = "function f1() { };" 1164 "function f2() { };" 1165 "function f3() { };" 1166 "function f4() { };" 1167 "function f5() { };"; 1168 CompileRun(source); 1169 CHECK_EQ(0, CountOptimizedUserFunctions(ctx[0])); 1170 CompileRun("f1()"); 1171 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctions(ctx[0])); 1172 CHECK_EQ(opt ? 1 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1)); 1173 CompileRun("f2()"); 1174 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctions(ctx[0])); 1175 CHECK_EQ(opt ? 2 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1)); 1176 CompileRun("f3()"); 1177 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctions(ctx[0])); 1178 CHECK_EQ(opt ? 3 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 1)); 1179 CompileRun("f4()"); 1180 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctions(ctx[0])); 1181 CHECK_EQ(opt ? 4 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 2)); 1182 CompileRun("f5()"); 1183 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctions(ctx[0])); 1184 CHECK_EQ(opt ? 5 : 0, CountOptimizedUserFunctionsWithGC(ctx[0], 4)); 1185 1186 ctx[0]->Exit(); 1187 } 1188 1189 1190 TEST(TestSizeOfObjects) { 1191 v8::V8::Initialize(); 1192 1193 // Get initial heap size after several full GCs, which will stabilize 1194 // the heap size and return with sweeping finished completely. 1195 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1196 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1197 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1198 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1199 CHECK(HEAP->old_pointer_space()->IsSweepingComplete()); 1200 int initial_size = static_cast<int>(HEAP->SizeOfObjects()); 1201 1202 { 1203 // Allocate objects on several different old-space pages so that 1204 // lazy sweeping kicks in for subsequent GC runs. 1205 AlwaysAllocateScope always_allocate; 1206 int filler_size = static_cast<int>(FixedArray::SizeFor(8192)); 1207 for (int i = 1; i <= 100; i++) { 1208 HEAP->AllocateFixedArray(8192, TENURED)->ToObjectChecked(); 1209 CHECK_EQ(initial_size + i * filler_size, 1210 static_cast<int>(HEAP->SizeOfObjects())); 1211 } 1212 } 1213 1214 // The heap size should go back to initial size after a full GC, even 1215 // though sweeping didn't finish yet. 1216 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1217 CHECK(!HEAP->old_pointer_space()->IsSweepingComplete()); 1218 CHECK_EQ(initial_size, static_cast<int>(HEAP->SizeOfObjects())); 1219 1220 // Advancing the sweeper step-wise should not change the heap size. 1221 while (!HEAP->old_pointer_space()->IsSweepingComplete()) { 1222 HEAP->old_pointer_space()->AdvanceSweeper(KB); 1223 CHECK_EQ(initial_size, static_cast<int>(HEAP->SizeOfObjects())); 1224 } 1225 } 1226 1227 1228 TEST(TestSizeOfObjectsVsHeapIteratorPrecision) { 1229 InitializeVM(); 1230 HEAP->EnsureHeapIsIterable(); 1231 intptr_t size_of_objects_1 = HEAP->SizeOfObjects(); 1232 HeapIterator iterator; 1233 intptr_t size_of_objects_2 = 0; 1234 for (HeapObject* obj = iterator.next(); 1235 obj != NULL; 1236 obj = iterator.next()) { 1237 size_of_objects_2 += obj->Size(); 1238 } 1239 // Delta must be within 5% of the larger result. 1240 // TODO(gc): Tighten this up by distinguishing between byte 1241 // arrays that are real and those that merely mark free space 1242 // on the heap. 1243 if (size_of_objects_1 > size_of_objects_2) { 1244 intptr_t delta = size_of_objects_1 - size_of_objects_2; 1245 PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, " 1246 "Iterator: %" V8_PTR_PREFIX "d, " 1247 "delta: %" V8_PTR_PREFIX "d\n", 1248 size_of_objects_1, size_of_objects_2, delta); 1249 CHECK_GT(size_of_objects_1 / 20, delta); 1250 } else { 1251 intptr_t delta = size_of_objects_2 - size_of_objects_1; 1252 PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, " 1253 "Iterator: %" V8_PTR_PREFIX "d, " 1254 "delta: %" V8_PTR_PREFIX "d\n", 1255 size_of_objects_1, size_of_objects_2, delta); 1256 CHECK_GT(size_of_objects_2 / 20, delta); 1257 } 1258 } 1259 1260 1261 static void FillUpNewSpace(NewSpace* new_space) { 1262 // Fill up new space to the point that it is completely full. Make sure 1263 // that the scavenger does not undo the filling. 1264 v8::HandleScope scope; 1265 AlwaysAllocateScope always_allocate; 1266 intptr_t available = new_space->EffectiveCapacity() - new_space->Size(); 1267 intptr_t number_of_fillers = (available / FixedArray::SizeFor(1000)) - 10; 1268 for (intptr_t i = 0; i < number_of_fillers; i++) { 1269 CHECK(HEAP->InNewSpace(*FACTORY->NewFixedArray(1000, NOT_TENURED))); 1270 } 1271 } 1272 1273 1274 TEST(GrowAndShrinkNewSpace) { 1275 InitializeVM(); 1276 NewSpace* new_space = HEAP->new_space(); 1277 1278 // Explicitly growing should double the space capacity. 1279 intptr_t old_capacity, new_capacity; 1280 old_capacity = new_space->Capacity(); 1281 new_space->Grow(); 1282 new_capacity = new_space->Capacity(); 1283 CHECK(2 * old_capacity == new_capacity); 1284 1285 old_capacity = new_space->Capacity(); 1286 FillUpNewSpace(new_space); 1287 new_capacity = new_space->Capacity(); 1288 CHECK(old_capacity == new_capacity); 1289 1290 // Explicitly shrinking should not affect space capacity. 1291 old_capacity = new_space->Capacity(); 1292 new_space->Shrink(); 1293 new_capacity = new_space->Capacity(); 1294 CHECK(old_capacity == new_capacity); 1295 1296 // Let the scavenger empty the new space. 1297 HEAP->CollectGarbage(NEW_SPACE); 1298 CHECK_LE(new_space->Size(), old_capacity); 1299 1300 // Explicitly shrinking should halve the space capacity. 1301 old_capacity = new_space->Capacity(); 1302 new_space->Shrink(); 1303 new_capacity = new_space->Capacity(); 1304 CHECK(old_capacity == 2 * new_capacity); 1305 1306 // Consecutive shrinking should not affect space capacity. 1307 old_capacity = new_space->Capacity(); 1308 new_space->Shrink(); 1309 new_space->Shrink(); 1310 new_space->Shrink(); 1311 new_capacity = new_space->Capacity(); 1312 CHECK(old_capacity == new_capacity); 1313 } 1314 1315 1316 TEST(CollectingAllAvailableGarbageShrinksNewSpace) { 1317 InitializeVM(); 1318 v8::HandleScope scope; 1319 NewSpace* new_space = HEAP->new_space(); 1320 intptr_t old_capacity, new_capacity; 1321 old_capacity = new_space->Capacity(); 1322 new_space->Grow(); 1323 new_capacity = new_space->Capacity(); 1324 CHECK(2 * old_capacity == new_capacity); 1325 FillUpNewSpace(new_space); 1326 HEAP->CollectAllAvailableGarbage(); 1327 new_capacity = new_space->Capacity(); 1328 CHECK(old_capacity == new_capacity); 1329 } 1330 1331 1332 static int NumberOfGlobalObjects() { 1333 int count = 0; 1334 HeapIterator iterator; 1335 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { 1336 if (obj->IsGlobalObject()) count++; 1337 } 1338 return count; 1339 } 1340 1341 1342 // Test that we don't embed maps from foreign contexts into 1343 // optimized code. 1344 TEST(LeakGlobalContextViaMap) { 1345 i::FLAG_allow_natives_syntax = true; 1346 v8::HandleScope outer_scope; 1347 v8::Persistent<v8::Context> ctx1 = v8::Context::New(); 1348 v8::Persistent<v8::Context> ctx2 = v8::Context::New(); 1349 ctx1->Enter(); 1350 1351 HEAP->CollectAllAvailableGarbage(); 1352 CHECK_EQ(4, NumberOfGlobalObjects()); 1353 1354 { 1355 v8::HandleScope inner_scope; 1356 CompileRun("var v = {x: 42}"); 1357 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); 1358 ctx2->Enter(); 1359 ctx2->Global()->Set(v8_str("o"), v); 1360 v8::Local<v8::Value> res = CompileRun( 1361 "function f() { return o.x; }" 1362 "for (var i = 0; i < 10; ++i) f();" 1363 "%OptimizeFunctionOnNextCall(f);" 1364 "f();"); 1365 CHECK_EQ(42, res->Int32Value()); 1366 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0)); 1367 ctx2->Exit(); 1368 ctx1->Exit(); 1369 ctx1.Dispose(); 1370 } 1371 HEAP->CollectAllAvailableGarbage(); 1372 CHECK_EQ(2, NumberOfGlobalObjects()); 1373 ctx2.Dispose(); 1374 HEAP->CollectAllAvailableGarbage(); 1375 CHECK_EQ(0, NumberOfGlobalObjects()); 1376 } 1377 1378 1379 // Test that we don't embed functions from foreign contexts into 1380 // optimized code. 1381 TEST(LeakGlobalContextViaFunction) { 1382 i::FLAG_allow_natives_syntax = true; 1383 v8::HandleScope outer_scope; 1384 v8::Persistent<v8::Context> ctx1 = v8::Context::New(); 1385 v8::Persistent<v8::Context> ctx2 = v8::Context::New(); 1386 ctx1->Enter(); 1387 1388 HEAP->CollectAllAvailableGarbage(); 1389 CHECK_EQ(4, NumberOfGlobalObjects()); 1390 1391 { 1392 v8::HandleScope inner_scope; 1393 CompileRun("var v = function() { return 42; }"); 1394 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); 1395 ctx2->Enter(); 1396 ctx2->Global()->Set(v8_str("o"), v); 1397 v8::Local<v8::Value> res = CompileRun( 1398 "function f(x) { return x(); }" 1399 "for (var i = 0; i < 10; ++i) f(o);" 1400 "%OptimizeFunctionOnNextCall(f);" 1401 "f(o);"); 1402 CHECK_EQ(42, res->Int32Value()); 1403 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0)); 1404 ctx2->Exit(); 1405 ctx1->Exit(); 1406 ctx1.Dispose(); 1407 } 1408 HEAP->CollectAllAvailableGarbage(); 1409 CHECK_EQ(2, NumberOfGlobalObjects()); 1410 ctx2.Dispose(); 1411 HEAP->CollectAllAvailableGarbage(); 1412 CHECK_EQ(0, NumberOfGlobalObjects()); 1413 } 1414 1415 1416 TEST(LeakGlobalContextViaMapKeyed) { 1417 i::FLAG_allow_natives_syntax = true; 1418 v8::HandleScope outer_scope; 1419 v8::Persistent<v8::Context> ctx1 = v8::Context::New(); 1420 v8::Persistent<v8::Context> ctx2 = v8::Context::New(); 1421 ctx1->Enter(); 1422 1423 HEAP->CollectAllAvailableGarbage(); 1424 CHECK_EQ(4, NumberOfGlobalObjects()); 1425 1426 { 1427 v8::HandleScope inner_scope; 1428 CompileRun("var v = [42, 43]"); 1429 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); 1430 ctx2->Enter(); 1431 ctx2->Global()->Set(v8_str("o"), v); 1432 v8::Local<v8::Value> res = CompileRun( 1433 "function f() { return o[0]; }" 1434 "for (var i = 0; i < 10; ++i) f();" 1435 "%OptimizeFunctionOnNextCall(f);" 1436 "f();"); 1437 CHECK_EQ(42, res->Int32Value()); 1438 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0)); 1439 ctx2->Exit(); 1440 ctx1->Exit(); 1441 ctx1.Dispose(); 1442 } 1443 HEAP->CollectAllAvailableGarbage(); 1444 CHECK_EQ(2, NumberOfGlobalObjects()); 1445 ctx2.Dispose(); 1446 HEAP->CollectAllAvailableGarbage(); 1447 CHECK_EQ(0, NumberOfGlobalObjects()); 1448 } 1449 1450 1451 TEST(LeakGlobalContextViaMapProto) { 1452 i::FLAG_allow_natives_syntax = true; 1453 v8::HandleScope outer_scope; 1454 v8::Persistent<v8::Context> ctx1 = v8::Context::New(); 1455 v8::Persistent<v8::Context> ctx2 = v8::Context::New(); 1456 ctx1->Enter(); 1457 1458 HEAP->CollectAllAvailableGarbage(); 1459 CHECK_EQ(4, NumberOfGlobalObjects()); 1460 1461 { 1462 v8::HandleScope inner_scope; 1463 CompileRun("var v = { y: 42}"); 1464 v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v")); 1465 ctx2->Enter(); 1466 ctx2->Global()->Set(v8_str("o"), v); 1467 v8::Local<v8::Value> res = CompileRun( 1468 "function f() {" 1469 " var p = {x: 42};" 1470 " p.__proto__ = o;" 1471 " return p.x;" 1472 "}" 1473 "for (var i = 0; i < 10; ++i) f();" 1474 "%OptimizeFunctionOnNextCall(f);" 1475 "f();"); 1476 CHECK_EQ(42, res->Int32Value()); 1477 ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0)); 1478 ctx2->Exit(); 1479 ctx1->Exit(); 1480 ctx1.Dispose(); 1481 } 1482 HEAP->CollectAllAvailableGarbage(); 1483 CHECK_EQ(2, NumberOfGlobalObjects()); 1484 ctx2.Dispose(); 1485 HEAP->CollectAllAvailableGarbage(); 1486 CHECK_EQ(0, NumberOfGlobalObjects()); 1487 } 1488 1489 1490 TEST(InstanceOfStubWriteBarrier) { 1491 i::FLAG_allow_natives_syntax = true; 1492 #ifdef DEBUG 1493 i::FLAG_verify_heap = true; 1494 #endif 1495 InitializeVM(); 1496 if (!i::V8::UseCrankshaft()) return; 1497 v8::HandleScope outer_scope; 1498 1499 { 1500 v8::HandleScope scope; 1501 CompileRun( 1502 "function foo () { }" 1503 "function mkbar () { return new (new Function(\"\")) (); }" 1504 "function f (x) { return (x instanceof foo); }" 1505 "function g () { f(mkbar()); }" 1506 "f(new foo()); f(new foo());" 1507 "%OptimizeFunctionOnNextCall(f);" 1508 "f(new foo()); g();"); 1509 } 1510 1511 IncrementalMarking* marking = HEAP->incremental_marking(); 1512 marking->Abort(); 1513 marking->Start(); 1514 1515 Handle<JSFunction> f = 1516 v8::Utils::OpenHandle( 1517 *v8::Handle<v8::Function>::Cast( 1518 v8::Context::GetCurrent()->Global()->Get(v8_str("f")))); 1519 1520 CHECK(f->IsOptimized()); 1521 1522 while (!Marking::IsBlack(Marking::MarkBitFrom(f->code())) && 1523 !marking->IsStopped()) { 1524 // Discard any pending GC requests otherwise we will get GC when we enter 1525 // code below. 1526 marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); 1527 } 1528 1529 CHECK(marking->IsMarking()); 1530 1531 { 1532 v8::HandleScope scope; 1533 v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global(); 1534 v8::Handle<v8::Function> g = 1535 v8::Handle<v8::Function>::Cast(global->Get(v8_str("g"))); 1536 g->Call(global, 0, NULL); 1537 } 1538 1539 HEAP->incremental_marking()->set_should_hurry(true); 1540 HEAP->CollectGarbage(OLD_POINTER_SPACE); 1541 } 1542 1543 1544 TEST(PrototypeTransitionClearing) { 1545 InitializeVM(); 1546 v8::HandleScope scope; 1547 1548 CompileRun( 1549 "var base = {};" 1550 "var live = [];" 1551 "for (var i = 0; i < 10; i++) {" 1552 " var object = {};" 1553 " var prototype = {};" 1554 " object.__proto__ = prototype;" 1555 " if (i >= 3) live.push(object, prototype);" 1556 "}"); 1557 1558 Handle<JSObject> baseObject = 1559 v8::Utils::OpenHandle( 1560 *v8::Handle<v8::Object>::Cast( 1561 v8::Context::GetCurrent()->Global()->Get(v8_str("base")))); 1562 1563 // Verify that only dead prototype transitions are cleared. 1564 CHECK_EQ(10, baseObject->map()->NumberOfProtoTransitions()); 1565 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1566 CHECK_EQ(10 - 3, baseObject->map()->NumberOfProtoTransitions()); 1567 1568 // Verify that prototype transitions array was compacted. 1569 FixedArray* trans = baseObject->map()->prototype_transitions(); 1570 for (int i = 0; i < 10 - 3; i++) { 1571 int j = Map::kProtoTransitionHeaderSize + 1572 i * Map::kProtoTransitionElementsPerEntry; 1573 CHECK(trans->get(j + Map::kProtoTransitionMapOffset)->IsMap()); 1574 CHECK(trans->get(j + Map::kProtoTransitionPrototypeOffset)->IsJSObject()); 1575 } 1576 1577 // Make sure next prototype is placed on an old-space evacuation candidate. 1578 Handle<JSObject> prototype; 1579 PagedSpace* space = HEAP->old_pointer_space(); 1580 do { 1581 prototype = FACTORY->NewJSArray(32 * KB, FAST_ELEMENTS, TENURED); 1582 } while (space->FirstPage() == space->LastPage() || 1583 !space->LastPage()->Contains(prototype->address())); 1584 1585 // Add a prototype on an evacuation candidate and verify that transition 1586 // clearing correctly records slots in prototype transition array. 1587 i::FLAG_always_compact = true; 1588 Handle<Map> map(baseObject->map()); 1589 CHECK(!space->LastPage()->Contains(map->prototype_transitions()->address())); 1590 CHECK(space->LastPage()->Contains(prototype->address())); 1591 baseObject->SetPrototype(*prototype, false)->ToObjectChecked(); 1592 CHECK(map->GetPrototypeTransition(*prototype)->IsMap()); 1593 HEAP->CollectAllGarbage(Heap::kNoGCFlags); 1594 CHECK(map->GetPrototypeTransition(*prototype)->IsMap()); 1595 } 1596 1597 1598 TEST(ResetSharedFunctionInfoCountersDuringIncrementalMarking) { 1599 i::FLAG_allow_natives_syntax = true; 1600 #ifdef DEBUG 1601 i::FLAG_verify_heap = true; 1602 #endif 1603 InitializeVM(); 1604 if (!i::V8::UseCrankshaft()) return; 1605 v8::HandleScope outer_scope; 1606 1607 { 1608 v8::HandleScope scope; 1609 CompileRun( 1610 "function f () {" 1611 " var s = 0;" 1612 " for (var i = 0; i < 100; i++) s += i;" 1613 " return s;" 1614 "}" 1615 "f(); f();" 1616 "%OptimizeFunctionOnNextCall(f);" 1617 "f();"); 1618 } 1619 Handle<JSFunction> f = 1620 v8::Utils::OpenHandle( 1621 *v8::Handle<v8::Function>::Cast( 1622 v8::Context::GetCurrent()->Global()->Get(v8_str("f")))); 1623 CHECK(f->IsOptimized()); 1624 1625 IncrementalMarking* marking = HEAP->incremental_marking(); 1626 marking->Abort(); 1627 marking->Start(); 1628 1629 // The following two calls will increment HEAP->global_ic_age(). 1630 const int kLongIdlePauseInMs = 1000; 1631 v8::V8::ContextDisposedNotification(); 1632 v8::V8::IdleNotification(kLongIdlePauseInMs); 1633 1634 while (!marking->IsStopped() && !marking->IsComplete()) { 1635 marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); 1636 } 1637 1638 CHECK_EQ(HEAP->global_ic_age(), f->shared()->ic_age()); 1639 CHECK_EQ(0, f->shared()->opt_count()); 1640 CHECK_EQ(0, f->shared()->code()->profiler_ticks()); 1641 } 1642 1643 1644 TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) { 1645 i::FLAG_allow_natives_syntax = true; 1646 #ifdef DEBUG 1647 i::FLAG_verify_heap = true; 1648 #endif 1649 InitializeVM(); 1650 if (!i::V8::UseCrankshaft()) return; 1651 v8::HandleScope outer_scope; 1652 1653 { 1654 v8::HandleScope scope; 1655 CompileRun( 1656 "function f () {" 1657 " var s = 0;" 1658 " for (var i = 0; i < 100; i++) s += i;" 1659 " return s;" 1660 "}" 1661 "f(); f();" 1662 "%OptimizeFunctionOnNextCall(f);" 1663 "f();"); 1664 } 1665 Handle<JSFunction> f = 1666 v8::Utils::OpenHandle( 1667 *v8::Handle<v8::Function>::Cast( 1668 v8::Context::GetCurrent()->Global()->Get(v8_str("f")))); 1669 CHECK(f->IsOptimized()); 1670 1671 HEAP->incremental_marking()->Abort(); 1672 1673 // The following two calls will increment HEAP->global_ic_age(). 1674 // Since incremental marking is off, IdleNotification will do full GC. 1675 const int kLongIdlePauseInMs = 1000; 1676 v8::V8::ContextDisposedNotification(); 1677 v8::V8::IdleNotification(kLongIdlePauseInMs); 1678 1679 CHECK_EQ(HEAP->global_ic_age(), f->shared()->ic_age()); 1680 CHECK_EQ(0, f->shared()->opt_count()); 1681 CHECK_EQ(0, f->shared()->code()->profiler_ticks()); 1682 } 1683