blob: e02b570084b7ff3d6916006dec2185f8fe745727 [file] [log] [blame]
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-rab-gsab --allow-natives-syntax --harmony-array-find-last
"use strict";
d8.file.execute('test/mjsunit/typedarray-helpers.js');
(function ConstructorThrowsIfBufferDetached() {
const rab = CreateResizableArrayBuffer(40, 80);
%ArrayBufferDetach(rab);
for (let ctor of ctors) {
assertThrows(() => { ctor(rab); }, TypeError);
assertThrows(() => { ctor(rab, 8); }, TypeError);
assertThrows(() => { ctor(rab, 8, 1); }, TypeError);
}
})();
(function TypedArrayLengthAndByteLength() {
const rab = CreateResizableArrayBuffer(40, 80);
let tas = [];
for (let ctor of ctors) {
tas.push(new ctor(rab, 0, 3));
tas.push(new ctor(rab, 8, 3));
tas.push(new ctor(rab));
tas.push(new ctor(rab, 8));
}
%ArrayBufferDetach(rab);
for (let ta of tas) {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
})();
(function AccessDetachedTypedArray() {
const rab = CreateResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
// Initial values
for (let i = 0; i < 4; ++i) {
assertEquals(0, i8a[i]);
}
// Within-bounds write
for (let i = 0; i < 4; ++i) {
i8a[i] = i;
}
%ArrayBufferDetach(rab);
// OOB read
for (let i = 0; i < 4; ++i) {
assertEquals(undefined, i8a[i]);
}
// OOB write (has no effect)
for (let i = 0; i < 4; ++i) {
i8a[i] = 10;
}
for (let i = 0; i < 4; ++i) {
assertEquals(undefined, i8a[i]);
}
})();
(function LoadFromOutOfScopeTypedArrayWithFeedback() {
function ReadElement2(ta) {
return ta[2];
}
%EnsureFeedbackVectorForFunction(ReadElement2);
const rab = CreateResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
assertEquals(0, ReadElement2(i8a));
// Within-bounds write
for (let i = 0; i < 4; ++i) {
i8a[i] = i;
}
%ArrayBufferDetach(rab);
// OOB read
for (let i = 0; i < 3; ++i) {
assertEquals(undefined, ReadElement2(i8a));
}
})();
(function StoreToOutOfScopeTypedArrayWithFeedback() {
function WriteElement2(ta, i) {
ta[2] = i;
}
%EnsureFeedbackVectorForFunction(WriteElement2);
const rab = CreateResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
assertEquals(0, i8a[2]);
// Within-bounds write
for (let i = 0; i < 3; ++i) {
WriteElement2(i8a, 3);
}
%ArrayBufferDetach(rab);
// OOB write (has no effect)
for (let i = 0; i < 3; ++i) {
WriteElement2(i8a, 4);
}
// OOB read
for (let i = 0; i < 3; ++i) {
assertEquals(undefined, i8a[2]);
}
})();
(function FillParameterConversionDetaches() {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
let evil = { valueOf: () => { %ArrayBufferDetach(rab); return 1;}};
// The length is read after converting the first parameter ('value'), so the
// detaching parameter has to be the 2nd ('start') or 3rd ('end').
assertThrows(function() {
FillHelper(fixedLength, 1, 0, evil);
}, TypeError);
}
})();
(function CopyWithinParameterConversionDetaches() {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
let evil = { valueOf: () => { %ArrayBufferDetach(rab); return 2;}};
assertThrows(function() {
fixedLength.copyWithin(evil, 0, 1);
}, TypeError);
}
})();
(function EveryDetachMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values = [];
let rab;
let detachAfter;
function CollectValuesAndDetach(n) {
if (n == undefined) {
values.push(n);
} else {
values.push(Number(n));
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
return true;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
values = [];
detachAfter = 2;
assertTrue(fixedLength.every(CollectValuesAndDetach));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertTrue(fixedLengthWithOffset.every(CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertTrue(lengthTracking.every(CollectValuesAndDetach));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertTrue(lengthTrackingWithOffset.every(CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
})();
(function SomeDetachMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values;
let rab;
let detachAfter;
function CollectValuesAndDetach(n) {
if (n == undefined) {
values.push(n);
} else {
values.push(Number(n));
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
return false;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
values = [];
detachAfter = 2;
assertFalse(fixedLength.some(CollectValuesAndDetach));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertFalse(fixedLengthWithOffset.some(CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertFalse(lengthTracking.some(CollectValuesAndDetach));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertFalse(lengthTrackingWithOffset.some(CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
})();
(function FindDetachMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values;
let rab;
let detachAfter;
function CollectValuesAndDetach(n) {
if (n == undefined) {
values.push(n);
} else {
values.push(Number(n));
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
return false;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
values = [];
detachAfter = 2;
assertEquals(undefined, fixedLength.find(CollectValuesAndDetach));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertEquals(undefined, fixedLengthWithOffset.find(CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals(undefined, lengthTracking.find(CollectValuesAndDetach));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertEquals(undefined, lengthTrackingWithOffset.find(CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
})();
(function FindIndexDetachMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values;
let rab;
let detachAfter;
function CollectValuesAndDetach(n) {
if (n == undefined) {
values.push(n);
} else {
values.push(Number(n));
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
return false;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
values = [];
detachAfter = 2;
assertEquals(-1, fixedLength.findIndex(CollectValuesAndDetach));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertEquals(-1, fixedLengthWithOffset.findIndex(CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals(-1, lengthTracking.findIndex(CollectValuesAndDetach));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertEquals(-1, lengthTrackingWithOffset.findIndex(CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
})();
(function FindLastDetachMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values;
let rab;
let detachAfter;
function CollectValuesAndDetach(n) {
if (n == undefined) {
values.push(n);
} else {
values.push(Number(n));
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
return false;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
values = [];
detachAfter = 2;
assertEquals(undefined, fixedLength.findLast(CollectValuesAndDetach));
assertEquals([6, 4, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertEquals(undefined, fixedLengthWithOffset.findLast(CollectValuesAndDetach));
assertEquals([6, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals(undefined, lengthTracking.findLast(CollectValuesAndDetach));
assertEquals([6, 4, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertEquals(undefined, lengthTrackingWithOffset.findLast(CollectValuesAndDetach));
assertEquals([6, undefined], values);
}
})();
(function FindLastIndexDetachMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values;
let rab;
let detachAfter;
function CollectValuesAndDetach(n) {
if (n == undefined) {
values.push(n);
} else {
values.push(Number(n));
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
return false;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
values = [];
detachAfter = 2;
assertEquals(-1, fixedLength.findLastIndex(CollectValuesAndDetach));
assertEquals([6, 4, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertEquals(-1, fixedLengthWithOffset.findLastIndex(CollectValuesAndDetach));
assertEquals([6, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals(-1, lengthTracking.findLastIndex(CollectValuesAndDetach));
assertEquals([6, 4, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertEquals(-1, lengthTrackingWithOffset.findLastIndex(CollectValuesAndDetach));
assertEquals([6, undefined], values);
}
})();
(function FilterShrinkMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values;
let rab;
let detachAfter;
function CollectValuesAndDetach(n) {
if (n == undefined) {
values.push(n);
} else {
values.push(Number(n));
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
return false;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
values = [];
detachAfter = 2;
assertEquals([], ToNumbers(fixedLength.filter(CollectValuesAndDetach)));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertEquals([], ToNumbers(fixedLengthWithOffset.filter(CollectValuesAndDetach)));
assertEquals([4, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals([], ToNumbers(lengthTracking.filter(CollectValuesAndDetach)));
assertEquals([0, 2, undefined, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertEquals([], ToNumbers(lengthTrackingWithOffset.filter(CollectValuesAndDetach)));
assertEquals([4, undefined], values);
}
})();
(function ForEachReduceReduceRightDetachMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values;
let rab;
let detachAfter;
function CollectValuesAndDetach(n) {
if (typeof n == 'bigint') {
values.push(Number(n));
} else {
values.push(n);
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
return true;
}
function ForEachHelper(array) {
values = [];
array.forEach(CollectValuesAndDetach);
return values;
}
function ReduceHelper(array) {
values = [];
array.reduce((acc, n) => { CollectValuesAndDetach(n); }, "initial value");
return values;
}
function ReduceRightHelper(array) {
values = [];
array.reduceRight((acc, n) => { CollectValuesAndDetach(n); },
"initial value");
return values;
}
// Test for forEach.
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
detachAfter = 2;
assertEquals([0, 2, undefined, undefined], ForEachHelper(fixedLength));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
detachAfter = 1;
assertEquals([4, undefined], ForEachHelper(fixedLengthWithOffset));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
detachAfter = 2;
assertEquals([0, 2, undefined, undefined], ForEachHelper(lengthTracking));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
detachAfter = 1;
assertEquals([4, undefined], ForEachHelper(lengthTrackingWithOffset));
}
// Tests for reduce.
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
detachAfter = 2;
assertEquals([0, 2, undefined, undefined], ReduceHelper(fixedLength));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
detachAfter = 1;
assertEquals([4, undefined], ReduceHelper(fixedLengthWithOffset));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
detachAfter = 2;
assertEquals([0, 2, undefined, undefined], ReduceHelper(lengthTracking));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
detachAfter = 1;
assertEquals([4, undefined], ReduceHelper(lengthTrackingWithOffset));
}
// Tests for reduceRight.
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
detachAfter = 2;
assertEquals([6, 4, undefined, undefined], ReduceRightHelper(fixedLength));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
detachAfter = 1;
assertEquals([6, undefined], ReduceRightHelper(fixedLengthWithOffset));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
detachAfter = 2;
assertEquals([6, 4, undefined, undefined], ReduceRightHelper(lengthTracking));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
detachAfter = 1;
assertEquals([6, undefined], ReduceRightHelper(lengthTrackingWithOffset));
}
})();
(function IncludesParameterConversionDetaches() {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertFalse(IncludesHelper(fixedLength, undefined));
// The TA is detached so it includes only "undefined".
assertTrue(IncludesHelper(fixedLength, undefined, evil));
}
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertTrue(IncludesHelper(fixedLength, 0));
// The TA is detached so it includes only "undefined".
assertFalse(IncludesHelper(fixedLength, 0, evil));
}
})();
(function IndexOfParameterConversionDetaches() {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertEquals(0, IndexOfHelper(lengthTracking, 0));
// The buffer is detached so indexOf returns -1.
assertEquals(-1, IndexOfHelper(lengthTracking, 0, evil));
}
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertEquals(0, IndexOfHelper(lengthTracking, 0));
// The buffer is detached so indexOf returns -1, also for undefined).
assertEquals(-1, IndexOfHelper(lengthTracking, undefined, evil));
}
})();
(function LastIndexOfParameterConversionDetaches() {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 2;
}};
assertEquals(3, LastIndexOfHelper(lengthTracking, 0));
// The buffer is detached so lastIndexOf returns -1.
assertEquals(-1, LastIndexOfHelper(lengthTracking, 0, evil));
}
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 2;
}};
assertEquals(3, LastIndexOfHelper(lengthTracking, 0));
// The buffer is detached so lastIndexOf returns -1, also for undefined).
assertEquals(-1, LastIndexOfHelper(lengthTracking, undefined, evil));
}
})();
(function JoinToLocaleString() {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new ctor(rab, 0);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
%ArrayBufferDetach(rab);
assertThrows(() => { fixedLength.join(); });
assertThrows(() => { fixedLength.toLocaleString(); });
assertThrows(() => { fixedLengthWithOffset.join(); });
assertThrows(() => { fixedLengthWithOffset.toLocaleString(); });
assertThrows(() => { lengthTracking.join(); });
assertThrows(() => { lengthTracking.toLocaleString(); });
assertThrows(() => { lengthTrackingWithOffset.join(); });
assertThrows(() => { lengthTrackingWithOffset.toLocaleString(); });
}
})();
(function JoinParameterConversionDetaches() {
// Detaching + fixed-length TA.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
let evil = { toString: () => {
%ArrayBufferDetach(rab);
return '.';
}};
// We iterate 4 elements, since it was the starting length, but the TA is
// OOB right after parameter conversion, so all elements are converted to
// the empty string.
assertEquals('...', fixedLength.join(evil));
}
// Detaching + length-tracking TA.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
let evil = { toString: () => {
%ArrayBufferDetach(rab);
return '.';
}};
// We iterate 4 elements, since it was the starting length, but the TA is
// OOB right after parameter conversion, so all elements are converted to
// the empty string.
assertEquals('...', lengthTracking.join(evil));
}
})();
(function ToLocaleStringNumberPrototypeToLocaleStringDetaches() {
const oldNumberPrototypeToLocaleString = Number.prototype.toLocaleString;
const oldBigIntPrototypeToLocaleString = BigInt.prototype.toLocaleString;
// Detaching + fixed-length TA.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
let detachAfter = 2;
Number.prototype.toLocaleString = function() {
--detachAfter;
if (detachAfter == 0) {
%ArrayBufferDetach(rab);
}
return oldNumberPrototypeToLocaleString.call(this);
}
BigInt.prototype.toLocaleString = function() {
--detachAfter;
if (detachAfter == 0) {
%ArrayBufferDetach(rab);
}
return oldBigIntPrototypeToLocaleString.call(this);
}
// We iterate 4 elements, since it was the starting length. The TA goes
// OOB after 2 elements.
assertEquals('0,0,,', fixedLength.toLocaleString());
}
// Detaching + length-tracking TA.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
let detachAfter = 2;
Number.prototype.toLocaleString = function() {
--detachAfter;
if (detachAfter == 0) {
%ArrayBufferDetach(rab);
}
return oldNumberPrototypeToLocaleString.call(this);
}
BigInt.prototype.toLocaleString = function() {
--detachAfter;
if (detachAfter == 0) {
%ArrayBufferDetach(rab);
}
return oldBigIntPrototypeToLocaleString.call(this);
}
// We iterate 4 elements, since it was the starting length. The TA goes
// OOB after 2 elements.
assertEquals('0,0,,', lengthTracking.toLocaleString());
}
Number.prototype.toLocaleString = oldNumberPrototypeToLocaleString;
BigInt.prototype.toLocaleString = oldBigIntPrototypeToLocaleString;
})();
(function MapDetachMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let values;
let rab;
let detachAfter;
function CollectValuesAndDetach(n, ix, ta) {
if (typeof n == 'bigint') {
values.push(Number(n));
} else {
values.push(n);
}
if (values.length == detachAfter) {
%ArrayBufferDetach(rab);
}
// We still need to return a valid BigInt / non-BigInt, even if
// n is `undefined`.
if (IsBigIntTypedArray(ta)) {
return 0n;
} else {
return 0;
}
}
function Helper(array) {
values = [];
array.map(CollectValuesAndDetach);
return values;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
detachAfter = 2;
assertEquals([0, 2, undefined, undefined], Helper(fixedLength));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
detachAfter = 1;
assertEquals([4, undefined], Helper(fixedLengthWithOffset));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
detachAfter = 2;
assertEquals([0, 2, undefined, undefined], Helper(lengthTracking));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
detachAfter = 1;
assertEquals([4, undefined], Helper(lengthTrackingWithOffset));
}
})();
(function MapSpeciesCreateDetaches() {
let values;
let rab;
function CollectValues(n, ix, ta) {
if (typeof n == 'bigint') {
values.push(Number(n));
} else {
values.push(n);
}
// We still need to return a valid BigInt / non-BigInt, even if
// n is `undefined`.
if (IsBigIntTypedArray(ta)) {
return 0n;
}
return 0;
}
function Helper(array) {
values = [];
array.map(CollectValues);
return values;
}
for (let ctor of ctors) {
rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
let detachWhenConstructorCalled = false;
class MyArray extends ctor {
constructor(...params) {
super(...params);
if (detachWhenConstructorCalled) {
%ArrayBufferDetach(rab);
}
}
};
const fixedLength = new MyArray(rab, 0, 4);
detachWhenConstructorCalled = true;
assertEquals([undefined, undefined, undefined, undefined],
Helper(fixedLength));
}
for (let ctor of ctors) {
rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, i);
}
let detachWhenConstructorCalled = false;
class MyArray extends ctor {
constructor(...params) {
super(...params);
if (detachWhenConstructorCalled) {
%ArrayBufferDetach(rab);
}
}
};
const lengthTracking = new MyArray(rab);
detachWhenConstructorCalled = true;
assertEquals([undefined, undefined, undefined, undefined],
Helper(lengthTracking));
}
})();
(function SetSourceLengthGetterDetachesTarget() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let rab;
function CreateSourceProxy(length) {
return new Proxy({}, {
get(target, prop, receiver) {
if (prop == 'length') {
%ArrayBufferDetach(rab);
return length;
}
return true; // Can be converted to both BigInt and Number.
}
});
}
// Tests where the length getter detaches -> these are no-op.
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
fixedLength.set(CreateSourceProxy(1));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
fixedLengthWithOffset.set(CreateSourceProxy(1));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
lengthTracking.set(CreateSourceProxy(1));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
lengthTrackingWithOffset.set(CreateSourceProxy(1));
}
// Tests where the length getter returns a zero -> these don't throw.
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
fixedLength.set(CreateSourceProxy(0));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
fixedLengthWithOffset.set(CreateSourceProxy(0));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
lengthTracking.set(CreateSourceProxy(0));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
lengthTrackingWithOffset.set(CreateSourceProxy(0));
}
})();
(function SetDetachTargetMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let rab;
// Detaching will happen when we're calling Get for the `detachAt`:th data
// element, but we haven't yet written it to the target.
let detachAt;
function CreateSourceProxy(length) {
let requestedIndices = [];
return new Proxy({}, {
get(target, prop, receiver) {
if (prop == 'length') {
return length;
}
requestedIndices.push(prop);
if (requestedIndices.length == detachAt) {
%ArrayBufferDetach(rab);
}
return true; // Can be converted to both BigInt and Number.
}
});
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
detachAt = 2;
fixedLength.set(CreateSourceProxy(4));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
detachAt = 2;
fixedLengthWithOffset.set(CreateSourceProxy(2));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
detachAt = 2;
lengthTracking.set(CreateSourceProxy(2));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
detachAt = 2;
lengthTrackingWithOffset.set(CreateSourceProxy(2));
}
})();
(function Subarray() {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new ctor(rab, 0);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
const fixedLengthSubFull = fixedLength.subarray(0);
assertEquals([0, 2, 4, 6], ToNumbers(fixedLengthSubFull));
const fixedLengthWithOffsetSubFull = fixedLengthWithOffset.subarray(0);
assertEquals([4, 6], ToNumbers(fixedLengthWithOffsetSubFull));
const lengthTrackingSubFull = lengthTracking.subarray(0);
assertEquals([0, 2, 4, 6], ToNumbers(lengthTrackingSubFull));
const lengthTrackingWithOffsetSubFull =
lengthTrackingWithOffset.subarray(0);
assertEquals([4, 6], ToNumbers(lengthTrackingWithOffsetSubFull));
%ArrayBufferDetach(rab);
// The previously created subarrays are OOB.
assertEquals(0, fixedLengthSubFull.length);
assertEquals(0, fixedLengthWithOffsetSubFull.length);
assertEquals(0, lengthTrackingSubFull.length);
assertEquals(0, lengthTrackingWithOffsetSubFull.length);
// Trying to create new subarrays fails.
assertThrows(() => { fixedLength.subarray(0); }, TypeError);
assertThrows(() => { fixedLengthWithOffset.subarray(0); }, TypeError);
assertThrows(() => { lengthTracking.subarray(0); }, TypeError);
assertThrows(() => { lengthTrackingWithOffset.subarray(0); }, TypeError);
}
})();
(function SubarrayParameterConversionDetaches() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [0, 2, 4, 6, ...] << lengthTracking
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
// Fixed-length TA + first parameter conversion detaches. Can't construct
// even zero-length TAs with a detached buffer.
for (let ctor of ctors) {
const rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLength.subarray(evil, 0); }, TypeError);
}
// Length-tracking TA + first parameter conversion detaches. Can't construct
// even zero-length TAs with a detached buffer.
for (let ctor of ctors) {
const rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLength.subarray(evil, 0); }, TypeError);
}
// Fixed-length TA + second parameter conversion detaches. Can't construct
// even zero-length TAs with a detached buffer.
for (let ctor of ctors) {
const rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLength.subarray(0, evil); }, TypeError);
}
// Length-tracking TA + second parameter conversion detaches. Can't construct
// even zero-length TAs with a detached buffer.
for (let ctor of ctors) {
const rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLength.subarray(0, evil); }, TypeError);
}
})();
(function SortCallbackDetaches() {
function WriteUnsortedData(taFull) {
for (let i = 0; i < taFull.length; ++i) {
WriteToTypedArray(taFull, i, 10 - i);
}
}
let rab;
function CustomComparison(a, b) {
%ArrayBufferDetach(rab);
return 0;
}
function AssertIsDetached(ta) {
assertEquals(0, ta.byteLength);
assertEquals(0, ta.byteOffset);
assertEquals(0, ta.length);
}
// Fixed length TA.
for (let ctor of ctors) {
rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const taFull = new ctor(rab, 0);
WriteUnsortedData(taFull);
fixedLength.sort(CustomComparison);
AssertIsDetached(fixedLength);
}
// Length-tracking TA.
for (let ctor of ctors) {
rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab, 0);
const taFull = new ctor(rab, 0);
WriteUnsortedData(taFull);
lengthTracking.sort(CustomComparison);
AssertIsDetached(lengthTracking);
}
})();
(function ObjectDefineProperty() {
for (let helper of
[ObjectDefinePropertyHelper, ObjectDefinePropertiesHelper]) {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const fixedLengthWithOffset = new ctor(
rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new ctor(rab, 0);
const lengthTrackingWithOffset = new ctor(
rab, 2 * ctor.BYTES_PER_ELEMENT);
// Orig. array: [0, 0, 0, 0]
// [0, 0, 0, 0] << fixedLength
// [0, 0] << fixedLengthWithOffset
// [0, 0, 0, 0, ...] << lengthTracking
// [0, 0, ...] << lengthTrackingWithOffset
%ArrayBufferDetach(rab);
assertThrows(() => { helper(fixedLength, 0, 8); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 0, 8); }, TypeError);
assertThrows(() => { helper(lengthTracking, 0, 8); }, TypeError);
assertThrows(() => { helper(lengthTrackingWithOffset, 0, 8); },
TypeError);
}
}
})();
(function ObjectDefinePropertyParameterConversionDetaches() {
const helper = ObjectDefinePropertyHelper;
// Fixed length.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const evil = {toString: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { helper(fixedLength, evil, 8); }, TypeError);
}
// Length tracking.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab, 0);
const evil = {toString: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { helper(lengthTracking, evil, 8); }, TypeError);
}
})();