1// Copyright 2008 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/**
29 * @fileoverview Test splice, shift, unshift, slice and join on small
30 * and large arrays.  Some of these methods are specified such that they
31 * should work on other objects too, so we test that too.
32 */
33
34var LARGE = 4000000;
35var VERYLARGE = 4000000000;
36
37// Nicer for firefox 1.5.  Unless you uncomment the following two lines,
38// smjs will appear to hang on this file.
39//var LARGE = 40000;
40//var VERYLARGE = 40000;
41
42var fourhundredth = LARGE/400;
43
44function PseudoArray() {
45};
46
47for (var use_real_arrays = 0; use_real_arrays <= 1; use_real_arrays++) {
48  var poses = [0, 140, 20000, VERYLARGE];
49  var the_prototype;
50  var new_function;
51  var push_function;
52  var concat_function;
53  var slice_function;
54  var splice_function;
55  var splice_function_2;
56  var unshift_function;
57  var unshift_function_2;
58  var shift_function;
59  if (use_real_arrays) {
60    new_function = function(length) {
61      return new Array(length);
62    };
63    the_prototype = Array.prototype;
64    push_function = function(array, elt) {
65      return array.push(elt);
66    };
67    concat_function = function(array, other) {
68      return array.concat(other);
69    };
70    slice_function = function(array, start, len) {
71      return array.slice(start, len);
72    };
73    splice_function = function(array, start, len) {
74      return array.splice(start, len);
75    };
76    splice_function_2 = function(array, start, len, elt) {
77      return array.splice(start, len, elt);
78    };
79    unshift_function = function(array, elt) {
80      return array.unshift(elt);
81    };
82    unshift_function_2 = function(array, elt1, elt2) {
83      return array.unshift(elt1, elt2);
84    };
85    shift_function = function(array) {
86      return array.shift();
87    };
88  } else {
89    // Don't run largest size on non-arrays or we'll be here for ever.
90    poses.pop();
91    new_function = function(length) {
92      var obj = new PseudoArray();
93      obj.length = length;
94      return obj;
95    };
96    the_prototype = PseudoArray.prototype;
97    push_function = function(array, elt) {
98      array[array.length] = elt;
99      array.length++;
100    };
101    concat_function = function(array, other) {
102      return Array.prototype.concat.call(array, other);
103    };
104    slice_function = function(array, start, len) {
105      return Array.prototype.slice.call(array, start, len);
106    };
107    splice_function = function(array, start, len) {
108      return Array.prototype.splice.call(array, start, len);
109    };
110    splice_function_2 = function(array, start, len, elt) {
111      return Array.prototype.splice.call(array, start, len, elt);
112    };
113    unshift_function = function(array, elt) {
114      return Array.prototype.unshift.call(array, elt);
115    };
116    unshift_function_2 = function(array, elt1, elt2) {
117      return Array.prototype.unshift.call(array, elt1, elt2);
118    };
119    shift_function = function(array) {
120      return Array.prototype.shift.call(array);
121    };
122  }
123
124  for (var pos_pos = 0; pos_pos < poses.length; pos_pos++) {
125    var pos = poses[pos_pos];
126    if (pos > 100) {
127      var a = new_function(pos);
128      assertEquals(pos, a.length);
129      push_function(a, 'foo');
130      assertEquals(pos + 1, a.length);
131      var b = ['bar'];
132      // Delete a huge number of holes.
133      var c = splice_function(a, 10, pos - 20);
134      assertEquals(pos - 20, c.length);
135      assertEquals(21, a.length);
136    }
137
138    // Add a numeric property to the prototype of the array class.  This
139    // allows us to test some borderline stuff relative to the standard.
140    the_prototype["" + (pos + 1)] = 'baz';
141
142    if (use_real_arrays) {
143      // It seems quite clear from ECMAScript spec 15.4.4.5.  Just call Get on
144      // every integer in the range.
145      // IE, Safari get this right.
146      // FF, Opera get this wrong.
147      var a = ['zero', ,'two'];
148      if (pos == 0) {
149        assertEquals("zero,baz,two", a.join(","));
150      }
151
152      // Concat only applies to real arrays, unlike most of the other methods.
153      var a = new_function(pos);
154      push_function(a, "con");
155      assertEquals("con", a[pos]);
156      assertEquals(pos + 1, a.length);
157      var b = new_function(0);
158      push_function(b, "cat");
159      assertEquals("cat", b[0]);
160      var ab = concat_function(a, b);
161      assertEquals("con", ab[pos]);
162      assertEquals(pos + 2, ab.length);
163      assertEquals("cat", ab[pos + 1]);
164      var ba = concat_function(b, a);
165      assertEquals("con", ba[pos + 1]);
166      assertEquals(pos + 2, ba.length);
167      assertEquals("cat", ba[0]);
168
169      // Join with '' as separator.
170      var join = a.join('');
171      assertEquals("con", join);
172      join = b.join('');
173      assertEquals("cat", join);
174      join = ab.join('');
175      assertEquals("concat", join);
176      join = ba.join('');
177      assertEquals("catcon", join);
178
179      var sparse = [];
180      sparse[pos + 1000] = 'is ';
181      sparse[pos + 271828] = 'time ';
182      sparse[pos + 31415] = 'the ';
183      sparse[pos + 012260199] = 'all ';
184      sparse[-1] = 'foo';
185      sparse[pos + 22591927] = 'good ';
186      sparse[pos + 1618033] = 'for ';
187      sparse[pos + 91] = ': Now ';
188      sparse[pos + 86720199] = 'men.';
189      sparse.hest = 'fisk';
190
191      assertEquals("baz: Now is the time for all good men.", sparse.join(''));
192    }
193
194    a = new_function(pos);
195    push_function(a, 'zero');
196    push_function(a, void 0);
197    push_function(a, 'two');
198
199    // Splice works differently from join.
200    // IE, Safari get this wrong.
201    // FF, Opera get this right.
202    // 15.4.4.12 line 24 says the object itself has to have the property...
203    var zero = splice_function(a, pos, 1);
204    assertEquals("undefined", typeof(a[pos]));
205    assertEquals("two", a[pos+1], "pos1:" + pos);
206    assertEquals(pos + 2, a.length, "a length");
207    assertEquals(1, zero.length, "zero length");
208    assertEquals("zero", zero[0]);
209
210    // 15.4.4.12 line 41 says the object itself has to have the property...
211    a = new_function(pos);
212    push_function(a, 'zero');
213    push_function(a, void 0);
214    push_function(a, 'two');
215    var nothing = splice_function_2(a, pos, 0, 'minus1');
216    assertEquals("minus1", a[pos]);
217    assertEquals("zero", a[pos+1]);
218    assertEquals("undefined", typeof(a[pos+2]), "toot!");
219    assertEquals("two", a[pos+3], "pos3");
220    assertEquals(pos + 4, a.length);
221    assertEquals(1, zero.length);
222    assertEquals("zero", zero[0]);
223
224    // 15.4.4.12 line 10 says the object itself has to have the property...
225    a = new_function(pos);
226    push_function(a, 'zero');
227    push_function(a, void 0);
228    push_function(a, 'two');
229    var one = splice_function(a, pos + 1, 1);
230    assertEquals("", one.join(","));
231    assertEquals(pos + 2, a.length);
232    assertEquals("zero", a[pos]);
233    assertEquals("two", a[pos+1]);
234
235    // Set things back to the way they were.
236    the_prototype[pos + 1] = undefined;
237
238    // Unshift.
239    var a = new_function(pos);
240    push_function(a, "foo");
241    assertEquals("foo", a[pos]);
242    assertEquals(pos + 1, a.length);
243    unshift_function(a, "bar");
244    assertEquals("foo", a[pos+1]);
245    assertEquals(pos + 2, a.length);
246    assertEquals("bar", a[0]);
247    unshift_function_2(a, "baz", "boo");
248    assertEquals("foo", a[pos+3]);
249    assertEquals(pos + 4, a.length);
250    assertEquals("baz", a[0]);
251    assertEquals("boo", a[1]);
252    assertEquals("bar", a[2]);
253
254    // Shift.
255    var baz = shift_function(a);
256    assertEquals("baz", baz);
257    assertEquals("boo", a[0]);
258    assertEquals(pos + 3, a.length);
259    assertEquals("foo", a[pos + 2]);
260
261    // Slice.
262    var bar = slice_function(a, 1, 0);  // don't throw an exception please.
263    bar = slice_function(a, 1, 2);
264    assertEquals("bar", bar[0]);
265    assertEquals(1, bar.length);
266    assertEquals("bar", a[1]);
267
268  }
269}
270
271// Lets see if performance is reasonable.
272
273var a = new Array(LARGE + 10);
274for (var i = 0; i < a.length; i += 1000) {
275  a[i] = i;
276}
277
278// Take something near the end of the array.
279for (var i = 0; i < 10; i++) {
280  var top = a.splice(LARGE, 5);
281  assertEquals(5, top.length);
282  assertEquals(LARGE, top[0]);
283  assertEquals("undefined", typeof(top[1]));
284  assertEquals(LARGE + 5, a.length);
285  a.splice(LARGE, 0, LARGE);
286  a.length = LARGE + 10;
287}
288
289var a = new Array(LARGE + 10);
290for (var i = 0; i < a.length; i += fourhundredth) {
291  a[i] = i;
292}
293
294// Take something near the middle of the array.
295for (var i = 0; i < 10; i++) {
296  var top = a.splice(LARGE >> 1, 5);
297  assertEquals(5, top.length);
298  assertEquals(LARGE >> 1, top[0]);
299  assertEquals("undefined", typeof(top[1]));
300  assertEquals(LARGE + 5, a.length);
301  a.splice(LARGE >> 1, 0, LARGE >> 1, void 0, void 0, void 0, void 0);
302}
303
304
305// Test http://b/issue?id=1202711
306arr = [0];
307arr.length = 2;
308Array.prototype[1] = 1;
309assertEquals(1, arr.pop());
310assertEquals(0, arr.pop());
311Array.prototype[1] = undefined;
312
313// Test http://code.google.com/p/chromium/issues/detail?id=21860
314Array.prototype.push.apply([], [1].splice(0, -(-1 % 5)));
315
316
317// Check that the Array functions work also properly on non-Arrays
318var receiver;
319
320receiver = 'a string';
321assertThrows(function(){
322  Array.prototype.push.call(receiver);
323});
324
325receiver = 0;
326assertEquals(undefined, receiver.length);
327assertEquals(0, Array.prototype.push.call(receiver));
328assertEquals(1, Array.prototype.push.call(receiver, 'first'));
329assertEquals(undefined, receiver.length);
330
331receiver = {};
332assertEquals(undefined, receiver.length);
333assertEquals(0, Array.prototype.push.call(receiver));
334assertEquals(0, Array.prototype.push.call(receiver));
335assertEquals(0, receiver.length);
336assertEquals(1, Array.prototype.push.call(receiver, 'first'));
337assertEquals(1, receiver.length);
338assertEquals('first', receiver[0]);
339assertEquals(2, Array.prototype.push.call(receiver, 'second'));
340assertEquals(2, receiver.length);
341assertEquals('first', receiver[0]);
342assertEquals('second', receiver[1]);
343
344receiver = {'length': 10};
345assertEquals(10, Array.prototype.push.call(receiver));
346assertEquals(10, receiver.length);
347assertEquals(11, Array.prototype.push.call(receiver, 'first'));
348assertEquals(11, receiver.length);
349assertEquals('first', receiver[10]);
350assertEquals(13, Array.prototype.push.call(receiver, 'second', 'third'));
351assertEquals(13, receiver.length);
352assertEquals('first', receiver[10]);
353assertEquals('second', receiver[11]);
354assertEquals('third', receiver[12]);
355
356receiver = {
357  get length() { return 10; },
358  set length(l) {}
359};
360assertEquals(10, Array.prototype.push.call(receiver));
361assertEquals(10, receiver.length);
362assertEquals(11, Array.prototype.push.call(receiver, 'first'));
363assertEquals(10, receiver.length);
364assertEquals('first', receiver[10]);
365assertEquals(12, Array.prototype.push.call(receiver, 'second', 'third'));
366assertEquals(10, receiver.length);
367assertEquals('second', receiver[10]);
368assertEquals('third', receiver[11]);
369
370// readonly length
371receiver = {
372  get length() { return 10; },
373};
374assertThrows(function(){
375  Array.prototype.push.call(receiver);
376});
377
378receiver = {
379  set length(l) {}
380};
381assertEquals(0, Array.prototype.push.call(receiver));
382assertEquals(undefined, receiver.length);
383assertEquals(1, Array.prototype.push.call(receiver, 'first'));
384assertEquals(undefined, receiver.length);
385assertEquals(2, Array.prototype.push.call(receiver, 'third', 'second'));
386assertEquals(undefined, receiver.length);
387