• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <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 
19 using namespace v8::internal;
20 
21 
22 //
23 // Helper functions.
24 //
25 
26 namespace {
27 
MakeString(const char * str)28 Handle<String> MakeString(const char* str) {
29   Isolate* isolate = CcTest::i_isolate();
30   Factory* factory = isolate->factory();
31   return factory->InternalizeUtf8String(str);
32 }
33 
34 
MakeName(const char * str,int suffix)35 Handle<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 
42 template <typename T, typename M>
EQUALS(Handle<T> left,Handle<M> right)43 bool 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 
51 template <typename T, typename M>
EQUALS(Handle<T> left,M right)52 bool EQUALS(Handle<T> left, M right) {
53   return EQUALS(left, handle(right));
54 }
55 
56 
57 template <typename T, typename M>
EQUALS(T left,Handle<M> right)58 bool EQUALS(T left, Handle<M> right) {
59   return EQUALS(handle(left), right);
60 }
61 
62 }  // namespace
63 
64 
65 //
66 // Tests
67 //
68 
TEST(JSObjectAddingProperties)69 TEST(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 
TEST(JSObjectInObjectAddingProperties)97 TEST(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 
TEST(JSObjectAddingElements)142 TEST(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 
TEST(JSArrayAddingProperties)195 TEST(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 
TEST(JSArrayAddingElements)226 TEST(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 
TEST(JSArrayAddingElementsGeneralizingiFastSmiElements)285 TEST(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 
TEST(JSArrayAddingElementsGeneralizingFastElements)359 TEST(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 
TEST(JSArrayAddingElementsGeneralizingiFastDoubleElements)408 TEST(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 }
476