1// Copyright 2015 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <stdlib.h> 6#include <sstream> 7#include <utility> 8 9#include "src/api.h" 10#include "src/objects.h" 11#include "src/v8.h" 12 13#include "test/cctest/cctest.h" 14 15using namespace v8::base; 16using namespace v8::internal; 17 18 19static const int kMaxInobjectProperties = 20 (JSObject::kMaxInstanceSize - JSObject::kHeaderSize) >> kPointerSizeLog2; 21 22 23template <typename T> 24static Handle<T> OpenHandle(v8::Local<v8::Value> value) { 25 Handle<Object> obj = v8::Utils::OpenHandle(*value); 26 return Handle<T>::cast(obj); 27} 28 29 30static inline v8::Local<v8::Value> Run(v8::Local<v8::Script> script) { 31 v8::Local<v8::Value> result; 32 if (script->Run(v8::Isolate::GetCurrent()->GetCurrentContext()) 33 .ToLocal(&result)) { 34 return result; 35 } 36 return v8::Local<v8::Value>(); 37} 38 39 40template <typename T = Object> 41Handle<T> GetGlobal(const char* name) { 42 Isolate* isolate = CcTest::i_isolate(); 43 Factory* factory = isolate->factory(); 44 Handle<String> str_name = factory->InternalizeUtf8String(name); 45 46 Handle<Object> value = 47 Object::GetProperty(isolate->global_object(), str_name).ToHandleChecked(); 48 return Handle<T>::cast(value); 49} 50 51 52template <typename T = Object> 53Handle<T> GetLexical(const char* name) { 54 Isolate* isolate = CcTest::i_isolate(); 55 Factory* factory = isolate->factory(); 56 57 Handle<String> str_name = factory->InternalizeUtf8String(name); 58 Handle<ScriptContextTable> script_contexts( 59 isolate->native_context()->script_context_table()); 60 61 ScriptContextTable::LookupResult lookup_result; 62 if (ScriptContextTable::Lookup(script_contexts, str_name, &lookup_result)) { 63 Handle<Object> result = 64 FixedArray::get(ScriptContextTable::GetContext( 65 script_contexts, lookup_result.context_index), 66 lookup_result.slot_index); 67 return Handle<T>::cast(result); 68 } 69 return Handle<T>(); 70} 71 72 73template <typename T = Object> 74Handle<T> GetLexical(const std::string& name) { 75 return GetLexical<T>(name.c_str()); 76} 77 78 79template <typename T> 80static inline Handle<T> Run(v8::Local<v8::Script> script) { 81 return OpenHandle<T>(Run(script)); 82} 83 84 85template <typename T> 86static inline Handle<T> CompileRun(const char* script) { 87 return OpenHandle<T>(CompileRun(script)); 88} 89 90 91static Object* GetFieldValue(JSObject* obj, int property_index) { 92 FieldIndex index = FieldIndex::ForPropertyIndex(obj->map(), property_index); 93 return obj->RawFastPropertyAt(index); 94} 95 96 97static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) { 98 if (obj->IsUnboxedDoubleField(field_index)) { 99 return obj->RawFastDoublePropertyAt(field_index); 100 } else { 101 Object* value = obj->RawFastPropertyAt(field_index); 102 CHECK(value->IsMutableHeapNumber()); 103 return HeapNumber::cast(value)->value(); 104 } 105} 106 107 108static double GetDoubleFieldValue(JSObject* obj, int property_index) { 109 FieldIndex index = FieldIndex::ForPropertyIndex(obj->map(), property_index); 110 return GetDoubleFieldValue(obj, index); 111} 112 113 114bool IsObjectShrinkable(JSObject* obj) { 115 Handle<Map> filler_map = 116 CcTest::i_isolate()->factory()->one_pointer_filler_map(); 117 118 int inobject_properties = obj->map()->GetInObjectProperties(); 119 int unused = obj->map()->unused_property_fields(); 120 if (unused == 0) return false; 121 122 for (int i = inobject_properties - unused; i < inobject_properties; i++) { 123 if (*filler_map != GetFieldValue(obj, i)) { 124 return false; 125 } 126 } 127 return true; 128} 129 130 131TEST(JSObjectBasic) { 132 // Avoid eventual completion of in-object slack tracking. 133 FLAG_inline_construct = false; 134 FLAG_always_opt = false; 135 CcTest::InitializeVM(); 136 v8::HandleScope scope(CcTest::isolate()); 137 const char* source = 138 "function A() {" 139 " this.a = 42;" 140 " this.d = 4.2;" 141 " this.o = this;" 142 "}"; 143 CompileRun(source); 144 145 Handle<JSFunction> func = GetGlobal<JSFunction>("A"); 146 147 // Zero instances were created so far. 148 CHECK(!func->has_initial_map()); 149 150 v8::Local<v8::Script> new_A_script = v8_compile("new A();"); 151 152 Handle<JSObject> obj = Run<JSObject>(new_A_script); 153 154 CHECK(func->has_initial_map()); 155 Handle<Map> initial_map(func->initial_map()); 156 157 // One instance created. 158 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 159 initial_map->construction_counter()); 160 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 161 162 // There must be at least some slack. 163 CHECK_LT(5, obj->map()->GetInObjectProperties()); 164 CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, 0)); 165 CHECK_EQ(4.2, GetDoubleFieldValue(*obj, 1)); 166 CHECK_EQ(*obj, GetFieldValue(*obj, 2)); 167 CHECK(IsObjectShrinkable(*obj)); 168 169 // Create several objects to complete the tracking. 170 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 171 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 172 Handle<JSObject> tmp = Run<JSObject>(new_A_script); 173 CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(), 174 IsObjectShrinkable(*tmp)); 175 } 176 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 177 CHECK(!IsObjectShrinkable(*obj)); 178 179 // No slack left. 180 CHECK_EQ(3, obj->map()->GetInObjectProperties()); 181} 182 183 184TEST(JSObjectBasicNoInlineNew) { 185 FLAG_inline_new = false; 186 TestJSObjectBasic(); 187} 188 189 190TEST(JSObjectComplex) { 191 // Avoid eventual completion of in-object slack tracking. 192 FLAG_inline_construct = false; 193 FLAG_always_opt = false; 194 CcTest::InitializeVM(); 195 v8::HandleScope scope(CcTest::isolate()); 196 const char* source = 197 "function A(n) {" 198 " if (n > 0) this.a = 42;" 199 " if (n > 1) this.d = 4.2;" 200 " if (n > 2) this.o1 = this;" 201 " if (n > 3) this.o2 = this;" 202 " if (n > 4) this.o3 = this;" 203 " if (n > 5) this.o4 = this;" 204 "}"; 205 CompileRun(source); 206 207 Handle<JSFunction> func = GetGlobal<JSFunction>("A"); 208 209 // Zero instances were created so far. 210 CHECK(!func->has_initial_map()); 211 212 Handle<JSObject> obj1 = CompileRun<JSObject>("new A(1);"); 213 Handle<JSObject> obj3 = CompileRun<JSObject>("new A(3);"); 214 Handle<JSObject> obj5 = CompileRun<JSObject>("new A(5);"); 215 216 CHECK(func->has_initial_map()); 217 Handle<Map> initial_map(func->initial_map()); 218 219 // Three instances created. 220 CHECK_EQ(Map::kSlackTrackingCounterStart - 3, 221 initial_map->construction_counter()); 222 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 223 224 // There must be at least some slack. 225 CHECK_LT(5, obj3->map()->GetInObjectProperties()); 226 CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj3, 0)); 227 CHECK_EQ(4.2, GetDoubleFieldValue(*obj3, 1)); 228 CHECK_EQ(*obj3, GetFieldValue(*obj3, 2)); 229 CHECK(IsObjectShrinkable(*obj1)); 230 CHECK(IsObjectShrinkable(*obj3)); 231 CHECK(IsObjectShrinkable(*obj5)); 232 233 // Create several objects to complete the tracking. 234 for (int i = 3; i < Map::kGenerousAllocationCount; i++) { 235 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 236 CompileRun("new A(3);"); 237 } 238 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 239 240 // obj1 and obj2 stays shrinkable because we don't clear unused fields. 241 CHECK(IsObjectShrinkable(*obj1)); 242 CHECK(IsObjectShrinkable(*obj3)); 243 CHECK(!IsObjectShrinkable(*obj5)); 244 245 CHECK_EQ(5, obj1->map()->GetInObjectProperties()); 246 CHECK_EQ(4, obj1->map()->unused_property_fields()); 247 248 CHECK_EQ(5, obj3->map()->GetInObjectProperties()); 249 CHECK_EQ(2, obj3->map()->unused_property_fields()); 250 251 CHECK_EQ(5, obj5->map()->GetInObjectProperties()); 252 CHECK_EQ(0, obj5->map()->unused_property_fields()); 253 254 // Since slack tracking is complete, the new objects should not be shrinkable. 255 obj1 = CompileRun<JSObject>("new A(1);"); 256 obj3 = CompileRun<JSObject>("new A(3);"); 257 obj5 = CompileRun<JSObject>("new A(5);"); 258 259 CHECK(!IsObjectShrinkable(*obj1)); 260 CHECK(!IsObjectShrinkable(*obj3)); 261 CHECK(!IsObjectShrinkable(*obj5)); 262} 263 264 265TEST(JSObjectComplexNoInlineNew) { 266 FLAG_inline_new = false; 267 TestJSObjectComplex(); 268} 269 270 271TEST(JSGeneratorObjectBasic) { 272 // Avoid eventual completion of in-object slack tracking. 273 FLAG_inline_construct = false; 274 FLAG_always_opt = false; 275 CcTest::InitializeVM(); 276 v8::HandleScope scope(CcTest::isolate()); 277 const char* source = 278 "function* A() {" 279 " var i = 0;" 280 " while(true) {" 281 " yield i++;" 282 " }" 283 "};" 284 "function CreateGenerator() {" 285 " var o = A();" 286 " o.a = 42;" 287 " o.d = 4.2;" 288 " o.o = o;" 289 " return o;" 290 "}"; 291 CompileRun(source); 292 293 Handle<JSFunction> func = GetGlobal<JSFunction>("A"); 294 295 // Zero instances were created so far. 296 CHECK(!func->has_initial_map()); 297 298 v8::Local<v8::Script> new_A_script = v8_compile("CreateGenerator();"); 299 300 Handle<JSObject> obj = Run<JSObject>(new_A_script); 301 302 CHECK(func->has_initial_map()); 303 Handle<Map> initial_map(func->initial_map()); 304 305 // One instance created. 306 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 307 initial_map->construction_counter()); 308 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 309 310 // There must be at least some slack. 311 CHECK_LT(5, obj->map()->GetInObjectProperties()); 312 CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, 0)); 313 CHECK_EQ(4.2, GetDoubleFieldValue(*obj, 1)); 314 CHECK_EQ(*obj, GetFieldValue(*obj, 2)); 315 CHECK(IsObjectShrinkable(*obj)); 316 317 // Create several objects to complete the tracking. 318 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 319 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 320 Handle<JSObject> tmp = Run<JSObject>(new_A_script); 321 CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(), 322 IsObjectShrinkable(*tmp)); 323 } 324 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 325 CHECK(!IsObjectShrinkable(*obj)); 326 327 // No slack left. 328 CHECK_EQ(3, obj->map()->GetInObjectProperties()); 329} 330 331 332TEST(JSGeneratorObjectBasicNoInlineNew) { 333 FLAG_inline_new = false; 334 TestJSGeneratorObjectBasic(); 335} 336 337 338TEST(SubclassBasicNoBaseClassInstances) { 339 // Avoid eventual completion of in-object slack tracking. 340 FLAG_inline_construct = false; 341 FLAG_always_opt = false; 342 CcTest::InitializeVM(); 343 v8::HandleScope scope(CcTest::isolate()); 344 345 // Check that base class' and subclass' slack tracking do not interfere with 346 // each other. 347 // In this test we never create base class instances. 348 349 const char* source = 350 "'use strict';" 351 "class A {" 352 " constructor(...args) {" 353 " this.aa = 42;" 354 " this.ad = 4.2;" 355 " this.ao = this;" 356 " }" 357 "};" 358 "class B extends A {" 359 " constructor(...args) {" 360 " super(...args);" 361 " this.ba = 142;" 362 " this.bd = 14.2;" 363 " this.bo = this;" 364 " }" 365 "};"; 366 CompileRun(source); 367 368 Handle<JSFunction> a_func = GetLexical<JSFunction>("A"); 369 Handle<JSFunction> b_func = GetLexical<JSFunction>("B"); 370 371 // Zero instances were created so far. 372 CHECK(!a_func->has_initial_map()); 373 CHECK(!b_func->has_initial_map()); 374 375 v8::Local<v8::Script> new_B_script = v8_compile("new B();"); 376 377 Handle<JSObject> obj = Run<JSObject>(new_B_script); 378 379 CHECK(a_func->has_initial_map()); 380 Handle<Map> a_initial_map(a_func->initial_map()); 381 382 CHECK(b_func->has_initial_map()); 383 Handle<Map> b_initial_map(b_func->initial_map()); 384 385 // Zero instances of A created. 386 CHECK_EQ(Map::kSlackTrackingCounterStart, 387 a_initial_map->construction_counter()); 388 CHECK(a_initial_map->IsInobjectSlackTrackingInProgress()); 389 390 // One instance of B created. 391 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 392 b_initial_map->construction_counter()); 393 CHECK(b_initial_map->IsInobjectSlackTrackingInProgress()); 394 395 // There must be at least some slack. 396 CHECK_LT(10, obj->map()->GetInObjectProperties()); 397 CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, 0)); 398 CHECK_EQ(4.2, GetDoubleFieldValue(*obj, 1)); 399 CHECK_EQ(*obj, GetFieldValue(*obj, 2)); 400 CHECK_EQ(Smi::FromInt(142), GetFieldValue(*obj, 3)); 401 CHECK_EQ(14.2, GetDoubleFieldValue(*obj, 4)); 402 CHECK_EQ(*obj, GetFieldValue(*obj, 5)); 403 CHECK(IsObjectShrinkable(*obj)); 404 405 // Create several subclass instances to complete the tracking. 406 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 407 CHECK(b_initial_map->IsInobjectSlackTrackingInProgress()); 408 Handle<JSObject> tmp = Run<JSObject>(new_B_script); 409 CHECK_EQ(b_initial_map->IsInobjectSlackTrackingInProgress(), 410 IsObjectShrinkable(*tmp)); 411 } 412 CHECK(!b_initial_map->IsInobjectSlackTrackingInProgress()); 413 CHECK(!IsObjectShrinkable(*obj)); 414 415 // Zero instances of A created. 416 CHECK_EQ(Map::kSlackTrackingCounterStart, 417 a_initial_map->construction_counter()); 418 CHECK(a_initial_map->IsInobjectSlackTrackingInProgress()); 419 420 // No slack left. 421 CHECK_EQ(6, obj->map()->GetInObjectProperties()); 422} 423 424 425TEST(SubclassBasicNoBaseClassInstancesNoInlineNew) { 426 FLAG_inline_new = false; 427 TestSubclassBasicNoBaseClassInstances(); 428} 429 430 431TEST(SubclassBasic) { 432 // Avoid eventual completion of in-object slack tracking. 433 FLAG_inline_construct = false; 434 FLAG_always_opt = false; 435 CcTest::InitializeVM(); 436 v8::HandleScope scope(CcTest::isolate()); 437 438 // Check that base class' and subclass' slack tracking do not interfere with 439 // each other. 440 // In this test we first create enough base class instances to complete 441 // the slack tracking and then proceed creating subclass instances. 442 443 const char* source = 444 "'use strict';" 445 "class A {" 446 " constructor(...args) {" 447 " this.aa = 42;" 448 " this.ad = 4.2;" 449 " this.ao = this;" 450 " }" 451 "};" 452 "class B extends A {" 453 " constructor(...args) {" 454 " super(...args);" 455 " this.ba = 142;" 456 " this.bd = 14.2;" 457 " this.bo = this;" 458 " }" 459 "};"; 460 CompileRun(source); 461 462 Handle<JSFunction> a_func = GetLexical<JSFunction>("A"); 463 Handle<JSFunction> b_func = GetLexical<JSFunction>("B"); 464 465 // Zero instances were created so far. 466 CHECK(!a_func->has_initial_map()); 467 CHECK(!b_func->has_initial_map()); 468 469 v8::Local<v8::Script> new_A_script = v8_compile("new A();"); 470 v8::Local<v8::Script> new_B_script = v8_compile("new B();"); 471 472 Handle<JSObject> a_obj = Run<JSObject>(new_A_script); 473 Handle<JSObject> b_obj = Run<JSObject>(new_B_script); 474 475 CHECK(a_func->has_initial_map()); 476 Handle<Map> a_initial_map(a_func->initial_map()); 477 478 CHECK(b_func->has_initial_map()); 479 Handle<Map> b_initial_map(b_func->initial_map()); 480 481 // One instance of a base class created. 482 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 483 a_initial_map->construction_counter()); 484 CHECK(a_initial_map->IsInobjectSlackTrackingInProgress()); 485 486 // One instance of a subclass created. 487 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 488 b_initial_map->construction_counter()); 489 CHECK(b_initial_map->IsInobjectSlackTrackingInProgress()); 490 491 // Create several base class instances to complete the tracking. 492 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 493 CHECK(a_initial_map->IsInobjectSlackTrackingInProgress()); 494 Handle<JSObject> tmp = Run<JSObject>(new_A_script); 495 CHECK_EQ(a_initial_map->IsInobjectSlackTrackingInProgress(), 496 IsObjectShrinkable(*tmp)); 497 } 498 CHECK(!a_initial_map->IsInobjectSlackTrackingInProgress()); 499 CHECK(!IsObjectShrinkable(*a_obj)); 500 501 // No slack left. 502 CHECK_EQ(3, a_obj->map()->GetInObjectProperties()); 503 504 // There must be at least some slack. 505 CHECK_LT(10, b_obj->map()->GetInObjectProperties()); 506 CHECK_EQ(Smi::FromInt(42), GetFieldValue(*b_obj, 0)); 507 CHECK_EQ(4.2, GetDoubleFieldValue(*b_obj, 1)); 508 CHECK_EQ(*b_obj, GetFieldValue(*b_obj, 2)); 509 CHECK_EQ(Smi::FromInt(142), GetFieldValue(*b_obj, 3)); 510 CHECK_EQ(14.2, GetDoubleFieldValue(*b_obj, 4)); 511 CHECK_EQ(*b_obj, GetFieldValue(*b_obj, 5)); 512 CHECK(IsObjectShrinkable(*b_obj)); 513 514 // Create several subclass instances to complete the tracking. 515 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 516 CHECK(b_initial_map->IsInobjectSlackTrackingInProgress()); 517 Handle<JSObject> tmp = Run<JSObject>(new_B_script); 518 CHECK_EQ(b_initial_map->IsInobjectSlackTrackingInProgress(), 519 IsObjectShrinkable(*tmp)); 520 } 521 CHECK(!b_initial_map->IsInobjectSlackTrackingInProgress()); 522 CHECK(!IsObjectShrinkable(*b_obj)); 523 524 // No slack left. 525 CHECK_EQ(6, b_obj->map()->GetInObjectProperties()); 526} 527 528 529TEST(SubclassBasicNoInlineNew) { 530 FLAG_inline_new = false; 531 TestSubclassBasic(); 532} 533 534 535// Creates class hierachy of length matching the |hierarchy_desc| length and 536// with the number of fields at i'th level equal to |hierarchy_desc[i]|. 537static void CreateClassHierarchy(const std::vector<int>& hierarchy_desc) { 538 std::ostringstream os; 539 os << "'use strict';\n\n"; 540 541 int n = static_cast<int>(hierarchy_desc.size()); 542 for (int cur_class = 0; cur_class < n; cur_class++) { 543 os << "class A" << cur_class; 544 if (cur_class > 0) { 545 os << " extends A" << (cur_class - 1); 546 } 547 os << " {\n" 548 " constructor(...args) {\n"; 549 if (cur_class > 0) { 550 os << " super(...args);\n"; 551 } 552 int fields_count = hierarchy_desc[cur_class]; 553 for (int k = 0; k < fields_count; k++) { 554 os << " this.f" << cur_class << "_" << k << " = " << k << ";\n"; 555 } 556 os << " }\n" 557 "};\n\n"; 558 } 559 CompileRun(os.str().c_str()); 560} 561 562 563static std::string GetClassName(int class_index) { 564 std::ostringstream os; 565 os << "A" << class_index; 566 return os.str(); 567} 568 569 570static v8::Local<v8::Script> GetNewObjectScript(const std::string& class_name) { 571 std::ostringstream os; 572 os << "new " << class_name << "();"; 573 return v8_compile(os.str().c_str()); 574} 575 576 577// Test that in-object slack tracking works as expected for first |n| classes 578// in the hierarchy. 579// This test works only for if the total property count is less than maximum 580// in-object properties count. 581static void TestClassHierarchy(const std::vector<int>& hierarchy_desc, int n) { 582 int fields_count = 0; 583 for (int cur_class = 0; cur_class < n; cur_class++) { 584 std::string class_name = GetClassName(cur_class); 585 int fields_count_at_current_level = hierarchy_desc[cur_class]; 586 fields_count += fields_count_at_current_level; 587 588 // This test is not suitable for in-object properties count overflow case. 589 CHECK_LT(fields_count, kMaxInobjectProperties); 590 591 // Create |class_name| objects and check slack tracking. 592 v8::Local<v8::Script> new_script = GetNewObjectScript(class_name); 593 594 Handle<JSFunction> func = GetLexical<JSFunction>(class_name); 595 596 Handle<JSObject> obj = Run<JSObject>(new_script); 597 598 CHECK(func->has_initial_map()); 599 Handle<Map> initial_map(func->initial_map()); 600 601 // There must be at least some slack. 602 CHECK_LT(fields_count, obj->map()->GetInObjectProperties()); 603 604 // One instance was created. 605 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 606 initial_map->construction_counter()); 607 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 608 609 // Create several instances to complete the tracking. 610 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 611 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 612 Handle<JSObject> tmp = Run<JSObject>(new_script); 613 CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(), 614 IsObjectShrinkable(*tmp)); 615 } 616 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 617 CHECK(!IsObjectShrinkable(*obj)); 618 619 // No slack left. 620 CHECK_EQ(fields_count, obj->map()->GetInObjectProperties()); 621 } 622} 623 624 625static void TestSubclassChain(const std::vector<int>& hierarchy_desc) { 626 // Avoid eventual completion of in-object slack tracking. 627 FLAG_inline_construct = false; 628 FLAG_always_opt = false; 629 CcTest::InitializeVM(); 630 v8::HandleScope scope(CcTest::isolate()); 631 632 CreateClassHierarchy(hierarchy_desc); 633 TestClassHierarchy(hierarchy_desc, static_cast<int>(hierarchy_desc.size())); 634} 635 636 637TEST(LongSubclassChain1) { 638 std::vector<int> hierarchy_desc; 639 for (int i = 0; i < 7; i++) { 640 hierarchy_desc.push_back(i * 10); 641 } 642 TestSubclassChain(hierarchy_desc); 643} 644 645 646TEST(LongSubclassChain2) { 647 std::vector<int> hierarchy_desc; 648 hierarchy_desc.push_back(10); 649 for (int i = 0; i < 42; i++) { 650 hierarchy_desc.push_back(0); 651 } 652 hierarchy_desc.push_back(230); 653 TestSubclassChain(hierarchy_desc); 654} 655 656 657TEST(LongSubclassChain3) { 658 std::vector<int> hierarchy_desc; 659 for (int i = 0; i < 42; i++) { 660 hierarchy_desc.push_back(5); 661 } 662 TestSubclassChain(hierarchy_desc); 663} 664 665 666TEST(InobjectPropetiesCountOverflowInSubclass) { 667 // Avoid eventual completion of in-object slack tracking. 668 FLAG_inline_construct = false; 669 FLAG_always_opt = false; 670 CcTest::InitializeVM(); 671 v8::HandleScope scope(CcTest::isolate()); 672 673 std::vector<int> hierarchy_desc; 674 const int kNoOverflowCount = 5; 675 for (int i = 0; i < kNoOverflowCount; i++) { 676 hierarchy_desc.push_back(50); 677 } 678 // In this class we are going to have properties in the backing store. 679 hierarchy_desc.push_back(100); 680 681 CreateClassHierarchy(hierarchy_desc); 682 683 // For the last class in the hierarchy we need different checks. 684 { 685 int cur_class = kNoOverflowCount; 686 std::string class_name = GetClassName(cur_class); 687 688 // Create |class_name| objects and check slack tracking. 689 v8::Local<v8::Script> new_script = GetNewObjectScript(class_name); 690 691 Handle<JSFunction> func = GetLexical<JSFunction>(class_name); 692 693 Handle<JSObject> obj = Run<JSObject>(new_script); 694 695 CHECK(func->has_initial_map()); 696 Handle<Map> initial_map(func->initial_map()); 697 698 // There must be no slack left. 699 CHECK_EQ(JSObject::kMaxInstanceSize, obj->map()->instance_size()); 700 CHECK_EQ(kMaxInobjectProperties, obj->map()->GetInObjectProperties()); 701 702 // One instance was created. 703 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 704 initial_map->construction_counter()); 705 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 706 707 // Create several instances to complete the tracking. 708 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 709 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 710 Handle<JSObject> tmp = Run<JSObject>(new_script); 711 CHECK(!IsObjectShrinkable(*tmp)); 712 } 713 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 714 CHECK(!IsObjectShrinkable(*obj)); 715 716 // No slack left. 717 CHECK_EQ(kMaxInobjectProperties, obj->map()->GetInObjectProperties()); 718 } 719 720 // The other classes in the hierarchy are not affected. 721 TestClassHierarchy(hierarchy_desc, kNoOverflowCount); 722} 723 724 725TEST(SlowModeSubclass) { 726 // Avoid eventual completion of in-object slack tracking. 727 FLAG_inline_construct = false; 728 FLAG_always_opt = false; 729 CcTest::InitializeVM(); 730 v8::HandleScope scope(CcTest::isolate()); 731 732 std::vector<int> hierarchy_desc; 733 const int kNoOverflowCount = 5; 734 for (int i = 0; i < kNoOverflowCount; i++) { 735 hierarchy_desc.push_back(50); 736 } 737 // This class should go dictionary mode. 738 hierarchy_desc.push_back(1000); 739 740 CreateClassHierarchy(hierarchy_desc); 741 742 // For the last class in the hierarchy we need different checks. 743 { 744 int cur_class = kNoOverflowCount; 745 std::string class_name = GetClassName(cur_class); 746 747 // Create |class_name| objects and check slack tracking. 748 v8::Local<v8::Script> new_script = GetNewObjectScript(class_name); 749 750 Handle<JSFunction> func = GetLexical<JSFunction>(class_name); 751 752 Handle<JSObject> obj = Run<JSObject>(new_script); 753 754 CHECK(func->has_initial_map()); 755 Handle<Map> initial_map(func->initial_map()); 756 757 // Object should go dictionary mode. 758 CHECK_EQ(JSObject::kHeaderSize, obj->map()->instance_size()); 759 CHECK(obj->map()->is_dictionary_map()); 760 761 // One instance was created. 762 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 763 initial_map->construction_counter()); 764 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 765 766 // Create several instances to complete the tracking. 767 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 768 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 769 Handle<JSObject> tmp = Run<JSObject>(new_script); 770 CHECK(!IsObjectShrinkable(*tmp)); 771 } 772 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 773 CHECK(!IsObjectShrinkable(*obj)); 774 775 // Object should stay in dictionary mode. 776 CHECK_EQ(JSObject::kHeaderSize, obj->map()->instance_size()); 777 CHECK(obj->map()->is_dictionary_map()); 778 } 779 780 // The other classes in the hierarchy are not affected. 781 TestClassHierarchy(hierarchy_desc, kNoOverflowCount); 782} 783 784 785static void TestSubclassBuiltin(const char* subclass_name, 786 InstanceType instance_type, 787 const char* builtin_name, 788 const char* ctor_arguments = "", 789 int builtin_properties_count = 0) { 790 { 791 std::ostringstream os; 792 os << "'use strict';\n" 793 "class " 794 << subclass_name << " extends " << builtin_name 795 << " {\n" 796 " constructor(...args) {\n" 797 " super(...args);\n" 798 " this.a = 42;\n" 799 " this.d = 4.2;\n" 800 " this.o = this;\n" 801 " }\n" 802 "};\n"; 803 CompileRun(os.str().c_str()); 804 } 805 806 Handle<JSFunction> func = GetLexical<JSFunction>(subclass_name); 807 808 // Zero instances were created so far. 809 CHECK(!func->has_initial_map()); 810 811 v8::Local<v8::Script> new_script; 812 { 813 std::ostringstream os; 814 os << "new " << subclass_name << "(" << ctor_arguments << ");"; 815 new_script = v8_compile(os.str().c_str()); 816 } 817 818 Run<JSObject>(new_script); 819 820 CHECK(func->has_initial_map()); 821 Handle<Map> initial_map(func->initial_map()); 822 823 CHECK_EQ(instance_type, initial_map->instance_type()); 824 825 // One instance of a subclass created. 826 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 827 initial_map->construction_counter()); 828 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 829 830 // Create two instances in order to ensure that |obj|.o is a data field 831 // in case of Function subclassing. 832 Handle<JSObject> obj = Run<JSObject>(new_script); 833 834 // Two instances of a subclass created. 835 CHECK_EQ(Map::kSlackTrackingCounterStart - 2, 836 initial_map->construction_counter()); 837 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 838 839 // There must be at least some slack. 840 CHECK_LT(builtin_properties_count + 5, obj->map()->GetInObjectProperties()); 841 CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, builtin_properties_count + 0)); 842 CHECK_EQ(4.2, GetDoubleFieldValue(*obj, builtin_properties_count + 1)); 843 CHECK_EQ(*obj, GetFieldValue(*obj, builtin_properties_count + 2)); 844 CHECK(IsObjectShrinkable(*obj)); 845 846 // Create several subclass instances to complete the tracking. 847 for (int i = 2; i < Map::kGenerousAllocationCount; i++) { 848 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 849 Handle<JSObject> tmp = Run<JSObject>(new_script); 850 CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(), 851 IsObjectShrinkable(*tmp)); 852 } 853 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 854 CHECK(!IsObjectShrinkable(*obj)); 855 856 // No slack left. 857 CHECK_EQ(builtin_properties_count + 3, obj->map()->GetInObjectProperties()); 858 859 CHECK_EQ(instance_type, obj->map()->instance_type()); 860} 861 862 863TEST(SubclassObjectBuiltin) { 864 // Avoid eventual completion of in-object slack tracking. 865 FLAG_inline_construct = false; 866 FLAG_always_opt = false; 867 CcTest::InitializeVM(); 868 v8::HandleScope scope(CcTest::isolate()); 869 870 TestSubclassBuiltin("A1", JS_OBJECT_TYPE, "Object", "true"); 871 TestSubclassBuiltin("A2", JS_OBJECT_TYPE, "Object", "42"); 872 TestSubclassBuiltin("A3", JS_OBJECT_TYPE, "Object", "'some string'"); 873} 874 875 876TEST(SubclassObjectBuiltinNoInlineNew) { 877 FLAG_inline_new = false; 878 TestSubclassObjectBuiltin(); 879} 880 881 882TEST(SubclassFunctionBuiltin) { 883 // Avoid eventual completion of in-object slack tracking. 884 FLAG_inline_construct = false; 885 FLAG_always_opt = false; 886 CcTest::InitializeVM(); 887 v8::HandleScope scope(CcTest::isolate()); 888 889 TestSubclassBuiltin("A1", JS_FUNCTION_TYPE, "Function", "'return 153;'"); 890 TestSubclassBuiltin("A2", JS_FUNCTION_TYPE, "Function", "'this.a = 44;'"); 891} 892 893 894TEST(SubclassFunctionBuiltinNoInlineNew) { 895 FLAG_inline_new = false; 896 TestSubclassFunctionBuiltin(); 897} 898 899 900TEST(SubclassBooleanBuiltin) { 901 // Avoid eventual completion of in-object slack tracking. 902 FLAG_inline_construct = false; 903 FLAG_always_opt = false; 904 CcTest::InitializeVM(); 905 v8::HandleScope scope(CcTest::isolate()); 906 907 TestSubclassBuiltin("A1", JS_VALUE_TYPE, "Boolean", "true"); 908 TestSubclassBuiltin("A2", JS_VALUE_TYPE, "Boolean", "false"); 909} 910 911 912TEST(SubclassBooleanBuiltinNoInlineNew) { 913 FLAG_inline_new = false; 914 TestSubclassBooleanBuiltin(); 915} 916 917 918TEST(SubclassErrorBuiltin) { 919 // Avoid eventual completion of in-object slack tracking. 920 FLAG_inline_construct = false; 921 FLAG_always_opt = false; 922 CcTest::InitializeVM(); 923 v8::HandleScope scope(CcTest::isolate()); 924 925 const int first_field = 2; 926 TestSubclassBuiltin("A1", JS_OBJECT_TYPE, "Error", "'err'", first_field); 927 TestSubclassBuiltin("A2", JS_OBJECT_TYPE, "EvalError", "'err'", first_field); 928 TestSubclassBuiltin("A3", JS_OBJECT_TYPE, "RangeError", "'err'", first_field); 929 TestSubclassBuiltin("A4", JS_OBJECT_TYPE, "ReferenceError", "'err'", 930 first_field); 931 TestSubclassBuiltin("A5", JS_OBJECT_TYPE, "SyntaxError", "'err'", 932 first_field); 933 TestSubclassBuiltin("A6", JS_OBJECT_TYPE, "TypeError", "'err'", first_field); 934 TestSubclassBuiltin("A7", JS_OBJECT_TYPE, "URIError", "'err'", first_field); 935} 936 937 938TEST(SubclassErrorBuiltinNoInlineNew) { 939 FLAG_inline_new = false; 940 TestSubclassErrorBuiltin(); 941} 942 943 944TEST(SubclassNumberBuiltin) { 945 // Avoid eventual completion of in-object slack tracking. 946 FLAG_inline_construct = false; 947 FLAG_always_opt = false; 948 CcTest::InitializeVM(); 949 v8::HandleScope scope(CcTest::isolate()); 950 951 TestSubclassBuiltin("A1", JS_VALUE_TYPE, "Number", "42"); 952 TestSubclassBuiltin("A2", JS_VALUE_TYPE, "Number", "4.2"); 953} 954 955 956TEST(SubclassNumberBuiltinNoInlineNew) { 957 FLAG_inline_new = false; 958 TestSubclassNumberBuiltin(); 959} 960 961 962TEST(SubclassDateBuiltin) { 963 // Avoid eventual completion of in-object slack tracking. 964 FLAG_inline_construct = false; 965 FLAG_always_opt = false; 966 CcTest::InitializeVM(); 967 v8::HandleScope scope(CcTest::isolate()); 968 969 TestSubclassBuiltin("A1", JS_DATE_TYPE, "Date", "123456789"); 970} 971 972 973TEST(SubclassDateBuiltinNoInlineNew) { 974 FLAG_inline_new = false; 975 TestSubclassDateBuiltin(); 976} 977 978 979TEST(SubclassStringBuiltin) { 980 // Avoid eventual completion of in-object slack tracking. 981 FLAG_inline_construct = false; 982 FLAG_always_opt = false; 983 CcTest::InitializeVM(); 984 v8::HandleScope scope(CcTest::isolate()); 985 986 TestSubclassBuiltin("A1", JS_VALUE_TYPE, "String", "'some string'"); 987 TestSubclassBuiltin("A2", JS_VALUE_TYPE, "String", ""); 988} 989 990 991TEST(SubclassStringBuiltinNoInlineNew) { 992 FLAG_inline_new = false; 993 TestSubclassStringBuiltin(); 994} 995 996 997TEST(SubclassRegExpBuiltin) { 998 // Avoid eventual completion of in-object slack tracking. 999 FLAG_inline_construct = false; 1000 FLAG_always_opt = false; 1001 CcTest::InitializeVM(); 1002 v8::HandleScope scope(CcTest::isolate()); 1003 1004 const int first_field = 1; 1005 TestSubclassBuiltin("A1", JS_REGEXP_TYPE, "RegExp", "'o(..)h', 'g'", 1006 first_field); 1007} 1008 1009 1010TEST(SubclassRegExpBuiltinNoInlineNew) { 1011 FLAG_inline_new = false; 1012 TestSubclassRegExpBuiltin(); 1013} 1014 1015 1016TEST(SubclassArrayBuiltin) { 1017 // Avoid eventual completion of in-object slack tracking. 1018 FLAG_inline_construct = false; 1019 FLAG_always_opt = false; 1020 CcTest::InitializeVM(); 1021 v8::HandleScope scope(CcTest::isolate()); 1022 1023 TestSubclassBuiltin("A1", JS_ARRAY_TYPE, "Array", "42"); 1024} 1025 1026 1027TEST(SubclassArrayBuiltinNoInlineNew) { 1028 FLAG_inline_new = false; 1029 TestSubclassArrayBuiltin(); 1030} 1031 1032 1033TEST(SubclassTypedArrayBuiltin) { 1034 // Avoid eventual completion of in-object slack tracking. 1035 FLAG_inline_construct = false; 1036 FLAG_always_opt = false; 1037 CcTest::InitializeVM(); 1038 v8::HandleScope scope(CcTest::isolate()); 1039 1040#define TYPED_ARRAY_TEST(Type, type, TYPE, elementType, size) \ 1041 TestSubclassBuiltin("A" #Type, JS_TYPED_ARRAY_TYPE, #Type "Array", "42"); 1042 1043 TYPED_ARRAYS(TYPED_ARRAY_TEST) 1044 1045#undef TYPED_ARRAY_TEST 1046} 1047 1048 1049TEST(SubclassTypedArrayBuiltinNoInlineNew) { 1050 FLAG_inline_new = false; 1051 TestSubclassTypedArrayBuiltin(); 1052} 1053 1054 1055TEST(SubclassCollectionBuiltin) { 1056 // Avoid eventual completion of in-object slack tracking. 1057 FLAG_inline_construct = false; 1058 FLAG_always_opt = false; 1059 CcTest::InitializeVM(); 1060 v8::HandleScope scope(CcTest::isolate()); 1061 1062 TestSubclassBuiltin("A1", JS_SET_TYPE, "Set", ""); 1063 TestSubclassBuiltin("A2", JS_MAP_TYPE, "Map", ""); 1064 TestSubclassBuiltin("A3", JS_WEAK_SET_TYPE, "WeakSet", ""); 1065 TestSubclassBuiltin("A4", JS_WEAK_MAP_TYPE, "WeakMap", ""); 1066} 1067 1068 1069TEST(SubclassCollectionBuiltinNoInlineNew) { 1070 FLAG_inline_new = false; 1071 TestSubclassCollectionBuiltin(); 1072} 1073 1074 1075TEST(SubclassArrayBufferBuiltin) { 1076 // Avoid eventual completion of in-object slack tracking. 1077 FLAG_inline_construct = false; 1078 FLAG_always_opt = false; 1079 CcTest::InitializeVM(); 1080 v8::HandleScope scope(CcTest::isolate()); 1081 1082 TestSubclassBuiltin("A1", JS_ARRAY_BUFFER_TYPE, "ArrayBuffer", "42"); 1083 TestSubclassBuiltin("A2", JS_DATA_VIEW_TYPE, "DataView", 1084 "new ArrayBuffer(42)"); 1085} 1086 1087 1088TEST(SubclassArrayBufferBuiltinNoInlineNew) { 1089 FLAG_inline_new = false; 1090 TestSubclassArrayBufferBuiltin(); 1091} 1092 1093 1094TEST(SubclassPromiseBuiltin) { 1095 // Avoid eventual completion of in-object slack tracking. 1096 FLAG_inline_construct = false; 1097 FLAG_always_opt = false; 1098 CcTest::InitializeVM(); 1099 v8::HandleScope scope(CcTest::isolate()); 1100 1101 const int first_field = 4; 1102 TestSubclassBuiltin("A1", JS_PROMISE_TYPE, "Promise", 1103 "function(resolve, reject) { resolve('ok'); }", 1104 first_field); 1105} 1106 1107 1108TEST(SubclassPromiseBuiltinNoInlineNew) { 1109 FLAG_inline_new = false; 1110 TestSubclassPromiseBuiltin(); 1111} 1112