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