// Copyright 2022 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 "use strict"; d8.file.execute('test/mjsunit/typedarray-helpers.js'); const notSharedErrorMessage = 'TypeError: [object Object] is not a shared typed array.'; (function AtomicsWait() { // Test that trying to wait on a non-shared ArrayBuffer fails, even // when done on a worker thread. const workerScript = function() { onmessage = function(msg) { const rab = new ArrayBuffer(100, {maxByteLength: 200}); const i32a = new Int32Array(rab, 0); try { Atomics.wait(i32a, 0, 0, 5000); postMessage('Didn\'t get an error'); } catch (e) { postMessage('Got error: ' + e); } }; } const worker = new Worker(workerScript, {type: 'function'}); worker.postMessage('start'); assertEquals('Got error: ' + notSharedErrorMessage, worker.getMessage()); worker.terminate(); })(); (function AtomicsWaitAsync() { // Test that trying to waitAsync on a non-shared ArrayBuffer fails. for (let ctor of [Int32Array, BigInt64Array, MyBigInt64Array]) { const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); const lengthTracking = new ctor(rab, 0); const initialValue = false; // Can be converted to both Number and BigInt. assertThrows(() => { Atomics.waitAsync(lengthTracking, 0, initialValue); }, TypeError); } })(); (function AtomicsWaitFailWithWrongArrayTypes() { const rab = CreateResizableArrayBuffer(400, 800); const i8a = new Int8Array(rab); const i16a = new Int16Array(rab); const ui8a = new Uint8Array(rab); const ui8ca = new Uint8ClampedArray(rab); const ui16a = new Uint16Array(rab); const ui32a = new Uint32Array(rab); const f32a = new Float32Array(rab); const f64a = new Float64Array(rab); const myui8 = new MyUint8Array(rab); const bui64 = new BigUint64Array(rab); [i8a, i16a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a, myui8, bui64].forEach( function(ta) { // Can be converted both to Number and BigInt. const exampleValue = false; assertThrows(() => { Atomics.wait(ta, 0, exampleValue); }, TypeError); assertThrows(() => { Atomics.notify(ta, 0, 1); }, TypeError); assertThrows(() => { Atomics.waitAsync(ta, 0, exampleValue); }, TypeError); }); })(); (function TestAtomics() { for (let ctor of intCtors) { 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); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); TestAtomicsOperations(fixedLength, 0); TestAtomicsOperations(fixedLengthWithOffset, 0); TestAtomicsOperations(lengthTracking, 0); TestAtomicsOperations(lengthTrackingWithOffset, 0); AssertAtomicsOperationsThrow(fixedLength, 4, RangeError); AssertAtomicsOperationsThrow(fixedLengthWithOffset, 2, RangeError); AssertAtomicsOperationsThrow(lengthTracking, 4, RangeError); AssertAtomicsOperationsThrow(lengthTrackingWithOffset, 2, RangeError); // Shrink so that fixed length TAs go out of bounds. rab.resize(3 * ctor.BYTES_PER_ELEMENT); AssertAtomicsOperationsThrow(fixedLength, 0, RangeError); AssertAtomicsOperationsThrow(fixedLengthWithOffset, 0, RangeError); TestAtomicsOperations(lengthTracking, 0); TestAtomicsOperations(lengthTrackingWithOffset, 0); // Shrink so that the TAs with offset go out of bounds. rab.resize(1 * ctor.BYTES_PER_ELEMENT); AssertAtomicsOperationsThrow(fixedLength, 0, RangeError); AssertAtomicsOperationsThrow(fixedLengthWithOffset, 0, RangeError); AssertAtomicsOperationsThrow(lengthTrackingWithOffset, 0, RangeError); TestAtomicsOperations(lengthTracking, 0); // Shrink to zero. rab.resize(0); AssertAtomicsOperationsThrow(fixedLength, 0, RangeError); AssertAtomicsOperationsThrow(fixedLengthWithOffset, 0, RangeError); AssertAtomicsOperationsThrow(lengthTracking, 0, RangeError); AssertAtomicsOperationsThrow(lengthTrackingWithOffset, 0, RangeError); // Grow so that all TAs are back in-bounds. rab.resize(6 * ctor.BYTES_PER_ELEMENT); TestAtomicsOperations(fixedLength, 0); TestAtomicsOperations(fixedLengthWithOffset, 0); TestAtomicsOperations(lengthTracking, 0); TestAtomicsOperations(lengthTrackingWithOffset, 0); AssertAtomicsOperationsThrow(fixedLength, 4, RangeError); AssertAtomicsOperationsThrow(fixedLengthWithOffset, 2, RangeError); TestAtomicsOperations(lengthTracking, 5); TestAtomicsOperations(lengthTrackingWithOffset, 3); } })(); (function AtomicsFailWithNonIntegerArray() { const rab = CreateResizableArrayBuffer(400, 800); const ui8ca = new Uint8ClampedArray(rab); const f32a = new Float32Array(rab); const f64a = new Float64Array(rab); const mf32a = new MyFloat32Array(rab); [ui8ca, f32a, f64a, mf32a].forEach((ta) => { AssertAtomicsOperationsThrow(ta, 0, TypeError); }); })(); const oneParameterFuncs = [(ta, index) => { Atomics.load(ta, index); }]; const twoParameterFuncs = [ (ta, index, value) => { Atomics.store(ta, index, value); }, (ta, index, value) => { Atomics.add(ta, index, value); }, (ta, index, value) => { Atomics.sub(ta, index, value); }, (ta, index, value) => { Atomics.and(ta, index, value); }, (ta, index, value) => { Atomics.or(ta, index, value); }, (ta, index, value) => { Atomics.xor(ta, index, value); }, (ta, index, value) => { Atomics.exchange(ta, index, value); }, ]; const threeParameterFuncs = [ (ta, index, value1, value2) => { Atomics.compareExchange(ta, index, value1, value2); } ]; (function TestAtomicsParameterConversionShrinks() { let rab; let resizeTo; const evilIndex = { valueOf: () => { rab.resize(resizeTo); return 2; }}; // false can be converted both to Number and BigInt. const evilValue = { valueOf: () => { rab.resize(resizeTo); return false; }}; for (let func of oneParameterFuncs) { // Fixed-length TA + first parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const fixedLength = new ctor(rab, 0, 4); assertThrows(() => { func(fixedLength, evilIndex); }, TypeError); } // Length tracking TA + first parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const lengthTracking = new ctor(rab); assertThrows(() => { func(lengthTracking, evilIndex); }, TypeError); } } for (let func of twoParameterFuncs) { // Fixed-length TA + first parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const fixedLength = new ctor(rab, 0, 4); const one = IsBigIntTypedArray(fixedLength) ? 1n : 1; assertThrows(() => { func(fixedLength, evilIndex, one); }, TypeError); } // Fixed-length TA + second parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const fixedLength = new ctor(rab, 0, 4); assertThrows(() => { func(fixedLength, 0, evilValue); }, TypeError); } // Length tracking TA + first parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const lengthTracking = new ctor(rab); const one = IsBigIntTypedArray(lengthTracking) ? 1n : 1; assertThrows(() => { func(lengthTracking, evilIndex, one); }, TypeError); } // Length tracking TA + second parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const lengthTracking = new ctor(rab); assertThrows(() => { func(lengthTracking, 2, evilValue); }, TypeError); } } for (let func of threeParameterFuncs) { // Fixed-length TA + first parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const fixedLength = new ctor(rab, 0, 4); const one = IsBigIntTypedArray(fixedLength) ? 1n : 1; assertThrows(() => { func(fixedLength, evilIndex, one, one); }, TypeError); } // Fixed-length TA + second parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const fixedLength = new ctor(rab, 0, 4); const one = IsBigIntTypedArray(fixedLength) ? 1n : 1; assertThrows(() => { func(fixedLength, 0, evilValue, one); }, TypeError); } // Fixed-length TA + third parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const fixedLength = new ctor(rab, 0, 4); const one = IsBigIntTypedArray(fixedLength) ? 1n : 1; assertThrows(() => { func(fixedLength, 0, one, evilValue); }, TypeError); } // Length tracking TA + first parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const lengthTracking = new ctor(rab); const one = IsBigIntTypedArray(lengthTracking) ? 1n : 1; assertThrows(() => { func(lengthTracking, evilIndex, one, one); }, TypeError); } // Length tracking TA + second parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const lengthTracking = new ctor(rab); const one = IsBigIntTypedArray(lengthTracking) ? 1n : 1; assertThrows(() => { func(lengthTracking, 2, evilValue, one); }, TypeError); } // Length tracking TA + third parameter resizes. for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); resizeTo = 2 * ctor.BYTES_PER_ELEMENT; const lengthTracking = new ctor(rab); const one = IsBigIntTypedArray(lengthTracking) ? 1n : 1; assertThrows(() => { func(lengthTracking, 2, one, evilValue); }, TypeError); } } })(); (function TestAtomicsParameterConversionDetaches() { let rab; const evilIndex = { valueOf: () => { %ArrayBufferDetach(rab); return 0; }}; // false can be converted both to Number and BigInt. const evilValue = { valueOf: () => { %ArrayBufferDetach(rab); return false; }}; for (let func of oneParameterFuncs) { for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); const fixedLength = new ctor(rab, 0, 4); assertThrows(() => { func(fixedLength, evilIndex); }, TypeError); } } for (let func of twoParameterFuncs) { for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); const fixedLength = new ctor(rab, 0, 4); const one = IsBigIntTypedArray(fixedLength) ? 1n : 1; assertThrows(() => { func(fixedLength, evilIndex, one); }, TypeError); } for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); const fixedLength = new ctor(rab, 0, 4); assertThrows(() => { func(fixedLength, 0, evilValue); }, TypeError); } } for (let func of threeParameterFuncs) { for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); const fixedLength = new ctor(rab, 0, 4); const one = IsBigIntTypedArray(fixedLength) ? 1n : 1; assertThrows( () => { func(fixedLength, evilIndex, one, one); }, TypeError); } for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); const fixedLength = new ctor(rab, 0, 4); const one = IsBigIntTypedArray(fixedLength) ? 1n : 1; assertThrows( () => { func(fixedLength, 0, evilValue, one); }, TypeError); } for (let ctor of intCtors) { rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); const fixedLength = new ctor(rab, 0, 4); const one = IsBigIntTypedArray(fixedLength) ? 1n : 1; assertThrows( () => { Atomics.compareExchange(fixedLength, 0, one, evilValue); }, TypeError); } } })();