1package goja
2
3import "testing"
4
5func TestArrayProtoProp(t *testing.T) {
6	const SCRIPT = `
7	Object.defineProperty(Array.prototype, '0', {value: 42, configurable: true, writable: false})
8	var a = []
9	a[0] = 1
10	a[0]
11	`
12
13	testScript1(SCRIPT, valueInt(42), t)
14}
15
16func TestArrayDelete(t *testing.T) {
17	const SCRIPT = `
18	var a = [1, 2];
19	var deleted = delete a[0];
20	var undef = a[0] === undefined;
21	var len = a.length;
22
23	deleted && undef && len === 2;
24	`
25
26	testScript1(SCRIPT, valueTrue, t)
27}
28
29func TestArrayDeleteNonexisting(t *testing.T) {
30	const SCRIPT = `
31	Array.prototype[0] = 42;
32	var a = [];
33	delete a[0] && a[0] === 42;
34	`
35
36	testScript1(SCRIPT, valueTrue, t)
37}
38
39func TestArraySetLength(t *testing.T) {
40	const SCRIPT = `
41	var a = [1, 2];
42	var assert0 = a.length == 2;
43	a.length = "1";
44	a.length = 1.0;
45	a.length = 1;
46	var assert1 = a.length == 1;
47	a.length = 2;
48	var assert2 = a.length == 2;
49	assert0 && assert1 && assert2 && a[1] === undefined;
50
51	`
52
53	testScript1(SCRIPT, valueTrue, t)
54}
55
56func TestArrayReverseNonOptimisable(t *testing.T) {
57	const SCRIPT = `
58	var a = [];
59	Object.defineProperty(a, "0", {get: function() {return 42}, set: function(v) {Object.defineProperty(a, "0", {value: v + 1, writable: true, configurable: true})}, configurable: true})
60	a[1] = 43;
61	a.reverse();
62
63	a.length === 2 && a[0] === 44 && a[1] === 42;
64	`
65
66	testScript1(SCRIPT, valueTrue, t)
67}
68
69func TestArrayPushNonOptimisable(t *testing.T) {
70	const SCRIPT = `
71	Object.defineProperty(Object.prototype, "0", {value: 42});
72	var a = [];
73	var thrown = false;
74	try {
75		a.push(1);
76	} catch (e) {
77		thrown = e instanceof TypeError;
78	}
79	thrown;
80	`
81
82	testScript1(SCRIPT, valueTrue, t)
83}
84
85func TestArraySetLengthWithPropItems(t *testing.T) {
86	const SCRIPT = `
87	var a = [1,2,3,4];
88	var thrown = false;
89
90	Object.defineProperty(a, "2", {value: 42, configurable: false, writable: false});
91	try {
92		Object.defineProperty(a, "length", {value: 0, writable: false});
93	} catch (e) {
94		thrown = e instanceof TypeError;
95	}
96	thrown && a.length === 3;
97	`
98
99	testScript1(SCRIPT, valueTrue, t)
100}
101
102func TestArrayFrom(t *testing.T) {
103	const SCRIPT = `
104	function checkDestHoles(dest, prefix) {
105		assert(dest !== source, prefix + ": dest !== source");
106		assert.sameValue(dest.length, 3, prefix + ": dest.length");
107		assert.sameValue(dest[0], 1, prefix + ": [0]");
108		assert.sameValue(dest[1], undefined, prefix + ": [1]");
109		assert(dest.hasOwnProperty("1"), prefix + ': hasOwnProperty("1")');
110		assert.sameValue(dest[2], 3, prefix + ": [2]");
111	}
112
113	function checkDest(dest, prefix) {
114		assert(dest !== source, prefix + ": dest !== source");
115		assert.sameValue(dest.length, 3, prefix + ": dest.length");
116		assert.sameValue(dest[0], 1, prefix + ": [0]");
117		assert.sameValue(dest[1], 2, prefix + ": [1]");
118		assert.sameValue(dest[2], 3, prefix + ": [2]");
119	}
120
121	var source = [1,2,3];
122	var srcHoles = [1,,3];
123
124	checkDest(Array.from(source), "std source/std dest");
125	checkDestHoles(Array.from(srcHoles), "std source (holes)/std dest");
126
127	function Iter() {
128		this.idx = 0;
129	}
130	Iter.prototype.next = function() {
131		if (this.idx < source.length) {
132			return {value: source[this.idx++]};
133		} else {
134			return {done: true};
135		}
136	}
137
138	var src = {};
139	src[Symbol.iterator] = function() {
140		return new Iter();
141	}
142	checkDest(Array.from(src), "iter src/std dest");
143
144	src = {0: 1, 2: 3, length: 3};
145	checkDestHoles(Array.from(src), "arrayLike src/std dest");
146
147	function A() {}
148	A.from = Array.from;
149
150	checkDest(A.from(source), "std src/cust dest");
151	checkDestHoles(A.from(srcHoles), "std src (holes)/cust dest");
152	checkDestHoles(A.from(src), "arrayLike src/cust dest");
153
154	function T2() {
155	  Object.defineProperty(this, 0, {
156		configurable: false,
157		writable: true,
158		enumerable: true
159	  });
160	}
161
162	assert.throws(TypeError, function() {
163		Array.from.call(T2, source);
164	});
165
166	`
167
168	testScript1(TESTLIB+SCRIPT, _undefined, t)
169}
170
171func TestArrayOf(t *testing.T) {
172	const SCRIPT = `
173	function T1() {
174	  Object.preventExtensions(this);
175	}
176
177	assert.throws(TypeError, function() {
178	  Array.of.call(T1, 'Bob');
179	});
180
181	function T2() {
182	  Object.defineProperty(this, 0, {
183		configurable: false,
184		writable: true,
185		enumerable: true
186	  });
187	}
188
189	assert.throws(TypeError, function() {
190	  Array.of.call(T2, 'Bob');
191	})
192
193	result = Array.of.call(undefined);
194	assert(
195	  result instanceof Array,
196	  'this is not a constructor'
197	);
198
199	result = Array.of.call(Math.cos);
200	assert(
201	  result instanceof Array,
202	  'this is a builtin function with no [[Construct]] slot'
203	);
204
205	`
206
207	testScript1(TESTLIB+SCRIPT, _undefined, t)
208}
209
210func TestUnscopables(t *testing.T) {
211	const SCRIPT = `
212	var keys = [];
213	var _length;
214	with (Array.prototype) {
215		_length = length;
216		keys.push('something');
217	}
218	_length === 0 && keys.length === 1 && keys[0] === "something";
219	`
220	testScript1(SCRIPT, valueTrue, t)
221}
222
223func TestArraySort(t *testing.T) {
224	const SCRIPT = `
225	assert.throws(TypeError, function() {
226		[1,2].sort(null);
227	}, "null compare function");
228	assert.throws(TypeError, function() {
229		[1,2].sort({});
230	}, "non-callable compare function");
231	`
232	testScript1(TESTLIB+SCRIPT, _undefined, t)
233}
234
235func TestArraySortNonStdArray(t *testing.T) {
236	const SCRIPT = `
237	const array = [undefined, 'c', /*hole*/, 'b', undefined, /*hole*/, 'a', 'd'];
238
239	Object.defineProperty(array, '2', {
240	  get() {
241		array.pop();
242		array.pop();
243		return this.foo;
244	  },
245	  set(v) {
246		this.foo = v;
247	  }
248	});
249
250	array.sort();
251
252	assert.sameValue(array[0], 'b');
253	assert.sameValue(array[1], 'c');
254	assert.sameValue(array[3], undefined);
255	assert.sameValue(array[4], undefined);
256	assert.sameValue('5' in array, false);
257	assert.sameValue(array.hasOwnProperty('5'), false);
258	assert.sameValue(array.length, 6);
259	assert.sameValue(array.foo, undefined);
260
261	assert.sameValue(array[2], undefined);
262	assert.sameValue(array.length, 4);
263	`
264	testScript1(TESTLIB+SCRIPT, _undefined, t)
265}
266
267func TestArrayConcat(t *testing.T) {
268	const SCRIPT = `
269	var concat = Array.prototype.concat;
270	var array = [1, 2];
271	var sparseArray = [1, , 2];
272	var nonSpreadableArray = [1, 2];
273	nonSpreadableArray[Symbol.isConcatSpreadable] = false;
274	var arrayLike = { 0: 1, 1: 2, length: 2 };
275	var spreadableArrayLike = { 0: 1, 1: 2, length: 2 };
276	spreadableArrayLike[Symbol.isConcatSpreadable] = true;
277	assert(looksNative(concat));
278	assert(deepEqual(array.concat(), [1, 2]), '#1');
279	assert(deepEqual(sparseArray.concat(), [1, , 2]), '#2');
280	assert(deepEqual(nonSpreadableArray.concat(), [[1, 2]]), '#3');
281	assert(deepEqual(concat.call(arrayLike), [{ 0: 1, 1: 2, length: 2 }]), '#4');
282	assert(deepEqual(concat.call(spreadableArrayLike), [1, 2]), '#5');
283	assert(deepEqual([].concat(array), [1, 2]), '#6');
284	assert(deepEqual([].concat(sparseArray), [1, , 2]), '#7');
285	assert(deepEqual([].concat(nonSpreadableArray), [[1, 2]]), '#8');
286	assert(deepEqual([].concat(arrayLike), [{ 0: 1, 1: 2, length: 2 }]), '#9');
287	assert(deepEqual([].concat(spreadableArrayLike), [1, 2]), '#10');
288	assert(deepEqual(array.concat(sparseArray, nonSpreadableArray, arrayLike, spreadableArrayLike), [
289	1, 2, 1, , 2, [1, 2], { 0: 1, 1: 2, length: 2 }, 1, 2,
290	]), '#11');
291	array = [];
292	array.constructor = {};
293	array.constructor[Symbol.species] = function () {
294		return { foo: 1 };
295	}
296	assert.sameValue(array.concat().foo, 1, '@@species');
297	`
298	testScript1(TESTLIBX+SCRIPT, _undefined, t)
299}
300
301func TestArrayFlat(t *testing.T) {
302	const SCRIPT = `
303	var array = [1, [2,3,[4,5,6]], [[[[7,8,9]]]]];
304	assert(deepEqual(array.flat(), [1,2,3,[4,5,6],[[[7,8,9]]]]), '#1');
305	assert(deepEqual(array.flat(1), [1,2,3,[4,5,6],[[[7,8,9]]]]), '#2');
306	assert(deepEqual(array.flat(3), [1,2,3,4,5,6,[7,8,9]]), '#3');
307	assert(deepEqual(array.flat(4), [1,2,3,4,5,6,7,8,9]), '#4');
308	assert(deepEqual(array.flat(10), [1,2,3,4,5,6,7,8,9]), '#5');
309	`
310	testScript1(TESTLIBX+SCRIPT, _undefined, t)
311}
312
313func TestArrayFlatMap(t *testing.T) {
314	const SCRIPT = `
315	var double = function(x) {
316		if (isNaN(x)) {
317			return x
318		}
319		return x * 2
320	}
321	var array = [1, [2,3,[4,5,6]], [[[[7,8,9]]]]];
322	assert(deepEqual(array.flatMap(double), [2,2,3,[4,5,6],[[[7,8,9]]]]), '#1');
323	`
324	testScript1(TESTLIBX+SCRIPT, _undefined, t)
325}
326