Source code

Revision control

Copy as Markdown

Other Tools

// Tests for detached ArrayBuffer checks in %TypedArray%.prototype.set(array|typedArray, offset).
function* createTypedArrays(lengths = [0, 1, 4, 4096]) {
for (let length of lengths) {
let buffer = new ArrayBuffer(length * Int32Array.BYTES_PER_ELEMENT);
let typedArray = new Int32Array(buffer);
yield {typedArray, buffer};
}
}
if (typeof detachArrayBuffer === "function") {
class ExpectedError extends Error {}
// No detached check on function entry.
for (let {typedArray, buffer} of createTypedArrays()) {
detachArrayBuffer(buffer);
assertThrowsInstanceOf(() => typedArray.set(null, {
valueOf() {
throw new ExpectedError();
}
}), ExpectedError);
}
// Check for detached buffer after calling ToInteger(offset). Test with:
// - valid offset,
// - too large offset,
// - and negative offset.
for (let [offset, error] of [[0, TypeError], [1000000, TypeError], [-1, RangeError]]) {
for (let source of [[], [0], new Int32Array(0), new Int32Array(1)]) {
for (let {typedArray, buffer} of createTypedArrays()) {
assertThrowsInstanceOf(() => typedArray.set(source, {
valueOf() {
detachArrayBuffer(buffer);
return offset;
}
}), error);
}
}
}
// Tests when called with detached typed array as source.
for (let {typedArray} of createTypedArrays()) {
for (let {typedArray: source, buffer: sourceBuffer} of createTypedArrays()) {
detachArrayBuffer(sourceBuffer);
assertThrowsInstanceOf(() => typedArray.set(source, {
valueOf() {
throw new ExpectedError();
}
}), ExpectedError);
}
}
// Check when detaching source buffer in ToInteger(offset). Test with:
// - valid offset,
// - too large offset,
// - and negative offset.
for (let [offset, error] of [[0, TypeError], [1000000, TypeError], [-1, RangeError]]) {
for (let {typedArray} of createTypedArrays()) {
for (let {typedArray: source, buffer: sourceBuffer} of createTypedArrays()) {
assertThrowsInstanceOf(() => typedArray.set(source, {
valueOf() {
detachArrayBuffer(sourceBuffer);
return offset;
}
}), error);
}
}
}
// Test when target and source use the same underlying buffer and
// ToInteger(offset) detaches the buffer. Test with:
// - same typed array,
// - different typed array, but same element type,
// - and different element type.
for (let src of [ta => ta, ta => new Int32Array(ta.buffer), ta => new Float32Array(ta.buffer)]) {
for (let {typedArray, buffer} of createTypedArrays()) {
let source = src(typedArray);
assertThrowsInstanceOf(() => typedArray.set(source, {
valueOf() {
detachArrayBuffer(buffer);
return 0;
}
}), TypeError);
}
}
// Test when Get(src, "length") detaches the buffer, but srcLength is 0.
// Also use different offsets to ensure bounds checks use the typed array's
// length value from before detaching the buffer.
for (let offset of [() => 0, ta => Math.min(1, ta.length), ta => Math.max(0, ta.length - 1)]) {
for (let {typedArray, buffer} of createTypedArrays()) {
let source = {
get length() {
detachArrayBuffer(buffer);
return 0;
}
};
typedArray.set(source, offset(typedArray));
}
}
// Test when ToLength(Get(src, "length")) detaches the buffer, but
// srcLength is 0. Also use different offsets to ensure bounds checks use
// the typed array's length value from before detaching the buffer.
for (let offset of [() => 0, ta => Math.min(1, ta.length), ta => Math.max(0, ta.length - 1)]) {
for (let {typedArray, buffer} of createTypedArrays()) {
let source = {
length: {
valueOf() {
detachArrayBuffer(buffer);
return 0;
}
}
};
typedArray.set(source, offset(typedArray));
}
}
// Test no TypeError is thrown when the typed array is detached and
// srcLength > 0.
for (let {typedArray, buffer} of createTypedArrays()) {
let source = {
length: {
valueOf() {
detachArrayBuffer(buffer);
return 1;
}
}
};
if (typedArray.length === 0) {
assertThrowsInstanceOf(() => typedArray.set(source), RangeError);
} else {
typedArray.set(source);
}
}
// Same as above, but with side-effect when executing Get(src, "0").
for (let {typedArray, buffer} of createTypedArrays()) {
let source = {
get 0() {
throw new ExpectedError();
},
length: {
valueOf() {
detachArrayBuffer(buffer);
return 1;
}
}
};
let err = typedArray.length === 0 ? RangeError : ExpectedError;
assertThrowsInstanceOf(() => typedArray.set(source), err);
}
// Same as above, but with side-effect when executing ToNumber(Get(src, "0")).
for (let {typedArray, buffer} of createTypedArrays()) {
let source = {
get 0() {
return {
valueOf() {
throw new ExpectedError();
}
};
},
length: {
valueOf() {
detachArrayBuffer(buffer);
return 1;
}
}
};
let err = typedArray.length === 0 ? RangeError : ExpectedError;
assertThrowsInstanceOf(() => typedArray.set(source), err);
}
// Side-effects when getting the source elements detach the buffer.
for (let {typedArray, buffer} of createTypedArrays()) {
let source = Object.defineProperties([], {
0: {
get() {
detachArrayBuffer(buffer);
return 1;
}
}
});
if (typedArray.length === 0) {
assertThrowsInstanceOf(() => typedArray.set(source), RangeError);
} else {
typedArray.set(source);
}
}
// Side-effects when getting the source elements detach the buffer. Also
// ensure other elements are accessed.
for (let {typedArray, buffer} of createTypedArrays()) {
let accessed = false;
let source = Object.defineProperties([], {
0: {
get() {
detachArrayBuffer(buffer);
return 1;
}
},
1: {
get() {
assertEq(accessed, false);
accessed = true;
return 2;
}
}
});
if (typedArray.length <= 1) {
assertThrowsInstanceOf(() => typedArray.set(source), RangeError);
} else {
assertEq(accessed, false);
typedArray.set(source);
assertEq(accessed, true);
}
}
// Side-effects when converting the source elements detach the buffer.
for (let {typedArray, buffer} of createTypedArrays()) {
let source = [{
valueOf() {
detachArrayBuffer(buffer);
return 1;
}
}];
if (typedArray.length === 0) {
assertThrowsInstanceOf(() => typedArray.set(source), RangeError);
} else {
typedArray.set(source);
}
}
// Side-effects when converting the source elements detach the buffer. Also
// ensure other elements are accessed.
for (let {typedArray, buffer} of createTypedArrays()) {
let accessed = false;
let source = [{
valueOf() {
detachArrayBuffer(buffer);
return 1;
}
}, {
valueOf() {
assertEq(accessed, false);
accessed = true;
return 2;
}
}];
if (typedArray.length <= 1) {
assertThrowsInstanceOf(() => typedArray.set(source), RangeError);
} else {
assertEq(accessed, false);
typedArray.set(source);
assertEq(accessed, true);
}
}
}
if (typeof reportCompare === "function")
reportCompare(true, true);