Source code
Revision control
Copy as Markdown
Other Tools
/* Any copyright is dedicated to the Public Domain.
// 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);