• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright JS Foundation and other contributors, http://js.foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Copyright 2014 the V8 project authors. All rights reserved.
16// Use of this source code is governed by a BSD-style license that can be
17// found in the LICENSE file.
18
19// Some methods are taken from v8/test/mjsunit/mjsunit.js
20
21function classOf(object) {
22  // Argument must not be null or undefined.
23  var string = Object.prototype.toString.call(object);
24  // String has format [object <ClassName>].
25  return string.substring(8, string.length - 1);
26}
27
28/**
29 * Compares two objects for key/value equality.
30 * Returns true if they are equal, false otherwise.
31 */
32function deepObjectEquals(a, b) {
33  var aProps = Object.keys(a);
34  aProps.sort();
35  var bProps = Object.keys(b);
36  bProps.sort();
37  if (!deepEquals(aProps, bProps)) {
38    return false;
39  }
40  for (var i = 0; i < aProps.length; i++) {
41    if (!deepEquals(a[aProps[i]], b[aProps[i]])) {
42      return false;
43    }
44  }
45  return true;
46}
47
48var assertInstanceof = function assertInstanceof(obj, type) {
49  if (!(obj instanceof type)) {
50    var actualTypeName = null;
51    var actualConstructor = Object.getPrototypeOf(obj).constructor;
52    if (typeof actualConstructor == "function") {
53      actualTypeName = actualConstructor.name || String(actualConstructor);
54    } print("Object <" + obj + "> is not an instance of <" + (type.name || type) + ">" + (actualTypeName ? " but of < " + actualTypeName + ">" : ""));
55  }
56};
57
58function deepEquals(a, b) {
59  if (a === b) {
60    if (a === 0) return (1 / a) === (1 / b);
61    return true;
62  }
63  if (typeof a != typeof b) return false;
64  if (typeof a == "number") return isNaN(a) && isNaN(b);
65  if (typeof a !== "object" && typeof a !== "function") return false;
66  var objectClass = classOf(a);
67  if (objectClass !== classOf(b)) return false;
68  if (objectClass === "RegExp") {
69    return (a.toString() === b.toString());
70  }
71  if (objectClass === "Function") return false;
72  if (objectClass === "Array") {
73    var elementCount = 0;
74    if (a.length != b.length) {
75      return false;
76    }
77    for (var i = 0; i < a.length; i++) {
78      if (!deepEquals(a[i], b[i]))
79      return false;
80    }
81    return true;
82  }
83  if (objectClass == "String" || objectClass == "Number" || objectClass == "Boolean" || objectClass == "Date") {
84    if (a.valueOf() !== b.valueOf()) return false;
85  }
86  return deepObjectEquals(a, b);
87}
88
89var assertEquals = function assertEquals(expected, found, name_opt) {
90  if (!deepEquals(found, expected)) {
91    assert(false);
92  }
93};
94
95function assertArrayLikeEquals(value, expected, type) {
96  assertInstanceof(value, type);
97  assert(expected.length === value.length);
98  for (var i=0; i<value.length; ++i) {
99    assertEquals(expected[i], value[i]);
100  }
101}
102
103assert(1 === Array.from.length);
104
105// Assert that constructor is called with "length" for array-like objects
106
107var myCollectionCalled = false;
108function MyCollection(length) {
109  myCollectionCalled = true;
110  assert(1 === arguments.length);
111
112  assert(5 === length);
113}
114
115Array.from.call(MyCollection, {length: 5});
116assert(myCollectionCalled === true);
117// Assert that calling mapfn with / without thisArg in sloppy and strict modes
118// works as expected.
119
120var global = this;
121function non_strict(){ assert(global === this); }
122function strict(){ "use strict"; assert(void 0 === this); }
123function strict_null(){ "use strict"; assert(null === this); }
124Array.from([1], non_strict);
125Array.from([1], non_strict, void 0);
126Array.from([1], strict);
127Array.from([1], strict, void 0);
128
129function testArrayFrom(thisArg, constructor) {
130  assertArrayLikeEquals(Array.from.call(thisArg, [], undefined), [], constructor);
131  assertArrayLikeEquals(Array.from.call(thisArg, NaN), [], constructor);
132  assertArrayLikeEquals(Array.from.call(thisArg, Infinity), [], constructor);
133  assertArrayLikeEquals(Array.from.call(thisArg, 10000000), [], constructor);
134  assertArrayLikeEquals(Array.from.call(thisArg, 'test'), ['t', 'e', 's', 't'], constructor);
135
136  assertArrayLikeEquals(Array.from.call(thisArg,
137    { length: 1, '0': { 'foo': 'bar' } }), [{'foo': 'bar'}], constructor);
138  assertArrayLikeEquals(Array.from.call(thisArg, { length: -1, '0': { 'foo': 'bar' } }), [], constructor);
139  assertArrayLikeEquals(Array.from.call(thisArg,
140    [ 'foo', 'bar', 'baz' ]), ['foo', 'bar', 'baz'], constructor);
141  var kSet = new Set(['foo', 'bar', 'baz']);
142  assertArrayLikeEquals(Array.from.call(thisArg, kSet), ['foo', 'bar', 'baz'], constructor);
143  var kMap = new Map(['foo', 'bar', 'baz'].entries());
144  assertArrayLikeEquals(Array.from.call(thisArg, kMap), [[0, 'foo'], [1, 'bar'], [2, 'baz']], constructor);
145  assertArrayLikeEquals(Array.from.call(thisArg, 'test', function(x) {
146    return this.filter(x);
147  }, {
148    filter: function(x) { return x.toUpperCase(); }
149  }), ['T', 'E', 'S', 'T'], constructor);
150  assertArrayLikeEquals(Array.from.call(thisArg, 'test', function(x) {
151    return x.toUpperCase();
152  }), ['T', 'E', 'S', 'T'], constructor);
153
154  try {
155    Array.from.call(thisArg, null);
156    assert(false);
157  } catch (e) {
158    assert (e instanceof TypeError);
159  }
160
161  try {
162    Array.from.call(thisArg, undefined);
163    assert(false);
164  } catch (e) {
165    assert (e instanceof TypeError);
166  }
167
168  try {
169    Array.from.call(thisArg, [], null);
170    assert(false);
171  } catch (e) {
172    assert (e instanceof TypeError);
173  }
174
175  try {
176    Array.from.call(thisArg, [], "noncallable");
177    assert(false);
178  } catch (e) {
179    assert (e instanceof TypeError);
180  }
181
182  var nullIterator = {};
183  nullIterator[Symbol.iterator] = null;
184  assertArrayLikeEquals(Array.from.call(thisArg, nullIterator), [],
185  constructor);
186
187  var nonObjIterator = {};
188  nonObjIterator[Symbol.iterator] = function() { return "nonObject"; };
189
190  try {
191    Array.from.call(thisArg, nonObjIterator);
192    assert(false);
193  } catch (e) {
194    assert (e instanceof TypeError);
195  }
196
197  try {
198    Array.from.call(thisArg, [], null);
199    assert(false);
200  } catch (e) {
201    assert (e instanceof TypeError);
202  }
203
204  // Ensure iterator is only accessed once, and only invoked once
205  var called = false;
206  var arr = [1, 2, 3];
207  var obj = {};
208
209  // Test order --- only get iterator method once
210  function testIterator() {
211    assert(called !== "@@iterator should be called only once");
212    called = true;
213    assert(obj === this);
214    return arr[Symbol.iterator]();
215  }
216  var getCalled = false;
217  Object.defineProperty(obj, Symbol.iterator, {
218    get: function() {
219      assert(getCalled !== "@@iterator should be accessed only once");
220      getCalled = true;
221      return testIterator;
222    },
223    set: function() {
224      // "@@iterator should not be set"
225      assert(false);
226    }
227  });
228  assertArrayLikeEquals(Array.from.call(thisArg, obj), [1, 2, 3], constructor);
229}
230
231function Other() {}
232
233var boundFn = (function() {}).bind(Array, 27);
234
235testArrayFrom(Array, Array);
236testArrayFrom(null, Array);
237testArrayFrom({}, Array);
238testArrayFrom(Object, Object);
239testArrayFrom(Other, Other);
240testArrayFrom(Math.cos, Array);
241testArrayFrom(boundFn, boundFn);
242
243// Assert that [[DefineOwnProperty]] is used in ArrayFrom, meaning a
244// setter isn't called, and a failed [[DefineOwnProperty]] will throw.
245var setterCalled = 0;
246function exotic() {
247  Object.defineProperty(this,  '0', {
248    get: function() { return 2; },
249    set: function() { setterCalled++; }
250  });
251}
252
253// Non-configurable properties can't be overwritten with DefineOwnProperty
254// The setter wasn't called
255try {
256  Array.from.call(exotic, [1]);
257  assert(false);
258} catch (e) {
259  assert (e instanceof TypeError);
260}
261
262assert( 0 === setterCalled);
263
264// Non-callable iterators should cause a TypeError before calling the target
265// constructor.
266items = {};
267items[Symbol.iterator] = 7;
268function TestError() {}
269function ArrayLike() { throw new TestError() }
270
271try {
272  Array.from.call(ArrayLike, items);
273  assert(false);
274} catch (e) {
275  assert (e instanceof TypeError);
276}
277
278// Check that array properties defined are writable, enumerable, configurable
279function ordinary() { }
280var x = Array.from.call(ordinary, [2]);
281var xlength = Object.getOwnPropertyDescriptor(x, 'length');
282assert(1 === xlength.value);
283assert(true === xlength.writable);
284assert(true === xlength.enumerable);
285assert(true === xlength.configurable);
286var x0 = Object.getOwnPropertyDescriptor(x, 0);
287assert(2 === x0.value);
288assert(true === xlength.writable);
289assert(true === xlength.enumerable);
290assert(true === xlength.configurable);
291
292/* Test iterator close */
293function __createIterableObject (arr, methods) {
294  methods = methods || {};
295  if (typeof Symbol !== 'function' || !Symbol.iterator) {
296    return {};
297  }
298  arr.length++;
299  var iterator = {
300    next: function() {
301      return { value: arr.shift(), done: arr.length <= 0 };
302    },
303    'return': methods['return'],
304    'throw': methods['throw']
305  };
306  var iterable = {};
307  iterable[Symbol.iterator] = function () { return iterator; };
308  return iterable;
309};
310
311function close1() {
312  var closed = false;
313  var iter = __createIterableObject([1, 2, 3], {
314      'return': function() { closed = true; return {}; }
315  });
316
317  try {
318    Array.from(iter, x => { throw 5 });
319    assert(false);
320  } catch (e) {
321    assert(e === 5);
322  }
323
324  return closed;
325}
326assert(close1());
327
328function close2() {
329  var closed = false;
330  var iter = __createIterableObject([1, 2, 3], {
331      'return': function() { closed = true; throw 6 }
332  });
333
334  try {
335    Array.from(iter, x => { throw 5 });
336    assert(false);
337  } catch (e) {
338    assert(e === 5);
339  }
340
341  return closed;
342}
343assert(close2());
344
345