Home | History | Annotate | Download | only in cctest
      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 
     15 using namespace v8::base;
     16 using namespace v8::internal;
     17 
     18 
     19 static const int kMaxInobjectProperties =
     20     (JSObject::kMaxInstanceSize - JSObject::kHeaderSize) >> kPointerSizeLog2;
     21 
     22 
     23 template <typename T>
     24 static 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 
     30 static 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 
     40 template <typename T = Object>
     41 Handle<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 
     52 template <typename T = Object>
     53 Handle<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 
     73 template <typename T = Object>
     74 Handle<T> GetLexical(const std::string& name) {
     75   return GetLexical<T>(name.c_str());
     76 }
     77 
     78 
     79 template <typename T>
     80 static inline Handle<T> Run(v8::Local<v8::Script> script) {
     81   return OpenHandle<T>(Run(script));
     82 }
     83 
     84 
     85 template <typename T>
     86 static inline Handle<T> CompileRun(const char* script) {
     87   return OpenHandle<T>(CompileRun(script));
     88 }
     89 
     90 
     91 static Object* GetFieldValue(JSObject* obj, int property_index) {
     92   FieldIndex index = FieldIndex::ForPropertyIndex(obj->map(), property_index);
     93   return obj->RawFastPropertyAt(index);
     94 }
     95 
     96 
     97 static 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 
    108 static double GetDoubleFieldValue(JSObject* obj, int property_index) {
    109   FieldIndex index = FieldIndex::ForPropertyIndex(obj->map(), property_index);
    110   return GetDoubleFieldValue(obj, index);
    111 }
    112 
    113 
    114 bool 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 
    131 TEST(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 
    184 TEST(JSObjectBasicNoInlineNew) {
    185   FLAG_inline_new = false;
    186   TestJSObjectBasic();
    187 }
    188 
    189 
    190 TEST(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 
    265 TEST(JSObjectComplexNoInlineNew) {
    266   FLAG_inline_new = false;
    267   TestJSObjectComplex();
    268 }
    269 
    270 
    271 TEST(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 
    332 TEST(JSGeneratorObjectBasicNoInlineNew) {
    333   FLAG_inline_new = false;
    334   TestJSGeneratorObjectBasic();
    335 }
    336 
    337 
    338 TEST(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 
    425 TEST(SubclassBasicNoBaseClassInstancesNoInlineNew) {
    426   FLAG_inline_new = false;
    427   TestSubclassBasicNoBaseClassInstances();
    428 }
    429 
    430 
    431 TEST(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 
    529 TEST(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]|.
    537 static 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 
    563 static std::string GetClassName(int class_index) {
    564   std::ostringstream os;
    565   os << "A" << class_index;
    566   return os.str();
    567 }
    568 
    569 
    570 static 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.
    581 static 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 
    627 static 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 
    639 TEST(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 
    648 TEST(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 
    659 TEST(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 
    668 TEST(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 
    727 TEST(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 
    787 static 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 
    865 TEST(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 
    878 TEST(SubclassObjectBuiltinNoInlineNew) {
    879   FLAG_inline_new = false;
    880   TestSubclassObjectBuiltin();
    881 }
    882 
    883 
    884 TEST(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 
    896 TEST(SubclassFunctionBuiltinNoInlineNew) {
    897   FLAG_inline_new = false;
    898   TestSubclassFunctionBuiltin();
    899 }
    900 
    901 
    902 TEST(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 
    914 TEST(SubclassBooleanBuiltinNoInlineNew) {
    915   FLAG_inline_new = false;
    916   TestSubclassBooleanBuiltin();
    917 }
    918 
    919 
    920 TEST(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 
    939 TEST(SubclassErrorBuiltinNoInlineNew) {
    940   FLAG_inline_new = false;
    941   TestSubclassErrorBuiltin();
    942 }
    943 
    944 
    945 TEST(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 
    957 TEST(SubclassNumberBuiltinNoInlineNew) {
    958   FLAG_inline_new = false;
    959   TestSubclassNumberBuiltin();
    960 }
    961 
    962 
    963 TEST(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 
    974 TEST(SubclassDateBuiltinNoInlineNew) {
    975   FLAG_inline_new = false;
    976   TestSubclassDateBuiltin();
    977 }
    978 
    979 
    980 TEST(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 
    992 TEST(SubclassStringBuiltinNoInlineNew) {
    993   FLAG_inline_new = false;
    994   TestSubclassStringBuiltin();
    995 }
    996 
    997 
    998 TEST(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 
   1011 TEST(SubclassRegExpBuiltinNoInlineNew) {
   1012   FLAG_inline_new = false;
   1013   TestSubclassRegExpBuiltin();
   1014 }
   1015 
   1016 
   1017 TEST(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 
   1028 TEST(SubclassArrayBuiltinNoInlineNew) {
   1029   FLAG_inline_new = false;
   1030   TestSubclassArrayBuiltin();
   1031 }
   1032 
   1033 
   1034 TEST(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 
   1050 TEST(SubclassTypedArrayBuiltinNoInlineNew) {
   1051   FLAG_inline_new = false;
   1052   TestSubclassTypedArrayBuiltin();
   1053 }
   1054 
   1055 
   1056 TEST(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 
   1070 TEST(SubclassCollectionBuiltinNoInlineNew) {
   1071   FLAG_inline_new = false;
   1072   TestSubclassCollectionBuiltin();
   1073 }
   1074 
   1075 
   1076 TEST(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 
   1089 TEST(SubclassArrayBufferBuiltinNoInlineNew) {
   1090   FLAG_inline_new = false;
   1091   TestSubclassArrayBufferBuiltin();
   1092 }
   1093 
   1094 
   1095 TEST(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 
   1109 TEST(SubclassPromiseBuiltinNoInlineNew) {
   1110   FLAG_inline_new = false;
   1111   TestSubclassPromiseBuiltin();
   1112 }
   1113