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 --expose-gc
29// Flags: --noalways-opt
30
31var elements_kind = {
32  fast_smi_only            :  'fast smi only elements',
33  fast                     :  'fast elements',
34  fast_double              :  'fast double elements',
35  dictionary               :  'dictionary elements',
36  external_byte            :  'external byte elements',
37  external_unsigned_byte   :  'external unsigned byte elements',
38  external_short           :  'external short elements',
39  external_unsigned_short  :  'external unsigned short elements',
40  external_int             :  'external int elements',
41  external_unsigned_int    :  'external unsigned int elements',
42  external_float           :  'external float elements',
43  external_double          :  'external double elements',
44  external_pixel           :  'external pixel elements'
45}
46
47function getKind(obj) {
48  if (%HasFastSmiElements(obj)) return elements_kind.fast_smi_only;
49  if (%HasFastObjectElements(obj)) return elements_kind.fast;
50  if (%HasFastDoubleElements(obj)) return elements_kind.fast_double;
51  if (%HasDictionaryElements(obj)) return elements_kind.dictionary;
52}
53
54function isHoley(obj) {
55  if (%HasFastHoleyElements(obj)) return true;
56  return false;
57}
58
59function assertKind(expected, obj, name_opt) {
60  assertEquals(expected, getKind(obj), name_opt);
61}
62
63function assertHoley(obj, name_opt) {
64  assertEquals(true, isHoley(obj), name_opt);
65}
66
67function assertNotHoley(obj, name_opt) {
68  assertEquals(false, isHoley(obj), name_opt);
69}
70
71obj = [];
72assertNotHoley(obj);
73assertKind(elements_kind.fast_smi_only, obj);
74
75obj = [1, 2, 3];
76assertNotHoley(obj);
77assertKind(elements_kind.fast_smi_only, obj);
78
79obj = new Array();
80assertNotHoley(obj);
81assertKind(elements_kind.fast_smi_only, obj);
82
83obj = new Array(0);
84assertNotHoley(obj);
85assertKind(elements_kind.fast_smi_only, obj);
86
87obj = new Array(2);
88assertHoley(obj);
89assertKind(elements_kind.fast_smi_only, obj);
90
91obj = new Array(1,2,3);
92assertNotHoley(obj);
93assertKind(elements_kind.fast_smi_only, obj);
94
95obj = new Array(1, "hi", 2, undefined);
96assertNotHoley(obj);
97assertKind(elements_kind.fast, obj);
98
99function fastliteralcase(literal, value) {
100  literal[0] = value;
101  return literal;
102}
103
104function get_standard_literal() {
105  var literal = [1, 2, 3];
106  return literal;
107}
108
109// Case: [1,2,3] as allocation site
110obj = fastliteralcase(get_standard_literal(), 1);
111assertKind(elements_kind.fast_smi_only, obj);
112obj = fastliteralcase(get_standard_literal(), 1.5);
113assertKind(elements_kind.fast_double, obj);
114obj = fastliteralcase(get_standard_literal(), 2);
115assertKind(elements_kind.fast_double, obj);
116
117// The test below is in a loop because arrays that live
118// at global scope without the chance of being recreated
119// don't have allocation site information attached.
120for (i = 0; i < 2; i++) {
121  obj = fastliteralcase([5, 3, 2], 1.5);
122  assertKind(elements_kind.fast_double, obj);
123  obj = fastliteralcase([3, 6, 2], 1.5);
124  assertKind(elements_kind.fast_double, obj);
125
126  // Note: thanks to pessimistic transition store stubs, we'll attempt
127  // to transition to the most general elements kind seen at a particular
128  // store site. So, the elements kind will be double.
129  obj = fastliteralcase([2, 6, 3], 2);
130  assertKind(elements_kind.fast_double, obj);
131}
132
133// Verify that we will not pretransition the double->fast path.
134obj = fastliteralcase(get_standard_literal(), "elliot");
135assertKind(elements_kind.fast, obj);
136obj = fastliteralcase(get_standard_literal(), 3);
137assertKind(elements_kind.fast, obj);
138
139// Make sure this works in crankshafted code too.
140  %OptimizeFunctionOnNextCall(get_standard_literal);
141get_standard_literal();
142obj = get_standard_literal();
143assertKind(elements_kind.fast, obj);
144
145function fastliteralcase_smifast(value) {
146  var literal = [1, 2, 3, 4];
147  literal[0] = value;
148  return literal;
149}
150
151obj = fastliteralcase_smifast(1);
152assertKind(elements_kind.fast_smi_only, obj);
153obj = fastliteralcase_smifast("carter");
154assertKind(elements_kind.fast, obj);
155obj = fastliteralcase_smifast(2);
156assertKind(elements_kind.fast, obj);
157
158// Case: make sure transitions from packed to holey are tracked
159function fastliteralcase_smiholey(index, value) {
160  var literal = [1, 2, 3, 4];
161  literal[index] = value;
162  return literal;
163}
164
165obj = fastliteralcase_smiholey(5, 1);
166assertKind(elements_kind.fast_smi_only, obj);
167assertHoley(obj);
168obj = fastliteralcase_smiholey(0, 1);
169assertKind(elements_kind.fast_smi_only, obj);
170assertHoley(obj);
171
172function newarraycase_smidouble(value) {
173  var a = new Array();
174  a[0] = value;
175  return a;
176}
177
178// Case: new Array() as allocation site, smi->double
179obj = newarraycase_smidouble(1);
180assertKind(elements_kind.fast_smi_only, obj);
181obj = newarraycase_smidouble(1.5);
182assertKind(elements_kind.fast_double, obj);
183obj = newarraycase_smidouble(2);
184assertKind(elements_kind.fast_double, obj);
185
186function newarraycase_smiobj(value) {
187  var a = new Array();
188  a[0] = value;
189  return a;
190}
191
192// Case: new Array() as allocation site, smi->fast
193obj = newarraycase_smiobj(1);
194assertKind(elements_kind.fast_smi_only, obj);
195obj = newarraycase_smiobj("gloria");
196assertKind(elements_kind.fast, obj);
197obj = newarraycase_smiobj(2);
198assertKind(elements_kind.fast, obj);
199
200function newarraycase_length_smidouble(value) {
201  var a = new Array(3);
202  a[0] = value;
203  return a;
204}
205
206// Case: new Array(length) as allocation site
207obj = newarraycase_length_smidouble(1);
208assertKind(elements_kind.fast_smi_only, obj);
209obj = newarraycase_length_smidouble(1.5);
210assertKind(elements_kind.fast_double, obj);
211obj = newarraycase_length_smidouble(2);
212assertKind(elements_kind.fast_double, obj);
213
214// Try to continue the transition to fast object.
215// TODO(mvstanton): re-enable commented out code when
216// FLAG_pretenuring_call_new is turned on in the build.
217obj = newarraycase_length_smidouble("coates");
218assertKind(elements_kind.fast, obj);
219obj = newarraycase_length_smidouble(2);
220// assertKind(elements_kind.fast, obj);
221
222function newarraycase_length_smiobj(value) {
223  var a = new Array(3);
224  a[0] = value;
225  return a;
226}
227
228// Case: new Array(<length>) as allocation site, smi->fast
229obj = newarraycase_length_smiobj(1);
230assertKind(elements_kind.fast_smi_only, obj);
231obj = newarraycase_length_smiobj("gloria");
232assertKind(elements_kind.fast, obj);
233obj = newarraycase_length_smiobj(2);
234assertKind(elements_kind.fast, obj);
235
236function newarraycase_list_smidouble(value) {
237  var a = new Array(1, 2, 3);
238  a[0] = value;
239  return a;
240}
241
242obj = newarraycase_list_smidouble(1);
243assertKind(elements_kind.fast_smi_only, obj);
244obj = newarraycase_list_smidouble(1.5);
245assertKind(elements_kind.fast_double, obj);
246obj = newarraycase_list_smidouble(2);
247assertKind(elements_kind.fast_double, obj);
248
249function newarraycase_list_smiobj(value) {
250  var a = new Array(4, 5, 6);
251  a[0] = value;
252  return a;
253}
254
255obj = newarraycase_list_smiobj(1);
256assertKind(elements_kind.fast_smi_only, obj);
257obj = newarraycase_list_smiobj("coates");
258assertKind(elements_kind.fast, obj);
259obj = newarraycase_list_smiobj(2);
260assertKind(elements_kind.fast, obj);
261
262// Case: array constructor calls with out of date feedback.
263// The boilerplate should incorporate all feedback, but the input array
264// should be minimally transitioned based on immediate need.
265(function() {
266  function foo(i) {
267    // We have two cases, one for literals one for constructed arrays.
268    var a = (i == 0)
269      ? [1, 2, 3]
270      : new Array(1, 2, 3);
271    return a;
272  }
273
274  for (i = 0; i < 2; i++) {
275    a = foo(i);
276    b = foo(i);
277    b[5] = 1;  // boilerplate goes holey
278    assertHoley(foo(i));
279    a[0] = 3.5;  // boilerplate goes holey double
280    assertKind(elements_kind.fast_double, a);
281    assertNotHoley(a);
282    c = foo(i);
283    assertKind(elements_kind.fast_double, c);
284    assertHoley(c);
285  }
286})();
287
288function newarraycase_onearg(len, value) {
289  var a = new Array(len);
290  a[0] = value;
291  return a;
292}
293
294obj = newarraycase_onearg(5, 3.5);
295assertKind(elements_kind.fast_double, obj);
296obj = newarraycase_onearg(10, 5);
297assertKind(elements_kind.fast_double, obj);
298obj = newarraycase_onearg(0, 5);
299assertKind(elements_kind.fast_double, obj);
300
301// Verify that cross context calls work
302var realmA = Realm.current();
303var realmB = Realm.create();
304assertEquals(0, realmA);
305assertEquals(1, realmB);
306
307function instanceof_check(type) {
308  assertTrue(new type() instanceof type);
309  assertTrue(new type(5) instanceof type);
310  assertTrue(new type(1,2,3) instanceof type);
311}
312
313function instanceof_check2(type) {
314  assertTrue(new type() instanceof type);
315  assertTrue(new type(5) instanceof type);
316  assertTrue(new type(1,2,3) instanceof type);
317}
318
319var realmBArray = Realm.eval(realmB, "Array");
320instanceof_check(Array);
321instanceof_check(realmBArray);
322
323// instanceof_check2 is here because the call site goes through a state.
324// Since instanceof_check(Array) was first called with the current context
325// Array function, it went from (uninit->Array) then (Array->megamorphic).
326// We'll get a different state traversal if we start with realmBArray.
327// It'll go (uninit->realmBArray) then (realmBArray->megamorphic). Recognize
328// that state "Array" implies an AllocationSite is present, and code is
329// configured to use it.
330instanceof_check2(realmBArray);
331instanceof_check2(Array);
332
333  %OptimizeFunctionOnNextCall(instanceof_check);
334
335// No de-opt will occur because HCallNewArray wasn't selected, on account of
336// the call site not being monomorphic to Array.
337instanceof_check(Array);
338assertOptimized(instanceof_check);
339instanceof_check(realmBArray);
340assertOptimized(instanceof_check);
341
342// Try to optimize again, but first clear all type feedback, and allow it
343// to be monomorphic on first call. Only after crankshafting do we introduce
344// realmBArray. This should deopt the method.
345  %DeoptimizeFunction(instanceof_check);
346  %ClearFunctionTypeFeedback(instanceof_check);
347instanceof_check(Array);
348instanceof_check(Array);
349  %OptimizeFunctionOnNextCall(instanceof_check);
350instanceof_check(Array);
351assertOptimized(instanceof_check);
352
353instanceof_check(realmBArray);
354assertUnoptimized(instanceof_check);
355
356// Case: make sure nested arrays benefit from allocation site feedback as
357// well.
358(function() {
359  // Make sure we handle nested arrays
360  function get_nested_literal() {
361    var literal = [[1,2,3,4], [2], [3]];
362    return literal;
363  }
364
365  obj = get_nested_literal();
366  assertKind(elements_kind.fast, obj);
367  obj[0][0] = 3.5;
368  obj[2][0] = "hello";
369  obj = get_nested_literal();
370  assertKind(elements_kind.fast_double, obj[0]);
371  assertKind(elements_kind.fast_smi_only, obj[1]);
372  assertKind(elements_kind.fast, obj[2]);
373
374  // A more complex nested literal case.
375  function get_deep_nested_literal() {
376    var literal = [[1], [[2], "hello"], 3, [4]];
377    return literal;
378  }
379
380  obj = get_deep_nested_literal();
381  assertKind(elements_kind.fast_smi_only, obj[1][0]);
382  obj[0][0] = 3.5;
383  obj[1][0][0] = "goodbye";
384  assertKind(elements_kind.fast_double, obj[0]);
385  assertKind(elements_kind.fast, obj[1][0]);
386
387  obj = get_deep_nested_literal();
388  assertKind(elements_kind.fast_double, obj[0]);
389  assertKind(elements_kind.fast, obj[1][0]);
390})();
391
392// Perform a gc because without it the test below can experience an
393// allocation failure at an inconvenient point. Allocation mementos get
394// cleared on gc, and they can't deliver elements kind feedback when that
395// happens.
396gc();
397
398// Make sure object literals with array fields benefit from the type feedback
399// that allocation mementos provide.
400(function() {
401  // A literal in an object
402  function get_object_literal() {
403    var literal = {
404      array: [1,2,3],
405      data: 3.5
406    };
407    return literal;
408  }
409
410  obj = get_object_literal();
411  assertKind(elements_kind.fast_smi_only, obj.array);
412  obj.array[1] = 3.5;
413  assertKind(elements_kind.fast_double, obj.array);
414  obj = get_object_literal();
415  assertKind(elements_kind.fast_double, obj.array);
416
417  function get_nested_object_literal() {
418    var literal = {
419      array: [[1],[2],[3]],
420      data: 3.5
421    };
422    return literal;
423  }
424
425  obj = get_nested_object_literal();
426  assertKind(elements_kind.fast, obj.array);
427  assertKind(elements_kind.fast_smi_only, obj.array[1]);
428  obj.array[1][0] = 3.5;
429  assertKind(elements_kind.fast_double, obj.array[1]);
430  obj = get_nested_object_literal();
431  assertKind(elements_kind.fast_double, obj.array[1]);
432
433    %OptimizeFunctionOnNextCall(get_nested_object_literal);
434  get_nested_object_literal();
435  obj = get_nested_object_literal();
436  assertKind(elements_kind.fast_double, obj.array[1]);
437
438  // Make sure we handle nested arrays
439  function get_nested_literal() {
440    var literal = [[1,2,3,4], [2], [3]];
441    return literal;
442  }
443
444  obj = get_nested_literal();
445  assertKind(elements_kind.fast, obj);
446  obj[0][0] = 3.5;
447  obj[2][0] = "hello";
448  obj = get_nested_literal();
449  assertKind(elements_kind.fast_double, obj[0]);
450  assertKind(elements_kind.fast_smi_only, obj[1]);
451  assertKind(elements_kind.fast, obj[2]);
452
453  // A more complex nested literal case.
454  function get_deep_nested_literal() {
455    var literal = [[1], [[2], "hello"], 3, [4]];
456    return literal;
457  }
458
459  obj = get_deep_nested_literal();
460  assertKind(elements_kind.fast_smi_only, obj[1][0]);
461  obj[0][0] = 3.5;
462  obj[1][0][0] = "goodbye";
463  assertKind(elements_kind.fast_double, obj[0]);
464  assertKind(elements_kind.fast, obj[1][0]);
465
466  obj = get_deep_nested_literal();
467  assertKind(elements_kind.fast_double, obj[0]);
468  assertKind(elements_kind.fast, obj[1][0]);
469})();
470