/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ // Array.from throws if the argument is undefined or null. assertThrowsInstanceOf(() => Array.from(), TypeError); assertThrowsInstanceOf(() => Array.from(undefined), TypeError); assertThrowsInstanceOf(() => Array.from(null), TypeError); // Array.from throws if an element can't be defined on the new object. function ObjectWithReadOnlyElement() { Object.defineProperty(this, "0", {value: null}); this.length = 0; } ObjectWithReadOnlyElement.from = Array.from; assertDeepEq(ObjectWithReadOnlyElement.from([]), new ObjectWithReadOnlyElement); assertThrowsInstanceOf(() => ObjectWithReadOnlyElement.from([1]), TypeError); // The same, but via preventExtensions. function InextensibleObject() { Object.preventExtensions(this); } InextensibleObject.from = Array.from; assertThrowsInstanceOf(() => InextensibleObject.from([1]), TypeError); // We will now test this property, that Array.from throws if the .length can't // be assigned, using several different kinds of object. var obj; function init(self) { obj = self; self[0] = self[1] = self[2] = self[3] = 0; } function testUnsettableLength(C, Exc) { if (Exc === undefined) Exc = TypeError; // the usual expected exception type C.from = Array.from; obj = null; assertThrowsInstanceOf(() => C.from([]), Exc); assertEq(obj instanceof C, true); for (var i = 0; i < 4; i++) assertEq(obj[0], 0); obj = null; assertThrowsInstanceOf(() => C.from([0, 10, 20, 30]), Exc); assertEq(obj instanceof C, true); for (var i = 0; i < 4; i++) assertEq(obj[i], i * 10); } // Array.from throws if the new object's .length can't be assigned because // there is no .length and the object is inextensible. function InextensibleObject4() { init(this); Object.preventExtensions(this); } testUnsettableLength(InextensibleObject4); // Array.from throws if the new object's .length can't be assigned because it's // read-only. function ObjectWithReadOnlyLength() { init(this); Object.defineProperty(this, "length", {configurable: true, writable: false, value: 4}); } testUnsettableLength(ObjectWithReadOnlyLength); // The same, but using a builtin type. Uint8Array.from = Array.from; assertThrowsInstanceOf(() => Uint8Array.from([]), TypeError); // Array.from throws if the new object's .length can't be assigned because it // inherits a readonly .length along the prototype chain. function ObjectWithInheritedReadOnlyLength() { init(this); } Object.defineProperty(ObjectWithInheritedReadOnlyLength.prototype, "length", {configurable: true, writable: false, value: 4}); testUnsettableLength(ObjectWithInheritedReadOnlyLength); // The same, but using an object with a .length getter but no setter. function ObjectWithGetterOnlyLength() { init(this); Object.defineProperty(this, "length", {configurable: true, get: () => 4}); } testUnsettableLength(ObjectWithGetterOnlyLength); // The same, but with a setter that throws. function ObjectWithThrowingLengthSetter() { init(this); Object.defineProperty(this, "length", { configurable: true, get: () => 4, set: () => { throw new RangeError("surprise!"); } }); } testUnsettableLength(ObjectWithThrowingLengthSetter, RangeError); // Array.from throws if mapfn is neither callable nor undefined. assertThrowsInstanceOf(() => Array.from([3, 4, 5], {}), TypeError); assertThrowsInstanceOf(() => Array.from([3, 4, 5], "also not a function"), TypeError); assertThrowsInstanceOf(() => Array.from([3, 4, 5], null), TypeError); // Even if the function would not have been called. assertThrowsInstanceOf(() => Array.from([], JSON), TypeError); // If mapfn is not undefined and not callable, the error happens before anything else. // Before calling the constructor, before touching the arrayLike. var log = ""; function C() { log += "C"; obj = this; } var p = new Proxy({}, { has: function () { log += "1"; }, get: function () { log += "2"; }, getOwnPropertyDescriptor: function () { log += "3"; } }); assertThrowsInstanceOf(() => Array.from.call(C, p, {}), TypeError); assertEq(log, ""); // If mapfn throws, the new object has already been created. var arrayish = { get length() { log += "l"; return 1; }, get 0() { log += "0"; return "q"; } }; log = ""; var exc = {surprise: "ponies"}; assertThrowsValue(() => Array.from.call(C, arrayish, () => { throw exc; }), exc); assertEq(log, "lC0"); assertEq(obj instanceof C, true); // It's a TypeError if the @@iterator property is a primitive (except null and undefined). for (var primitive of ["foo", 17, Symbol(), true]) { assertThrowsInstanceOf(() => Array.from({[Symbol.iterator] : primitive}), TypeError); } assertDeepEq(Array.from({[Symbol.iterator]: null}), []); assertDeepEq(Array.from({[Symbol.iterator]: undefined}), []); // It's a TypeError if the iterator's .next() method returns a primitive. for (var primitive of [undefined, null, 17]) { assertThrowsInstanceOf( () => Array.from({ [Symbol.iterator]() { return {next() { return primitive; }}; } }), TypeError); } if (typeof reportCompare === 'function') reportCompare(0, 0);