blob: ee1f09d0a08588d9aefa47108b02f2df41b99545 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stdlib.h>
6#include <utility>
7
8#include "test/cctest/test-api.h"
9
10#include "src/v8.h"
11
12#include "src/compilation-cache.h"
13#include "src/execution.h"
14#include "src/factory.h"
15#include "src/global-handles.h"
16#include "src/ic/stub-cache.h"
17#include "src/objects.h"
18
19using namespace v8::internal;
20
21
22//
23// Helper functions.
24//
25
26namespace {
27
28Handle<String> MakeString(const char* str) {
29 Isolate* isolate = CcTest::i_isolate();
30 Factory* factory = isolate->factory();
31 return factory->InternalizeUtf8String(str);
32}
33
34
35Handle<String> MakeName(const char* str, int suffix) {
36 EmbeddedVector<char, 128> buffer;
37 SNPrintF(buffer, "%s%d", str, suffix);
38 return MakeString(buffer.start());
39}
40
41
42template <typename T, typename M>
43bool EQUALS(Handle<T> left, Handle<M> right) {
44 if (*left == *right) return true;
45 return JSObject::Equals(Handle<Object>::cast(left),
46 Handle<Object>::cast(right))
47 .FromJust();
48}
49
50
51template <typename T, typename M>
52bool EQUALS(Handle<T> left, M right) {
53 return EQUALS(left, handle(right));
54}
55
56
57template <typename T, typename M>
58bool EQUALS(T left, Handle<M> right) {
59 return EQUALS(handle(left), right);
60}
61
62} // namespace
63
64
65//
66// Tests
67//
68
69TEST(JSObjectAddingProperties) {
70 CcTest::InitializeVM();
71 Isolate* isolate = CcTest::i_isolate();
72 Factory* factory = isolate->factory();
73 v8::HandleScope scope(CcTest::isolate());
74
75 Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
76 Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
77 Handle<Object> value(Smi::FromInt(42), isolate);
78
79 Handle<JSObject> object = factory->NewJSObject(function);
80 Handle<Map> previous_map(object->map());
81 CHECK_EQ(previous_map->elements_kind(), FAST_HOLEY_ELEMENTS);
82 CHECK(EQUALS(object->properties(), empty_fixed_array));
83 CHECK(EQUALS(object->elements(), empty_fixed_array));
84
85 // for the default constructor function no in-object properties are reserved
86 // hence adding a single property will initialize the property-array
87 Handle<String> name = MakeName("property", 0);
88 JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
89 .Check();
90 CHECK_NE(object->map(), *previous_map);
91 CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
92 CHECK_LE(1, object->properties()->length());
93 CHECK(EQUALS(object->elements(), empty_fixed_array));
94}
95
96
97TEST(JSObjectInObjectAddingProperties) {
98 CcTest::InitializeVM();
99 Isolate* isolate = CcTest::i_isolate();
100 Factory* factory = isolate->factory();
101 v8::HandleScope scope(CcTest::isolate());
102
103 Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
104 Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
105 int nof_inobject_properties = 10;
106 // force in object properties by changing the expected_nof_properties
107 function->shared()->set_expected_nof_properties(nof_inobject_properties);
108 Handle<Object> value(Smi::FromInt(42), isolate);
109
110 Handle<JSObject> object = factory->NewJSObject(function);
111 Handle<Map> previous_map(object->map());
112 CHECK_EQ(previous_map->elements_kind(), FAST_HOLEY_ELEMENTS);
113 CHECK(EQUALS(object->properties(), empty_fixed_array));
114 CHECK(EQUALS(object->elements(), empty_fixed_array));
115
116 // we have reserved space for in-object properties, hence adding up to
117 // |nof_inobject_properties| will not create a property store
118 for (int i = 0; i < nof_inobject_properties; i++) {
119 Handle<String> name = MakeName("property", i);
120 JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
121 .Check();
122 }
123 CHECK_NE(object->map(), *previous_map);
124 CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
125 CHECK(EQUALS(object->properties(), empty_fixed_array));
126 CHECK(EQUALS(object->elements(), empty_fixed_array));
127
128 // adding one more property will not fit in the in-object properties, thus
129 // creating a property store
130 int index = nof_inobject_properties + 1;
131 Handle<String> name = MakeName("property", index);
132 JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
133 .Check();
134 CHECK_NE(object->map(), *previous_map);
135 CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
136 // there must be at least 1 element in the properies store
137 CHECK_LE(1, object->properties()->length());
138 CHECK(EQUALS(object->elements(), empty_fixed_array));
139}
140
141
142TEST(JSObjectAddingElements) {
143 CcTest::InitializeVM();
144 Isolate* isolate = CcTest::i_isolate();
145 Factory* factory = isolate->factory();
146 v8::HandleScope scope(CcTest::isolate());
147
148 Handle<String> name;
149 Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
150 Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
151 Handle<Object> value(Smi::FromInt(42), isolate);
152
153 Handle<JSObject> object = factory->NewJSObject(function);
154 Handle<Map> previous_map(object->map());
155 CHECK_EQ(previous_map->elements_kind(), FAST_HOLEY_ELEMENTS);
156 CHECK(EQUALS(object->properties(), empty_fixed_array));
157 CHECK(EQUALS(object->elements(), empty_fixed_array));
158
159 // Adding an indexed element initializes the elements array
160 name = MakeString("0");
161 JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
162 .Check();
163 // no change in elements_kind => no map transition
164 CHECK_EQ(object->map(), *previous_map);
165 CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
166 CHECK(EQUALS(object->properties(), empty_fixed_array));
167 CHECK_LE(1, object->elements()->length());
168
169 // Adding more consecutive elements without a change in the backing store
170 int non_dict_backing_store_limit = 100;
171 for (int i = 1; i < non_dict_backing_store_limit; i++) {
172 name = MakeName("", i);
173 JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
174 .Check();
175 }
176 // no change in elements_kind => no map transition
177 CHECK_EQ(object->map(), *previous_map);
178 CHECK_EQ(object->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
179 CHECK(EQUALS(object->properties(), empty_fixed_array));
180 CHECK_LE(non_dict_backing_store_limit, object->elements()->length());
181
182 // Adding an element at an very large index causes a change to
183 // DICTIONARY_ELEMENTS
184 name = MakeString("100000000");
185 JSObject::DefinePropertyOrElementIgnoreAttributes(object, name, value, NONE)
186 .Check();
187 // change in elements_kind => map transition
188 CHECK_NE(object->map(), *previous_map);
189 CHECK_EQ(object->map()->elements_kind(), DICTIONARY_ELEMENTS);
190 CHECK(EQUALS(object->properties(), empty_fixed_array));
191 CHECK_LE(non_dict_backing_store_limit, object->elements()->length());
192}
193
194
195TEST(JSArrayAddingProperties) {
196 CcTest::InitializeVM();
197 Isolate* isolate = CcTest::i_isolate();
198 Factory* factory = isolate->factory();
199 v8::HandleScope scope(CcTest::isolate());
200
201 Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
202 Handle<Object> value(Smi::FromInt(42), isolate);
203
204 Handle<JSArray> array =
205 factory->NewJSArray(ElementsKind::FAST_SMI_ELEMENTS, 0, 0);
206 Handle<Map> previous_map(array->map());
207 CHECK_EQ(previous_map->elements_kind(), FAST_SMI_ELEMENTS);
208 CHECK(EQUALS(array->properties(), empty_fixed_array));
209 CHECK(EQUALS(array->elements(), empty_fixed_array));
210 CHECK_EQ(Smi::cast(array->length())->value(), 0);
211
212 // for the default constructor function no in-object properties are reserved
213 // hence adding a single property will initialize the property-array
214 Handle<String> name = MakeName("property", 0);
215 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value, NONE)
216 .Check();
217 // No change in elements_kind but added property => new map
218 CHECK_NE(array->map(), *previous_map);
219 CHECK_EQ(array->map()->elements_kind(), FAST_SMI_ELEMENTS);
220 CHECK_LE(1, array->properties()->length());
221 CHECK(EQUALS(array->elements(), empty_fixed_array));
222 CHECK_EQ(Smi::cast(array->length())->value(), 0);
223}
224
225
226TEST(JSArrayAddingElements) {
227 CcTest::InitializeVM();
228 Isolate* isolate = CcTest::i_isolate();
229 Factory* factory = isolate->factory();
230 v8::HandleScope scope(CcTest::isolate());
231
232 Handle<String> name;
233 Handle<FixedArray> empty_fixed_array(factory->empty_fixed_array());
234 Handle<Object> value(Smi::FromInt(42), isolate);
235
236 Handle<JSArray> array =
237 factory->NewJSArray(ElementsKind::FAST_SMI_ELEMENTS, 0, 0);
238 Handle<Map> previous_map(array->map());
239 CHECK_EQ(previous_map->elements_kind(), FAST_SMI_ELEMENTS);
240 CHECK(EQUALS(array->properties(), empty_fixed_array));
241 CHECK(EQUALS(array->elements(), empty_fixed_array));
242 CHECK_EQ(Smi::cast(array->length())->value(), 0);
243
244 // Adding an indexed element initializes the elements array
245 name = MakeString("0");
246 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value, NONE)
247 .Check();
248 // no change in elements_kind => no map transition
249 CHECK_EQ(array->map(), *previous_map);
250 CHECK_EQ(array->map()->elements_kind(), FAST_SMI_ELEMENTS);
251 CHECK(EQUALS(array->properties(), empty_fixed_array));
252 CHECK_LE(1, array->elements()->length());
253 CHECK_EQ(1, Smi::cast(array->length())->value());
254
255 // Adding more consecutive elements without a change in the backing store
256 int non_dict_backing_store_limit = 100;
257 for (int i = 1; i < non_dict_backing_store_limit; i++) {
258 name = MakeName("", i);
259 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value, NONE)
260 .Check();
261 }
262 // no change in elements_kind => no map transition
263 CHECK_EQ(array->map(), *previous_map);
264 CHECK_EQ(array->map()->elements_kind(), FAST_SMI_ELEMENTS);
265 CHECK(EQUALS(array->properties(), empty_fixed_array));
266 CHECK_LE(non_dict_backing_store_limit, array->elements()->length());
267 CHECK_EQ(non_dict_backing_store_limit, Smi::cast(array->length())->value());
268
269 // Adding an element at an very large index causes a change to
270 // DICTIONARY_ELEMENTS
271 int index = 100000000;
272 name = MakeName("", index);
273 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value, NONE)
274 .Check();
275 // change in elements_kind => map transition
276 CHECK_NE(array->map(), *previous_map);
277 CHECK_EQ(array->map()->elements_kind(), DICTIONARY_ELEMENTS);
278 CHECK(EQUALS(array->properties(), empty_fixed_array));
279 CHECK_LE(non_dict_backing_store_limit, array->elements()->length());
280 CHECK_LE(array->elements()->length(), index);
281 CHECK_EQ(index + 1, Smi::cast(array->length())->value());
282}
283
284
285TEST(JSArrayAddingElementsGeneralizingiFastSmiElements) {
286 CcTest::InitializeVM();
287 Isolate* isolate = CcTest::i_isolate();
288 Factory* factory = isolate->factory();
289 v8::HandleScope scope(CcTest::isolate());
290
291 Handle<String> name;
292 Handle<Object> value_smi(Smi::FromInt(42), isolate);
293 Handle<Object> value_string(MakeString("value"));
294 Handle<Object> value_double = factory->NewNumber(3.1415);
295
296 Handle<JSArray> array =
297 factory->NewJSArray(ElementsKind::FAST_SMI_ELEMENTS, 0, 0);
298 Handle<Map> previous_map(array->map());
299 CHECK_EQ(previous_map->elements_kind(), FAST_SMI_ELEMENTS);
300 CHECK_EQ(Smi::cast(array->length())->value(), 0);
301
302 // `array[0] = smi_value` doesn't change the elements_kind
303 name = MakeString("0");
304 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
305 NONE)
306 .Check();
307 // no change in elements_kind => no map transition
308 CHECK_EQ(array->map(), *previous_map);
309 CHECK_EQ(array->map()->elements_kind(), FAST_SMI_ELEMENTS);
310 CHECK_EQ(1, Smi::cast(array->length())->value());
311
312 // `delete array[0]` does not alter length, but changes the elments_kind
313 name = MakeString("0");
314 CHECK(JSReceiver::DeletePropertyOrElement(array, name).FromMaybe(false));
315 CHECK_NE(array->map(), *previous_map);
316 CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_SMI_ELEMENTS);
317 CHECK_EQ(1, Smi::cast(array->length())->value());
318 previous_map = handle(array->map());
319
320 // add a couple of elements again
321 name = MakeString("0");
322 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
323 NONE)
324 .Check();
325 name = MakeString("1");
326 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
327 NONE)
328 .Check();
329 CHECK_EQ(array->map(), *previous_map);
330 CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_SMI_ELEMENTS);
331 CHECK_EQ(2, Smi::cast(array->length())->value());
332
333 // Adding a string to the array changes from FAST_HOLEY_SMI to FAST_HOLEY
334 name = MakeString("0");
335 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_string,
336 NONE)
337 .Check();
338 CHECK_NE(array->map(), *previous_map);
339 CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
340 CHECK_EQ(2, Smi::cast(array->length())->value());
341 previous_map = handle(array->map());
342
343 // We don't transition back to FAST_SMI even if we remove the string
344 name = MakeString("0");
345 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
346 NONE)
347 .Check();
348 CHECK_EQ(array->map(), *previous_map);
349
350 // Adding a double doesn't change the map either
351 name = MakeString("0");
352 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_double,
353 NONE)
354 .Check();
355 CHECK_EQ(array->map(), *previous_map);
356}
357
358
359TEST(JSArrayAddingElementsGeneralizingFastElements) {
360 CcTest::InitializeVM();
361 Isolate* isolate = CcTest::i_isolate();
362 Factory* factory = isolate->factory();
363 v8::HandleScope scope(CcTest::isolate());
364
365 Handle<String> name;
366 Handle<Object> value_smi(Smi::FromInt(42), isolate);
367 Handle<Object> value_string(MakeString("value"));
368
369 Handle<JSArray> array =
370 factory->NewJSArray(ElementsKind::FAST_ELEMENTS, 0, 0);
371 Handle<Map> previous_map(array->map());
372 CHECK_EQ(previous_map->elements_kind(), FAST_ELEMENTS);
373 CHECK_EQ(Smi::cast(array->length())->value(), 0);
374
375 // `array[0] = smi_value` doesn't change the elements_kind
376 name = MakeString("0");
377 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
378 NONE)
379 .Check();
380 // no change in elements_kind => no map transition
381 CHECK_EQ(array->map(), *previous_map);
382 CHECK_EQ(array->map()->elements_kind(), FAST_ELEMENTS);
383 CHECK_EQ(1, Smi::cast(array->length())->value());
384
385 // `delete array[0]` does not alter length, but changes the elments_kind
386 name = MakeString("0");
387 CHECK(JSReceiver::DeletePropertyOrElement(array, name).FromMaybe(false));
388 CHECK_NE(array->map(), *previous_map);
389 CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
390 CHECK_EQ(1, Smi::cast(array->length())->value());
391 previous_map = handle(array->map());
392
393 // add a couple of elements, elements_kind stays HOLEY
394 name = MakeString("0");
395 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_string,
396 NONE)
397 .Check();
398 name = MakeString("1");
399 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
400 NONE)
401 .Check();
402 CHECK_EQ(array->map(), *previous_map);
403 CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
404 CHECK_EQ(2, Smi::cast(array->length())->value());
405}
406
407
408TEST(JSArrayAddingElementsGeneralizingiFastDoubleElements) {
409 CcTest::InitializeVM();
410 Isolate* isolate = CcTest::i_isolate();
411 Factory* factory = isolate->factory();
412 v8::HandleScope scope(CcTest::isolate());
413
414 Handle<String> name;
415 Handle<Object> value_smi(Smi::FromInt(42), isolate);
416 Handle<Object> value_string(MakeString("value"));
417 Handle<Object> value_double = factory->NewNumber(3.1415);
418
419 Handle<JSArray> array =
420 factory->NewJSArray(ElementsKind::FAST_SMI_ELEMENTS, 0, 0);
421 Handle<Map> previous_map(array->map());
422
423 // `array[0] = value_double` changes |elements_kind| to FAST_DOUBLE_ELEMENTS
424 name = MakeString("0");
425 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_double,
426 NONE)
427 .Check();
428 CHECK_NE(array->map(), *previous_map);
429 CHECK_EQ(array->map()->elements_kind(), FAST_DOUBLE_ELEMENTS);
430 CHECK_EQ(1, Smi::cast(array->length())->value());
431 previous_map = handle(array->map());
432
433 // `array[1] = value_smi` doesn't alter the |elements_kind|
434 name = MakeString("1");
435 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_smi,
436 NONE)
437 .Check();
438 CHECK_EQ(array->map(), *previous_map);
439 CHECK_EQ(array->map()->elements_kind(), FAST_DOUBLE_ELEMENTS);
440 CHECK_EQ(2, Smi::cast(array->length())->value());
441
442 // `delete array[0]` does not alter length, but changes the elments_kind
443 name = MakeString("0");
444 CHECK(JSReceiver::DeletePropertyOrElement(array, name).FromMaybe(false));
445 CHECK_NE(array->map(), *previous_map);
446 CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_DOUBLE_ELEMENTS);
447 CHECK_EQ(2, Smi::cast(array->length())->value());
448 previous_map = handle(array->map());
449
450 // filling the hole `array[0] = value_smi` again doesn't transition back
451 name = MakeString("0");
452 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_double,
453 NONE)
454 .Check();
455 CHECK_EQ(array->map(), *previous_map);
456 CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_DOUBLE_ELEMENTS);
457 CHECK_EQ(2, Smi::cast(array->length())->value());
458
459 // Adding a string to the array changes to elements_kind FAST_ELEMENTS
460 name = MakeString("1");
461 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_string,
462 NONE)
463 .Check();
464 CHECK_NE(array->map(), *previous_map);
465 CHECK_EQ(array->map()->elements_kind(), FAST_HOLEY_ELEMENTS);
466 CHECK_EQ(2, Smi::cast(array->length())->value());
467 previous_map = handle(array->map());
468
469 // Adding a double doesn't change the map
470 name = MakeString("0");
471 JSObject::DefinePropertyOrElementIgnoreAttributes(array, name, value_double,
472 NONE)
473 .Check();
474 CHECK_EQ(array->map(), *previous_map);
475}