Commit 2179ac84 authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[rab/gsab] Add RAB / GSAB support for TA.p.includes

Bug: v8:11111
Change-Id: Icefaa3f1090f9b41dc7837e3a95bbfd633703757
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3251179Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77650}
parent 5395045f
......@@ -218,7 +218,7 @@ BUILTIN(TypedArrayPrototypeIncludes) {
if (args.length() < 2) return ReadOnlyRoots(isolate).false_value();
int64_t len = array->length();
int64_t len = array->GetLength();
if (len == 0) return ReadOnlyRoots(isolate).false_value();
int64_t index = 0;
......
......@@ -3331,21 +3331,29 @@ class TypedElementsAccessor
return Just(value->IsUndefined(isolate) && length > start_from);
}
if (value->IsUndefined(isolate) && length > typed_array.length()) {
bool out_of_bounds = false;
size_t new_length = typed_array.GetLengthOrOutOfBounds(out_of_bounds);
if (V8_UNLIKELY(out_of_bounds)) {
return Just(value->IsUndefined(isolate) && length > start_from);
}
if (value->IsUndefined(isolate) && length > new_length) {
return Just(true);
}
// Prototype has no elements, and not searching for the hole --- limit
// search to backing store length.
if (typed_array.length() < length) {
length = typed_array.length();
if (new_length < length) {
length = new_length;
}
ElementType typed_search_value;
ElementType* data_ptr =
reinterpret_cast<ElementType*>(typed_array.DataPtr());
auto is_shared = typed_array.buffer().is_shared() ? kShared : kUnshared;
if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS ||
Kind == RAB_GSAB_BIGINT64_ELEMENTS ||
Kind == RAB_GSAB_BIGUINT64_ELEMENTS) {
if (!value->IsBigInt()) return Just(false);
bool lossless;
typed_search_value = FromHandle(value, &lossless);
......@@ -3355,7 +3363,9 @@ class TypedElementsAccessor
double search_value = value->Number();
if (!std::isfinite(search_value)) {
// Integral types cannot represent +Inf or NaN.
if (Kind < FLOAT32_ELEMENTS || Kind > FLOAT64_ELEMENTS) {
if (!(Kind == FLOAT32_ELEMENTS || Kind == FLOAT64_ELEMENTS ||
Kind == RAB_GSAB_FLOAT32_ELEMENTS ||
Kind == RAB_GSAB_FLOAT64_ELEMENTS)) {
return Just(false);
}
if (std::isnan(search_value)) {
......
......@@ -2097,3 +2097,140 @@ function TestIterationAndGrow(ta, expected, gsab, grow_after,
assertEquals([6, 4], ReduceRightHelper(lengthTrackingWithOffset));
}
})();
(function Includes() {
for (let ctor of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(gsab, 0, 4);
const fixedLengthWithOffset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new ctor(gsab, 0);
const lengthTrackingWithOffset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(gsab);
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
assertTrue(IncludesHelper(fixedLength, 2));
assertFalse(IncludesHelper(fixedLength, undefined));
assertTrue(IncludesHelper(fixedLength, 2, 1));
assertFalse(IncludesHelper(fixedLength, 2, 2));
assertTrue(IncludesHelper(fixedLength, 2, -3));
assertFalse(IncludesHelper(fixedLength, 2, -2));
assertFalse(IncludesHelper(fixedLengthWithOffset, 2));
assertTrue(IncludesHelper(fixedLengthWithOffset, 4));
assertFalse(IncludesHelper(fixedLengthWithOffset, undefined));
assertTrue(IncludesHelper(fixedLengthWithOffset, 4, 0));
assertFalse(IncludesHelper(fixedLengthWithOffset, 4, 1));
assertTrue(IncludesHelper(fixedLengthWithOffset, 4, -2));
assertFalse(IncludesHelper(fixedLengthWithOffset, 4, -1));
assertTrue(IncludesHelper(lengthTracking, 2));
assertFalse(IncludesHelper(lengthTracking, undefined));
assertTrue(IncludesHelper(lengthTracking, 2, 1));
assertFalse(IncludesHelper(lengthTracking, 2, 2));
assertTrue(IncludesHelper(lengthTracking, 2, -3));
assertFalse(IncludesHelper(lengthTracking, 2, -2));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 2));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4));
assertFalse(IncludesHelper(lengthTrackingWithOffset, undefined));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4, 0));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 4, 1));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4, -2));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 4, -1));
// Grow.
gsab.grow(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
assertTrue(IncludesHelper(fixedLength, 2));
assertFalse(IncludesHelper(fixedLength, undefined));
assertFalse(IncludesHelper(fixedLength, 8));
assertFalse(IncludesHelper(fixedLengthWithOffset, 2));
assertTrue(IncludesHelper(fixedLengthWithOffset, 4));
assertFalse(IncludesHelper(fixedLengthWithOffset, undefined));
assertFalse(IncludesHelper(fixedLengthWithOffset, 8));
assertTrue(IncludesHelper(lengthTracking, 2));
assertFalse(IncludesHelper(lengthTracking, undefined));
assertTrue(IncludesHelper(lengthTracking, 8));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 2));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4));
assertFalse(IncludesHelper(lengthTrackingWithOffset, undefined));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 8));
}
})();
(function IncludesParameterConversionGrows() {
for (let ctor of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(gsab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(lengthTracking, i, 1);
}
let evil = { valueOf: () => {
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
return 0;
}};
assertFalse(IncludesHelper(lengthTracking, 0));
// The TA grew but we only look at the data until the original length.
assertFalse(IncludesHelper(lengthTracking, 0, evil));
}
for (let ctor of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(gsab);
WriteToTypedArray(lengthTracking, 0, 1);
let evil = { valueOf: () => {
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
return -4;
}};
assertTrue(IncludesHelper(lengthTracking, 1, -4));
// The TA grew but the start index conversion is done based on the original
// length.
assertTrue(IncludesHelper(lengthTracking, 1, evil));
}
})();
(function IncludesSpecialValues() {
const floatCtors = [
Float32Array,
Float64Array,
MyFloat32Array
];
for (let ctor of floatCtors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(gsab);
lengthTracking[0] = -Infinity;
lengthTracking[1] = Infinity;
lengthTracking[2] = NaN;
assertTrue(lengthTracking.includes(-Infinity));
assertTrue(lengthTracking.includes(Infinity));
assertTrue(lengthTracking.includes(NaN));
}
})();
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
class MyUint8Array extends Uint8Array {};
class MyFloat32Array extends Float32Array {};
class MyBigInt64Array extends BigInt64Array {};
const ctors = [
......@@ -17,6 +18,7 @@ const ctors = [
BigUint64Array,
BigInt64Array,
MyUint8Array,
MyFloat32Array,
MyBigInt64Array,
];
......@@ -96,3 +98,11 @@ function FillHelper(array, n, start, end) {
array.fill(n, start, end);
}
}
function IncludesHelper(array, n, fromIndex) {
if (typeof n == 'number' &&
(array instanceof BigInt64Array || array instanceof BigUint64Array)) {
return array.includes(BigInt(n), fromIndex);
}
return array.includes(n, fromIndex);
}
......@@ -775,3 +775,33 @@ d8.file.execute('test/mjsunit/typedarray-helpers.js');
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));
}
})();
......@@ -3821,3 +3821,216 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
assertEquals([6, 4], ReduceRightHelper(lengthTrackingWithOffset));
}
})();
(function Includes() {
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
assertTrue(IncludesHelper(fixedLength, 2));
assertFalse(IncludesHelper(fixedLength, undefined));
assertTrue(IncludesHelper(fixedLength, 2, 1));
assertFalse(IncludesHelper(fixedLength, 2, 2));
assertTrue(IncludesHelper(fixedLength, 2, -3));
assertFalse(IncludesHelper(fixedLength, 2, -2));
assertFalse(IncludesHelper(fixedLengthWithOffset, 2));
assertTrue(IncludesHelper(fixedLengthWithOffset, 4));
assertFalse(IncludesHelper(fixedLengthWithOffset, undefined));
assertTrue(IncludesHelper(fixedLengthWithOffset, 4, 0));
assertFalse(IncludesHelper(fixedLengthWithOffset, 4, 1));
assertTrue(IncludesHelper(fixedLengthWithOffset, 4, -2));
assertFalse(IncludesHelper(fixedLengthWithOffset, 4, -1));
assertTrue(IncludesHelper(lengthTracking, 2));
assertFalse(IncludesHelper(lengthTracking, undefined));
assertTrue(IncludesHelper(lengthTracking, 2, 1));
assertFalse(IncludesHelper(lengthTracking, 2, 2));
assertTrue(IncludesHelper(lengthTracking, 2, -3));
assertFalse(IncludesHelper(lengthTracking, 2, -2));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 2));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4));
assertFalse(IncludesHelper(lengthTrackingWithOffset, undefined));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4, 0));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 4, 1));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4, -2));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 4, -1));
// 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
assertThrows(() => { IncludesHelper(fixedLength, 2); });
assertThrows(() => { IncludesHelper(fixedLengthWithOffset, 2); });
assertTrue(IncludesHelper(lengthTracking, 2));
assertFalse(IncludesHelper(lengthTracking, undefined));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 2));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4));
assertFalse(IncludesHelper(lengthTrackingWithOffset, undefined));
// Shrink so that the TAs with offset go out of bounds.
rab.resize(1 * ctor.BYTES_PER_ELEMENT);
assertThrows(() => { IncludesHelper(fixedLength, 2); });
assertThrows(() => { IncludesHelper(fixedLengthWithOffset, 2); });
assertThrows(() => { IncludesHelper(lengthTrackingWithOffset, 2); });
// Shrink to zero.
rab.resize(0);
assertThrows(() => { IncludesHelper(fixedLength, 2); });
assertThrows(() => { IncludesHelper(fixedLengthWithOffset, 2); });
assertThrows(() => { IncludesHelper(lengthTrackingWithOffset, 2); });
assertFalse(IncludesHelper(lengthTracking, 2));
assertFalse(IncludesHelper(lengthTracking, undefined));
// 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
assertTrue(IncludesHelper(fixedLength, 2));
assertFalse(IncludesHelper(fixedLength, undefined));
assertFalse(IncludesHelper(fixedLength, 8));
assertFalse(IncludesHelper(fixedLengthWithOffset, 2));
assertTrue(IncludesHelper(fixedLengthWithOffset, 4));
assertFalse(IncludesHelper(fixedLengthWithOffset, undefined));
assertFalse(IncludesHelper(fixedLengthWithOffset, 8));
assertTrue(IncludesHelper(lengthTracking, 2));
assertFalse(IncludesHelper(lengthTracking, undefined));
assertTrue(IncludesHelper(lengthTracking, 8));
assertFalse(IncludesHelper(lengthTrackingWithOffset, 2));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 4));
assertFalse(IncludesHelper(lengthTrackingWithOffset, undefined));
assertTrue(IncludesHelper(lengthTrackingWithOffset, 8));
}
})();
(function IncludesParameterConversionResizes() {
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: () => {
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
return 0;
}};
assertFalse(IncludesHelper(fixedLength, undefined));
// The TA is OOB 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: () => {
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
return 0;
}};
assertTrue(IncludesHelper(fixedLength, 0));
// The TA is OOB so it includes only "undefined".
assertFalse(IncludesHelper(fixedLength, 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: () => {
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
return 0;
}};
assertFalse(IncludesHelper(lengthTracking, undefined));
// "includes" iterates until the original length and sees "undefined"s.
assertTrue(IncludesHelper(lengthTracking, undefined, evil));
}
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(lengthTracking, i, 1);
}
let evil = { valueOf: () => {
rab.resize(6 * ctor.BYTES_PER_ELEMENT);
return 0;
}};
assertFalse(IncludesHelper(lengthTracking, 0));
// The TA grew but we only look at the data until the original length.
assertFalse(IncludesHelper(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);
WriteToTypedArray(lengthTracking, 0, 1);
let evil = { valueOf: () => {
rab.resize(6 * ctor.BYTES_PER_ELEMENT);
return -4;
}};
assertTrue(IncludesHelper(lengthTracking, 1, -4));
// The TA grew but the start index conversion is done based on the original
// length.
assertTrue(IncludesHelper(lengthTracking, 1, evil));
}
})();
(function IncludesSpecialValues() {
const floatCtors = [
Float32Array,
Float64Array,
MyFloat32Array
];
for (let ctor of floatCtors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab);
lengthTracking[0] = -Infinity;
lengthTracking[1] = Infinity;
lengthTracking[2] = NaN;
assertTrue(lengthTracking.includes(-Infinity));
assertTrue(lengthTracking.includes(Infinity));
assertTrue(lengthTracking.includes(NaN));
}
})();
......@@ -425,8 +425,6 @@
'built-ins/DataView/prototype/setUint16/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setUint32/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setUint8/resizable-buffer': [FAIL],
'built-ins/TypedArray/prototype/includes/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/includes/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/indexOf/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/indexOf/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/join/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment