1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Flags: --allow-natives-syntax --smi-only-arrays --expose-gc
29// Flags: --track-allocation-sites --noalways-opt
30
31// Test element kind of objects.
32// Since --smi-only-arrays affects builtins, its default setting at compile
33// time sticks if built with snapshot.  If --smi-only-arrays is deactivated
34// by default, only a no-snapshot build actually has smi-only arrays enabled
35// in this test case.  Depending on whether smi-only arrays are actually
36// enabled, this test takes the appropriate code path to check smi-only arrays.
37
38// support_smi_only_arrays = %HasFastSmiElements(new Array(1,2,3,4,5,6,7,8));
39support_smi_only_arrays = true;
40
41if (support_smi_only_arrays) {
42  print("Tests include smi-only arrays.");
43} else {
44  print("Tests do NOT include smi-only arrays.");
45}
46
47var elements_kind = {
48  fast_smi_only            :  'fast smi only elements',
49  fast                     :  'fast elements',
50  fast_double              :  'fast double elements',
51  dictionary               :  'dictionary elements',
52  external_byte            :  'external byte elements',
53  external_unsigned_byte   :  'external unsigned byte elements',
54  external_short           :  'external short elements',
55  external_unsigned_short  :  'external unsigned short elements',
56  external_int             :  'external int elements',
57  external_unsigned_int    :  'external unsigned int elements',
58  external_float           :  'external float elements',
59  external_double          :  'external double elements',
60  external_pixel           :  'external pixel elements'
61}
62
63function getKind(obj) {
64  if (%HasFastSmiElements(obj)) return elements_kind.fast_smi_only;
65  if (%HasFastObjectElements(obj)) return elements_kind.fast;
66  if (%HasFastDoubleElements(obj)) return elements_kind.fast_double;
67  if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
68}
69
70function isHoley(obj) {
71  if (%HasFastHoleyElements(obj)) return true;
72  return false;
73}
74
75function assertKind(expected, obj, name_opt) {
76  if (!support_smi_only_arrays &&
77      expected == elements_kind.fast_smi_only) {
78    expected = elements_kind.fast;
79  }
80  assertEquals(expected, getKind(obj), name_opt);
81}
82
83function assertHoley(obj, name_opt) {
84  assertEquals(true, isHoley(obj), name_opt);
85}
86
87function assertNotHoley(obj, name_opt) {
88  assertEquals(false, isHoley(obj), name_opt);
89}
90
91if (support_smi_only_arrays) {
92  obj = [];
93  assertNotHoley(obj);
94  assertKind(elements_kind.fast_smi_only, obj);
95
96  obj = [1, 2, 3];
97  assertNotHoley(obj);
98  assertKind(elements_kind.fast_smi_only, obj);
99
100  obj = new Array();
101  assertNotHoley(obj);
102  assertKind(elements_kind.fast_smi_only, obj);
103
104  obj = new Array(0);
105  assertNotHoley(obj);
106  assertKind(elements_kind.fast_smi_only, obj);
107
108  obj = new Array(2);
109  assertHoley(obj);
110  assertKind(elements_kind.fast_smi_only, obj);
111
112  obj = new Array(1,2,3);
113  assertNotHoley(obj);
114  assertKind(elements_kind.fast_smi_only, obj);
115
116  obj = new Array(1, "hi", 2, undefined);
117  assertNotHoley(obj);
118  assertKind(elements_kind.fast, obj);
119
120  function fastliteralcase(literal, value) {
121    literal[0] = value;
122    return literal;
123  }
124
125  function get_standard_literal() {
126    var literal = [1, 2, 3];
127    return literal;
128  }
129
130  // Case: [1,2,3] as allocation site
131  obj = fastliteralcase(get_standard_literal(), 1);
132  assertKind(elements_kind.fast_smi_only, obj);
133  obj = fastliteralcase(get_standard_literal(), 1.5);
134  assertKind(elements_kind.fast_double, obj);
135  obj = fastliteralcase(get_standard_literal(), 2);
136  assertKind(elements_kind.fast_double, obj);
137
138  // The test below is in a loop because arrays that live
139  // at global scope without the chance of being recreated
140  // don't have allocation site information attached.
141  for (i = 0; i < 2; i++) {
142    obj = fastliteralcase([5, 3, 2], 1.5);
143    assertKind(elements_kind.fast_double, obj);
144    obj = fastliteralcase([3, 6, 2], 1.5);
145    assertKind(elements_kind.fast_double, obj);
146    obj = fastliteralcase([2, 6, 3], 2);
147    assertKind(elements_kind.fast_smi_only, obj);
148  }
149
150  // Verify that we will not pretransition the double->fast path.
151  obj = fastliteralcase(get_standard_literal(), "elliot");
152  assertKind(elements_kind.fast, obj);
153  // This fails until we turn off optimistic transitions to the
154  // most general elements kind seen on keyed stores. It's a goal
155  // to turn it off, but for now we need it.
156  // obj = fastliteralcase(3);
157  // assertKind(elements_kind.fast_double, obj);
158
159  // Make sure this works in crankshafted code too.
160  %OptimizeFunctionOnNextCall(get_standard_literal);
161  get_standard_literal();
162  obj = get_standard_literal();
163  assertKind(elements_kind.fast_double, obj);
164
165  function fastliteralcase_smifast(value) {
166    var literal = [1, 2, 3, 4];
167    literal[0] = value;
168    return literal;
169  }
170
171  obj = fastliteralcase_smifast(1);
172  assertKind(elements_kind.fast_smi_only, obj);
173  obj = fastliteralcase_smifast("carter");
174  assertKind(elements_kind.fast, obj);
175  obj = fastliteralcase_smifast(2);
176  assertKind(elements_kind.fast, obj);
177
178  // Case: make sure transitions from packed to holey are tracked
179  function fastliteralcase_smiholey(index, value) {
180    var literal = [1, 2, 3, 4];
181    literal[index] = value;
182    return literal;
183  }
184
185  obj = fastliteralcase_smiholey(5, 1);
186  assertKind(elements_kind.fast_smi_only, obj);
187  assertHoley(obj);
188  obj = fastliteralcase_smiholey(0, 1);
189  assertKind(elements_kind.fast_smi_only, obj);
190  assertHoley(obj);
191
192  function newarraycase_smidouble(value) {
193    var a = new Array();
194    a[0] = value;
195    return a;
196  }
197
198  // Case: new Array() as allocation site, smi->double
199  obj = newarraycase_smidouble(1);
200  assertKind(elements_kind.fast_smi_only, obj);
201  obj = newarraycase_smidouble(1.5);
202  assertKind(elements_kind.fast_double, obj);
203  obj = newarraycase_smidouble(2);
204  assertKind(elements_kind.fast_double, obj);
205
206  function newarraycase_smiobj(value) {
207    var a = new Array();
208    a[0] = value;
209    return a;
210  }
211
212  // Case: new Array() as allocation site, smi->fast
213  obj = newarraycase_smiobj(1);
214  assertKind(elements_kind.fast_smi_only, obj);
215  obj = newarraycase_smiobj("gloria");
216  assertKind(elements_kind.fast, obj);
217  obj = newarraycase_smiobj(2);
218  assertKind(elements_kind.fast, obj);
219
220  function newarraycase_length_smidouble(value) {
221    var a = new Array(3);
222    a[0] = value;
223    return a;
224  }
225
226  // Case: new Array(length) as allocation site
227  obj = newarraycase_length_smidouble(1);
228  assertKind(elements_kind.fast_smi_only, obj);
229  obj = newarraycase_length_smidouble(1.5);
230  assertKind(elements_kind.fast_double, obj);
231  obj = newarraycase_length_smidouble(2);
232  assertKind(elements_kind.fast_double, obj);
233
234  // Try to continue the transition to fast object, but
235  // we will not pretransition from double->fast, because
236  // it may hurt performance ("poisoning").
237  obj = newarraycase_length_smidouble("coates");
238  assertKind(elements_kind.fast, obj);
239  obj = newarraycase_length_smidouble(2.5);
240  // However, because of optimistic transitions, we will
241  // transition to the most general kind of elements kind found,
242  // therefore I can't count on this assert yet.
243  // assertKind(elements_kind.fast_double, obj);
244
245  function newarraycase_length_smiobj(value) {
246    var a = new Array(3);
247    a[0] = value;
248    return a;
249  }
250
251  // Case: new Array(<length>) as allocation site, smi->fast
252  obj = newarraycase_length_smiobj(1);
253  assertKind(elements_kind.fast_smi_only, obj);
254  obj = newarraycase_length_smiobj("gloria");
255  assertKind(elements_kind.fast, obj);
256  obj = newarraycase_length_smiobj(2);
257  assertKind(elements_kind.fast, obj);
258
259  function newarraycase_list_smidouble(value) {
260    var a = new Array(1, 2, 3);
261    a[0] = value;
262    return a;
263  }
264
265  obj = newarraycase_list_smidouble(1);
266  assertKind(elements_kind.fast_smi_only, obj);
267  obj = newarraycase_list_smidouble(1.5);
268  assertKind(elements_kind.fast_double, obj);
269  obj = newarraycase_list_smidouble(2);
270  assertKind(elements_kind.fast_double, obj);
271
272  function newarraycase_list_smiobj(value) {
273    var a = new Array(4, 5, 6);
274    a[0] = value;
275    return a;
276  }
277
278  obj = newarraycase_list_smiobj(1);
279  assertKind(elements_kind.fast_smi_only, obj);
280  obj = newarraycase_list_smiobj("coates");
281  assertKind(elements_kind.fast, obj);
282  obj = newarraycase_list_smiobj(2);
283  assertKind(elements_kind.fast, obj);
284
285  // Case: array constructor calls with out of date feedback.
286  // The boilerplate should incorporate all feedback, but the input array
287  // should be minimally transitioned based on immediate need.
288  (function() {
289    function foo(i) {
290      // We have two cases, one for literals one for constructed arrays.
291      var a = (i == 0)
292        ? [1, 2, 3]
293        : new Array(1, 2, 3);
294      return a;
295    }
296
297    for (i = 0; i < 2; i++) {
298      a = foo(i);
299      b = foo(i);
300      b[5] = 1;  // boilerplate goes holey
301      assertHoley(foo(i));
302      a[0] = 3.5;  // boilerplate goes holey double
303      assertKind(elements_kind.fast_double, a);
304      assertNotHoley(a);
305      c = foo(i);
306      assertKind(elements_kind.fast_double, c);
307      assertHoley(c);
308    }
309  })();
310
311  function newarraycase_onearg(len, value) {
312    var a = new Array(len);
313    a[0] = value;
314    return a;
315  }
316
317  obj = newarraycase_onearg(5, 3.5);
318  assertKind(elements_kind.fast_double, obj);
319  obj = newarraycase_onearg(10, 5);
320  assertKind(elements_kind.fast_double, obj);
321  obj = newarraycase_onearg(0, 5);
322  assertKind(elements_kind.fast_double, obj);
323  // Now pass a length that forces the dictionary path.
324  obj = newarraycase_onearg(100000, 5);
325  assertKind(elements_kind.dictionary, obj);
326  assertTrue(obj.length == 100000);
327
328  // Verify that cross context calls work
329  var realmA = Realm.current();
330  var realmB = Realm.create();
331  assertEquals(0, realmA);
332  assertEquals(1, realmB);
333
334  function instanceof_check(type) {
335    assertTrue(new type() instanceof type);
336    assertTrue(new type(5) instanceof type);
337    assertTrue(new type(1,2,3) instanceof type);
338  }
339
340  function instanceof_check2(type) {
341    assertTrue(new type() instanceof type);
342    assertTrue(new type(5) instanceof type);
343    assertTrue(new type(1,2,3) instanceof type);
344  }
345
346  var realmBArray = Realm.eval(realmB, "Array");
347  instanceof_check(Array);
348  instanceof_check(realmBArray);
349
350  // instanceof_check2 is here because the call site goes through a state.
351  // Since instanceof_check(Array) was first called with the current context
352  // Array function, it went from (uninit->Array) then (Array->megamorphic).
353  // We'll get a different state traversal if we start with realmBArray.
354  // It'll go (uninit->realmBArray) then (realmBArray->megamorphic). Recognize
355  // that state "Array" implies an AllocationSite is present, and code is
356  // configured to use it.
357  instanceof_check2(realmBArray);
358  instanceof_check2(Array);
359
360  %OptimizeFunctionOnNextCall(instanceof_check);
361
362  // No de-opt will occur because HCallNewArray wasn't selected, on account of
363  // the call site not being monomorphic to Array.
364  instanceof_check(Array);
365  assertOptimized(instanceof_check);
366  instanceof_check(realmBArray);
367  assertOptimized(instanceof_check);
368
369  // Try to optimize again, but first clear all type feedback, and allow it
370  // to be monomorphic on first call. Only after crankshafting do we introduce
371  // realmBArray. This should deopt the method.
372  %DeoptimizeFunction(instanceof_check);
373  %ClearFunctionTypeFeedback(instanceof_check);
374  instanceof_check(Array);
375  instanceof_check(Array);
376  %OptimizeFunctionOnNextCall(instanceof_check);
377  instanceof_check(Array);
378  assertOptimized(instanceof_check);
379
380  instanceof_check(realmBArray);
381  assertUnoptimized(instanceof_check);
382}
383