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