| // 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); |
| } |
| })(); |