Line data Source code
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-inl.h"
10 : #include "src/objects-inl.h"
11 : #include "src/objects/heap-number-inl.h"
12 : #include "src/v8.h"
13 :
14 : #include "test/cctest/cctest.h"
15 :
16 : namespace v8 {
17 : namespace internal {
18 : namespace test_inobject_slack_tracking {
19 :
20 : static const int kMaxInobjectProperties = JSObject::kMaxInObjectProperties;
21 :
22 : template <typename T>
23 : static Handle<T> OpenHandle(v8::Local<v8::Value> value) {
24 : Handle<Object> obj = v8::Utils::OpenHandle(*value);
25 : return Handle<T>::cast(obj);
26 : }
27 :
28 :
29 6860 : static inline v8::Local<v8::Value> Run(v8::Local<v8::Script> script) {
30 : v8::Local<v8::Value> result;
31 20580 : if (script->Run(v8::Isolate::GetCurrent()->GetCurrentContext())
32 : .ToLocal(&result)) {
33 6860 : return result;
34 : }
35 0 : return v8::Local<v8::Value>();
36 : }
37 :
38 :
39 :
40 : template <typename T = Object>
41 965 : Handle<T> GetLexical(const char* name) {
42 : Isolate* isolate = CcTest::i_isolate();
43 : Factory* factory = isolate->factory();
44 :
45 965 : Handle<String> str_name = factory->InternalizeUtf8String(name);
46 : Handle<ScriptContextTable> script_contexts(
47 2895 : isolate->native_context()->script_context_table(), isolate);
48 :
49 : ScriptContextTable::LookupResult lookup_result;
50 965 : if (ScriptContextTable::Lookup(isolate, *script_contexts, *str_name,
51 : &lookup_result)) {
52 965 : Handle<Context> script_context = ScriptContextTable::GetContext(
53 965 : isolate, script_contexts, lookup_result.context_index);
54 :
55 : Handle<Object> result(script_context->get(lookup_result.slot_index),
56 965 : isolate);
57 : return Handle<T>::cast(result);
58 : }
59 0 : return Handle<T>();
60 : }
61 :
62 :
63 : template <typename T = Object>
64 : Handle<T> GetLexical(const std::string& name) {
65 535 : return GetLexical<T>(name.c_str());
66 : }
67 :
68 : template <typename T>
69 : static inline Handle<T> RunI(v8::Local<v8::Script> script) {
70 6850 : return OpenHandle<T>(Run(script));
71 : }
72 :
73 : template <typename T>
74 145 : static inline Handle<T> CompileRunI(const char* script) {
75 145 : return OpenHandle<T>(CompileRun(script));
76 : }
77 :
78 87920 : static Object GetFieldValue(JSObject obj, int property_index) {
79 87920 : FieldIndex index = FieldIndex::ForPropertyIndex(obj->map(), property_index);
80 87920 : return obj->RawFastPropertyAt(index);
81 : }
82 :
83 460 : static double GetDoubleFieldValue(JSObject obj, FieldIndex field_index) {
84 460 : if (obj->IsUnboxedDoubleField(field_index)) {
85 460 : return obj->RawFastDoublePropertyAt(field_index);
86 : } else {
87 0 : Object value = obj->RawFastPropertyAt(field_index);
88 0 : CHECK(value->IsMutableHeapNumber());
89 : return MutableHeapNumber::cast(value)->value();
90 : }
91 : }
92 :
93 460 : static double GetDoubleFieldValue(JSObject obj, int property_index) {
94 460 : FieldIndex index = FieldIndex::ForPropertyIndex(obj->map(), property_index);
95 460 : return GetDoubleFieldValue(obj, index);
96 : }
97 :
98 7055 : bool IsObjectShrinkable(JSObject obj) {
99 : Handle<Map> filler_map =
100 : CcTest::i_isolate()->factory()->one_pointer_filler_map();
101 :
102 7055 : int inobject_properties = obj->map()->GetInObjectProperties();
103 7055 : int unused = obj->map()->UnusedPropertyFields();
104 7055 : if (unused == 0) return false;
105 :
106 92005 : for (int i = inobject_properties - unused; i < inobject_properties; i++) {
107 174000 : if (*filler_map != GetFieldValue(obj, i)) {
108 : return false;
109 : }
110 : }
111 : return true;
112 : }
113 :
114 26649 : TEST(JSObjectBasic) {
115 : // Avoid eventual completion of in-object slack tracking.
116 10 : FLAG_always_opt = false;
117 10 : CcTest::InitializeVM();
118 20 : v8::HandleScope scope(CcTest::isolate());
119 : const char* source =
120 : "function A() {"
121 : " this.a = 42;"
122 : " this.d = 4.2;"
123 : " this.o = this;"
124 : "}";
125 : CompileRun(source);
126 :
127 10 : Handle<JSFunction> func = GetGlobal<JSFunction>("A");
128 :
129 : // Zero instances were created so far.
130 10 : CHECK(!func->has_initial_map());
131 :
132 : v8::Local<v8::Script> new_A_script = v8_compile("new A();");
133 :
134 : Handle<JSObject> obj = RunI<JSObject>(new_A_script);
135 :
136 10 : CHECK(func->has_initial_map());
137 : Handle<Map> initial_map(func->initial_map(), func->GetIsolate());
138 :
139 : // One instance created.
140 10 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
141 : initial_map->construction_counter());
142 10 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
143 :
144 : // There must be at least some slack.
145 10 : CHECK_LT(5, obj->map()->GetInObjectProperties());
146 10 : CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, 0));
147 10 : CHECK_EQ(4.2, GetDoubleFieldValue(*obj, 1));
148 20 : CHECK_EQ(*obj, GetFieldValue(*obj, 2));
149 10 : CHECK(IsObjectShrinkable(*obj));
150 :
151 : // Create several objects to complete the tracking.
152 130 : for (int i = 1; i < Map::kGenerousAllocationCount; i++) {
153 60 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
154 : Handle<JSObject> tmp = RunI<JSObject>(new_A_script);
155 120 : CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(),
156 : IsObjectShrinkable(*tmp));
157 : }
158 10 : CHECK(!initial_map->IsInobjectSlackTrackingInProgress());
159 10 : CHECK(!IsObjectShrinkable(*obj));
160 :
161 : // No slack left.
162 10 : CHECK_EQ(3, obj->map()->GetInObjectProperties());
163 10 : }
164 :
165 :
166 26644 : TEST(JSObjectBasicNoInlineNew) {
167 5 : FLAG_inline_new = false;
168 5 : TestJSObjectBasic();
169 5 : }
170 :
171 :
172 26649 : TEST(JSObjectComplex) {
173 : // Avoid eventual completion of in-object slack tracking.
174 10 : FLAG_always_opt = false;
175 10 : CcTest::InitializeVM();
176 20 : v8::HandleScope scope(CcTest::isolate());
177 : const char* source =
178 : "function A(n) {"
179 : " if (n > 0) this.a = 42;"
180 : " if (n > 1) this.d = 4.2;"
181 : " if (n > 2) this.o1 = this;"
182 : " if (n > 3) this.o2 = this;"
183 : " if (n > 4) this.o3 = this;"
184 : " if (n > 5) this.o4 = this;"
185 : "}";
186 : CompileRun(source);
187 :
188 10 : Handle<JSFunction> func = GetGlobal<JSFunction>("A");
189 :
190 : // Zero instances were created so far.
191 10 : CHECK(!func->has_initial_map());
192 :
193 10 : Handle<JSObject> obj1 = CompileRunI<JSObject>("new A(1);");
194 10 : Handle<JSObject> obj3 = CompileRunI<JSObject>("new A(3);");
195 10 : Handle<JSObject> obj5 = CompileRunI<JSObject>("new A(5);");
196 :
197 10 : CHECK(func->has_initial_map());
198 : Handle<Map> initial_map(func->initial_map(), func->GetIsolate());
199 :
200 : // Three instances created.
201 10 : CHECK_EQ(Map::kSlackTrackingCounterStart - 3,
202 : initial_map->construction_counter());
203 10 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
204 :
205 : // There must be at least some slack.
206 10 : CHECK_LT(5, obj3->map()->GetInObjectProperties());
207 10 : CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj3, 0));
208 10 : CHECK_EQ(4.2, GetDoubleFieldValue(*obj3, 1));
209 20 : CHECK_EQ(*obj3, GetFieldValue(*obj3, 2));
210 10 : CHECK(IsObjectShrinkable(*obj1));
211 10 : CHECK(IsObjectShrinkable(*obj3));
212 10 : CHECK(IsObjectShrinkable(*obj5));
213 :
214 : // Create several objects to complete the tracking.
215 90 : for (int i = 3; i < Map::kGenerousAllocationCount; i++) {
216 40 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
217 : CompileRun("new A(3);");
218 : }
219 10 : CHECK(!initial_map->IsInobjectSlackTrackingInProgress());
220 :
221 : // obj1 and obj2 stays shrinkable because we don't clear unused fields.
222 10 : CHECK(IsObjectShrinkable(*obj1));
223 10 : CHECK(IsObjectShrinkable(*obj3));
224 10 : CHECK(!IsObjectShrinkable(*obj5));
225 :
226 10 : CHECK_EQ(5, obj1->map()->GetInObjectProperties());
227 10 : CHECK_EQ(4, obj1->map()->UnusedPropertyFields());
228 :
229 10 : CHECK_EQ(5, obj3->map()->GetInObjectProperties());
230 10 : CHECK_EQ(2, obj3->map()->UnusedPropertyFields());
231 :
232 10 : CHECK_EQ(5, obj5->map()->GetInObjectProperties());
233 10 : CHECK_EQ(0, obj5->map()->UnusedPropertyFields());
234 :
235 : // Since slack tracking is complete, the new objects should not be shrinkable.
236 10 : obj1 = CompileRunI<JSObject>("new A(1);");
237 10 : obj3 = CompileRunI<JSObject>("new A(3);");
238 10 : obj5 = CompileRunI<JSObject>("new A(5);");
239 :
240 10 : CHECK(!IsObjectShrinkable(*obj1));
241 10 : CHECK(!IsObjectShrinkable(*obj3));
242 10 : CHECK(!IsObjectShrinkable(*obj5));
243 10 : }
244 :
245 :
246 26644 : TEST(JSObjectComplexNoInlineNew) {
247 5 : FLAG_inline_new = false;
248 5 : TestJSObjectComplex();
249 5 : }
250 :
251 :
252 26649 : TEST(JSGeneratorObjectBasic) {
253 : // Avoid eventual completion of in-object slack tracking.
254 10 : FLAG_always_opt = false;
255 10 : CcTest::InitializeVM();
256 20 : v8::HandleScope scope(CcTest::isolate());
257 : const char* source =
258 : "function* A() {"
259 : " var i = 0;"
260 : " while(true) {"
261 : " yield i++;"
262 : " }"
263 : "};"
264 : "function CreateGenerator() {"
265 : " var o = A();"
266 : " o.a = 42;"
267 : " o.d = 4.2;"
268 : " o.o = o;"
269 : " return o;"
270 : "}";
271 : CompileRun(source);
272 :
273 10 : Handle<JSFunction> func = GetGlobal<JSFunction>("A");
274 :
275 : // Zero instances were created so far.
276 10 : CHECK(!func->has_initial_map());
277 :
278 : v8::Local<v8::Script> new_A_script = v8_compile("CreateGenerator();");
279 :
280 : Handle<JSObject> obj = RunI<JSObject>(new_A_script);
281 :
282 10 : CHECK(func->has_initial_map());
283 : Handle<Map> initial_map(func->initial_map(), func->GetIsolate());
284 :
285 : // One instance created.
286 10 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
287 : initial_map->construction_counter());
288 10 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
289 :
290 : // There must be at least some slack.
291 10 : CHECK_LT(5, obj->map()->GetInObjectProperties());
292 10 : CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, 0));
293 10 : CHECK_EQ(4.2, GetDoubleFieldValue(*obj, 1));
294 20 : CHECK_EQ(*obj, GetFieldValue(*obj, 2));
295 10 : CHECK(IsObjectShrinkable(*obj));
296 :
297 : // Create several objects to complete the tracking.
298 130 : for (int i = 1; i < Map::kGenerousAllocationCount; i++) {
299 60 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
300 : Handle<JSObject> tmp = RunI<JSObject>(new_A_script);
301 120 : CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(),
302 : IsObjectShrinkable(*tmp));
303 : }
304 10 : CHECK(!initial_map->IsInobjectSlackTrackingInProgress());
305 10 : CHECK(!IsObjectShrinkable(*obj));
306 :
307 : // No slack left.
308 10 : CHECK_EQ(3, obj->map()->GetInObjectProperties());
309 10 : }
310 :
311 :
312 26644 : TEST(JSGeneratorObjectBasicNoInlineNew) {
313 5 : FLAG_inline_new = false;
314 5 : TestJSGeneratorObjectBasic();
315 5 : }
316 :
317 :
318 26649 : TEST(SubclassBasicNoBaseClassInstances) {
319 : // Avoid eventual completion of in-object slack tracking.
320 10 : FLAG_always_opt = false;
321 10 : CcTest::InitializeVM();
322 20 : v8::HandleScope scope(CcTest::isolate());
323 :
324 : // Check that base class' and subclass' slack tracking do not interfere with
325 : // each other.
326 : // In this test we never create base class instances.
327 :
328 : const char* source =
329 : "'use strict';"
330 : "class A {"
331 : " constructor(...args) {"
332 : " this.aa = 42;"
333 : " this.ad = 4.2;"
334 : " this.ao = this;"
335 : " }"
336 : "};"
337 : "class B extends A {"
338 : " constructor(...args) {"
339 : " super(...args);"
340 : " this.ba = 142;"
341 : " this.bd = 14.2;"
342 : " this.bo = this;"
343 : " }"
344 : "};";
345 : CompileRun(source);
346 :
347 10 : Handle<JSFunction> a_func = GetLexical<JSFunction>("A");
348 10 : Handle<JSFunction> b_func = GetLexical<JSFunction>("B");
349 :
350 : // Zero instances were created so far.
351 10 : CHECK(!a_func->has_initial_map());
352 10 : CHECK(!b_func->has_initial_map());
353 :
354 : v8::Local<v8::Script> new_B_script = v8_compile("new B();");
355 :
356 : Handle<JSObject> obj = RunI<JSObject>(new_B_script);
357 :
358 10 : CHECK(a_func->has_initial_map());
359 : Handle<Map> a_initial_map(a_func->initial_map(), a_func->GetIsolate());
360 :
361 10 : CHECK(b_func->has_initial_map());
362 : Handle<Map> b_initial_map(b_func->initial_map(), a_func->GetIsolate());
363 :
364 : // Zero instances of A created.
365 10 : CHECK_EQ(Map::kSlackTrackingCounterStart,
366 : a_initial_map->construction_counter());
367 10 : CHECK(a_initial_map->IsInobjectSlackTrackingInProgress());
368 :
369 : // One instance of B created.
370 10 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
371 : b_initial_map->construction_counter());
372 10 : CHECK(b_initial_map->IsInobjectSlackTrackingInProgress());
373 :
374 : // There must be at least some slack.
375 10 : CHECK_LT(10, obj->map()->GetInObjectProperties());
376 10 : CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, 0));
377 10 : CHECK_EQ(4.2, GetDoubleFieldValue(*obj, 1));
378 20 : CHECK_EQ(*obj, GetFieldValue(*obj, 2));
379 10 : CHECK_EQ(Smi::FromInt(142), GetFieldValue(*obj, 3));
380 10 : CHECK_EQ(14.2, GetDoubleFieldValue(*obj, 4));
381 20 : CHECK_EQ(*obj, GetFieldValue(*obj, 5));
382 10 : CHECK(IsObjectShrinkable(*obj));
383 :
384 : // Create several subclass instances to complete the tracking.
385 130 : for (int i = 1; i < Map::kGenerousAllocationCount; i++) {
386 60 : CHECK(b_initial_map->IsInobjectSlackTrackingInProgress());
387 : Handle<JSObject> tmp = RunI<JSObject>(new_B_script);
388 120 : CHECK_EQ(b_initial_map->IsInobjectSlackTrackingInProgress(),
389 : IsObjectShrinkable(*tmp));
390 : }
391 10 : CHECK(!b_initial_map->IsInobjectSlackTrackingInProgress());
392 10 : CHECK(!IsObjectShrinkable(*obj));
393 :
394 : // Zero instances of A created.
395 10 : CHECK_EQ(Map::kSlackTrackingCounterStart,
396 : a_initial_map->construction_counter());
397 10 : CHECK(a_initial_map->IsInobjectSlackTrackingInProgress());
398 :
399 : // No slack left.
400 10 : CHECK_EQ(6, obj->map()->GetInObjectProperties());
401 10 : }
402 :
403 :
404 26644 : TEST(SubclassBasicNoBaseClassInstancesNoInlineNew) {
405 5 : FLAG_inline_new = false;
406 5 : TestSubclassBasicNoBaseClassInstances();
407 5 : }
408 :
409 :
410 26649 : TEST(SubclassBasic) {
411 : // Avoid eventual completion of in-object slack tracking.
412 10 : FLAG_always_opt = false;
413 10 : CcTest::InitializeVM();
414 20 : v8::HandleScope scope(CcTest::isolate());
415 :
416 : // Check that base class' and subclass' slack tracking do not interfere with
417 : // each other.
418 : // In this test we first create enough base class instances to complete
419 : // the slack tracking and then proceed creating subclass instances.
420 :
421 : const char* source =
422 : "'use strict';"
423 : "class A {"
424 : " constructor(...args) {"
425 : " this.aa = 42;"
426 : " this.ad = 4.2;"
427 : " this.ao = this;"
428 : " }"
429 : "};"
430 : "class B extends A {"
431 : " constructor(...args) {"
432 : " super(...args);"
433 : " this.ba = 142;"
434 : " this.bd = 14.2;"
435 : " this.bo = this;"
436 : " }"
437 : "};";
438 : CompileRun(source);
439 :
440 10 : Handle<JSFunction> a_func = GetLexical<JSFunction>("A");
441 10 : Handle<JSFunction> b_func = GetLexical<JSFunction>("B");
442 :
443 : // Zero instances were created so far.
444 10 : CHECK(!a_func->has_initial_map());
445 10 : CHECK(!b_func->has_initial_map());
446 :
447 : v8::Local<v8::Script> new_A_script = v8_compile("new A();");
448 : v8::Local<v8::Script> new_B_script = v8_compile("new B();");
449 :
450 : Handle<JSObject> a_obj = RunI<JSObject>(new_A_script);
451 : Handle<JSObject> b_obj = RunI<JSObject>(new_B_script);
452 :
453 10 : CHECK(a_func->has_initial_map());
454 : Handle<Map> a_initial_map(a_func->initial_map(), a_func->GetIsolate());
455 :
456 10 : CHECK(b_func->has_initial_map());
457 : Handle<Map> b_initial_map(b_func->initial_map(), a_func->GetIsolate());
458 :
459 : // One instance of a base class created.
460 10 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
461 : a_initial_map->construction_counter());
462 10 : CHECK(a_initial_map->IsInobjectSlackTrackingInProgress());
463 :
464 : // One instance of a subclass created.
465 10 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
466 : b_initial_map->construction_counter());
467 10 : CHECK(b_initial_map->IsInobjectSlackTrackingInProgress());
468 :
469 : // Create several base class instances to complete the tracking.
470 130 : for (int i = 1; i < Map::kGenerousAllocationCount; i++) {
471 60 : CHECK(a_initial_map->IsInobjectSlackTrackingInProgress());
472 : Handle<JSObject> tmp = RunI<JSObject>(new_A_script);
473 120 : CHECK_EQ(a_initial_map->IsInobjectSlackTrackingInProgress(),
474 : IsObjectShrinkable(*tmp));
475 : }
476 10 : CHECK(!a_initial_map->IsInobjectSlackTrackingInProgress());
477 10 : CHECK(!IsObjectShrinkable(*a_obj));
478 :
479 : // No slack left.
480 10 : CHECK_EQ(3, a_obj->map()->GetInObjectProperties());
481 :
482 : // There must be at least some slack.
483 10 : CHECK_LT(10, b_obj->map()->GetInObjectProperties());
484 10 : CHECK_EQ(Smi::FromInt(42), GetFieldValue(*b_obj, 0));
485 10 : CHECK_EQ(4.2, GetDoubleFieldValue(*b_obj, 1));
486 20 : CHECK_EQ(*b_obj, GetFieldValue(*b_obj, 2));
487 10 : CHECK_EQ(Smi::FromInt(142), GetFieldValue(*b_obj, 3));
488 10 : CHECK_EQ(14.2, GetDoubleFieldValue(*b_obj, 4));
489 20 : CHECK_EQ(*b_obj, GetFieldValue(*b_obj, 5));
490 10 : CHECK(IsObjectShrinkable(*b_obj));
491 :
492 : // Create several subclass instances to complete the tracking.
493 130 : for (int i = 1; i < Map::kGenerousAllocationCount; i++) {
494 60 : CHECK(b_initial_map->IsInobjectSlackTrackingInProgress());
495 : Handle<JSObject> tmp = RunI<JSObject>(new_B_script);
496 120 : CHECK_EQ(b_initial_map->IsInobjectSlackTrackingInProgress(),
497 : IsObjectShrinkable(*tmp));
498 : }
499 10 : CHECK(!b_initial_map->IsInobjectSlackTrackingInProgress());
500 10 : CHECK(!IsObjectShrinkable(*b_obj));
501 :
502 : // No slack left.
503 10 : CHECK_EQ(6, b_obj->map()->GetInObjectProperties());
504 10 : }
505 :
506 :
507 26644 : TEST(SubclassBasicNoInlineNew) {
508 5 : FLAG_inline_new = false;
509 5 : TestSubclassBasic();
510 5 : }
511 :
512 :
513 : // Creates class hierarchy of length matching the |hierarchy_desc| length and
514 : // with the number of fields at i'th level equal to |hierarchy_desc[i]|.
515 30 : static void CreateClassHierarchy(const std::vector<int>& hierarchy_desc) {
516 60 : std::ostringstream os;
517 30 : os << "'use strict';\n\n";
518 :
519 30 : int n = static_cast<int>(hierarchy_desc.size());
520 1100 : for (int cur_class = 0; cur_class < n; cur_class++) {
521 535 : os << "class A" << cur_class;
522 535 : if (cur_class > 0) {
523 1010 : os << " extends A" << (cur_class - 1);
524 : }
525 : os << " {\n"
526 535 : " constructor(...args) {\n";
527 535 : if (cur_class > 0) {
528 505 : os << " super(...args);\n";
529 : }
530 1070 : int fields_count = hierarchy_desc[cur_class];
531 24915 : for (int k = 0; k < fields_count; k++) {
532 36570 : os << " this.f" << cur_class << "_" << k << " = " << k << ";\n";
533 : }
534 : os << " }\n"
535 535 : "};\n\n";
536 : }
537 30 : CompileRun(os.str().c_str());
538 30 : }
539 :
540 :
541 535 : static std::string GetClassName(int class_index) {
542 1070 : std::ostringstream os;
543 535 : os << "A" << class_index;
544 535 : return os.str();
545 : }
546 :
547 :
548 535 : static v8::Local<v8::Script> GetNewObjectScript(const std::string& class_name) {
549 1070 : std::ostringstream os;
550 535 : os << "new " << class_name << "();";
551 1070 : return v8_compile(os.str().c_str());
552 : }
553 :
554 :
555 : // Test that in-object slack tracking works as expected for first |n| classes
556 : // in the hierarchy.
557 : // This test works only for if the total property count is less than maximum
558 : // in-object properties count.
559 30 : static void TestClassHierarchy(const std::vector<int>& hierarchy_desc, int n) {
560 : int fields_count = 0;
561 1080 : for (int cur_class = 0; cur_class < n; cur_class++) {
562 525 : std::string class_name = GetClassName(cur_class);
563 1050 : int fields_count_at_current_level = hierarchy_desc[cur_class];
564 525 : fields_count += fields_count_at_current_level;
565 :
566 : // This test is not suitable for in-object properties count overflow case.
567 525 : CHECK_LT(fields_count, kMaxInobjectProperties);
568 :
569 : // Create |class_name| objects and check slack tracking.
570 525 : v8::Local<v8::Script> new_script = GetNewObjectScript(class_name);
571 :
572 : Handle<JSFunction> func = GetLexical<JSFunction>(class_name);
573 :
574 : Handle<JSObject> obj = RunI<JSObject>(new_script);
575 :
576 525 : CHECK(func->has_initial_map());
577 : Handle<Map> initial_map(func->initial_map(), func->GetIsolate());
578 :
579 : // If the object is slow-mode already, bail out.
580 525 : if (obj->map()->is_dictionary_map()) continue;
581 :
582 : // There must be at least some slack.
583 525 : CHECK_LT(fields_count, obj->map()->GetInObjectProperties());
584 :
585 : // One instance was created.
586 525 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
587 : initial_map->construction_counter());
588 525 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
589 :
590 : // Create several instances to complete the tracking.
591 5775 : for (int i = 1; i < Map::kGenerousAllocationCount; i++) {
592 3150 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
593 : Handle<JSObject> tmp = RunI<JSObject>(new_script);
594 6300 : CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(),
595 : IsObjectShrinkable(*tmp));
596 3150 : if (!initial_map->IsInobjectSlackTrackingInProgress()) {
597 : // Turbofan can force completion of in-object slack tracking.
598 : break;
599 : }
600 2625 : CHECK_EQ(Map::kSlackTrackingCounterStart - i - 1,
601 : initial_map->construction_counter());
602 : }
603 525 : CHECK(!initial_map->IsInobjectSlackTrackingInProgress());
604 525 : CHECK(!IsObjectShrinkable(*obj));
605 :
606 : // No slack left.
607 525 : CHECK_EQ(fields_count, obj->map()->GetInObjectProperties());
608 : }
609 30 : }
610 :
611 :
612 20 : static void TestSubclassChain(const std::vector<int>& hierarchy_desc) {
613 : // Avoid eventual completion of in-object slack tracking.
614 20 : FLAG_always_opt = false;
615 20 : CcTest::InitializeVM();
616 40 : v8::HandleScope scope(CcTest::isolate());
617 :
618 20 : CreateClassHierarchy(hierarchy_desc);
619 20 : TestClassHierarchy(hierarchy_desc, static_cast<int>(hierarchy_desc.size()));
620 20 : }
621 :
622 26644 : TEST(Subclasses) {
623 : std::vector<int> hierarchy_desc;
624 10 : hierarchy_desc.push_back(50);
625 10 : hierarchy_desc.push_back(128);
626 5 : TestSubclassChain(hierarchy_desc);
627 5 : }
628 :
629 26644 : TEST(LongSubclassChain1) {
630 : std::vector<int> hierarchy_desc;
631 75 : for (int i = 0; i < 7; i++) {
632 70 : hierarchy_desc.push_back(i * 10);
633 : }
634 5 : TestSubclassChain(hierarchy_desc);
635 5 : }
636 :
637 :
638 26644 : TEST(LongSubclassChain2) {
639 : std::vector<int> hierarchy_desc;
640 10 : hierarchy_desc.push_back(10);
641 425 : for (int i = 0; i < 42; i++) {
642 420 : hierarchy_desc.push_back(0);
643 : }
644 10 : hierarchy_desc.push_back(230);
645 5 : TestSubclassChain(hierarchy_desc);
646 5 : }
647 :
648 :
649 26644 : TEST(LongSubclassChain3) {
650 : std::vector<int> hierarchy_desc;
651 425 : for (int i = 0; i < 42; i++) {
652 420 : hierarchy_desc.push_back(5);
653 : }
654 5 : TestSubclassChain(hierarchy_desc);
655 5 : }
656 :
657 :
658 26644 : TEST(InobjectPropetiesCountOverflowInSubclass) {
659 : // Avoid eventual completion of in-object slack tracking.
660 5 : FLAG_always_opt = false;
661 5 : CcTest::InitializeVM();
662 10 : v8::HandleScope scope(CcTest::isolate());
663 :
664 : std::vector<int> hierarchy_desc;
665 : const int kNoOverflowCount = 5;
666 55 : for (int i = 0; i < kNoOverflowCount; i++) {
667 50 : hierarchy_desc.push_back(50);
668 : }
669 : // In this class we are going to have properties in the backing store.
670 10 : hierarchy_desc.push_back(100);
671 :
672 5 : CreateClassHierarchy(hierarchy_desc);
673 :
674 : // For the last class in the hierarchy we need different checks.
675 : {
676 : int cur_class = kNoOverflowCount;
677 5 : std::string class_name = GetClassName(cur_class);
678 :
679 : // Create |class_name| objects and check slack tracking.
680 5 : v8::Local<v8::Script> new_script = GetNewObjectScript(class_name);
681 :
682 : Handle<JSFunction> func = GetLexical<JSFunction>(class_name);
683 :
684 : Handle<JSObject> obj = RunI<JSObject>(new_script);
685 :
686 5 : CHECK(func->has_initial_map());
687 : Handle<Map> initial_map(func->initial_map(), func->GetIsolate());
688 :
689 : // There must be no slack left.
690 5 : CHECK_EQ(JSObject::kMaxInstanceSize, obj->map()->instance_size());
691 5 : CHECK_EQ(kMaxInobjectProperties, obj->map()->GetInObjectProperties());
692 :
693 : // One instance was created.
694 5 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
695 : initial_map->construction_counter());
696 5 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
697 :
698 : // Create several instances to complete the tracking.
699 65 : for (int i = 1; i < Map::kGenerousAllocationCount; i++) {
700 30 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
701 : Handle<JSObject> tmp = RunI<JSObject>(new_script);
702 30 : CHECK(!IsObjectShrinkable(*tmp));
703 : }
704 5 : CHECK(!initial_map->IsInobjectSlackTrackingInProgress());
705 5 : CHECK(!IsObjectShrinkable(*obj));
706 :
707 : // No slack left.
708 5 : CHECK_EQ(kMaxInobjectProperties, obj->map()->GetInObjectProperties());
709 : }
710 :
711 : // The other classes in the hierarchy are not affected.
712 5 : TestClassHierarchy(hierarchy_desc, kNoOverflowCount);
713 5 : }
714 :
715 35 : static void CheckExpectedProperties(int expected, std::ostringstream& os) {
716 : Handle<HeapObject> obj = Handle<HeapObject>::cast(
717 35 : v8::Utils::OpenHandle(*CompileRun(os.str().c_str())));
718 35 : CHECK_EQ(expected, obj->map()->GetInObjectProperties());
719 35 : }
720 :
721 26644 : TEST(ObjectLiteralPropertyBackingStoreSize) {
722 10 : v8::HandleScope scope(CcTest::isolate());
723 5 : LocalContext env;
724 :
725 10 : std::ostringstream os;
726 :
727 : // An index key does not require space in the property backing store.
728 : os << "(function() {\n"
729 : " function f() {\n"
730 : " var o = {\n"
731 : " '-1': 42,\n" // Allocate for non-index key.
732 : " 1: 42,\n" // Do not allocate for index key.
733 : " '2': 42\n" // Do not allocate for index key.
734 : " };\n"
735 : " return o;\n"
736 : " }\n"
737 : "\n"
738 : " return f();\n"
739 5 : "} )();";
740 5 : CheckExpectedProperties(1, os);
741 :
742 : // Avoid over-/under-allocation for computed property names.
743 : os << "(function() {\n"
744 : " 'use strict';\n"
745 : " function f(x) {\n"
746 : " var o = {\n"
747 : " 1: 42,\n" // Do not allocate for index key.
748 : " '2': 42,\n" // Do not allocate for index key.
749 : " [x]: 42,\n" // Allocate for property with computed name.
750 : " 3: 42,\n" // Do not allocate for index key.
751 : " '4': 42\n" // Do not allocate for index key.
752 : " };\n"
753 : " return o;\n"
754 : " }\n"
755 : "\n"
756 : " var x = 'hello'\n"
757 : "\n"
758 : " return f(x);\n"
759 5 : "} )();";
760 5 : CheckExpectedProperties(1, os);
761 :
762 : // Conversion to index key.
763 : os << "(function() {\n"
764 : " function f(x) {\n"
765 : " var o = {\n"
766 : " 1: 42,\n" // Do not allocate for index key.
767 : " '2': 42,\n" // Do not allocate for index key.
768 : " [x]: 42,\n" // Allocate for property with computed name.
769 : " 3: 42,\n" // Do not allocate for index key.
770 : " get 12() {}\n" // Do not allocate for index key.
771 : " };\n"
772 : " return o;\n"
773 : " }\n"
774 : "\n"
775 : " var x = 'hello'\n"
776 : "\n"
777 : " return f(x);\n"
778 5 : "} )();";
779 5 : CheckExpectedProperties(1, os);
780 :
781 : os << "(function() {\n"
782 : " function f() {\n"
783 : " var o = {};\n"
784 : " return o;\n"
785 : " }\n"
786 : "\n"
787 : " return f();\n"
788 5 : "} )();";
789 : // Empty objects have slack for 4 properties.
790 5 : CheckExpectedProperties(4, os);
791 :
792 : os << "(function() {\n"
793 : " function f(x) {\n"
794 : " var o = {\n"
795 : " a: 42,\n" // Allocate for constant property.
796 : " [x]: 42,\n" // Allocate for property with computed name.
797 : " b: 42\n" // Allocate for constant property.
798 : " };\n"
799 : " return o;\n"
800 : " }\n"
801 : "\n"
802 : " var x = 'hello'\n"
803 : "\n"
804 : " return f(x);\n"
805 5 : "} )();";
806 5 : CheckExpectedProperties(3, os);
807 :
808 : os << "(function() {\n"
809 : " function f(x) {\n"
810 : " var o = {\n"
811 : " a: 42,\n" // Allocate for constant property.
812 : " __proto__: 42,\n" // Do not allocate for __proto__.
813 : " [x]: 42\n" // Allocate for property with computed name.
814 : " };\n"
815 : " return o;\n"
816 : " }\n"
817 : "\n"
818 : " var x = 'hello'\n"
819 : "\n"
820 : " return f(x);\n"
821 5 : "} )();";
822 : // __proto__ is not allocated in the backing store.
823 5 : CheckExpectedProperties(2, os);
824 :
825 : os << "(function() {\n"
826 : " function f(x) {\n"
827 : " var o = {\n"
828 : " a: 42,\n" // Allocate for constant property.
829 : " [x]: 42,\n" // Allocate for property with computed name.
830 : " __proto__: 42\n" // Do not allocate for __proto__.
831 : " };\n"
832 : " return o;\n"
833 : " }\n"
834 : "\n"
835 : " var x = 'hello'\n"
836 : "\n"
837 : " return f(x);\n"
838 5 : "} )();";
839 5 : CheckExpectedProperties(2, os);
840 5 : }
841 :
842 26644 : TEST(SlowModeSubclass) {
843 : // Avoid eventual completion of in-object slack tracking.
844 5 : FLAG_always_opt = false;
845 5 : CcTest::InitializeVM();
846 10 : v8::HandleScope scope(CcTest::isolate());
847 :
848 : std::vector<int> hierarchy_desc;
849 : const int kNoOverflowCount = 5;
850 55 : for (int i = 0; i < kNoOverflowCount; i++) {
851 50 : hierarchy_desc.push_back(50);
852 : }
853 : // This class should go dictionary mode.
854 10 : hierarchy_desc.push_back(1000);
855 :
856 5 : CreateClassHierarchy(hierarchy_desc);
857 :
858 : // For the last class in the hierarchy we need different checks.
859 : {
860 : int cur_class = kNoOverflowCount;
861 5 : std::string class_name = GetClassName(cur_class);
862 :
863 : // Create |class_name| objects and check slack tracking.
864 5 : v8::Local<v8::Script> new_script = GetNewObjectScript(class_name);
865 :
866 : Handle<JSFunction> func = GetLexical<JSFunction>(class_name);
867 :
868 : Handle<JSObject> obj = RunI<JSObject>(new_script);
869 :
870 5 : CHECK(func->has_initial_map());
871 : Handle<Map> initial_map(func->initial_map(), func->GetIsolate());
872 :
873 : // Object should go dictionary mode.
874 5 : CHECK_EQ(JSObject::kHeaderSize, obj->map()->instance_size());
875 5 : CHECK(obj->map()->is_dictionary_map());
876 :
877 : // One instance was created.
878 5 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
879 : initial_map->construction_counter());
880 5 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
881 :
882 : // Create several instances to complete the tracking.
883 65 : for (int i = 1; i < Map::kGenerousAllocationCount; i++) {
884 30 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
885 : Handle<JSObject> tmp = RunI<JSObject>(new_script);
886 30 : CHECK(!IsObjectShrinkable(*tmp));
887 : }
888 5 : CHECK(!initial_map->IsInobjectSlackTrackingInProgress());
889 5 : CHECK(!IsObjectShrinkable(*obj));
890 :
891 : // Object should stay in dictionary mode.
892 5 : CHECK_EQ(JSObject::kHeaderSize, obj->map()->instance_size());
893 5 : CHECK(obj->map()->is_dictionary_map());
894 : }
895 :
896 : // The other classes in the hierarchy are not affected.
897 5 : TestClassHierarchy(hierarchy_desc, kNoOverflowCount);
898 5 : }
899 :
900 :
901 390 : static void TestSubclassBuiltin(const char* subclass_name,
902 : InstanceType instance_type,
903 : const char* builtin_name,
904 : const char* ctor_arguments = "",
905 : int builtin_properties_count = 0) {
906 : {
907 780 : std::ostringstream os;
908 : os << "'use strict';\n"
909 : "class "
910 780 : << subclass_name << " extends " << builtin_name
911 : << " {\n"
912 : " constructor(...args) {\n"
913 : " super(...args);\n"
914 : " this.a = 42;\n"
915 : " this.d = 4.2;\n"
916 : " this.o = this;\n"
917 : " }\n"
918 390 : "};\n";
919 390 : CompileRun(os.str().c_str());
920 : }
921 :
922 390 : Handle<JSFunction> func = GetLexical<JSFunction>(subclass_name);
923 :
924 : // Zero instances were created so far.
925 390 : CHECK(!func->has_initial_map());
926 :
927 : v8::Local<v8::Script> new_script;
928 : {
929 780 : std::ostringstream os;
930 780 : os << "new " << subclass_name << "(" << ctor_arguments << ");";
931 390 : new_script = v8_compile(os.str().c_str());
932 : }
933 :
934 : RunI<JSObject>(new_script);
935 :
936 390 : CHECK(func->has_initial_map());
937 : Handle<Map> initial_map(func->initial_map(), func->GetIsolate());
938 :
939 390 : CHECK_EQ(instance_type, initial_map->instance_type());
940 :
941 : // One instance of a subclass created.
942 390 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
943 : initial_map->construction_counter());
944 390 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
945 :
946 : // Create two instances in order to ensure that |obj|.o is a data field
947 : // in case of Function subclassing.
948 : Handle<JSObject> obj = RunI<JSObject>(new_script);
949 :
950 : // Two instances of a subclass created.
951 390 : CHECK_EQ(Map::kSlackTrackingCounterStart - 2,
952 : initial_map->construction_counter());
953 390 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
954 :
955 : // There must be at least some slack.
956 390 : CHECK_LT(builtin_properties_count + 5, obj->map()->GetInObjectProperties());
957 390 : CHECK_EQ(Smi::FromInt(42), GetFieldValue(*obj, builtin_properties_count + 0));
958 780 : CHECK_EQ(4.2, GetDoubleFieldValue(*obj, builtin_properties_count + 1));
959 1170 : CHECK_EQ(*obj, GetFieldValue(*obj, builtin_properties_count + 2));
960 390 : CHECK(IsObjectShrinkable(*obj));
961 :
962 : // Create several subclass instances to complete the tracking.
963 4290 : for (int i = 2; i < Map::kGenerousAllocationCount; i++) {
964 1950 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
965 : Handle<JSObject> tmp = RunI<JSObject>(new_script);
966 3900 : CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(),
967 : IsObjectShrinkable(*tmp));
968 : }
969 390 : CHECK(!initial_map->IsInobjectSlackTrackingInProgress());
970 390 : CHECK(!IsObjectShrinkable(*obj));
971 :
972 : // No slack left.
973 390 : CHECK_EQ(builtin_properties_count + 3, obj->map()->GetInObjectProperties());
974 :
975 390 : CHECK_EQ(instance_type, obj->map()->instance_type());
976 390 : }
977 :
978 :
979 26649 : TEST(SubclassObjectBuiltin) {
980 : // Avoid eventual completion of in-object slack tracking.
981 10 : FLAG_always_opt = false;
982 10 : CcTest::InitializeVM();
983 20 : v8::HandleScope scope(CcTest::isolate());
984 :
985 10 : TestSubclassBuiltin("A1", JS_OBJECT_TYPE, "Object", "true");
986 10 : TestSubclassBuiltin("A2", JS_OBJECT_TYPE, "Object", "42");
987 10 : TestSubclassBuiltin("A3", JS_OBJECT_TYPE, "Object", "'some string'");
988 10 : }
989 :
990 :
991 26644 : TEST(SubclassObjectBuiltinNoInlineNew) {
992 5 : FLAG_inline_new = false;
993 5 : TestSubclassObjectBuiltin();
994 5 : }
995 :
996 :
997 26649 : TEST(SubclassFunctionBuiltin) {
998 : // Avoid eventual completion of in-object slack tracking.
999 10 : FLAG_always_opt = false;
1000 10 : CcTest::InitializeVM();
1001 20 : v8::HandleScope scope(CcTest::isolate());
1002 :
1003 10 : TestSubclassBuiltin("A1", JS_FUNCTION_TYPE, "Function", "'return 153;'");
1004 10 : TestSubclassBuiltin("A2", JS_FUNCTION_TYPE, "Function", "'this.a = 44;'");
1005 10 : }
1006 :
1007 :
1008 26644 : TEST(SubclassFunctionBuiltinNoInlineNew) {
1009 5 : FLAG_inline_new = false;
1010 5 : TestSubclassFunctionBuiltin();
1011 5 : }
1012 :
1013 :
1014 26649 : TEST(SubclassBooleanBuiltin) {
1015 : // Avoid eventual completion of in-object slack tracking.
1016 10 : FLAG_always_opt = false;
1017 10 : CcTest::InitializeVM();
1018 20 : v8::HandleScope scope(CcTest::isolate());
1019 :
1020 10 : TestSubclassBuiltin("A1", JS_VALUE_TYPE, "Boolean", "true");
1021 10 : TestSubclassBuiltin("A2", JS_VALUE_TYPE, "Boolean", "false");
1022 10 : }
1023 :
1024 :
1025 26644 : TEST(SubclassBooleanBuiltinNoInlineNew) {
1026 5 : FLAG_inline_new = false;
1027 5 : TestSubclassBooleanBuiltin();
1028 5 : }
1029 :
1030 :
1031 26649 : TEST(SubclassErrorBuiltin) {
1032 : // Avoid eventual completion of in-object slack tracking.
1033 10 : FLAG_always_opt = false;
1034 10 : CcTest::InitializeVM();
1035 20 : v8::HandleScope scope(CcTest::isolate());
1036 :
1037 : const int first_field = 2;
1038 10 : TestSubclassBuiltin("A1", JS_ERROR_TYPE, "Error", "'err'", first_field);
1039 10 : TestSubclassBuiltin("A2", JS_ERROR_TYPE, "EvalError", "'err'", first_field);
1040 10 : TestSubclassBuiltin("A3", JS_ERROR_TYPE, "RangeError", "'err'", first_field);
1041 : TestSubclassBuiltin("A4", JS_ERROR_TYPE, "ReferenceError", "'err'",
1042 10 : first_field);
1043 10 : TestSubclassBuiltin("A5", JS_ERROR_TYPE, "SyntaxError", "'err'", first_field);
1044 10 : TestSubclassBuiltin("A6", JS_ERROR_TYPE, "TypeError", "'err'", first_field);
1045 10 : TestSubclassBuiltin("A7", JS_ERROR_TYPE, "URIError", "'err'", first_field);
1046 10 : }
1047 :
1048 :
1049 26644 : TEST(SubclassErrorBuiltinNoInlineNew) {
1050 5 : FLAG_inline_new = false;
1051 5 : TestSubclassErrorBuiltin();
1052 5 : }
1053 :
1054 :
1055 26649 : TEST(SubclassNumberBuiltin) {
1056 : // Avoid eventual completion of in-object slack tracking.
1057 10 : FLAG_always_opt = false;
1058 10 : CcTest::InitializeVM();
1059 20 : v8::HandleScope scope(CcTest::isolate());
1060 :
1061 10 : TestSubclassBuiltin("A1", JS_VALUE_TYPE, "Number", "42");
1062 10 : TestSubclassBuiltin("A2", JS_VALUE_TYPE, "Number", "4.2");
1063 10 : }
1064 :
1065 :
1066 26644 : TEST(SubclassNumberBuiltinNoInlineNew) {
1067 5 : FLAG_inline_new = false;
1068 5 : TestSubclassNumberBuiltin();
1069 5 : }
1070 :
1071 :
1072 26649 : TEST(SubclassDateBuiltin) {
1073 : // Avoid eventual completion of in-object slack tracking.
1074 10 : FLAG_always_opt = false;
1075 10 : CcTest::InitializeVM();
1076 20 : v8::HandleScope scope(CcTest::isolate());
1077 :
1078 10 : TestSubclassBuiltin("A1", JS_DATE_TYPE, "Date", "123456789");
1079 10 : }
1080 :
1081 :
1082 26644 : TEST(SubclassDateBuiltinNoInlineNew) {
1083 5 : FLAG_inline_new = false;
1084 5 : TestSubclassDateBuiltin();
1085 5 : }
1086 :
1087 :
1088 26649 : TEST(SubclassStringBuiltin) {
1089 : // Avoid eventual completion of in-object slack tracking.
1090 10 : FLAG_always_opt = false;
1091 10 : CcTest::InitializeVM();
1092 20 : v8::HandleScope scope(CcTest::isolate());
1093 :
1094 10 : TestSubclassBuiltin("A1", JS_VALUE_TYPE, "String", "'some string'");
1095 10 : TestSubclassBuiltin("A2", JS_VALUE_TYPE, "String", "");
1096 10 : }
1097 :
1098 :
1099 26644 : TEST(SubclassStringBuiltinNoInlineNew) {
1100 5 : FLAG_inline_new = false;
1101 5 : TestSubclassStringBuiltin();
1102 5 : }
1103 :
1104 :
1105 26649 : TEST(SubclassRegExpBuiltin) {
1106 : // Avoid eventual completion of in-object slack tracking.
1107 10 : FLAG_always_opt = false;
1108 10 : CcTest::InitializeVM();
1109 20 : v8::HandleScope scope(CcTest::isolate());
1110 :
1111 : const int first_field = 1;
1112 : TestSubclassBuiltin("A1", JS_REGEXP_TYPE, "RegExp", "'o(..)h', 'g'",
1113 10 : first_field);
1114 10 : }
1115 :
1116 :
1117 26644 : TEST(SubclassRegExpBuiltinNoInlineNew) {
1118 5 : FLAG_inline_new = false;
1119 5 : TestSubclassRegExpBuiltin();
1120 5 : }
1121 :
1122 :
1123 26649 : TEST(SubclassArrayBuiltin) {
1124 : // Avoid eventual completion of in-object slack tracking.
1125 10 : FLAG_always_opt = false;
1126 10 : CcTest::InitializeVM();
1127 20 : v8::HandleScope scope(CcTest::isolate());
1128 :
1129 10 : TestSubclassBuiltin("A1", JS_ARRAY_TYPE, "Array", "42");
1130 10 : }
1131 :
1132 :
1133 26644 : TEST(SubclassArrayBuiltinNoInlineNew) {
1134 5 : FLAG_inline_new = false;
1135 5 : TestSubclassArrayBuiltin();
1136 5 : }
1137 :
1138 :
1139 26649 : TEST(SubclassTypedArrayBuiltin) {
1140 : // Avoid eventual completion of in-object slack tracking.
1141 10 : FLAG_always_opt = false;
1142 10 : CcTest::InitializeVM();
1143 20 : v8::HandleScope scope(CcTest::isolate());
1144 :
1145 : #define TYPED_ARRAY_TEST(Type, type, TYPE, elementType) \
1146 : TestSubclassBuiltin("A" #Type, JS_TYPED_ARRAY_TYPE, #Type "Array", "42");
1147 :
1148 10 : TYPED_ARRAYS(TYPED_ARRAY_TEST)
1149 :
1150 : #undef TYPED_ARRAY_TEST
1151 10 : }
1152 :
1153 :
1154 26644 : TEST(SubclassTypedArrayBuiltinNoInlineNew) {
1155 5 : FLAG_inline_new = false;
1156 5 : TestSubclassTypedArrayBuiltin();
1157 5 : }
1158 :
1159 :
1160 26649 : TEST(SubclassCollectionBuiltin) {
1161 : // Avoid eventual completion of in-object slack tracking.
1162 10 : FLAG_always_opt = false;
1163 10 : CcTest::InitializeVM();
1164 20 : v8::HandleScope scope(CcTest::isolate());
1165 :
1166 10 : TestSubclassBuiltin("A1", JS_SET_TYPE, "Set", "");
1167 10 : TestSubclassBuiltin("A2", JS_MAP_TYPE, "Map", "");
1168 10 : TestSubclassBuiltin("A3", JS_WEAK_SET_TYPE, "WeakSet", "");
1169 10 : TestSubclassBuiltin("A4", JS_WEAK_MAP_TYPE, "WeakMap", "");
1170 10 : }
1171 :
1172 :
1173 26644 : TEST(SubclassCollectionBuiltinNoInlineNew) {
1174 5 : FLAG_inline_new = false;
1175 5 : TestSubclassCollectionBuiltin();
1176 5 : }
1177 :
1178 :
1179 26649 : TEST(SubclassArrayBufferBuiltin) {
1180 : // Avoid eventual completion of in-object slack tracking.
1181 10 : FLAG_always_opt = false;
1182 10 : CcTest::InitializeVM();
1183 20 : v8::HandleScope scope(CcTest::isolate());
1184 :
1185 10 : TestSubclassBuiltin("A1", JS_ARRAY_BUFFER_TYPE, "ArrayBuffer", "42");
1186 : TestSubclassBuiltin("A2", JS_DATA_VIEW_TYPE, "DataView",
1187 10 : "new ArrayBuffer(42)");
1188 10 : }
1189 :
1190 :
1191 26644 : TEST(SubclassArrayBufferBuiltinNoInlineNew) {
1192 5 : FLAG_inline_new = false;
1193 5 : TestSubclassArrayBufferBuiltin();
1194 5 : }
1195 :
1196 :
1197 26649 : TEST(SubclassPromiseBuiltin) {
1198 : // Avoid eventual completion of in-object slack tracking.
1199 10 : FLAG_always_opt = false;
1200 10 : CcTest::InitializeVM();
1201 20 : v8::HandleScope scope(CcTest::isolate());
1202 :
1203 : TestSubclassBuiltin("A1", JS_PROMISE_TYPE, "Promise",
1204 10 : "function(resolve, reject) { resolve('ok'); }");
1205 10 : }
1206 :
1207 :
1208 26644 : TEST(SubclassPromiseBuiltinNoInlineNew) {
1209 5 : FLAG_inline_new = false;
1210 5 : TestSubclassPromiseBuiltin();
1211 5 : }
1212 :
1213 26644 : TEST(SubclassTranspiledClassHierarchy) {
1214 5 : CcTest::InitializeVM();
1215 10 : v8::HandleScope scope(CcTest::isolate());
1216 :
1217 : CompileRun(
1218 : "Object.setPrototypeOf(B, A);\n"
1219 : "function A() {\n"
1220 : " this.a0 = 0;\n"
1221 : " this.a1 = 1;\n"
1222 : " this.a2 = 1;\n"
1223 : " this.a3 = 1;\n"
1224 : " this.a4 = 1;\n"
1225 : " this.a5 = 1;\n"
1226 : " this.a6 = 1;\n"
1227 : " this.a7 = 1;\n"
1228 : " this.a8 = 1;\n"
1229 : " this.a9 = 1;\n"
1230 : " this.a10 = 1;\n"
1231 : " this.a11 = 1;\n"
1232 : " this.a12 = 1;\n"
1233 : " this.a13 = 1;\n"
1234 : " this.a14 = 1;\n"
1235 : " this.a15 = 1;\n"
1236 : " this.a16 = 1;\n"
1237 : " this.a17 = 1;\n"
1238 : " this.a18 = 1;\n"
1239 : " this.a19 = 1;\n"
1240 : "};\n"
1241 : "function B() {\n"
1242 : " A.call(this);\n"
1243 : " this.b = 1;\n"
1244 : "};\n");
1245 :
1246 5 : Handle<JSFunction> func = GetGlobal<JSFunction>("B");
1247 :
1248 : // Zero instances have been created so far.
1249 5 : CHECK(!func->has_initial_map());
1250 :
1251 : v8::Local<v8::Script> new_script = v8_compile("new B()");
1252 :
1253 : RunI<JSObject>(new_script);
1254 :
1255 5 : CHECK(func->has_initial_map());
1256 : Handle<Map> initial_map(func->initial_map(), func->GetIsolate());
1257 :
1258 5 : CHECK_EQ(JS_OBJECT_TYPE, initial_map->instance_type());
1259 :
1260 : // One instance of a subclass created.
1261 5 : CHECK_EQ(Map::kSlackTrackingCounterStart - 1,
1262 : initial_map->construction_counter());
1263 5 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
1264 :
1265 : // Create two instances in order to ensure that |obj|.o is a data field
1266 : // in case of Function subclassing.
1267 : Handle<JSObject> obj = RunI<JSObject>(new_script);
1268 :
1269 : // Two instances of a subclass created.
1270 5 : CHECK_EQ(Map::kSlackTrackingCounterStart - 2,
1271 : initial_map->construction_counter());
1272 5 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
1273 5 : CHECK(IsObjectShrinkable(*obj));
1274 :
1275 : // Create several subclass instances to complete the tracking.
1276 55 : for (int i = 2; i < Map::kGenerousAllocationCount; i++) {
1277 25 : CHECK(initial_map->IsInobjectSlackTrackingInProgress());
1278 : Handle<JSObject> tmp = RunI<JSObject>(new_script);
1279 50 : CHECK_EQ(initial_map->IsInobjectSlackTrackingInProgress(),
1280 : IsObjectShrinkable(*tmp));
1281 : }
1282 5 : CHECK(!initial_map->IsInobjectSlackTrackingInProgress());
1283 5 : CHECK(!IsObjectShrinkable(*obj));
1284 :
1285 : // No slack left.
1286 5 : CHECK_EQ(21, obj->map()->GetInObjectProperties());
1287 5 : CHECK_EQ(JS_OBJECT_TYPE, obj->map()->instance_type());
1288 5 : }
1289 :
1290 26644 : TEST(Regress8853_ClassConstructor) {
1291 5 : CcTest::InitializeVM();
1292 10 : v8::HandleScope scope(CcTest::isolate());
1293 :
1294 : // For classes without any this.prop assignments in their
1295 : // constructors we start out with 10 inobject properties.
1296 5 : Handle<JSObject> obj = CompileRunI<JSObject>("new (class {});\n");
1297 5 : CHECK(obj->map()->IsInobjectSlackTrackingInProgress());
1298 5 : CHECK(IsObjectShrinkable(*obj));
1299 5 : CHECK_EQ(10, obj->map()->GetInObjectProperties());
1300 :
1301 : // For classes with N explicit this.prop assignments in their
1302 : // constructors we start out with N+8 inobject properties.
1303 : obj = CompileRunI<JSObject>(
1304 : "new (class {\n"
1305 : " constructor() {\n"
1306 : " this.x = 1;\n"
1307 : " this.y = 2;\n"
1308 : " this.z = 3;\n"
1309 : " }\n"
1310 5 : "});\n");
1311 5 : CHECK(obj->map()->IsInobjectSlackTrackingInProgress());
1312 5 : CHECK(IsObjectShrinkable(*obj));
1313 5 : CHECK_EQ(3 + 8, obj->map()->GetInObjectProperties());
1314 5 : }
1315 :
1316 26644 : TEST(Regress8853_ClassHierarchy) {
1317 5 : CcTest::InitializeVM();
1318 10 : v8::HandleScope scope(CcTest::isolate());
1319 :
1320 : // For class hierarchies without any this.prop assignments in their
1321 : // constructors we reserve 2 inobject properties per constructor plus
1322 : // 8 inobject properties slack on top.
1323 5 : std::string base = "(class {})";
1324 95 : for (int i = 1; i < 10; ++i) {
1325 90 : std::string script = "new " + base + ";\n";
1326 45 : Handle<JSObject> obj = CompileRunI<JSObject>(script.c_str());
1327 45 : CHECK(obj->map()->IsInobjectSlackTrackingInProgress());
1328 45 : CHECK(IsObjectShrinkable(*obj));
1329 45 : CHECK_EQ(8 + 2 * i, obj->map()->GetInObjectProperties());
1330 135 : base = "(class extends " + base + " {})";
1331 : }
1332 5 : }
1333 :
1334 26644 : TEST(Regress8853_FunctionConstructor) {
1335 5 : CcTest::InitializeVM();
1336 10 : v8::HandleScope scope(CcTest::isolate());
1337 :
1338 : // For constructor functions without any this.prop assignments in
1339 : // them we start out with 10 inobject properties.
1340 5 : Handle<JSObject> obj = CompileRunI<JSObject>("new (function() {});\n");
1341 5 : CHECK(obj->map()->IsInobjectSlackTrackingInProgress());
1342 5 : CHECK(IsObjectShrinkable(*obj));
1343 5 : CHECK_EQ(10, obj->map()->GetInObjectProperties());
1344 :
1345 : // For constructor functions with N explicit this.prop assignments
1346 : // in them we start out with N+8 inobject properties.
1347 : obj = CompileRunI<JSObject>(
1348 : "new (function() {\n"
1349 : " this.a = 1;\n"
1350 : " this.b = 2;\n"
1351 : " this.c = 3;\n"
1352 : " this.d = 3;\n"
1353 : " this.c = 3;\n"
1354 : " this.f = 3;\n"
1355 5 : "});\n");
1356 5 : CHECK(obj->map()->IsInobjectSlackTrackingInProgress());
1357 5 : CHECK(IsObjectShrinkable(*obj));
1358 5 : CHECK_EQ(6 + 8, obj->map()->GetInObjectProperties());
1359 5 : }
1360 :
1361 26644 : TEST(InstanceFieldsArePropertiesDefaultConstructorLazy) {
1362 5 : CcTest::InitializeVM();
1363 10 : v8::HandleScope scope(CcTest::isolate());
1364 :
1365 : Handle<JSObject> obj = CompileRunI<JSObject>(
1366 : "new (class {\n"
1367 : " x00 = null;\n"
1368 : " x01 = null;\n"
1369 : " x02 = null;\n"
1370 : " x03 = null;\n"
1371 : " x04 = null;\n"
1372 : " x05 = null;\n"
1373 : " x06 = null;\n"
1374 : " x07 = null;\n"
1375 : " x08 = null;\n"
1376 : " x09 = null;\n"
1377 : " x10 = null;\n"
1378 5 : "});\n");
1379 5 : CHECK_EQ(11 + 8, obj->map()->GetInObjectProperties());
1380 5 : }
1381 :
1382 26644 : TEST(InstanceFieldsArePropertiesFieldsAndConstructorLazy) {
1383 5 : CcTest::InitializeVM();
1384 10 : v8::HandleScope scope(CcTest::isolate());
1385 :
1386 : Handle<JSObject> obj = CompileRunI<JSObject>(
1387 : "new (class {\n"
1388 : " x00 = null;\n"
1389 : " x01 = null;\n"
1390 : " x02 = null;\n"
1391 : " x03 = null;\n"
1392 : " x04 = null;\n"
1393 : " x05 = null;\n"
1394 : " x06 = null;\n"
1395 : " x07 = null;\n"
1396 : " x08 = null;\n"
1397 : " x09 = null;\n"
1398 : " x10 = null;\n"
1399 : " constructor() {\n"
1400 : " this.x11 = null;\n"
1401 : " this.x12 = null;\n"
1402 : " this.x12 = null;\n"
1403 : " this.x14 = null;\n"
1404 : " this.x15 = null;\n"
1405 : " this.x16 = null;\n"
1406 : " this.x17 = null;\n"
1407 : " this.x18 = null;\n"
1408 : " this.x19 = null;\n"
1409 : " this.x20 = null;\n"
1410 : " }\n"
1411 5 : "});\n");
1412 5 : CHECK_EQ(21 + 8, obj->map()->GetInObjectProperties());
1413 5 : }
1414 :
1415 26644 : TEST(InstanceFieldsArePropertiesDefaultConstructorEager) {
1416 5 : i::FLAG_lazy = false;
1417 5 : CcTest::InitializeVM();
1418 10 : v8::HandleScope scope(CcTest::isolate());
1419 :
1420 : Handle<JSObject> obj = CompileRunI<JSObject>(
1421 : "new (class {\n"
1422 : " x00 = null;\n"
1423 : " x01 = null;\n"
1424 : " x02 = null;\n"
1425 : " x03 = null;\n"
1426 : " x04 = null;\n"
1427 : " x05 = null;\n"
1428 : " x06 = null;\n"
1429 : " x07 = null;\n"
1430 : " x08 = null;\n"
1431 : " x09 = null;\n"
1432 : " x10 = null;\n"
1433 5 : "});\n");
1434 5 : CHECK_EQ(11 + 8, obj->map()->GetInObjectProperties());
1435 5 : }
1436 :
1437 26644 : TEST(InstanceFieldsArePropertiesFieldsAndConstructorEager) {
1438 5 : i::FLAG_lazy = false;
1439 5 : CcTest::InitializeVM();
1440 10 : v8::HandleScope scope(CcTest::isolate());
1441 :
1442 : Handle<JSObject> obj = CompileRunI<JSObject>(
1443 : "new (class {\n"
1444 : " x00 = null;\n"
1445 : " x01 = null;\n"
1446 : " x02 = null;\n"
1447 : " x03 = null;\n"
1448 : " x04 = null;\n"
1449 : " x05 = null;\n"
1450 : " x06 = null;\n"
1451 : " x07 = null;\n"
1452 : " x08 = null;\n"
1453 : " x09 = null;\n"
1454 : " x10 = null;\n"
1455 : " constructor() {\n"
1456 : " this.x11 = null;\n"
1457 : " this.x12 = null;\n"
1458 : " this.x12 = null;\n"
1459 : " this.x14 = null;\n"
1460 : " this.x15 = null;\n"
1461 : " this.x16 = null;\n"
1462 : " this.x17 = null;\n"
1463 : " this.x18 = null;\n"
1464 : " this.x19 = null;\n"
1465 : " this.x20 = null;\n"
1466 : " }\n"
1467 5 : "});\n");
1468 5 : CHECK_EQ(21 + 8, obj->map()->GetInObjectProperties());
1469 5 : }
1470 :
1471 : } // namespace test_inobject_slack_tracking
1472 : } // namespace internal
1473 79917 : } // namespace v8
|