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, isolate); 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 CHECK_EQ(Map::kSlackTrackingCounterStart - i - 1, 616 initial_map->construction_counter()); 617 } 618 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 619 CHECK(!IsObjectShrinkable(*obj)); 620 621 // No slack left. 622 CHECK_EQ(fields_count, obj->map()->GetInObjectProperties()); 623 } 624} 625 626 627static void TestSubclassChain(const std::vector<int>& hierarchy_desc) { 628 // Avoid eventual completion of in-object slack tracking. 629 FLAG_inline_construct = false; 630 FLAG_always_opt = false; 631 CcTest::InitializeVM(); 632 v8::HandleScope scope(CcTest::isolate()); 633 634 CreateClassHierarchy(hierarchy_desc); 635 TestClassHierarchy(hierarchy_desc, static_cast<int>(hierarchy_desc.size())); 636} 637 638 639TEST(LongSubclassChain1) { 640 std::vector<int> hierarchy_desc; 641 for (int i = 0; i < 7; i++) { 642 hierarchy_desc.push_back(i * 10); 643 } 644 TestSubclassChain(hierarchy_desc); 645} 646 647 648TEST(LongSubclassChain2) { 649 std::vector<int> hierarchy_desc; 650 hierarchy_desc.push_back(10); 651 for (int i = 0; i < 42; i++) { 652 hierarchy_desc.push_back(0); 653 } 654 hierarchy_desc.push_back(230); 655 TestSubclassChain(hierarchy_desc); 656} 657 658 659TEST(LongSubclassChain3) { 660 std::vector<int> hierarchy_desc; 661 for (int i = 0; i < 42; i++) { 662 hierarchy_desc.push_back(5); 663 } 664 TestSubclassChain(hierarchy_desc); 665} 666 667 668TEST(InobjectPropetiesCountOverflowInSubclass) { 669 // Avoid eventual completion of in-object slack tracking. 670 FLAG_inline_construct = false; 671 FLAG_always_opt = false; 672 CcTest::InitializeVM(); 673 v8::HandleScope scope(CcTest::isolate()); 674 675 std::vector<int> hierarchy_desc; 676 const int kNoOverflowCount = 5; 677 for (int i = 0; i < kNoOverflowCount; i++) { 678 hierarchy_desc.push_back(50); 679 } 680 // In this class we are going to have properties in the backing store. 681 hierarchy_desc.push_back(100); 682 683 CreateClassHierarchy(hierarchy_desc); 684 685 // For the last class in the hierarchy we need different checks. 686 { 687 int cur_class = kNoOverflowCount; 688 std::string class_name = GetClassName(cur_class); 689 690 // Create |class_name| objects and check slack tracking. 691 v8::Local<v8::Script> new_script = GetNewObjectScript(class_name); 692 693 Handle<JSFunction> func = GetLexical<JSFunction>(class_name); 694 695 Handle<JSObject> obj = Run<JSObject>(new_script); 696 697 CHECK(func->has_initial_map()); 698 Handle<Map> initial_map(func->initial_map()); 699 700 // There must be no slack left. 701 CHECK_EQ(JSObject::kMaxInstanceSize, obj->map()->instance_size()); 702 CHECK_EQ(kMaxInobjectProperties, obj->map()->GetInObjectProperties()); 703 704 // One instance was created. 705 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 706 initial_map->construction_counter()); 707 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 708 709 // Create several instances to complete the tracking. 710 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 711 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 712 Handle<JSObject> tmp = Run<JSObject>(new_script); 713 CHECK(!IsObjectShrinkable(*tmp)); 714 } 715 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 716 CHECK(!IsObjectShrinkable(*obj)); 717 718 // No slack left. 719 CHECK_EQ(kMaxInobjectProperties, obj->map()->GetInObjectProperties()); 720 } 721 722 // The other classes in the hierarchy are not affected. 723 TestClassHierarchy(hierarchy_desc, kNoOverflowCount); 724} 725 726 727TEST(SlowModeSubclass) { 728 // Avoid eventual completion of in-object slack tracking. 729 FLAG_inline_construct = false; 730 FLAG_always_opt = false; 731 CcTest::InitializeVM(); 732 v8::HandleScope scope(CcTest::isolate()); 733 734 std::vector<int> hierarchy_desc; 735 const int kNoOverflowCount = 5; 736 for (int i = 0; i < kNoOverflowCount; i++) { 737 hierarchy_desc.push_back(50); 738 } 739 // This class should go dictionary mode. 740 hierarchy_desc.push_back(1000); 741 742 CreateClassHierarchy(hierarchy_desc); 743 744 // For the last class in the hierarchy we need different checks. 745 { 746 int cur_class = kNoOverflowCount; 747 std::string class_name = GetClassName(cur_class); 748 749 // Create |class_name| objects and check slack tracking. 750 v8::Local<v8::Script> new_script = GetNewObjectScript(class_name); 751 752 Handle<JSFunction> func = GetLexical<JSFunction>(class_name); 753 754 Handle<JSObject> obj = Run<JSObject>(new_script); 755 756 CHECK(func->has_initial_map()); 757 Handle<Map> initial_map(func->initial_map()); 758 759 // Object should go dictionary mode. 760 CHECK_EQ(JSObject::kHeaderSize, obj->map()->instance_size()); 761 CHECK(obj->map()->is_dictionary_map()); 762 763 // One instance was created. 764 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 765 initial_map->construction_counter()); 766 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 767 768 // Create several instances to complete the tracking. 769 for (int i = 1; i < Map::kGenerousAllocationCount; i++) { 770 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 771 Handle<JSObject> tmp = Run<JSObject>(new_script); 772 CHECK(!IsObjectShrinkable(*tmp)); 773 } 774 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 775 CHECK(!IsObjectShrinkable(*obj)); 776 777 // Object should stay in dictionary mode. 778 CHECK_EQ(JSObject::kHeaderSize, obj->map()->instance_size()); 779 CHECK(obj->map()->is_dictionary_map()); 780 } 781 782 // The other classes in the hierarchy are not affected. 783 TestClassHierarchy(hierarchy_desc, kNoOverflowCount); 784} 785 786 787static void TestSubclassBuiltin(const char* subclass_name, 788 InstanceType instance_type, 789 const char* builtin_name, 790 const char* ctor_arguments = "", 791 int builtin_properties_count = 0) { 792 { 793 std::ostringstream os; 794 os << "'use strict';\n" 795 "class " 796 << subclass_name << " extends " << builtin_name 797 << " {\n" 798 " constructor(...args) {\n" 799 " super(...args);\n" 800 " this.a = 42;\n" 801 " this.d = 4.2;\n" 802 " this.o = this;\n" 803 " }\n" 804 "};\n"; 805 CompileRun(os.str().c_str()); 806 } 807 808 Handle<JSFunction> func = GetLexical<JSFunction>(subclass_name); 809 810 // Zero instances were created so far. 811 CHECK(!func->has_initial_map()); 812 813 v8::Local<v8::Script> new_script; 814 { 815 std::ostringstream os; 816 os << "new " << subclass_name << "(" << ctor_arguments << ");"; 817 new_script = v8_compile(os.str().c_str()); 818 } 819 820 Run<JSObject>(new_script); 821 822 CHECK(func->has_initial_map()); 823 Handle<Map> initial_map(func->initial_map()); 824 825 CHECK_EQ(instance_type, initial_map->instance_type()); 826 827 // One instance of a subclass created. 828 CHECK_EQ(Map::kSlackTrackingCounterStart - 1, 829 initial_map->construction_counter()); 830 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 831 832 // Create two instances in order to ensure that |obj|.o is a data field 833 // in case of Function subclassing. 834 Handle<JSObject> obj = Run<JSObject>(new_script); 835 836 // Two instances of a subclass created. 837 CHECK_EQ(Map::kSlackTrackingCounterStart - 2, 838 initial_map->construction_counter()); 839 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 840 841 // There must be at least some slack. 842 CHECK_LT(builtin_properties_count + 5, obj->map()->GetInObjectProperties()); 843 CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, builtin_properties_count + 0)); 844 CHECK_EQ(4.2, GetDoubleFieldValue(*obj, builtin_properties_count + 1)); 845 CHECK_EQ(*obj, GetFieldValue(*obj, builtin_properties_count + 2)); 846 CHECK(IsObjectShrinkable(*obj)); 847 848 // Create several subclass instances to complete the tracking. 849 for (int i = 2; i < Map::kGenerousAllocationCount; i++) { 850 CHECK(initial_map->IsInobjectSlackTrackingInProgress()); 851 Handle<JSObject> tmp = Run<JSObject>(new_script); 852 CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(), 853 IsObjectShrinkable(*tmp)); 854 } 855 CHECK(!initial_map->IsInobjectSlackTrackingInProgress()); 856 CHECK(!IsObjectShrinkable(*obj)); 857 858 // No slack left. 859 CHECK_EQ(builtin_properties_count + 3, obj->map()->GetInObjectProperties()); 860 861 CHECK_EQ(instance_type, obj->map()->instance_type()); 862} 863 864 865TEST(SubclassObjectBuiltin) { 866 // Avoid eventual completion of in-object slack tracking. 867 FLAG_inline_construct = false; 868 FLAG_always_opt = false; 869 CcTest::InitializeVM(); 870 v8::HandleScope scope(CcTest::isolate()); 871 872 TestSubclassBuiltin("A1", JS_OBJECT_TYPE, "Object", "true"); 873 TestSubclassBuiltin("A2", JS_OBJECT_TYPE, "Object", "42"); 874 TestSubclassBuiltin("A3", JS_OBJECT_TYPE, "Object", "'some string'"); 875} 876 877 878TEST(SubclassObjectBuiltinNoInlineNew) { 879 FLAG_inline_new = false; 880 TestSubclassObjectBuiltin(); 881} 882 883 884TEST(SubclassFunctionBuiltin) { 885 // Avoid eventual completion of in-object slack tracking. 886 FLAG_inline_construct = false; 887 FLAG_always_opt = false; 888 CcTest::InitializeVM(); 889 v8::HandleScope scope(CcTest::isolate()); 890 891 TestSubclassBuiltin("A1", JS_FUNCTION_TYPE, "Function", "'return 153;'"); 892 TestSubclassBuiltin("A2", JS_FUNCTION_TYPE, "Function", "'this.a = 44;'"); 893} 894 895 896TEST(SubclassFunctionBuiltinNoInlineNew) { 897 FLAG_inline_new = false; 898 TestSubclassFunctionBuiltin(); 899} 900 901 902TEST(SubclassBooleanBuiltin) { 903 // Avoid eventual completion of in-object slack tracking. 904 FLAG_inline_construct = false; 905 FLAG_always_opt = false; 906 CcTest::InitializeVM(); 907 v8::HandleScope scope(CcTest::isolate()); 908 909 TestSubclassBuiltin("A1", JS_VALUE_TYPE, "Boolean", "true"); 910 TestSubclassBuiltin("A2", JS_VALUE_TYPE, "Boolean", "false"); 911} 912 913 914TEST(SubclassBooleanBuiltinNoInlineNew) { 915 FLAG_inline_new = false; 916 TestSubclassBooleanBuiltin(); 917} 918 919 920TEST(SubclassErrorBuiltin) { 921 // Avoid eventual completion of in-object slack tracking. 922 FLAG_inline_construct = false; 923 FLAG_always_opt = false; 924 CcTest::InitializeVM(); 925 v8::HandleScope scope(CcTest::isolate()); 926 927 const int first_field = 2; 928 TestSubclassBuiltin("A1", JS_ERROR_TYPE, "Error", "'err'", first_field); 929 TestSubclassBuiltin("A2", JS_ERROR_TYPE, "EvalError", "'err'", first_field); 930 TestSubclassBuiltin("A3", JS_ERROR_TYPE, "RangeError", "'err'", first_field); 931 TestSubclassBuiltin("A4", JS_ERROR_TYPE, "ReferenceError", "'err'", 932 first_field); 933 TestSubclassBuiltin("A5", JS_ERROR_TYPE, "SyntaxError", "'err'", first_field); 934 TestSubclassBuiltin("A6", JS_ERROR_TYPE, "TypeError", "'err'", first_field); 935 TestSubclassBuiltin("A7", JS_ERROR_TYPE, "URIError", "'err'", first_field); 936} 937 938 939TEST(SubclassErrorBuiltinNoInlineNew) { 940 FLAG_inline_new = false; 941 TestSubclassErrorBuiltin(); 942} 943 944 945TEST(SubclassNumberBuiltin) { 946 // Avoid eventual completion of in-object slack tracking. 947 FLAG_inline_construct = false; 948 FLAG_always_opt = false; 949 CcTest::InitializeVM(); 950 v8::HandleScope scope(CcTest::isolate()); 951 952 TestSubclassBuiltin("A1", JS_VALUE_TYPE, "Number", "42"); 953 TestSubclassBuiltin("A2", JS_VALUE_TYPE, "Number", "4.2"); 954} 955 956 957TEST(SubclassNumberBuiltinNoInlineNew) { 958 FLAG_inline_new = false; 959 TestSubclassNumberBuiltin(); 960} 961 962 963TEST(SubclassDateBuiltin) { 964 // Avoid eventual completion of in-object slack tracking. 965 FLAG_inline_construct = false; 966 FLAG_always_opt = false; 967 CcTest::InitializeVM(); 968 v8::HandleScope scope(CcTest::isolate()); 969 970 TestSubclassBuiltin("A1", JS_DATE_TYPE, "Date", "123456789"); 971} 972 973 974TEST(SubclassDateBuiltinNoInlineNew) { 975 FLAG_inline_new = false; 976 TestSubclassDateBuiltin(); 977} 978 979 980TEST(SubclassStringBuiltin) { 981 // Avoid eventual completion of in-object slack tracking. 982 FLAG_inline_construct = false; 983 FLAG_always_opt = false; 984 CcTest::InitializeVM(); 985 v8::HandleScope scope(CcTest::isolate()); 986 987 TestSubclassBuiltin("A1", JS_VALUE_TYPE, "String", "'some string'"); 988 TestSubclassBuiltin("A2", JS_VALUE_TYPE, "String", ""); 989} 990 991 992TEST(SubclassStringBuiltinNoInlineNew) { 993 FLAG_inline_new = false; 994 TestSubclassStringBuiltin(); 995} 996 997 998TEST(SubclassRegExpBuiltin) { 999 // Avoid eventual completion of in-object slack tracking. 1000 FLAG_inline_construct = false; 1001 FLAG_always_opt = false; 1002 CcTest::InitializeVM(); 1003 v8::HandleScope scope(CcTest::isolate()); 1004 1005 const int first_field = 1; 1006 TestSubclassBuiltin("A1", JS_REGEXP_TYPE, "RegExp", "'o(..)h', 'g'", 1007 first_field); 1008} 1009 1010 1011TEST(SubclassRegExpBuiltinNoInlineNew) { 1012 FLAG_inline_new = false; 1013 TestSubclassRegExpBuiltin(); 1014} 1015 1016 1017TEST(SubclassArrayBuiltin) { 1018 // Avoid eventual completion of in-object slack tracking. 1019 FLAG_inline_construct = false; 1020 FLAG_always_opt = false; 1021 CcTest::InitializeVM(); 1022 v8::HandleScope scope(CcTest::isolate()); 1023 1024 TestSubclassBuiltin("A1", JS_ARRAY_TYPE, "Array", "42"); 1025} 1026 1027 1028TEST(SubclassArrayBuiltinNoInlineNew) { 1029 FLAG_inline_new = false; 1030 TestSubclassArrayBuiltin(); 1031} 1032 1033 1034TEST(SubclassTypedArrayBuiltin) { 1035 // Avoid eventual completion of in-object slack tracking. 1036 FLAG_inline_construct = false; 1037 FLAG_always_opt = false; 1038 CcTest::InitializeVM(); 1039 v8::HandleScope scope(CcTest::isolate()); 1040 1041#define TYPED_ARRAY_TEST(Type, type, TYPE, elementType, size) \ 1042 TestSubclassBuiltin("A" #Type, JS_TYPED_ARRAY_TYPE, #Type "Array", "42"); 1043 1044 TYPED_ARRAYS(TYPED_ARRAY_TEST) 1045 1046#undef TYPED_ARRAY_TEST 1047} 1048 1049 1050TEST(SubclassTypedArrayBuiltinNoInlineNew) { 1051 FLAG_inline_new = false; 1052 TestSubclassTypedArrayBuiltin(); 1053} 1054 1055 1056TEST(SubclassCollectionBuiltin) { 1057 // Avoid eventual completion of in-object slack tracking. 1058 FLAG_inline_construct = false; 1059 FLAG_always_opt = false; 1060 CcTest::InitializeVM(); 1061 v8::HandleScope scope(CcTest::isolate()); 1062 1063 TestSubclassBuiltin("A1", JS_SET_TYPE, "Set", ""); 1064 TestSubclassBuiltin("A2", JS_MAP_TYPE, "Map", ""); 1065 TestSubclassBuiltin("A3", JS_WEAK_SET_TYPE, "WeakSet", ""); 1066 TestSubclassBuiltin("A4", JS_WEAK_MAP_TYPE, "WeakMap", ""); 1067} 1068 1069 1070TEST(SubclassCollectionBuiltinNoInlineNew) { 1071 FLAG_inline_new = false; 1072 TestSubclassCollectionBuiltin(); 1073} 1074 1075 1076TEST(SubclassArrayBufferBuiltin) { 1077 // Avoid eventual completion of in-object slack tracking. 1078 FLAG_inline_construct = false; 1079 FLAG_always_opt = false; 1080 CcTest::InitializeVM(); 1081 v8::HandleScope scope(CcTest::isolate()); 1082 1083 TestSubclassBuiltin("A1", JS_ARRAY_BUFFER_TYPE, "ArrayBuffer", "42"); 1084 TestSubclassBuiltin("A2", JS_DATA_VIEW_TYPE, "DataView", 1085 "new ArrayBuffer(42)"); 1086} 1087 1088 1089TEST(SubclassArrayBufferBuiltinNoInlineNew) { 1090 FLAG_inline_new = false; 1091 TestSubclassArrayBufferBuiltin(); 1092} 1093 1094 1095TEST(SubclassPromiseBuiltin) { 1096 // Avoid eventual completion of in-object slack tracking. 1097 FLAG_inline_construct = false; 1098 FLAG_always_opt = false; 1099 CcTest::InitializeVM(); 1100 v8::HandleScope scope(CcTest::isolate()); 1101 1102 const int first_field = 5; 1103 TestSubclassBuiltin("A1", JS_PROMISE_TYPE, "Promise", 1104 "function(resolve, reject) { resolve('ok'); }", 1105 first_field); 1106} 1107 1108 1109TEST(SubclassPromiseBuiltinNoInlineNew) { 1110 FLAG_inline_new = false; 1111 TestSubclassPromiseBuiltin(); 1112} 1113