1425bb815Sopenharmony_ci// Copyright JS Foundation and other contributors, http://js.foundation
2425bb815Sopenharmony_ci//
3425bb815Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4425bb815Sopenharmony_ci// you may not use this file except in compliance with the License.
5425bb815Sopenharmony_ci// You may obtain a copy of the License at
6425bb815Sopenharmony_ci//
7425bb815Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8425bb815Sopenharmony_ci//
9425bb815Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10425bb815Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS
11425bb815Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12425bb815Sopenharmony_ci// See the License for the specific language governing permissions and
13425bb815Sopenharmony_ci// limitations under the License.
14425bb815Sopenharmony_ci
15425bb815Sopenharmony_ci// Copyright 2014 the V8 project authors. All rights reserved.
16425bb815Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
17425bb815Sopenharmony_ci// found in the LICENSE file.
18425bb815Sopenharmony_ci
19425bb815Sopenharmony_ci// Some methods are taken from v8/test/mjsunit/mjsunit.js
20425bb815Sopenharmony_ci
21425bb815Sopenharmony_cifunction classOf(object) {
22425bb815Sopenharmony_ci  // Argument must not be null or undefined.
23425bb815Sopenharmony_ci  var string = Object.prototype.toString.call(object);
24425bb815Sopenharmony_ci  // String has format [object <ClassName>].
25425bb815Sopenharmony_ci  return string.substring(8, string.length - 1);
26425bb815Sopenharmony_ci}
27425bb815Sopenharmony_ci
28425bb815Sopenharmony_ci/**
29425bb815Sopenharmony_ci * Compares two objects for key/value equality.
30425bb815Sopenharmony_ci * Returns true if they are equal, false otherwise.
31425bb815Sopenharmony_ci */
32425bb815Sopenharmony_cifunction deepObjectEquals(a, b) {
33425bb815Sopenharmony_ci  var aProps = Object.keys(a);
34425bb815Sopenharmony_ci  aProps.sort();
35425bb815Sopenharmony_ci  var bProps = Object.keys(b);
36425bb815Sopenharmony_ci  bProps.sort();
37425bb815Sopenharmony_ci  if (!deepEquals(aProps, bProps)) {
38425bb815Sopenharmony_ci    return false;
39425bb815Sopenharmony_ci  }
40425bb815Sopenharmony_ci  for (var i = 0; i < aProps.length; i++) {
41425bb815Sopenharmony_ci    if (!deepEquals(a[aProps[i]], b[aProps[i]])) {
42425bb815Sopenharmony_ci      return false;
43425bb815Sopenharmony_ci    }
44425bb815Sopenharmony_ci  }
45425bb815Sopenharmony_ci  return true;
46425bb815Sopenharmony_ci}
47425bb815Sopenharmony_ci
48425bb815Sopenharmony_civar assertInstanceof = function assertInstanceof(obj, type) {
49425bb815Sopenharmony_ci  if (!(obj instanceof type)) {
50425bb815Sopenharmony_ci    var actualTypeName = null;
51425bb815Sopenharmony_ci    var actualConstructor = Object.getPrototypeOf(obj).constructor;
52425bb815Sopenharmony_ci    if (typeof actualConstructor == "function") {
53425bb815Sopenharmony_ci      actualTypeName = actualConstructor.name || String(actualConstructor);
54425bb815Sopenharmony_ci    } print("Object <" + obj + "> is not an instance of <" + (type.name || type) + ">" + (actualTypeName ? " but of < " + actualTypeName + ">" : ""));
55425bb815Sopenharmony_ci  }
56425bb815Sopenharmony_ci};
57425bb815Sopenharmony_ci
58425bb815Sopenharmony_cifunction deepEquals(a, b) {
59425bb815Sopenharmony_ci  if (a === b) {
60425bb815Sopenharmony_ci    if (a === 0) return (1 / a) === (1 / b);
61425bb815Sopenharmony_ci    return true;
62425bb815Sopenharmony_ci  }
63425bb815Sopenharmony_ci  if (typeof a != typeof b) return false;
64425bb815Sopenharmony_ci  if (typeof a == "number") return isNaN(a) && isNaN(b);
65425bb815Sopenharmony_ci  if (typeof a !== "object" && typeof a !== "function") return false;
66425bb815Sopenharmony_ci  var objectClass = classOf(a);
67425bb815Sopenharmony_ci  if (objectClass !== classOf(b)) return false;
68425bb815Sopenharmony_ci  if (objectClass === "RegExp") {
69425bb815Sopenharmony_ci    return (a.toString() === b.toString());
70425bb815Sopenharmony_ci  }
71425bb815Sopenharmony_ci  if (objectClass === "Function") return false;
72425bb815Sopenharmony_ci  if (objectClass === "Array") {
73425bb815Sopenharmony_ci    var elementCount = 0;
74425bb815Sopenharmony_ci    if (a.length != b.length) {
75425bb815Sopenharmony_ci      return false;
76425bb815Sopenharmony_ci    }
77425bb815Sopenharmony_ci    for (var i = 0; i < a.length; i++) {
78425bb815Sopenharmony_ci      if (!deepEquals(a[i], b[i]))
79425bb815Sopenharmony_ci      return false;
80425bb815Sopenharmony_ci    }
81425bb815Sopenharmony_ci    return true;
82425bb815Sopenharmony_ci  }
83425bb815Sopenharmony_ci  if (objectClass == "String" || objectClass == "Number" || objectClass == "Boolean" || objectClass == "Date") {
84425bb815Sopenharmony_ci    if (a.valueOf() !== b.valueOf()) return false;
85425bb815Sopenharmony_ci  }
86425bb815Sopenharmony_ci  return deepObjectEquals(a, b);
87425bb815Sopenharmony_ci}
88425bb815Sopenharmony_ci
89425bb815Sopenharmony_civar assertEquals = function assertEquals(expected, found, name_opt) {
90425bb815Sopenharmony_ci  if (!deepEquals(found, expected)) {
91425bb815Sopenharmony_ci    assert(false);
92425bb815Sopenharmony_ci  }
93425bb815Sopenharmony_ci};
94425bb815Sopenharmony_ci
95425bb815Sopenharmony_cifunction assertArrayLikeEquals(value, expected, type) {
96425bb815Sopenharmony_ci  assertInstanceof(value, type);
97425bb815Sopenharmony_ci  assert(expected.length === value.length);
98425bb815Sopenharmony_ci  for (var i=0; i<value.length; ++i) {
99425bb815Sopenharmony_ci    assertEquals(expected[i], value[i]);
100425bb815Sopenharmony_ci  }
101425bb815Sopenharmony_ci}
102425bb815Sopenharmony_ci
103425bb815Sopenharmony_ciassert(1 === Array.from.length);
104425bb815Sopenharmony_ci
105425bb815Sopenharmony_ci// Assert that constructor is called with "length" for array-like objects
106425bb815Sopenharmony_ci
107425bb815Sopenharmony_civar myCollectionCalled = false;
108425bb815Sopenharmony_cifunction MyCollection(length) {
109425bb815Sopenharmony_ci  myCollectionCalled = true;
110425bb815Sopenharmony_ci  assert(1 === arguments.length);
111425bb815Sopenharmony_ci
112425bb815Sopenharmony_ci  assert(5 === length);
113425bb815Sopenharmony_ci}
114425bb815Sopenharmony_ci
115425bb815Sopenharmony_ciArray.from.call(MyCollection, {length: 5});
116425bb815Sopenharmony_ciassert(myCollectionCalled === true);
117425bb815Sopenharmony_ci// Assert that calling mapfn with / without thisArg in sloppy and strict modes
118425bb815Sopenharmony_ci// works as expected.
119425bb815Sopenharmony_ci
120425bb815Sopenharmony_civar global = this;
121425bb815Sopenharmony_cifunction non_strict(){ assert(global === this); }
122425bb815Sopenharmony_cifunction strict(){ "use strict"; assert(void 0 === this); }
123425bb815Sopenharmony_cifunction strict_null(){ "use strict"; assert(null === this); }
124425bb815Sopenharmony_ciArray.from([1], non_strict);
125425bb815Sopenharmony_ciArray.from([1], non_strict, void 0);
126425bb815Sopenharmony_ciArray.from([1], strict);
127425bb815Sopenharmony_ciArray.from([1], strict, void 0);
128425bb815Sopenharmony_ci
129425bb815Sopenharmony_cifunction testArrayFrom(thisArg, constructor) {
130425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, [], undefined), [], constructor);
131425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, NaN), [], constructor);
132425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, Infinity), [], constructor);
133425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, 10000000), [], constructor);
134425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, 'test'), ['t', 'e', 's', 't'], constructor);
135425bb815Sopenharmony_ci
136425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg,
137425bb815Sopenharmony_ci    { length: 1, '0': { 'foo': 'bar' } }), [{'foo': 'bar'}], constructor);
138425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, { length: -1, '0': { 'foo': 'bar' } }), [], constructor);
139425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg,
140425bb815Sopenharmony_ci    [ 'foo', 'bar', 'baz' ]), ['foo', 'bar', 'baz'], constructor);
141425bb815Sopenharmony_ci  var kSet = new Set(['foo', 'bar', 'baz']);
142425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, kSet), ['foo', 'bar', 'baz'], constructor);
143425bb815Sopenharmony_ci  var kMap = new Map(['foo', 'bar', 'baz'].entries());
144425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, kMap), [[0, 'foo'], [1, 'bar'], [2, 'baz']], constructor);
145425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, 'test', function(x) {
146425bb815Sopenharmony_ci    return this.filter(x);
147425bb815Sopenharmony_ci  }, {
148425bb815Sopenharmony_ci    filter: function(x) { return x.toUpperCase(); }
149425bb815Sopenharmony_ci  }), ['T', 'E', 'S', 'T'], constructor);
150425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, 'test', function(x) {
151425bb815Sopenharmony_ci    return x.toUpperCase();
152425bb815Sopenharmony_ci  }), ['T', 'E', 'S', 'T'], constructor);
153425bb815Sopenharmony_ci
154425bb815Sopenharmony_ci  try {
155425bb815Sopenharmony_ci    Array.from.call(thisArg, null);
156425bb815Sopenharmony_ci    assert(false);
157425bb815Sopenharmony_ci  } catch (e) {
158425bb815Sopenharmony_ci    assert (e instanceof TypeError);
159425bb815Sopenharmony_ci  }
160425bb815Sopenharmony_ci
161425bb815Sopenharmony_ci  try {
162425bb815Sopenharmony_ci    Array.from.call(thisArg, undefined);
163425bb815Sopenharmony_ci    assert(false);
164425bb815Sopenharmony_ci  } catch (e) {
165425bb815Sopenharmony_ci    assert (e instanceof TypeError);
166425bb815Sopenharmony_ci  }
167425bb815Sopenharmony_ci
168425bb815Sopenharmony_ci  try {
169425bb815Sopenharmony_ci    Array.from.call(thisArg, [], null);
170425bb815Sopenharmony_ci    assert(false);
171425bb815Sopenharmony_ci  } catch (e) {
172425bb815Sopenharmony_ci    assert (e instanceof TypeError);
173425bb815Sopenharmony_ci  }
174425bb815Sopenharmony_ci
175425bb815Sopenharmony_ci  try {
176425bb815Sopenharmony_ci    Array.from.call(thisArg, [], "noncallable");
177425bb815Sopenharmony_ci    assert(false);
178425bb815Sopenharmony_ci  } catch (e) {
179425bb815Sopenharmony_ci    assert (e instanceof TypeError);
180425bb815Sopenharmony_ci  }
181425bb815Sopenharmony_ci
182425bb815Sopenharmony_ci  var nullIterator = {};
183425bb815Sopenharmony_ci  nullIterator[Symbol.iterator] = null;
184425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, nullIterator), [],
185425bb815Sopenharmony_ci  constructor);
186425bb815Sopenharmony_ci
187425bb815Sopenharmony_ci  var nonObjIterator = {};
188425bb815Sopenharmony_ci  nonObjIterator[Symbol.iterator] = function() { return "nonObject"; };
189425bb815Sopenharmony_ci
190425bb815Sopenharmony_ci  try {
191425bb815Sopenharmony_ci    Array.from.call(thisArg, nonObjIterator);
192425bb815Sopenharmony_ci    assert(false);
193425bb815Sopenharmony_ci  } catch (e) {
194425bb815Sopenharmony_ci    assert (e instanceof TypeError);
195425bb815Sopenharmony_ci  }
196425bb815Sopenharmony_ci
197425bb815Sopenharmony_ci  try {
198425bb815Sopenharmony_ci    Array.from.call(thisArg, [], null);
199425bb815Sopenharmony_ci    assert(false);
200425bb815Sopenharmony_ci  } catch (e) {
201425bb815Sopenharmony_ci    assert (e instanceof TypeError);
202425bb815Sopenharmony_ci  }
203425bb815Sopenharmony_ci
204425bb815Sopenharmony_ci  // Ensure iterator is only accessed once, and only invoked once
205425bb815Sopenharmony_ci  var called = false;
206425bb815Sopenharmony_ci  var arr = [1, 2, 3];
207425bb815Sopenharmony_ci  var obj = {};
208425bb815Sopenharmony_ci
209425bb815Sopenharmony_ci  // Test order --- only get iterator method once
210425bb815Sopenharmony_ci  function testIterator() {
211425bb815Sopenharmony_ci    assert(called !== "@@iterator should be called only once");
212425bb815Sopenharmony_ci    called = true;
213425bb815Sopenharmony_ci    assert(obj === this);
214425bb815Sopenharmony_ci    return arr[Symbol.iterator]();
215425bb815Sopenharmony_ci  }
216425bb815Sopenharmony_ci  var getCalled = false;
217425bb815Sopenharmony_ci  Object.defineProperty(obj, Symbol.iterator, {
218425bb815Sopenharmony_ci    get: function() {
219425bb815Sopenharmony_ci      assert(getCalled !== "@@iterator should be accessed only once");
220425bb815Sopenharmony_ci      getCalled = true;
221425bb815Sopenharmony_ci      return testIterator;
222425bb815Sopenharmony_ci    },
223425bb815Sopenharmony_ci    set: function() {
224425bb815Sopenharmony_ci      // "@@iterator should not be set"
225425bb815Sopenharmony_ci      assert(false);
226425bb815Sopenharmony_ci    }
227425bb815Sopenharmony_ci  });
228425bb815Sopenharmony_ci  assertArrayLikeEquals(Array.from.call(thisArg, obj), [1, 2, 3], constructor);
229425bb815Sopenharmony_ci}
230425bb815Sopenharmony_ci
231425bb815Sopenharmony_cifunction Other() {}
232425bb815Sopenharmony_ci
233425bb815Sopenharmony_civar boundFn = (function() {}).bind(Array, 27);
234425bb815Sopenharmony_ci
235425bb815Sopenharmony_citestArrayFrom(Array, Array);
236425bb815Sopenharmony_citestArrayFrom(null, Array);
237425bb815Sopenharmony_citestArrayFrom({}, Array);
238425bb815Sopenharmony_citestArrayFrom(Object, Object);
239425bb815Sopenharmony_citestArrayFrom(Other, Other);
240425bb815Sopenharmony_citestArrayFrom(Math.cos, Array);
241425bb815Sopenharmony_citestArrayFrom(boundFn, boundFn);
242425bb815Sopenharmony_ci
243425bb815Sopenharmony_ci// Assert that [[DefineOwnProperty]] is used in ArrayFrom, meaning a
244425bb815Sopenharmony_ci// setter isn't called, and a failed [[DefineOwnProperty]] will throw.
245425bb815Sopenharmony_civar setterCalled = 0;
246425bb815Sopenharmony_cifunction exotic() {
247425bb815Sopenharmony_ci  Object.defineProperty(this,  '0', {
248425bb815Sopenharmony_ci    get: function() { return 2; },
249425bb815Sopenharmony_ci    set: function() { setterCalled++; }
250425bb815Sopenharmony_ci  });
251425bb815Sopenharmony_ci}
252425bb815Sopenharmony_ci
253425bb815Sopenharmony_ci// Non-configurable properties can't be overwritten with DefineOwnProperty
254425bb815Sopenharmony_ci// The setter wasn't called
255425bb815Sopenharmony_citry {
256425bb815Sopenharmony_ci  Array.from.call(exotic, [1]);
257425bb815Sopenharmony_ci  assert(false);
258425bb815Sopenharmony_ci} catch (e) {
259425bb815Sopenharmony_ci  assert (e instanceof TypeError);
260425bb815Sopenharmony_ci}
261425bb815Sopenharmony_ci
262425bb815Sopenharmony_ciassert( 0 === setterCalled);
263425bb815Sopenharmony_ci
264425bb815Sopenharmony_ci// Non-callable iterators should cause a TypeError before calling the target
265425bb815Sopenharmony_ci// constructor.
266425bb815Sopenharmony_ciitems = {};
267425bb815Sopenharmony_ciitems[Symbol.iterator] = 7;
268425bb815Sopenharmony_cifunction TestError() {}
269425bb815Sopenharmony_cifunction ArrayLike() { throw new TestError() }
270425bb815Sopenharmony_ci
271425bb815Sopenharmony_citry {
272425bb815Sopenharmony_ci  Array.from.call(ArrayLike, items);
273425bb815Sopenharmony_ci  assert(false);
274425bb815Sopenharmony_ci} catch (e) {
275425bb815Sopenharmony_ci  assert (e instanceof TypeError);
276425bb815Sopenharmony_ci}
277425bb815Sopenharmony_ci
278425bb815Sopenharmony_ci// Check that array properties defined are writable, enumerable, configurable
279425bb815Sopenharmony_cifunction ordinary() { }
280425bb815Sopenharmony_civar x = Array.from.call(ordinary, [2]);
281425bb815Sopenharmony_civar xlength = Object.getOwnPropertyDescriptor(x, 'length');
282425bb815Sopenharmony_ciassert(1 === xlength.value);
283425bb815Sopenharmony_ciassert(true === xlength.writable);
284425bb815Sopenharmony_ciassert(true === xlength.enumerable);
285425bb815Sopenharmony_ciassert(true === xlength.configurable);
286425bb815Sopenharmony_civar x0 = Object.getOwnPropertyDescriptor(x, 0);
287425bb815Sopenharmony_ciassert(2 === x0.value);
288425bb815Sopenharmony_ciassert(true === xlength.writable);
289425bb815Sopenharmony_ciassert(true === xlength.enumerable);
290425bb815Sopenharmony_ciassert(true === xlength.configurable);
291425bb815Sopenharmony_ci
292425bb815Sopenharmony_ci/* Test iterator close */
293425bb815Sopenharmony_cifunction __createIterableObject (arr, methods) {
294425bb815Sopenharmony_ci  methods = methods || {};
295425bb815Sopenharmony_ci  if (typeof Symbol !== 'function' || !Symbol.iterator) {
296425bb815Sopenharmony_ci    return {};
297425bb815Sopenharmony_ci  }
298425bb815Sopenharmony_ci  arr.length++;
299425bb815Sopenharmony_ci  var iterator = {
300425bb815Sopenharmony_ci    next: function() {
301425bb815Sopenharmony_ci      return { value: arr.shift(), done: arr.length <= 0 };
302425bb815Sopenharmony_ci    },
303425bb815Sopenharmony_ci    'return': methods['return'],
304425bb815Sopenharmony_ci    'throw': methods['throw']
305425bb815Sopenharmony_ci  };
306425bb815Sopenharmony_ci  var iterable = {};
307425bb815Sopenharmony_ci  iterable[Symbol.iterator] = function () { return iterator; };
308425bb815Sopenharmony_ci  return iterable;
309425bb815Sopenharmony_ci};
310425bb815Sopenharmony_ci
311425bb815Sopenharmony_cifunction close1() {
312425bb815Sopenharmony_ci  var closed = false;
313425bb815Sopenharmony_ci  var iter = __createIterableObject([1, 2, 3], {
314425bb815Sopenharmony_ci      'return': function() { closed = true; return {}; }
315425bb815Sopenharmony_ci  });
316425bb815Sopenharmony_ci
317425bb815Sopenharmony_ci  try {
318425bb815Sopenharmony_ci    Array.from(iter, x => { throw 5 });
319425bb815Sopenharmony_ci    assert(false);
320425bb815Sopenharmony_ci  } catch (e) {
321425bb815Sopenharmony_ci    assert(e === 5);
322425bb815Sopenharmony_ci  }
323425bb815Sopenharmony_ci
324425bb815Sopenharmony_ci  return closed;
325425bb815Sopenharmony_ci}
326425bb815Sopenharmony_ciassert(close1());
327425bb815Sopenharmony_ci
328425bb815Sopenharmony_cifunction close2() {
329425bb815Sopenharmony_ci  var closed = false;
330425bb815Sopenharmony_ci  var iter = __createIterableObject([1, 2, 3], {
331425bb815Sopenharmony_ci      'return': function() { closed = true; throw 6 }
332425bb815Sopenharmony_ci  });
333425bb815Sopenharmony_ci
334425bb815Sopenharmony_ci  try {
335425bb815Sopenharmony_ci    Array.from(iter, x => { throw 5 });
336425bb815Sopenharmony_ci    assert(false);
337425bb815Sopenharmony_ci  } catch (e) {
338425bb815Sopenharmony_ci    assert(e === 5);
339425bb815Sopenharmony_ci  }
340425bb815Sopenharmony_ci
341425bb815Sopenharmony_ci  return closed;
342425bb815Sopenharmony_ci}
343425bb815Sopenharmony_ciassert(close2());
344425bb815Sopenharmony_ci
345