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

[rab/gsab] RAB/GSAB support for A.p.concat

It mostly worked out of the box. Only the dictionary mode prototype
chain walk code paths had to be updated.

Bug: v8:11111
Change-Id: Ia8336964d29304916a34e305f32bb33bb06e211a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3683340
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80935}
parent 40738e6c
......@@ -972,8 +972,8 @@ void CollectElementIndices(Isolate* isolate, Handle<JSObject> object,
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE) {
size_t length = Handle<JSTypedArray>::cast(object)->length();
TYPED_ARRAYS(TYPED_ARRAY_CASE) RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE) {
size_t length = Handle<JSTypedArray>::cast(object)->GetLength();
if (range <= length) {
length = range;
// We will add all indices, so we might as well clear it first
......@@ -988,9 +988,6 @@ void CollectElementIndices(Isolate* isolate, Handle<JSObject> object,
if (length == range) return; // All indices accounted for already.
break;
}
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
// TODO(v8:11111): Support RAB / GSAB.
UNREACHABLE();
#undef TYPED_ARRAY_CASE
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
......
// 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');
(function ArrayConcatDefault() {
for (let ctor of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(gsab);
const taWrite = new ctor(gsab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, i + 1);
}
// Orig. array: [1, 2, 3, 4]
// [1, 2, 3, 4, ...] << lengthTracking
function helper(receiver, ...params) {
return ToNumbers(Array.prototype.concat.call(receiver, ...params));
}
// TypedArrays aren't concat spreadable.
assertEquals([lengthTracking, 5, 6, 7],
helper(lengthTracking, [5, 6], [7]));
// Resizing doesn't matter since the TA is added as a single item.
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
assertEquals([lengthTracking, 5, 6, 7],
helper(lengthTracking, [5, 6], [7]));
}
})();
(function ArrayConcatConcatSpreadable() {
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);
fixedLength[Symbol.isConcatSpreadable] = true;
fixedLengthWithOffset[Symbol.isConcatSpreadable] = true;
lengthTracking[Symbol.isConcatSpreadable] = true;
lengthTrackingWithOffset[Symbol.isConcatSpreadable] = true;
const taWrite = new ctor(gsab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, i + 1);
}
// Orig. array: [1, 2, 3, 4]
// [1, 2, 3, 4] << fixedLength
// [3, 4] << fixedLengthWithOffset
// [1, 2, 3, 4, ...] << lengthTracking
// [3, 4, ...] << lengthTrackingWithOffset
function helper(receiver, ...params) {
return ToNumbers(Array.prototype.concat.call(receiver, ...params));
}
assertEquals([0, 1, 2, 3, 4, 5, 6], helper([0], fixedLength, [5, 6]));
assertEquals([0, 3, 4, 5, 6], helper([0], fixedLengthWithOffset, [5, 6]));
assertEquals([0, 1, 2, 3, 4, 5, 6], helper([0], lengthTracking, [5, 6]));
assertEquals([0, 3, 4, 5, 6],
helper([0], lengthTrackingWithOffset, [5, 6]));
// Grow.
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
for (let i = 0; i < 6; ++i) {
WriteToTypedArray(taWrite, i, i + 1);
}
// Orig. array: [1, 2, 3, 4, 5, 6]
// [1, 2, 3, 4] << fixedLength
// [3, 4] << fixedLengthWithOffset
// [1, 2, 3, 4, 5, 6, ...] << lengthTracking
// [3, 4, 5, 6, ...] << lengthTrackingWithOffset
assertEquals([0, 1, 2, 3, 4, 7, 8], helper([0], fixedLength, [7, 8]));
assertEquals([0, 3, 4, 7, 8], helper([0], fixedLengthWithOffset, [7, 8]));
assertEquals([0, 1, 2, 3, 4, 5, 6, 7, 8],
helper([0], lengthTracking, [7, 8]));
assertEquals([0, 3, 4, 5, 6, 7, 8],
helper([0], lengthTrackingWithOffset, [7, 8]));
}
})();
// Hand-crafted test to hit a somewhat esoteric code path in Array.p.concat.
(function ArrayConcatConcatDictionaryElementsProto() {
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);
const taWrite = new ctor(gsab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, i + 1);
}
// Orig. array: [1, 2, 3, 4]
// [1, 2, 3, 4] << fixedLength
// [3, 4] << fixedLengthWithOffset
// [1, 2, 3, 4, ...] << lengthTracking
// [3, 4, ...] << lengthTrackingWithOffset
const largeIndex = 5000;
function helper(ta) {
let newArray = [];
newArray[largeIndex] = 11111; // Force dictionary mode.
assertTrue(%HasDictionaryElements(newArray));
newArray.__proto__ = ta;
return ToNumbers(Array.prototype.concat.call([], newArray));
}
function expected(arr) {
arr[largeIndex] = 11111;
return arr;
}
assertEquals(expected([1, 2, 3, 4]), helper(fixedLength));
assertEquals(expected([3, 4]), helper(fixedLengthWithOffset));
assertEquals(expected([1, 2, 3, 4]), helper(lengthTracking));
assertEquals(expected([3, 4]), helper(lengthTrackingWithOffset));
// Grow.
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
for (let i = 0; i < 6; ++i) {
WriteToTypedArray(taWrite, i, i + 1);
}
// Orig. array: [1, 2, 3, 4, 5, 6]
// [1, 2, 3, 4] << fixedLength
// [3, 4] << fixedLengthWithOffset
// [1, 2, 3, 4, 5, 6, ...] << lengthTracking
// [3, 4, 5, 6, ...] << lengthTrackingWithOffset
assertEquals(expected([1, 2, 3, 4]), helper(fixedLength));
assertEquals(expected([3, 4]), helper(fixedLengthWithOffset));
assertEquals(expected([1, 2, 3, 4, 5, 6]), helper(lengthTracking));
assertEquals(expected([3, 4, 5, 6]), helper(lengthTrackingWithOffset));
}
})();
// 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');
(function ArrayConcatDefault() {
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 taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, i + 1);
}
// Orig. array: [1, 2, 3, 4]
// [1, 2, 3, 4] << fixedLength
function helper(receiver, ...params) {
return ToNumbers(Array.prototype.concat.call(receiver, ...params));
}
// TypedArrays aren't concat spreadable by default.
assertEquals([fixedLength, 5, 6, 7], helper(fixedLength, [5, 6], [7]));
// OOBness doesn't matter since the TA is added as a single item.
rab.resize(0);
assertEquals([fixedLength, 5, 6, 7], helper(fixedLength, [5, 6], [7]));
// The same for detached buffers.
%ArrayBufferDetach(rab);
assertEquals([fixedLength, 5, 6, 7], helper(fixedLength, [5, 6], [7]));
}
})();
(function ArrayConcatConcatSpreadable() {
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);
fixedLength[Symbol.isConcatSpreadable] = true;
fixedLengthWithOffset[Symbol.isConcatSpreadable] = true;
lengthTracking[Symbol.isConcatSpreadable] = true;
lengthTrackingWithOffset[Symbol.isConcatSpreadable] = true;
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, i + 1);
}
// Orig. array: [1, 2, 3, 4]
// [1, 2, 3, 4] << fixedLength
// [3, 4] << fixedLengthWithOffset
// [1, 2, 3, 4, ...] << lengthTracking
// [3, 4, ...] << lengthTrackingWithOffset
function helper(receiver, ...params) {
return ToNumbers(Array.prototype.concat.call(receiver, ...params));
}
assertEquals([0, 1, 2, 3, 4, 5, 6], helper([0], fixedLength, [5, 6]));
assertEquals([0, 3, 4, 5, 6], helper([0], fixedLengthWithOffset, [5, 6]));
assertEquals([0, 1, 2, 3, 4, 5, 6], helper([0], lengthTracking, [5, 6]));
assertEquals([0, 3, 4, 5, 6],
helper([0], lengthTrackingWithOffset, [5, 6]));
// Shrink so that fixed length TAs go out of bounds.
rab.resize(3 * ctor.BYTES_PER_ELEMENT);
// Orig. array: [1, 2, 3]
// [1, 2, 3, ...] << lengthTracking
// [3, ...] << lengthTrackingWithOffset
assertEquals([0, 5, 6], helper([0], fixedLength, [5, 6]));
assertEquals([0, 5, 6], helper([0], fixedLengthWithOffset, [5, 6]));
assertEquals([0, 1, 2, 3, 5, 6], helper([0], lengthTracking, [5, 6]));
assertEquals([0, 3, 5, 6],
helper([0], lengthTrackingWithOffset, [5, 6]));
// Shrink so that the TAs with offset go out of bounds.
rab.resize(1 * ctor.BYTES_PER_ELEMENT);
// Orig. array: [1]
// [1, ...] << lengthTracking
assertEquals([0, 5, 6], helper([0], fixedLength, [5, 6]));
assertEquals([0, 5, 6], helper([0], fixedLengthWithOffset, [5, 6]));
assertEquals([0, 1, 5, 6], helper([0], lengthTracking, [5, 6]));
assertEquals([0, 5, 6], helper([0], lengthTrackingWithOffset, [5, 6]));
// Shrink to zero.
rab.resize(0);
// Orig. array: []
// [...] << lengthTracking
assertEquals([0, 5, 6], helper([0], fixedLength, [5, 6]));
assertEquals([0, 5, 6], helper([0], fixedLengthWithOffset, [5, 6]));
assertEquals([0, 5, 6], helper([0], lengthTracking, [5, 6]));
assertEquals([0, 5, 6], helper([0], lengthTrackingWithOffset, [5, 6]));
// 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, i + 1);
}
// Orig. array: [1, 2, 3, 4, 5, 6]
// [1, 2, 3, 4] << fixedLength
// [3, 4] << fixedLengthWithOffset
// [1, 2, 3, 4, 5, 6, ...] << lengthTracking
// [3, 4, 5, 6, ...] << lengthTrackingWithOffset
assertEquals([0, 1, 2, 3, 4, 7, 8], helper([0], fixedLength, [7, 8]));
assertEquals([0, 3, 4, 7, 8], helper([0], fixedLengthWithOffset, [7, 8]));
assertEquals([0, 1, 2, 3, 4, 5, 6, 7, 8],
helper([0], lengthTracking, [7, 8]));
assertEquals([0, 3, 4, 5, 6, 7, 8],
helper([0], lengthTrackingWithOffset, [7, 8]));
// After detaching, all TAs behave like zero length.
%ArrayBufferDetach(rab);
assertEquals([0, 5, 6], helper([0], fixedLength, [5, 6]));
assertEquals([0, 5, 6], helper([0], fixedLengthWithOffset, [5, 6]));
assertEquals([0, 5, 6], helper([0], lengthTracking, [5, 6]));
assertEquals([0, 5, 6], helper([0], lengthTrackingWithOffset, [5, 6]));
}
})();
// Hand-crafted test to hit a somewhat esoteric code path in Array.p.concat.
(function ArrayConcatConcatDictionaryElementsProto() {
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);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, i + 1);
}
// Orig. array: [1, 2, 3, 4]
// [1, 2, 3, 4] << fixedLength
// [3, 4] << fixedLengthWithOffset
// [1, 2, 3, 4, ...] << lengthTracking
// [3, 4, ...] << lengthTrackingWithOffset
const largeIndex = 5000;
function helper(ta) {
let newArray = [];
newArray[largeIndex] = 11111; // Force dictionary mode.
assertTrue(%HasDictionaryElements(newArray));
newArray.__proto__ = ta;
return ToNumbers(Array.prototype.concat.call([], newArray));
}
function expected(arr) {
arr[largeIndex] = 11111;
return arr;
}
assertEquals(expected([1, 2, 3, 4]), helper(fixedLength));
assertEquals(expected([3, 4]), helper(fixedLengthWithOffset));
assertEquals(expected([1, 2, 3, 4]), helper(lengthTracking));
assertEquals(expected([3, 4]), helper(lengthTrackingWithOffset));
// Shrink so that fixed length TAs go out of bounds.
rab.resize(3 * ctor.BYTES_PER_ELEMENT);
// Orig. array: [1, 2, 3]
// [1, 2, 3, ...] << lengthTracking
// [3, ...] << lengthTrackingWithOffset
assertEquals(expected([]), helper(fixedLength));
assertEquals(expected([]), helper(fixedLengthWithOffset));
assertEquals(expected([1, 2, 3]), helper(lengthTracking));
assertEquals(expected([3]), helper(lengthTrackingWithOffset));
// Shrink so that the TAs with offset go out of bounds.
rab.resize(1 * ctor.BYTES_PER_ELEMENT);
// Orig. array: [1]
// [1, ...] << lengthTracking
assertEquals(expected([]), helper(fixedLength));
assertEquals(expected([]), helper(fixedLengthWithOffset));
assertEquals(expected([1]), helper(lengthTracking));
assertEquals(expected([]), helper(lengthTrackingWithOffset));
// Shrink to zero.
rab.resize(0);
// Orig. array: []
// [...] << lengthTracking
assertEquals(expected([]), helper(fixedLength));
assertEquals(expected([]), helper(fixedLengthWithOffset));
assertEquals(expected([]), helper(lengthTracking));
assertEquals(expected([]), helper(lengthTrackingWithOffset));
// 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, i + 1);
}
// Orig. array: [1, 2, 3, 4, 5, 6]
// [1, 2, 3, 4] << fixedLength
// [3, 4] << fixedLengthWithOffset
// [1, 2, 3, 4, 5, 6, ...] << lengthTracking
// [3, 4, 5, 6, ...] << lengthTrackingWithOffset
assertEquals(expected([1, 2, 3, 4]), helper(fixedLength));
assertEquals(expected([3, 4]), helper(fixedLengthWithOffset));
assertEquals(expected([1, 2, 3, 4, 5, 6]), helper(lengthTracking));
assertEquals(expected([3, 4, 5, 6]), helper(lengthTrackingWithOffset));
// After detaching, all TAs behave like zero length.
%ArrayBufferDetach(rab);
assertEquals(expected([]), helper(fixedLength));
assertEquals(expected([]), helper(fixedLengthWithOffset));
assertEquals(expected([]), helper(lengthTracking));
assertEquals(expected([]), helper(lengthTrackingWithOffset));
}
})();
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