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 concat on small and large arrays
30 */
31
32
33(function testStringWrapperConcat() {
34  var concat = Array.prototype.concat;
35  var str = new String('abcd');
36  assertEquals([1,2,3,new String('abcd')], [1, 2, 3].concat(str));
37  assertEquals([new String("abcd")], concat.call(str));
38
39  var array = [1, 2, 3];
40  array.__proto__ = str;
41  array.length = 4;
42  assertEquals([1,2,3,'d'], concat.call(array));
43})()
44
45var poses;
46
47poses = [140, 4000000000];
48while (pos = poses.shift()) {
49  var a = new Array(pos);
50  var array_proto = [];
51  a.__proto__ = array_proto;
52  assertEquals(pos, a.length);
53  a.push('foo');
54  assertEquals(pos + 1, a.length);
55  var b = ['bar'];
56  var c = a.concat(b);
57  assertEquals(pos + 2, c.length);
58  assertEquals("undefined", typeof(c[pos - 1]));
59  assertEquals("foo", c[pos]);
60  assertEquals("bar", c[pos + 1]);
61
62  // Can we fool the system by putting a number in a string?
63  var onetwofour = "124";
64  a[onetwofour] = 'doo';
65  assertEquals(a[124], 'doo');
66  c = a.concat(b);
67  assertEquals(c[124], 'doo');
68
69  // If we put a number in the prototype, then the spec says it should be
70  // copied on concat.
71  array_proto["123"] = 'baz';
72  assertEquals(a[123], 'baz');
73
74  c = a.concat(b);
75  assertEquals(pos + 2, c.length);
76  assertEquals("baz", c[123]);
77  assertEquals("undefined", typeof(c[pos - 1]));
78  assertEquals("foo", c[pos]);
79  assertEquals("bar", c[pos + 1]);
80
81  // When we take the number off the prototype it disappears from a, but
82  // the concat put it in c itself.
83  array_proto["123"] = undefined;
84  assertEquals("undefined", typeof(a[123]));
85  assertEquals("baz", c[123]);
86
87  // If the element of prototype is shadowed, the element on the instance
88  // should be copied, but not the one on the prototype.
89  array_proto[123] = 'baz';
90  a[123] = 'xyz';
91  assertEquals('xyz', a[123]);
92  c = a.concat(b);
93  assertEquals('xyz', c[123]);
94
95  // Non-numeric properties on the prototype or the array shouldn't get
96  // copied.
97  array_proto.moe = 'joe';
98  a.ben = 'jerry';
99  assertEquals(a["moe"], 'joe');
100  assertEquals(a["ben"], 'jerry');
101  c = a.concat(b);
102  // ben was not copied
103  assertEquals("undefined", typeof(c.ben));
104
105  // When we take moe off the prototype it disappears from all arrays.
106  array_proto.moe = undefined;
107  assertEquals("undefined", typeof(c.moe));
108
109  // Negative indices don't get concated.
110  a[-1] = 'minus1';
111  assertEquals("minus1", a[-1]);
112  assertEquals("undefined", typeof(a[0xffffffff]));
113  c = a.concat(b);
114  assertEquals("undefined", typeof(c[-1]));
115  assertEquals("undefined", typeof(c[0xffffffff]));
116  assertEquals(c.length, a.length + 1);
117}
118
119poses = [140, 4000000000];
120while (pos = poses.shift()) {
121  var a = new Array(pos);
122  assertEquals(pos, a.length);
123  a.push('foo');
124  assertEquals(pos + 1, a.length);
125  var b = ['bar'];
126  var c = a.concat(b);
127  assertEquals(pos + 2, c.length);
128  assertEquals("undefined", typeof(c[pos - 1]));
129  assertEquals("foo", c[pos]);
130  assertEquals("bar", c[pos + 1]);
131
132  // Can we fool the system by putting a number in a string?
133  var onetwofour = "124";
134  a[onetwofour] = 'doo';
135  assertEquals(a[124], 'doo');
136  c = a.concat(b);
137  assertEquals(c[124], 'doo');
138
139  // If we put a number in the prototype, then the spec says it should be
140  // copied on concat.
141  Array.prototype["123"] = 'baz';
142  assertEquals(a[123], 'baz');
143
144  c = a.concat(b);
145  assertEquals(pos + 2, c.length);
146  assertEquals("baz", c[123]);
147  assertEquals("undefined", typeof(c[pos - 1]));
148  assertEquals("foo", c[pos]);
149  assertEquals("bar", c[pos + 1]);
150
151  // When we take the number off the prototype it disappears from a, but
152  // the concat put it in c itself.
153  Array.prototype["123"] = undefined;
154  assertEquals("undefined", typeof(a[123]));
155  assertEquals("baz", c[123]);
156
157  // If the element of prototype is shadowed, the element on the instance
158  // should be copied, but not the one on the prototype.
159  Array.prototype[123] = 'baz';
160  a[123] = 'xyz';
161  assertEquals('xyz', a[123]);
162  c = a.concat(b);
163  assertEquals('xyz', c[123]);
164
165  // Non-numeric properties on the prototype or the array shouldn't get
166  // copied.
167  Array.prototype.moe = 'joe';
168  a.ben = 'jerry';
169  assertEquals(a["moe"], 'joe');
170  assertEquals(a["ben"], 'jerry');
171  c = a.concat(b);
172  // ben was not copied
173  assertEquals("undefined", typeof(c.ben));
174  // moe was not copied, but we can see it through the prototype
175  assertEquals("joe", c.moe);
176
177  // When we take moe off the prototype it disappears from all arrays.
178  Array.prototype.moe = undefined;
179  assertEquals("undefined", typeof(c.moe));
180
181  // Negative indices don't get concated.
182  a[-1] = 'minus1';
183  assertEquals("minus1", a[-1]);
184  assertEquals("undefined", typeof(a[0xffffffff]));
185  c = a.concat(b);
186  assertEquals("undefined", typeof(c[-1]));
187  assertEquals("undefined", typeof(c[0xffffffff]));
188  assertEquals(c.length, a.length + 1);
189
190}
191
192a = [];
193c = a.concat('Hello');
194assertEquals(1, c.length);
195assertEquals("Hello", c[0]);
196assertEquals("Hello", c.toString());
197
198// Check that concat preserves holes.
199var holey = [void 0,'a',,'c'].concat(['d',,'f',[0,,2],void 0])
200assertEquals(9, holey.length);  // hole in embedded array is ignored
201for (var i = 0; i < holey.length; i++) {
202  if (i == 2 || i == 5) {
203    assertFalse(i in holey);
204  } else {
205    assertTrue(i in holey);
206  }
207}
208
209// Polluted prototype from prior tests.
210delete Array.prototype[123];
211
212// Check that concat reads getters in the correct order.
213var arr1 = [,2];
214var arr2 = [1,3];
215var r1 = [].concat(arr1, arr2);  // [,2,1,3]
216assertEquals([,2,1,3], r1);
217
218// Make first array change length of second array.
219Object.defineProperty(arr1, 0, {get: function() {
220      arr2.push("X");
221      return undefined;
222    }, configurable: true})
223var r2 = [].concat(arr1, arr2);  // [undefined,2,1,3,"X"]
224assertEquals([undefined,2,1,3,"X"], r2);
225
226// Make first array change length of second array massively.
227arr2.length = 2;
228Object.defineProperty(arr1, 0, {get: function() {
229      arr2[500000] = "X";
230      return undefined;
231    }, configurable: true})
232var r3 = [].concat(arr1, arr2);  // [undefined,2,1,3,"X"]
233var expected = [undefined,2,1,3];
234expected[500000 + 2] = "X";
235
236assertEquals(expected, r3);
237
238var arr3 = [];
239var trace = [];
240var expectedTrace = []
241function mkGetter(i) { return function() { trace.push(i); }; }
242arr3.length = 10000;
243for (var i = 0; i < 100; i++) {
244  Object.defineProperty(arr3, i * i, {get: mkGetter(i)});
245  expectedTrace[i] = i;
246  expectedTrace[100 + i] = i;
247}
248var r4 = [0].concat(arr3, arr3);
249assertEquals(1 + arr3.length * 2, r4.length);
250assertEquals(expectedTrace, trace);
251