blob: 5cc4e94a028735ef177c0bbc20a5f7ab70042a2c [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));
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}