blob: 9489d961e84bc68be14547cb82c804c67b23d67a [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
// Flags: --js-float16array
"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() {
TypedArrayFillHelper(fixedLength, 1, 0, evil);
}, TypeError);
}
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').
// Assert that this doesn't throw (since the buffer is detached, we cannot
// assert anything about the contents):
ArrayFillHelper(fixedLength, 1, 0, evil);
assertEquals(0, fixedLength.length);
}
})();
(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 EntriesKeysValues(entriesHelper, keysHelper, valuesHelper, oobThrows) {
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);
// TypedArray.prototype.{entries, keys, values} throw right away when
// called. Array.prototype.{entries, keys, values} don't throw, but when
// we try to iterate the returned ArrayIterator, that throws.
if (oobThrows) {
assertThrows(() => { entriesHelper(fixedLength); });
assertThrows(() => { valuesHelper(fixedLength); });
assertThrows(() => { keysHelper(fixedLength); });
assertThrows(() => { entriesHelper(fixedLengthWithOffset); });
assertThrows(() => { valuesHelper(fixedLengthWithOffset); });
assertThrows(() => { keysHelper(fixedLengthWithOffset); });
assertThrows(() => { entriesHelper(lengthTracking); });
assertThrows(() => { valuesHelper(lengthTracking); });
assertThrows(() => { keysHelper(lengthTracking); });
assertThrows(() => { entriesHelper(lengthTrackingWithOffset); });
assertThrows(() => { valuesHelper(lengthTrackingWithOffset); });
assertThrows(() => { keysHelper(lengthTrackingWithOffset); });
} else {
entriesHelper(fixedLength);
valuesHelper(fixedLength);
keysHelper(fixedLength);
entriesHelper(fixedLengthWithOffset);
valuesHelper(fixedLengthWithOffset);
keysHelper(fixedLengthWithOffset);
entriesHelper(lengthTracking);
valuesHelper(lengthTracking);
keysHelper(lengthTracking);
entriesHelper(lengthTrackingWithOffset);
valuesHelper(lengthTrackingWithOffset);
keysHelper(lengthTrackingWithOffset);
}
assertThrows(() => { Array.from(entriesHelper(fixedLength)); });
assertThrows(() => { Array.from(valuesHelper(fixedLength)); });
assertThrows(() => { Array.from(keysHelper(fixedLength)); });
assertThrows(() => { Array.from(entriesHelper(fixedLengthWithOffset)); });
assertThrows(() => { Array.from(valuesHelper(fixedLengthWithOffset)); });
assertThrows(() => { Array.from(keysHelper(fixedLengthWithOffset)); });
assertThrows(() => { Array.from(entriesHelper(lengthTracking)); });
assertThrows(() => { Array.from(valuesHelper(lengthTracking)); });
assertThrows(() => { Array.from(keysHelper(lengthTracking)); });
assertThrows(() => {
Array.from(entriesHelper(lengthTrackingWithOffset)); });
assertThrows(() => { Array.from(valuesHelper(lengthTrackingWithOffset)); });
assertThrows(() => { Array.from(keysHelper(lengthTrackingWithOffset)); });
}
}
EntriesKeysValues(
TypedArrayEntriesHelper, TypedArrayKeysHelper, TypedArrayValuesHelper, true);
EntriesKeysValues(
ArrayEntriesHelper, ArrayKeysHelper, ArrayValuesHelper, false);
function EveryDetachMidIteration(everyHelper, hasUndefined) {
// 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(everyHelper(fixedLength, CollectValuesAndDetach));
if (hasUndefined) {
assertEquals([0, 2, undefined, undefined], values);
} else {
assertEquals([0, 2], values);
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertTrue(everyHelper(fixedLengthWithOffset, CollectValuesAndDetach));
if (hasUndefined) {
assertEquals([4, undefined], values);
} else {
assertEquals([4], values);
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertTrue(everyHelper(lengthTracking, CollectValuesAndDetach));
if (hasUndefined) {
assertEquals([0, 2, undefined, undefined], values);
} else {
assertEquals([0, 2], values);
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertTrue(everyHelper(lengthTrackingWithOffset, CollectValuesAndDetach));
if (hasUndefined) {
assertEquals([4, undefined], values);
} else {
assertEquals([4], values);
}
}
}
EveryDetachMidIteration(TypedArrayEveryHelper, true);
EveryDetachMidIteration(ArrayEveryHelper, false);
function SomeDetachMidIteration(someHelper, hasUndefined) {
// 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(someHelper(fixedLength, CollectValuesAndDetach));
if (hasUndefined) {
assertEquals([0, 2, undefined, undefined], values);
} else {
assertEquals([0, 2], values);
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
values = [];
detachAfter = 1;
assertFalse(someHelper(fixedLengthWithOffset, CollectValuesAndDetach));
if (hasUndefined) {
assertEquals([4, undefined], values);
} else {
assertEquals([4], values);
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertFalse(someHelper(lengthTracking, CollectValuesAndDetach));
if (hasUndefined) {
assertEquals([0, 2, undefined, undefined], values);
} else {
assertEquals([0, 2], values);
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
values = [];
detachAfter = 1;
assertFalse(someHelper(lengthTrackingWithOffset, CollectValuesAndDetach));
if (hasUndefined) {
assertEquals([4, undefined], values);
} else {
assertEquals([4], values);
}
}
}
SomeDetachMidIteration(TypedArraySomeHelper, true);
SomeDetachMidIteration(ArraySomeHelper, false);
function FindDetachMidIteration(findHelper) {
// 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, findHelper(fixedLength, 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,
findHelper(fixedLengthWithOffset, CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals(undefined, findHelper(lengthTracking, 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,
findHelper(lengthTrackingWithOffset, CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
}
FindDetachMidIteration(TypedArrayFindHelper);
FindDetachMidIteration(ArrayFindHelper);
function FindIndexDetachMidIteration(findIndexHelper) {
// 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, findIndexHelper(fixedLength, 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,
findIndexHelper(fixedLengthWithOffset, CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals(-1, findIndexHelper(lengthTracking, 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,
findIndexHelper(lengthTrackingWithOffset, CollectValuesAndDetach));
assertEquals([4, undefined], values);
}
}
FindIndexDetachMidIteration(TypedArrayFindIndexHelper);
FindIndexDetachMidIteration(ArrayFindIndexHelper);
function FindLastDetachMidIteration(findLastHelper) {
// 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,
findLastHelper(fixedLength, 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,
findLastHelper(fixedLengthWithOffset, CollectValuesAndDetach));
assertEquals([6, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals(undefined,
findLastHelper(lengthTracking, 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,
findLastHelper(lengthTrackingWithOffset, CollectValuesAndDetach));
assertEquals([6, undefined], values);
}
}
FindLastDetachMidIteration(TypedArrayFindLastHelper);
FindLastDetachMidIteration(ArrayFindLastHelper);
function FindLastIndexDetachMidIteration(findLastIndexHelper) {
// 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, findLastIndexHelper(fixedLength, 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,
findLastIndexHelper(fixedLengthWithOffset, CollectValuesAndDetach));
assertEquals([6, undefined], values);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
values = [];
detachAfter = 2;
assertEquals(-1,
findLastIndexHelper(lengthTracking, 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,
findLastIndexHelper(lengthTrackingWithOffset, CollectValuesAndDetach));
assertEquals([6, undefined], values);
}
}
FindLastIndexDetachMidIteration(TypedArrayFindLastIndexHelper);
FindLastIndexDetachMidIteration(ArrayFindLastIndexHelper);
// The corresponding tests for Array.prototype.filter are in
// typedarray-resizablearraybuffer-array-methods.js.
(function FilterDetachMidIteration() {
// 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(includesHelper) {
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));
}
}
IncludesParameterConversionDetaches(TypedArrayIncludesHelper);
IncludesParameterConversionDetaches(ArrayIncludesHelper);
function IndexOfParameterConversionDetaches(indexOfHelper) {
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));
}
}
IndexOfParameterConversionDetaches(TypedArrayIndexOfHelper);
IndexOfParameterConversionDetaches(ArrayIndexOfHelper);
function LastIndexOfParameterConversionDetaches(lastIndexOfHelper) {
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));
}
}
LastIndexOfParameterConversionDetaches(TypedArrayLastIndexOfHelper);
LastIndexOfParameterConversionDetaches(ArrayLastIndexOfHelper);
(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 ArrayJoinToLocaleString() {
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);
assertEquals('', ArrayJoinHelper(fixedLength));
assertEquals('', ArrayToLocaleStringHelper(fixedLength));
assertEquals('', ArrayJoinHelper(fixedLengthWithOffset));
assertEquals('', ArrayToLocaleStringHelper(fixedLengthWithOffset));
assertEquals('', ArrayJoinHelper(lengthTracking));
assertEquals('', ArrayToLocaleStringHelper(lengthTracking));
assertEquals('', ArrayJoinHelper(lengthTrackingWithOffset));
assertEquals('', ArrayToLocaleStringHelper(lengthTrackingWithOffset));
}
})();
function JoinParameterConversionDetaches(joinHelper) {
// 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('...', joinHelper(fixedLength, 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('...', joinHelper(lengthTracking, evil));
}
}
JoinParameterConversionDetaches(TypedArrayJoinHelper);
JoinParameterConversionDetaches(ArrayJoinHelper);
function ToLocaleStringNumberPrototypeToLocaleStringDetaches(
toLocaleStringHelper) {
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,,', toLocaleStringHelper(fixedLength));
}
// 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,,', toLocaleStringHelper(lengthTracking));
}
Number.prototype.toLocaleString = oldNumberPrototypeToLocaleString;
BigInt.prototype.toLocaleString = oldBigIntPrototypeToLocaleString;
}
ToLocaleStringNumberPrototypeToLocaleStringDetaches(
TypedArrayToLocaleStringHelper);
ToLocaleStringNumberPrototypeToLocaleStringDetaches(ArrayToLocaleStringHelper);
function MapDetachMidIteration(mapHelper, hasUndefined) {
// 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 = [];
mapHelper(array, CollectValuesAndDetach);
return values;
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
detachAfter = 2;
if (hasUndefined) {
assertEquals([0, 2, undefined, undefined], Helper(fixedLength));
} else {
assertEquals([0, 2], Helper(fixedLength));
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
detachAfter = 1;
if (hasUndefined) {
assertEquals([4, undefined], Helper(fixedLengthWithOffset));
} else {
assertEquals([4], Helper(fixedLengthWithOffset));
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
detachAfter = 2;
if (hasUndefined) {
assertEquals([0, 2, undefined, undefined], Helper(lengthTracking));
} else {
assertEquals([0, 2], Helper(lengthTracking));
}
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
detachAfter = 1;
if (hasUndefined) {
assertEquals([4, undefined], Helper(lengthTrackingWithOffset));
} else {
assertEquals([4], Helper(lengthTrackingWithOffset));
}
}
}
MapDetachMidIteration(TypedArrayMapHelper, true);
MapDetachMidIteration(ArrayMapHelper, false);
(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(sortHelper) {
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);
sortHelper(fixedLength, 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);
sortHelper(lengthTracking, CustomComparison);
AssertIsDetached(lengthTracking);
}
}
SortCallbackDetaches(TypedArraySortHelper);
SortCallbackDetaches(ArraySortHelper);
(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);
}
})();
(function FunctionApply() {
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);
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, i);
}
function func(...args) {
return [...args];
}
assertEquals([0, 1, 2, 3], ToNumbers(func.apply(null, fixedLength)));
assertEquals([2, 3], ToNumbers(func.apply(null, fixedLengthWithOffset)));
assertEquals([0, 1, 2, 3], ToNumbers(func.apply(null, lengthTracking)));
assertEquals([2, 3], ToNumbers(func.apply(null, lengthTrackingWithOffset)));
%ArrayBufferDetach(rab);
assertEquals([], ToNumbers(func.apply(null, fixedLength)));
assertEquals([], ToNumbers(func.apply(null, fixedLengthWithOffset)));
assertEquals([], ToNumbers(func.apply(null, lengthTracking)));
assertEquals([], ToNumbers(func.apply(null, lengthTrackingWithOffset)));
}
})();
// The corresponding tests for Array.prototype.slice are in
// typedarray-resizablearraybuffer-array-methods.js.
(function SliceParameterConversionDetaches() {
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 = { valueOf: () => { rab.resize(2 * ctor.BYTES_PER_ELEMENT);
return 0; }};
assertThrows(() => { fixedLength.slice(evil); }, TypeError);
assertEquals(2 * ctor.BYTES_PER_ELEMENT, rab.byteLength);
}
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(lengthTracking, i, i + 1);
}
const evil = { valueOf: () => { rab.resize(2 * ctor.BYTES_PER_ELEMENT);
return 0; }};
assertEquals([1, 2, 0, 0], ToNumbers(lengthTracking.slice(evil)));
assertEquals(2 * ctor.BYTES_PER_ELEMENT, rab.byteLength);
}
})();
function AtParameterConversionDetaches(atHelper) {
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;}};
assertEquals(undefined, atHelper(fixedLength, 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 -1;}};
// The TypedArray is *not* out of bounds since it's length-tracking.
assertEquals(undefined, atHelper(lengthTracking, evil));
}
}
AtParameterConversionDetaches(TypedArrayAtHelper);
AtParameterConversionDetaches(ArrayAtHelper);
(function TypedArrayFrom() {
AllBigIntMatchedCtorCombinations((targetCtor, sourceCtor) => {
const rab = CreateResizableArrayBuffer(
4 * sourceCtor.BYTES_PER_ELEMENT,
8 * sourceCtor.BYTES_PER_ELEMENT);
const fixedLength = new sourceCtor(rab, 0, 4);
const fixedLengthWithOffset = new sourceCtor(
rab, 2 * sourceCtor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new sourceCtor(rab, 0);
const lengthTrackingWithOffset = new sourceCtor(
rab, 2 * sourceCtor.BYTES_PER_ELEMENT);
%ArrayBufferDetach(rab);
assertThrows(() => { targetCtor.from(fixedLength); }, TypeError);
assertThrows(() => { targetCtor.from(fixedLengthWithOffset); }, TypeError);
assertThrows(() => { targetCtor.from(lengthTracking); }, TypeError);
assertThrows(() => { targetCtor.from(lengthTrackingWithOffset); },
TypeError);
});
AllBigIntUnmatchedCtorCombinations((targetCtor, sourceCtor) => {
const rab = CreateResizableArrayBuffer(
4 * sourceCtor.BYTES_PER_ELEMENT,
8 * sourceCtor.BYTES_PER_ELEMENT);
const fixedLength = new sourceCtor(rab, 0, 4);
const fixedLengthWithOffset = new sourceCtor(
rab, 2 * sourceCtor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new sourceCtor(rab, 0);
const lengthTrackingWithOffset = new sourceCtor(
rab, 2 * sourceCtor.BYTES_PER_ELEMENT);
%ArrayBufferDetach(rab);
assertThrows(() => { targetCtor.from(fixedLength); }, TypeError);
assertThrows(() => { targetCtor.from(fixedLengthWithOffset); }, TypeError);
assertThrows(() => { targetCtor.from(lengthTracking); }, TypeError);
assertThrows(() => { targetCtor.from(lengthTrackingWithOffset); },
TypeError);
});
})();