From 609d00f6c93895de09c60146ad93f8a9ced80641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 9 Jan 2025 17:49:06 +0100 Subject: [PATCH] Add tests for no-tear requirement for integer typed arrays Implementations may incorrectly call memcpy/memset (or similar functions) for fast copying of typed array contents. This can lead to teared reads or writes for integer typed arrays, which isn't allowed per spec. --- harness/tearing.js | 154 ++++++++++++++++++ .../TypedArray/prototype/set/no-tear-int16.js | 86 ++++++++++ .../TypedArray/prototype/set/no-tear-int32.js | 86 ++++++++++ .../prototype/slice/no-tear-int16.js | 78 +++++++++ .../prototype/slice/no-tear-int32.js | 78 +++++++++ .../prototype/sort/no-tear-int16.js | 97 +++++++++++ .../prototype/sort/no-tear-int32.js | 97 +++++++++++ .../prototype/toSorted/no-tear-int16.js | 73 +++++++++ .../prototype/toSorted/no-tear-int32.js | 73 +++++++++ .../prototype/with/no-tear-int16.js | 66 ++++++++ .../prototype/with/no-tear-int32.js | 66 ++++++++ .../ctors/typedarray-arg/no-tear-int16.js | 90 ++++++++++ .../ctors/typedarray-arg/no-tear-int32.js | 90 ++++++++++ 13 files changed, 1134 insertions(+) create mode 100644 harness/tearing.js create mode 100644 test/built-ins/TypedArray/prototype/set/no-tear-int16.js create mode 100644 test/built-ins/TypedArray/prototype/set/no-tear-int32.js create mode 100644 test/built-ins/TypedArray/prototype/slice/no-tear-int16.js create mode 100644 test/built-ins/TypedArray/prototype/slice/no-tear-int32.js create mode 100644 test/built-ins/TypedArray/prototype/sort/no-tear-int16.js create mode 100644 test/built-ins/TypedArray/prototype/sort/no-tear-int32.js create mode 100644 test/built-ins/TypedArray/prototype/toSorted/no-tear-int16.js create mode 100644 test/built-ins/TypedArray/prototype/toSorted/no-tear-int32.js create mode 100644 test/built-ins/TypedArray/prototype/with/no-tear-int16.js create mode 100644 test/built-ins/TypedArray/prototype/with/no-tear-int32.js create mode 100644 test/built-ins/TypedArrayConstructors/ctors/typedarray-arg/no-tear-int16.js create mode 100644 test/built-ins/TypedArrayConstructors/ctors/typedarray-arg/no-tear-int32.js diff --git a/harness/tearing.js b/harness/tearing.js new file mode 100644 index 00000000000..3c9665208e6 --- /dev/null +++ b/harness/tearing.js @@ -0,0 +1,154 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: > + Helper function for no-tear tests. +defines: + - testNoTear +---*/ + +/** + * @param TypedArray An integer TypedArray + * @param length Number of elements + * @param test Function to modify the array + * @param setup Optional setup function + */ +function testNoTear( + TypedArray, + length, + test, + setup = undefined, +) { + assert.sameValue(typeof TypedArray, "function"); + assert.sameValue(typeof length, "number"); + assert.sameValue(typeof test, "function"); + if (setup !== undefined) { + assert.sameValue(typeof setup, "function"); + } + + assert( + TypedArray.BYTES_PER_ELEMENT === 2 || + TypedArray.BYTES_PER_ELEMENT === 4 + ); + + // Number of loop iterations in no-tear tests. + // + // Larger numbers increase the likelihood that teared writes can be observed + // in non-conformant implementations, but also increase the test duration for + // conformant implementations. + const LOOP_COUNT = 10000; + + // Length of the synchronization Int32 TypedArray. + const SYNC_LENGTH = 2; + + // Accounting of live agents. + const RUNNING_INDEX = 0; + + // Index all agents are waiting on. + const WAIT_INDEX = 1; + + // Number of agents to perform concurrent writes. + const NUM_AGENTS = 2; + + // Constructor to create the TypedArray object which will be tested. + const TYPED_ARRAY = TypedArray; + + // Length of the TypedArray object. + const LENGTH = length; + + // Start offset of the TypedArray object within the SharedArrayBuffer. The + // offset shouldn't be word-aligned to make it more likely that non-conformant + // implementations perform tearing, so we add `TYPED_ARRAY.BYTES_PER_ELEMENT`. + const OFFSET = SYNC_LENGTH * Int32Array.BYTES_PER_ELEMENT + + TYPED_ARRAY.BYTES_PER_ELEMENT; + + // Total byte length of the SharedArrayBuffer. + const SAB_BYTE_LENGTH = OFFSET + LENGTH * TYPED_ARRAY.BYTES_PER_ELEMENT; + + // Byte pattern which will be written into the TypedArray. + const BYTE_PATTERN = TypedArray.BYTES_PER_ELEMENT === 2 ? 0x1111 : 0x1111_1111; + + // SharedArrayBuffer passed to agents. + const sab = new SharedArrayBuffer(SAB_BYTE_LENGTH); + + // Int32 TypedArray used for cross-agent synchronization. + const sync = new Int32Array(sab, 0, SYNC_LENGTH); + + // TypedArray used for testing. + const ta = new TYPED_ARRAY(sab, OFFSET, LENGTH); + + // Call the optional setup function, if present. + if (setup !== undefined) { + setup(ta); + } + + // Create a new agent. + function createAgent(value) { + // Template function for the agent. + function agent(sab) { + let sync = new Int32Array(sab, 0, SYNC_LENGTH); + + // Notify agent has started. + Atomics.add(sync, RUNNING_INDEX, 1); + + // Wait until all agents have started. + Atomics.wait(sync, WAIT_INDEX, 0); + + // Repeatedly write into the buffer. Uses Atomics.store to ensure JIT + // compilers won't move the store before the loop. + let ta = new TYPED_ARRAY(sab, OFFSET, LENGTH); + for (let i = 0; i < LOOP_COUNT; ++i) { + for (let j = 0; j < LENGTH; ++j) { + Atomics.store(ta, j, VALUE); + } + } + + $262.agent.leaving(); + } + + // Replace all constants in the template. + let source = agent.toString() + .replaceAll("SYNC_LENGTH", SYNC_LENGTH) + .replaceAll("RUNNING_INDEX", RUNNING_INDEX) + .replaceAll("WAIT_INDEX", WAIT_INDEX) + .replaceAll("LOOP_COUNT", LOOP_COUNT) + .replaceAll("TYPED_ARRAY", TYPED_ARRAY.name) + .replaceAll("OFFSET", OFFSET) + .replaceAll("LENGTH", LENGTH) + .replaceAll("VALUE", value); + + // Return the agent script source. + return `$262.agent.receiveBroadcast(${source});`; + } + + // Create agents. + for (let i = 0; i < NUM_AGENTS; ++i) { + let value = BYTE_PATTERN * (i + 1); + $262.agent.start(createAgent(value)); + } + + // Broadcast the buffer to all agents. + $262.agent.safeBroadcast(sync); + + // Wait until agents are ready. + $262.agent.waitUntil(sync, RUNNING_INDEX, NUM_AGENTS); + + // Wake up agents. + let woken = 0; + while ((woken += Atomics.notify(sync, WAIT_INDEX)) < NUM_AGENTS); + + // Loop to increase the likelihood of concurrent writes. + for (let i = 0; i < LOOP_COUNT; ++i) { + let r = test(ta); + + // Ensure no copied elements were teared. + for (let j = 0; j < LENGTH; ++j) { + let v = r[j]; + assert( + v % BYTE_PATTERN === 0, + `i=${i}: r[${j}] = ${v.toString(16)}` + ); + } + } +} diff --git a/test/built-ins/TypedArray/prototype/set/no-tear-int16.js b/test/built-ins/TypedArray/prototype/set/no-tear-int16.js new file mode 100644 index 00000000000..810d2d4f3bd --- /dev/null +++ b/test/built-ins/TypedArray/prototype/set/no-tear-int16.js @@ -0,0 +1,86 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.set +description: > + No tearing when modifying an Int16Array. +info: | + %TypedArray%.prototype.set ( source [ , offset ] ) + ... + 6. If source is an Object that has a [[TypedArrayName]] internal slot, then + a. Perform ? SetTypedArrayFromTypedArray(target, targetOffset, source). + ... + + + SetTypedArrayFromTypedArray ( target, targetOffset, source ) + ... + 23. If srcType is targetType, then + ... + 24. Else, + a. Repeat, while targetByteIndex < limit, + i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, unordered). + ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, unordered). + ... + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ) + ... + 8. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + d. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, + [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } + to eventsRecord.[[EventList]]. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 1|. +const length = 1; + +// Must be a different element type, because the same element case copies the +// contents bytewise. +const source = new Uint16Array(length); + +testNoTear( + Int16Array, + length, + ta => { ta.set(source); return ta; } +); diff --git a/test/built-ins/TypedArray/prototype/set/no-tear-int32.js b/test/built-ins/TypedArray/prototype/set/no-tear-int32.js new file mode 100644 index 00000000000..decb39446f6 --- /dev/null +++ b/test/built-ins/TypedArray/prototype/set/no-tear-int32.js @@ -0,0 +1,86 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.set +description: > + No tearing when modifying an Int32Array. +info: | + %TypedArray%.prototype.set ( source [ , offset ] ) + ... + 6. If source is an Object that has a [[TypedArrayName]] internal slot, then + a. Perform ? SetTypedArrayFromTypedArray(target, targetOffset, source). + ... + + + SetTypedArrayFromTypedArray ( target, targetOffset, source ) + ... + 23. If srcType is targetType, then + ... + 24. Else, + a. Repeat, while targetByteIndex < limit, + i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, unordered). + ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, unordered). + ... + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ) + ... + 8. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + d. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, + [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } + to eventsRecord.[[EventList]]. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 1|. +const length = 1; + +// Must be a different element type, because the same element case copies the +// contents bytewise. +const source = new Uint32Array(length); + +testNoTear( + Int32Array, + length, + ta => { ta.set(source); return ta; } +); diff --git a/test/built-ins/TypedArray/prototype/slice/no-tear-int16.js b/test/built-ins/TypedArray/prototype/slice/no-tear-int16.js new file mode 100644 index 00000000000..fc735de9fa8 --- /dev/null +++ b/test/built-ins/TypedArray/prototype/slice/no-tear-int16.js @@ -0,0 +1,78 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.slice +description: > + No tearing when slicing an Int16Array. +info: | + %TypedArray%.prototype.slice ( start, end ) + ... + 14. If countBytes > 0, then + ... + g. If srcType is targetType, then + ... + h. Else, + ... + iii. Repeat, while k < endIndex, + ... + 2. Let kValue be ! Get(O, Pk). + ... + + + TypedArrayGetElement ( O, index ) + ... + 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, true, unordered). + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 1|. +const length = 1; + +testNoTear( + Int16Array, + length, + ta => ta.slice(0), + ta => { + // `slice` performs bytewise copying when the target TypedArray has the same + // type, therefore modify the `constructor` to ensure the target TypedArray + // has a different type. + ta.constructor = Uint16Array; + }, +); diff --git a/test/built-ins/TypedArray/prototype/slice/no-tear-int32.js b/test/built-ins/TypedArray/prototype/slice/no-tear-int32.js new file mode 100644 index 00000000000..3dc996f882f --- /dev/null +++ b/test/built-ins/TypedArray/prototype/slice/no-tear-int32.js @@ -0,0 +1,78 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.slice +description: > + No tearing when slicing an Int32Array. +info: | + %TypedArray%.prototype.slice ( start, end ) + ... + 14. If countBytes > 0, then + ... + g. If srcType is targetType, then + ... + h. Else, + ... + iii. Repeat, while k < endIndex, + ... + 2. Let kValue be ! Get(O, Pk). + ... + + + TypedArrayGetElement ( O, index ) + ... + 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, true, unordered). + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 1|. +const length = 1; + +testNoTear( + Int32Array, + length, + ta => ta.slice(0), + ta => { + // `slice` performs bytewise copying when the target TypedArray has the same + // type, therefore modify the `constructor` to ensure the target TypedArray + // has a different type. + ta.constructor = Uint32Array; + }, +); diff --git a/test/built-ins/TypedArray/prototype/sort/no-tear-int16.js b/test/built-ins/TypedArray/prototype/sort/no-tear-int16.js new file mode 100644 index 00000000000..bd72e51d8a2 --- /dev/null +++ b/test/built-ins/TypedArray/prototype/sort/no-tear-int16.js @@ -0,0 +1,97 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.sort +description: > + No tearing when sorting an Int16Array. +info: | + %TypedArray%.prototype.sort ( comparator ) + ... + 7. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, read-through-holes). + ... + 9. Repeat, while j < len, + a. Perform ! Set(obj, ! ToString(𝔽(j)), sortedList[j], true). + ... + + + SortIndexedProperties ( obj, len, SortCompare, holes ) + ... + 3. Repeat, while k < len, + ... + d. If kRead is true, then + i. Let kValue be ? Get(obj, Pk). + ... + + + TypedArrayGetElement ( O, index ) + ... + 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, true, unordered). + + + TypedArraySetElement ( O, index, value ) + ... + 3. If IsValidIntegerIndex(O, index) is true, then + ... + e. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, numValue, true, unordered). + ... + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ) + ... + 8. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + d. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, + [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } + to eventsRecord.[[EventList]]. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 3|. +const length = 3; + +testNoTear( + Int16Array, + length, + ta => ta.sort(), +); diff --git a/test/built-ins/TypedArray/prototype/sort/no-tear-int32.js b/test/built-ins/TypedArray/prototype/sort/no-tear-int32.js new file mode 100644 index 00000000000..7704d54c0a9 --- /dev/null +++ b/test/built-ins/TypedArray/prototype/sort/no-tear-int32.js @@ -0,0 +1,97 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.sort +description: > + No tearing when sorting an Int32Array. +info: | + %TypedArray%.prototype.sort ( comparator ) + ... + 7. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, read-through-holes). + ... + 9. Repeat, while j < len, + a. Perform ! Set(obj, ! ToString(𝔽(j)), sortedList[j], true). + ... + + + SortIndexedProperties ( obj, len, SortCompare, holes ) + ... + 3. Repeat, while k < len, + ... + d. If kRead is true, then + i. Let kValue be ? Get(obj, Pk). + ... + + + TypedArrayGetElement ( O, index ) + ... + 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, true, unordered). + + + TypedArraySetElement ( O, index, value ) + ... + 3. If IsValidIntegerIndex(O, index) is true, then + ... + e. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, numValue, true, unordered). + ... + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ) + ... + 8. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + d. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, + [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } + to eventsRecord.[[EventList]]. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 3|. +const length = 3; + +testNoTear( + Int32Array, + length, + ta => ta.sort(), +); diff --git a/test/built-ins/TypedArray/prototype/toSorted/no-tear-int16.js b/test/built-ins/TypedArray/prototype/toSorted/no-tear-int16.js new file mode 100644 index 00000000000..3e2a6cfc9d5 --- /dev/null +++ b/test/built-ins/TypedArray/prototype/toSorted/no-tear-int16.js @@ -0,0 +1,73 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.tosorted +description: > + No tearing when sorting an Int16Array. +info: | + %TypedArray%.prototype.toSorted ( comparator ) + ... + 8. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, read-through-holes). + ... + + + SortIndexedProperties ( obj, len, SortCompare, holes ) + ... + 3. Repeat, while k < len, + ... + d. If kRead is true, then + i. Let kValue be ? Get(obj, Pk). + ... + + + TypedArrayGetElement ( O, index ) + ... + 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, true, unordered). + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 1|. +const length = 1; + +testNoTear( + Int16Array, + length, + ta => ta.toSorted(), +); diff --git a/test/built-ins/TypedArray/prototype/toSorted/no-tear-int32.js b/test/built-ins/TypedArray/prototype/toSorted/no-tear-int32.js new file mode 100644 index 00000000000..0295868063f --- /dev/null +++ b/test/built-ins/TypedArray/prototype/toSorted/no-tear-int32.js @@ -0,0 +1,73 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.tosorted +description: > + No tearing when sorting an Int32Array. +info: | + %TypedArray%.prototype.toSorted ( comparator ) + ... + 8. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, read-through-holes). + ... + + + SortIndexedProperties ( obj, len, SortCompare, holes ) + ... + 3. Repeat, while k < len, + ... + d. If kRead is true, then + i. Let kValue be ? Get(obj, Pk). + ... + + + TypedArrayGetElement ( O, index ) + ... + 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, true, unordered). + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 1|. +const length = 1; + +testNoTear( + Int32Array, + length, + ta => ta.toSorted(), +); diff --git a/test/built-ins/TypedArray/prototype/with/no-tear-int16.js b/test/built-ins/TypedArray/prototype/with/no-tear-int16.js new file mode 100644 index 00000000000..5099d9dc4a9 --- /dev/null +++ b/test/built-ins/TypedArray/prototype/with/no-tear-int16.js @@ -0,0 +1,66 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.with +description: > + No tearing when creating an Int16Array. +info: | + %TypedArray%.prototype.with ( index, value ) + ... + 12. Repeat, while k < len, + ... + c. Else, let fromValue be ! Get(O, Pk). + ... + + + TypedArrayGetElement ( O, index ) + ... + 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, true, unordered). + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 3|. +const length = 3; + +testNoTear( + Int16Array, + length, + ta => ta.with(0, 0), +); diff --git a/test/built-ins/TypedArray/prototype/with/no-tear-int32.js b/test/built-ins/TypedArray/prototype/with/no-tear-int32.js new file mode 100644 index 00000000000..95bae5bba99 --- /dev/null +++ b/test/built-ins/TypedArray/prototype/with/no-tear-int32.js @@ -0,0 +1,66 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.with +description: > + No tearing when creating an Int32Array. +info: | + %TypedArray%.prototype.with ( index, value ) + ... + 12. Repeat, while k < len, + ... + c. Else, let fromValue be ! Get(O, Pk). + ... + + + TypedArrayGetElement ( O, index ) + ... + 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, + elementType, true, unordered). + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 3|. +const length = 3; + +testNoTear( + Int32Array, + length, + ta => ta.with(0, 0), +); diff --git a/test/built-ins/TypedArrayConstructors/ctors/typedarray-arg/no-tear-int16.js b/test/built-ins/TypedArrayConstructors/ctors/typedarray-arg/no-tear-int16.js new file mode 100644 index 00000000000..5797d0a3344 --- /dev/null +++ b/test/built-ins/TypedArrayConstructors/ctors/typedarray-arg/no-tear-int16.js @@ -0,0 +1,90 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-typedarray +description: > + No tearing when creating an Int16Array. +info: | + TypedArray ( ...args ) + ... + 6. Else, + a. Let firstArgument be args[0]. + b. If firstArgument is an Object, then + ... + ii. If firstArgument has a [[TypedArrayName]] internal slot, then + 1. Perform ? InitializeTypedArrayFromTypedArray(O, firstArgument). + ... + + + InitializeTypedArrayFromTypedArray ( O, srcArray ) + ... + 11. If elementType is srcType, then + ... + 12. Else, + ... + f. Repeat, while count > 0, + i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, unordered). + ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, unordered). + ... + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ) + ... + 8. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + d. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, + [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } + to eventsRecord.[[EventList]]. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 1|. +const length = 1; + +testNoTear( + Int16Array, + length, + + // Must be a different element type, because the same element case copies the + // contents bytewise. + ta => new Uint16Array(ta), +); diff --git a/test/built-ins/TypedArrayConstructors/ctors/typedarray-arg/no-tear-int32.js b/test/built-ins/TypedArrayConstructors/ctors/typedarray-arg/no-tear-int32.js new file mode 100644 index 00000000000..be1b4dcbd67 --- /dev/null +++ b/test/built-ins/TypedArrayConstructors/ctors/typedarray-arg/no-tear-int32.js @@ -0,0 +1,90 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-typedarray +description: > + No tearing when creating an Int32Array. +info: | + TypedArray ( ...args ) + ... + 6. Else, + a. Let firstArgument be args[0]. + b. If firstArgument is an Object, then + ... + ii. If firstArgument has a [[TypedArrayName]] internal slot, then + 1. Perform ? InitializeTypedArrayFromTypedArray(O, firstArgument). + ... + + + InitializeTypedArrayFromTypedArray ( O, srcArray ) + ... + 11. If elementType is srcType, then + ... + 12. Else, + ... + f. Repeat, while count > 0, + i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, unordered). + ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, unordered). + ... + + + GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ) + ... + 5. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + b. Let rawValue be GetRawBytesFromSharedBlock(block, byteIndex, type, + isTypedArray, order). + ... + + + GetRawBytesFromSharedBlock ( block, byteIndex, type, isTypedArray, order ) + ... + 4. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + ... + 7. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, + [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }. + ... + + + SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ) + ... + 8. If IsSharedArrayBuffer(arrayBuffer) is true, then + ... + c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, + let noTear be true; otherwise let noTear be false. + d. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, + [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } + to eventsRecord.[[EventList]]. + ... + + + IsNoTearConfiguration ( type, order ) + 1. If IsUnclampedIntegerElementType(type) is true, return true. + ... + + + Valid Executions + + A candidate execution execution is a valid execution (or simply an execution) + if all of the following are true. + ... + - execution has tear free reads. + ... + +includes: [atomicsHelper.js, tearing.js] +features: [TypedArray, Atomics, SharedArrayBuffer] +---*/ + +// Tearing was observed in implementations for |length = 1|. +const length = 1; + +testNoTear( + Int32Array, + length, + + // Must be a different element type, because the same element case copies the + // contents bytewise. + ta => new Uint32Array(ta), +);