diff --git a/test/built-ins/Array/prototype/join/coerced-separator-grow.js b/test/built-ins/Array/prototype/join/coerced-separator-grow.js new file mode 100644 index 00000000000..37cce09032f --- /dev/null +++ b/test/built-ins/Array/prototype/join/coerced-separator-grow.js @@ -0,0 +1,38 @@ +// Copyright 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.prototype.join +description: > + Array.p.join behaves correctly when the receiver is grown during + argument coercion +includes: [resizableArrayBufferUtils.js] +features: [resizable-arraybuffer] +---*/ + +// Growing + 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: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + assert.sameValue(Array.prototype.join.call(fixedLength, evil), '0.0.0.0'); +} + +// Growing + 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: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + // We iterate 4 elements, since it was the starting length. + assert.sameValue(Array.prototype.join.call(lengthTracking, evil), '0.0.0.0'); +} diff --git a/test/built-ins/Array/prototype/join/coerced-separator-shrink.js b/test/built-ins/Array/prototype/join/coerced-separator-shrink.js new file mode 100644 index 00000000000..302c433fad7 --- /dev/null +++ b/test/built-ins/Array/prototype/join/coerced-separator-shrink.js @@ -0,0 +1,42 @@ +// Copyright 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.prototype.join +description: > + Array.p.join behaves correctly when the receiver is shrunk during + argument coercion +includes: [resizableArrayBufferUtils.js] +features: [resizable-arraybuffer] +---*/ + +// Shrinking + 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: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + 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. + assert.sameValue(Array.prototype.join.call(fixedLength, evil), '...'); +} + +// Shrinking + 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: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + // We iterate 4 elements, since it was the starting length. Elements beyond + // the new length are converted to the empty string. + assert.sameValue(Array.prototype.join.call(lengthTracking, evil), '0.0..'); +} diff --git a/test/built-ins/Array/prototype/join/resizable-buffer.js b/test/built-ins/Array/prototype/join/resizable-buffer.js new file mode 100644 index 00000000000..e28414ab007 --- /dev/null +++ b/test/built-ins/Array/prototype/join/resizable-buffer.js @@ -0,0 +1,82 @@ +// Copyright 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.prototype.join +description: > + Array.p.join behaves correctly when the receiver is backed by resizable + buffer +includes: [resizableArrayBufferUtils.js] +features: [resizable-arraybuffer] +---*/ + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + + // Write some data into the array. + 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 + + assert.sameValue(Array.prototype.join.call(fixedLength), '0,2,4,6'); + assert.sameValue(Array.prototype.join.call(fixedLengthWithOffset), '4,6'); + assert.sameValue(Array.prototype.join.call(lengthTracking), '0,2,4,6'); + assert.sameValue(Array.prototype.join.call(lengthTrackingWithOffset), '4,6'); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + assert.sameValue(Array.prototype.join.call(fixedLength), ''); + assert.sameValue(Array.prototype.join.call(fixedLengthWithOffset), ''); + + assert.sameValue(Array.prototype.join.call(lengthTracking), '0,2,4'); + assert.sameValue(Array.prototype.join.call(lengthTrackingWithOffset), '4'); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + assert.sameValue(Array.prototype.join.call(fixedLength), ''); + assert.sameValue(Array.prototype.join.call(fixedLengthWithOffset), ''); + assert.sameValue(Array.prototype.join.call(lengthTrackingWithOffset), ''); + + assert.sameValue(Array.prototype.join.call(lengthTracking), '0'); + + // Shrink to zero. + rab.resize(0); + assert.sameValue(Array.prototype.join.call(fixedLength), ''); + assert.sameValue(Array.prototype.join.call(fixedLengthWithOffset), ''); + assert.sameValue(Array.prototype.join.call(lengthTrackingWithOffset), ''); + + assert.sameValue(Array.prototype.join.call(lengthTracking), ''); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert.sameValue(Array.prototype.join.call(fixedLength), '0,2,4,6'); + assert.sameValue(Array.prototype.join.call(fixedLengthWithOffset), '4,6'); + assert.sameValue(Array.prototype.join.call(lengthTracking), '0,2,4,6,8,10'); + assert.sameValue(Array.prototype.join.call(lengthTrackingWithOffset), '4,6,8,10'); +} diff --git a/test/built-ins/TypedArray/prototype/join/coerced-separator-grow.js b/test/built-ins/TypedArray/prototype/join/coerced-separator-grow.js new file mode 100644 index 00000000000..76cec6a13cf --- /dev/null +++ b/test/built-ins/TypedArray/prototype/join/coerced-separator-grow.js @@ -0,0 +1,38 @@ +// Copyright 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.join +description: > + TypedArray.p.join behaves correctly when the receiver is grown during + argument coercion +includes: [resizableArrayBufferUtils.js] +features: [resizable-arraybuffer] +---*/ + +// Growing + 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: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + assert.sameValue(fixedLength.join(evil), '0.0.0.0'); +} + +// Growing + 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: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + // We iterate 4 elements, since it was the starting length. + assert.sameValue(lengthTracking.join(evil), '0.0.0.0'); +} diff --git a/test/built-ins/TypedArray/prototype/join/coerced-separator-shrink.js b/test/built-ins/TypedArray/prototype/join/coerced-separator-shrink.js new file mode 100644 index 00000000000..aac4d7b108f --- /dev/null +++ b/test/built-ins/TypedArray/prototype/join/coerced-separator-shrink.js @@ -0,0 +1,42 @@ +// Copyright 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.join +description: > + TypedArray.p.join behaves correctly when the receiver is shrunk during + argument coercion +includes: [resizableArrayBufferUtils.js] +features: [resizable-arraybuffer] +---*/ + +// Shrinking + 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: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + 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. + assert.sameValue(fixedLength.join(evil), '...'); +} + +// Shrinking + 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: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + // We iterate 4 elements, since it was the starting length. Elements beyond + // the new length are converted to the empty string. + assert.sameValue(lengthTracking.join(evil), '0.0..'); +} diff --git a/test/built-ins/TypedArray/prototype/join/resizable-buffer.js b/test/built-ins/TypedArray/prototype/join/resizable-buffer.js new file mode 100644 index 00000000000..02d96a4f0c4 --- /dev/null +++ b/test/built-ins/TypedArray/prototype/join/resizable-buffer.js @@ -0,0 +1,98 @@ +// Copyright 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-%typedarray%.prototype.join +description: > + TypedArray.p.join behaves correctly when the receiver is backed by resizable + buffer +includes: [resizableArrayBufferUtils.js] +features: [resizable-arraybuffer] +---*/ + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + + // Write some data into the array. + 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 + + assert.sameValue(fixedLength.join(), '0,2,4,6'); + assert.sameValue(fixedLengthWithOffset.join(), '4,6'); + assert.sameValue(lengthTracking.join(), '0,2,4,6'); + assert.sameValue(lengthTrackingWithOffset.join(), '4,6'); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + assert.throws(TypeError, () => { + fixedLength.join(); + }); + assert.throws(TypeError, () => { + fixedLengthWithOffset.join(); + }); + + assert.sameValue(lengthTracking.join(), '0,2,4'); + assert.sameValue(lengthTrackingWithOffset.join(), '4'); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + fixedLength.join(); + }); + assert.throws(TypeError, () => { + fixedLengthWithOffset.join(); + }); + assert.throws(TypeError, () => { + lengthTrackingWithOffset.join(); + }); + + assert.sameValue(lengthTracking.join(), '0'); + + // Shrink to zero. + rab.resize(0); + assert.throws(TypeError, () => { + fixedLength.join(); + }); + assert.throws(TypeError, () => { + fixedLengthWithOffset.join(); + }); + assert.throws(TypeError, () => { + lengthTrackingWithOffset.join(); + }); + + assert.sameValue(lengthTracking.join(), ''); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert.sameValue(fixedLength.join(), '0,2,4,6'); + assert.sameValue(fixedLengthWithOffset.join(), '4,6'); + assert.sameValue(lengthTracking.join(), '0,2,4,6,8,10'); + assert.sameValue(lengthTrackingWithOffset.join(), '4,6,8,10'); +}