Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(296)

Side by Side Diff: test/cctest/test-inobject-slack-tracking.cc

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

Powered by Google App Engine
This is Rietveld 408576698