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

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

Powered by Google App Engine
This is Rietveld 408576698