Source code

Revision control

Copy as Markdown

Other Tools

const tests = [
[Int8Array, [9, 10, 11, 12, 13, 14, 15, 16]],
[Uint8Array, [9, 10, 11, 12, 13, 14, 15, 16]],
[Uint8ClampedArray, [9, 10, 11, 12, 13, 14, 15, 16]],
[Int16Array, [5, 6, 7, 8]],
[Uint16Array, [5, 6, 7, 8]],
[Int32Array, [3, 4]],
[Uint32Array, [3, 4]],
[Float32Array, [3, 4]],
[Float64Array, [2]],
];
let logs = [];
for (let [ctor, answer] of tests) {
let arr = new ctor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
let proxyProto = new Proxy({}, {
get(that, name) {
throw new Error("unexpected prop access");
}
});
class MyArrayBuffer extends ArrayBuffer {}
arr.buffer.constructor = new Proxy({}, {
get(that, name) {
if (name == Symbol.species) {
logs.push("get @@species");
let C = new Proxy(function(...args) {
logs.push("call ctor");
return new MyArrayBuffer(...args);
}, {
get(that, name) {
logs.push("get ctor." + String(name));
if (name == "prototype") {
return proxyProto;
}
throw new Error("unexpected prop access");
}
});
return C;
}
throw new Error("unexpected prop access");
}
});
logs.length = 0;
let buf = arr.buffer.slice(8, 16);
assertEq(buf.constructor, MyArrayBuffer);
assertDeepEq(logs, ["get @@species", "get ctor.prototype", "call ctor"]);
assertDeepEq([...new ctor(buf)], answer);
// modified @@species
let a = arr.buffer;
a.constructor = {
[Symbol.species]: MyArrayBuffer
};
let b = a.slice(8, 16);
assertEq(b.constructor, MyArrayBuffer);
assertDeepEq([...new ctor(b)], answer);
class MyArrayBufferWithSpecies extends ArrayBuffer {
get [Symbol.species]() {
return MyArrayBufferWithSpecies;
}
}
a = arr.buffer;
a.constructor = MyArrayBufferWithSpecies;
b = a.slice(8, 16);
assertEq(b.constructor, MyArrayBufferWithSpecies);
assertDeepEq([...new ctor(b)], answer);
// no @@species
a = arr.buffer;
a.constructor = {
[Symbol.species]: undefined
};
b = a.slice(8, 16);
assertEq(b.constructor, ArrayBuffer);
assertDeepEq([...new ctor(b)], answer);
a = arr.buffer;
a.constructor = {
[Symbol.species]: null
};
b = a.slice(8, 16);
assertEq(b.constructor, ArrayBuffer);
assertDeepEq([...new ctor(b)], answer);
// invalid @@species
for (let species of [0, 1.1, true, false, "a", /a/, Symbol.iterator, [], {}]) {
a = arr.buffer;
a.constructor = {
[Symbol.species]: species
};
assertThrowsInstanceOf(() => a.slice(8, 16), TypeError);
}
// undefined constructor
a = arr.buffer;
a.constructor = undefined;
b = a.slice(8, 16);
assertEq(b.constructor, ArrayBuffer);
assertDeepEq([...new ctor(b)], answer);
// invalid constructor
for (let ctor of [null, 0, 1.1, true, false, "a", Symbol.iterator]) {
a = arr.buffer;
a.constructor = ctor;
assertThrowsInstanceOf(() => a.slice(8, 16), TypeError);
}
// @@species from different global
let g = newGlobal();
g.eval("var MyArrayBuffer = class MyArrayBuffer extends ArrayBuffer {};");
a = arr.buffer;
a.constructor = {
[Symbol.species]: g.MyArrayBuffer
};
b = a.slice(8, 16);
assertEq(b.constructor, g.MyArrayBuffer);
assertDeepEq([...new ctor(b)], answer);
a = arr.buffer;
a.constructor = {
[Symbol.species]: g.ArrayBuffer
};
b = a.slice(8, 16);
assertEq(b.constructor, g.ArrayBuffer);
assertDeepEq([...new ctor(b)], answer);
// constructor from different global
g.eval(`
var MyArrayBufferWithSpecies = class MyArrayBufferWithSpecies extends ArrayBuffer {
get [Symbol.species]() {
return MyArrayBufferWithSpecies;
}
};
`);
a = arr.buffer;
a.constructor = g.MyArrayBufferWithSpecies;
b = a.slice(8, 16);
assertEq(b.constructor, g.MyArrayBufferWithSpecies);
assertDeepEq([...new ctor(b)], answer);
g.eval(`
var arr = new ${ctor.name}([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
var a = arr.buffer;
`);
b = ArrayBuffer.prototype.slice.call(g.a, 8, 16);
assertEq(b.constructor, g.ArrayBuffer);
assertDeepEq([...new ctor(b)], answer);
// running in different global
b = g.a.slice(8, 16);
assertEq(b.constructor, g.ArrayBuffer);
assertDeepEq([...new ctor(b)], answer);
// subclasses
// not-modified @@species
a = new MyArrayBuffer(16);
b = a.slice(8, 16);
assertEq(b.constructor, MyArrayBuffer);
// modified @@species
class MyArrayBuffer2 extends ArrayBuffer {
}
class MyArrayBuffer3 extends ArrayBuffer {
static get [Symbol.species]() {
return MyArrayBuffer2;
}
}
a = new MyArrayBuffer3(16);
b = a.slice(8, 16);
assertEq(b.constructor, MyArrayBuffer2);
}
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");