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);
     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     }
    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 
    625 static 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 
    637 TEST(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 
    646 TEST(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 
    657 TEST(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 
    666 TEST(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 
    725 TEST(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 
    785 static 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 
    863 TEST(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 
    876 TEST(SubclassObjectBuiltinNoInlineNew) {
    877   FLAG_inline_new = false;
    878   TestSubclassObjectBuiltin();
    879 }
    880 
    881 
    882 TEST(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 
    894 TEST(SubclassFunctionBuiltinNoInlineNew) {
    895   FLAG_inline_new = false;
    896   TestSubclassFunctionBuiltin();
    897 }
    898 
    899 
    900 TEST(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 
    912 TEST(SubclassBooleanBuiltinNoInlineNew) {
    913   FLAG_inline_new = false;
    914   TestSubclassBooleanBuiltin();
    915 }
    916 
    917 
    918 TEST(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 
    938 TEST(SubclassErrorBuiltinNoInlineNew) {
    939   FLAG_inline_new = false;
    940   TestSubclassErrorBuiltin();
    941 }
    942 
    943 
    944 TEST(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 
    956 TEST(SubclassNumberBuiltinNoInlineNew) {
    957   FLAG_inline_new = false;
    958   TestSubclassNumberBuiltin();
    959 }
    960 
    961 
    962 TEST(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 
    973 TEST(SubclassDateBuiltinNoInlineNew) {
    974   FLAG_inline_new = false;
    975   TestSubclassDateBuiltin();
    976 }
    977 
    978 
    979 TEST(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 
    991 TEST(SubclassStringBuiltinNoInlineNew) {
    992   FLAG_inline_new = false;
    993   TestSubclassStringBuiltin();
    994 }
    995 
    996 
    997 TEST(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 
   1010 TEST(SubclassRegExpBuiltinNoInlineNew) {
   1011   FLAG_inline_new = false;
   1012   TestSubclassRegExpBuiltin();
   1013 }
   1014 
   1015 
   1016 TEST(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 
   1027 TEST(SubclassArrayBuiltinNoInlineNew) {
   1028   FLAG_inline_new = false;
   1029   TestSubclassArrayBuiltin();
   1030 }
   1031 
   1032 
   1033 TEST(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 
   1049 TEST(SubclassTypedArrayBuiltinNoInlineNew) {
   1050   FLAG_inline_new = false;
   1051   TestSubclassTypedArrayBuiltin();
   1052 }
   1053 
   1054 
   1055 TEST(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 
   1069 TEST(SubclassCollectionBuiltinNoInlineNew) {
   1070   FLAG_inline_new = false;
   1071   TestSubclassCollectionBuiltin();
   1072 }
   1073 
   1074 
   1075 TEST(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 
   1088 TEST(SubclassArrayBufferBuiltinNoInlineNew) {
   1089   FLAG_inline_new = false;
   1090   TestSubclassArrayBufferBuiltin();
   1091 }
   1092 
   1093 
   1094 TEST(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 
   1108 TEST(SubclassPromiseBuiltinNoInlineNew) {
   1109   FLAG_inline_new = false;
   1110   TestSubclassPromiseBuiltin();
   1111 }
   1112