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>
OpenHandle(v8::Local<v8::Value> value)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 
Run(v8::Local<v8::Script> script)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>
GetGlobal(const char * name)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>
GetLexical(const char * name)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>
GetLexical(const std::string & name)74 Handle<T> GetLexical(const std::string& name) {
75   return GetLexical<T>(name.c_str());
76 }
77 
78 
79 template <typename T>
Run(v8::Local<v8::Script> script)80 static inline Handle<T> Run(v8::Local<v8::Script> script) {
81   return OpenHandle<T>(Run(script));
82 }
83 
84 
85 template <typename T>
CompileRun(const char * script)86 static inline Handle<T> CompileRun(const char* script) {
87   return OpenHandle<T>(CompileRun(script));
88 }
89 
90 
GetFieldValue(JSObject * obj,int property_index)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 
GetDoubleFieldValue(JSObject * obj,FieldIndex field_index)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 
GetDoubleFieldValue(JSObject * obj,int property_index)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 
IsObjectShrinkable(JSObject * obj)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 
TEST(JSObjectBasic)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 
TEST(JSObjectBasicNoInlineNew)184 TEST(JSObjectBasicNoInlineNew) {
185   FLAG_inline_new = false;
186   TestJSObjectBasic();
187 }
188 
189 
TEST(JSObjectComplex)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 
TEST(JSObjectComplexNoInlineNew)265 TEST(JSObjectComplexNoInlineNew) {
266   FLAG_inline_new = false;
267   TestJSObjectComplex();
268 }
269 
270 
TEST(JSGeneratorObjectBasic)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 
TEST(JSGeneratorObjectBasicNoInlineNew)332 TEST(JSGeneratorObjectBasicNoInlineNew) {
333   FLAG_inline_new = false;
334   TestJSGeneratorObjectBasic();
335 }
336 
337 
TEST(SubclassBasicNoBaseClassInstances)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 
TEST(SubclassBasicNoBaseClassInstancesNoInlineNew)425 TEST(SubclassBasicNoBaseClassInstancesNoInlineNew) {
426   FLAG_inline_new = false;
427   TestSubclassBasicNoBaseClassInstances();
428 }
429 
430 
TEST(SubclassBasic)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 
TEST(SubclassBasicNoInlineNew)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]|.
CreateClassHierarchy(const std::vector<int> & hierarchy_desc)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 
GetClassName(int class_index)563 static std::string GetClassName(int class_index) {
564   std::ostringstream os;
565   os << "A" << class_index;
566   return os.str();
567 }
568 
569 
GetNewObjectScript(const std::string & class_name)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.
TestClassHierarchy(const std::vector<int> & hierarchy_desc,int n)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 
TestSubclassChain(const std::vector<int> & hierarchy_desc)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 
TEST(LongSubclassChain1)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 
TEST(LongSubclassChain2)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 
TEST(LongSubclassChain3)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 
TEST(InobjectPropetiesCountOverflowInSubclass)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 
TEST(SlowModeSubclass)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 
TestSubclassBuiltin(const char * subclass_name,InstanceType instance_type,const char * builtin_name,const char * ctor_arguments="",int builtin_properties_count=0)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 
TEST(SubclassObjectBuiltin)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 
TEST(SubclassObjectBuiltinNoInlineNew)876 TEST(SubclassObjectBuiltinNoInlineNew) {
877   FLAG_inline_new = false;
878   TestSubclassObjectBuiltin();
879 }
880 
881 
TEST(SubclassFunctionBuiltin)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 
TEST(SubclassFunctionBuiltinNoInlineNew)894 TEST(SubclassFunctionBuiltinNoInlineNew) {
895   FLAG_inline_new = false;
896   TestSubclassFunctionBuiltin();
897 }
898 
899 
TEST(SubclassBooleanBuiltin)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 
TEST(SubclassBooleanBuiltinNoInlineNew)912 TEST(SubclassBooleanBuiltinNoInlineNew) {
913   FLAG_inline_new = false;
914   TestSubclassBooleanBuiltin();
915 }
916 
917 
TEST(SubclassErrorBuiltin)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 
TEST(SubclassErrorBuiltinNoInlineNew)938 TEST(SubclassErrorBuiltinNoInlineNew) {
939   FLAG_inline_new = false;
940   TestSubclassErrorBuiltin();
941 }
942 
943 
TEST(SubclassNumberBuiltin)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 
TEST(SubclassNumberBuiltinNoInlineNew)956 TEST(SubclassNumberBuiltinNoInlineNew) {
957   FLAG_inline_new = false;
958   TestSubclassNumberBuiltin();
959 }
960 
961 
TEST(SubclassDateBuiltin)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 
TEST(SubclassDateBuiltinNoInlineNew)973 TEST(SubclassDateBuiltinNoInlineNew) {
974   FLAG_inline_new = false;
975   TestSubclassDateBuiltin();
976 }
977 
978 
TEST(SubclassStringBuiltin)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 
TEST(SubclassStringBuiltinNoInlineNew)991 TEST(SubclassStringBuiltinNoInlineNew) {
992   FLAG_inline_new = false;
993   TestSubclassStringBuiltin();
994 }
995 
996 
TEST(SubclassRegExpBuiltin)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 
TEST(SubclassRegExpBuiltinNoInlineNew)1010 TEST(SubclassRegExpBuiltinNoInlineNew) {
1011   FLAG_inline_new = false;
1012   TestSubclassRegExpBuiltin();
1013 }
1014 
1015 
TEST(SubclassArrayBuiltin)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 
TEST(SubclassArrayBuiltinNoInlineNew)1027 TEST(SubclassArrayBuiltinNoInlineNew) {
1028   FLAG_inline_new = false;
1029   TestSubclassArrayBuiltin();
1030 }
1031 
1032 
TEST(SubclassTypedArrayBuiltin)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 
TEST(SubclassTypedArrayBuiltinNoInlineNew)1049 TEST(SubclassTypedArrayBuiltinNoInlineNew) {
1050   FLAG_inline_new = false;
1051   TestSubclassTypedArrayBuiltin();
1052 }
1053 
1054 
TEST(SubclassCollectionBuiltin)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 
TEST(SubclassCollectionBuiltinNoInlineNew)1069 TEST(SubclassCollectionBuiltinNoInlineNew) {
1070   FLAG_inline_new = false;
1071   TestSubclassCollectionBuiltin();
1072 }
1073 
1074 
TEST(SubclassArrayBufferBuiltin)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 
TEST(SubclassArrayBufferBuiltinNoInlineNew)1088 TEST(SubclassArrayBufferBuiltinNoInlineNew) {
1089   FLAG_inline_new = false;
1090   TestSubclassArrayBufferBuiltin();
1091 }
1092 
1093 
TEST(SubclassPromiseBuiltin)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 
TEST(SubclassPromiseBuiltinNoInlineNew)1108 TEST(SubclassPromiseBuiltinNoInlineNew) {
1109   FLAG_inline_new = false;
1110   TestSubclassPromiseBuiltin();
1111 }
1112