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