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

[rab/gsab] TypedArray.prototype.slice: Support RAB / GSAB

Bug: v8:11111
Change-Id: I6a86dd1313a7bfb72024e9857a0c18dd6c83fe3c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3160518
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76980}
parent 8ee227d6
......@@ -191,8 +191,6 @@ static Object SliceHelper(BuiltinArguments args, Isolate* isolate,
// * [SAB] If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
CHECK_SHARED(is_shared, array_buffer, kMethodName);
CHECK_RESIZABLE(false, array_buffer, kMethodName);
// * [AB] If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (!is_shared && array_buffer->was_detached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
......@@ -203,7 +201,7 @@ static Object SliceHelper(BuiltinArguments args, Isolate* isolate,
// * [AB] Let len be O.[[ArrayBufferByteLength]].
// * [SAB] Let len be O.[[ArrayBufferByteLength]].
double const len = array_buffer->byte_length();
double const len = array_buffer->GetByteLength();
// * Let relativeStart be ? ToInteger(start).
Handle<Object> relative_start;
......@@ -215,7 +213,6 @@ static Object SliceHelper(BuiltinArguments args, Isolate* isolate,
double const first = (relative_start->Number() < 0)
? std::max(len + relative_start->Number(), 0.0)
: std::min(relative_start->Number(), len);
Handle<Object> first_obj = isolate->factory()->NewNumber(first);
// * If end is undefined, let relativeEnd be len; else let relativeEnd be ?
// ToInteger(end).
......@@ -279,6 +276,9 @@ static Object SliceHelper(BuiltinArguments args, Isolate* isolate,
Handle<JSArrayBuffer> new_array_buffer = Handle<JSArrayBuffer>::cast(new_);
CHECK_SHARED(is_shared, new_array_buffer, kMethodName);
// The created ArrayBuffer might or might not be resizable, since the species
// constructor might return a non-resizable or a resizable buffer.
// * [AB] If IsDetachedBuffer(new) is true, throw a TypeError exception.
if (!is_shared && new_array_buffer->was_detached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
......@@ -302,7 +302,8 @@ static Object SliceHelper(BuiltinArguments args, Isolate* isolate,
}
// * If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
if (new_array_buffer->byte_length() < new_len) {
size_t new_array_buffer_byte_length = new_array_buffer->GetByteLength();
if (new_array_buffer_byte_length < new_len) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(is_shared ? MessageTemplate::kSharedArrayBufferTooShort
......@@ -321,14 +322,22 @@ static Object SliceHelper(BuiltinArguments args, Isolate* isolate,
// * Let fromBuf be O.[[ArrayBufferData]].
// * Let toBuf be new.[[ArrayBufferData]].
// * Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
size_t first_size = 0, new_len_size = 0;
CHECK(TryNumberToSize(*first_obj, &first_size));
CHECK(TryNumberToSize(*new_len_obj, &new_len_size));
DCHECK(new_array_buffer->byte_length() >= new_len_size);
size_t first_size = first;
size_t new_len_size = new_len;
DCHECK(new_array_buffer_byte_length >= new_len_size);
if (new_len_size != 0) {
size_t from_byte_length = array_buffer->byte_length();
USE(from_byte_length);
size_t from_byte_length = array_buffer->GetByteLength();
if (V8_UNLIKELY(!is_shared && array_buffer->is_resizable())) {
// The above steps might have resized the underlying buffer. In that case,
// only copy the still-accessible portion of the underlying data.
if (first_size > from_byte_length) {
return *new_; // Nothing to copy.
}
if (new_len_size > from_byte_length - first_size) {
new_len_size = from_byte_length - first_size;
}
}
DCHECK(first_size <= from_byte_length);
DCHECK(from_byte_length - first_size >= new_len_size);
uint8_t* from_data =
......@@ -479,17 +488,7 @@ BUILTIN(SharedArrayBufferPrototypeGetByteLength) {
array_buffer->GetBackingStore()->max_byte_length());
// 4. Let length be ArrayBufferByteLength(O, SeqCst).
size_t byte_length;
if (array_buffer->is_resizable()) {
// Invariant: byte_length for GSAB is 0 (it needs to be read from the
// BackingStore).
DCHECK_EQ(0, array_buffer->byte_length());
byte_length =
array_buffer->GetBackingStore()->byte_length(std::memory_order_seq_cst);
} else {
byte_length = array_buffer->byte_length();
}
size_t byte_length = array_buffer->GetByteLength();
// 5. Return F(length).
return *isolate->factory()->NewNumberFromSize(byte_length);
}
......
......@@ -192,7 +192,10 @@ TNode<BoolT> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
TNode<BoolT> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
TNode<Int32T> kind) {
STATIC_ASSERT(BIGUINT64_ELEMENTS + 1 == BIGINT64_ELEMENTS);
return IsElementsKindInRange(kind, BIGUINT64_ELEMENTS, BIGINT64_ELEMENTS);
return Word32Or(
IsElementsKindInRange(kind, BIGUINT64_ELEMENTS, BIGINT64_ELEMENTS),
IsElementsKindInRange(kind, RAB_GSAB_BIGUINT64_ELEMENTS,
RAB_GSAB_BIGINT64_ELEMENTS));
}
TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
......@@ -406,13 +409,14 @@ void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
BIND(&if_##type##array); \
{ \
case_function(TYPE##_ELEMENTS, sizeof(ctype), 0); \
Goto(&next); \
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, NON_RAB_GSAB_TYPE) \
BIND(&if_##type##array); \
{ \
case_function(TYPE##_ELEMENTS, sizeof(ctype), \
Context::NON_RAB_GSAB_TYPE##_ARRAY_FUN_INDEX); \
Goto(&next); \
}
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS_WITH_NON_RAB_GSAB_ELEMENTS_KIND(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
BIND(&if_unknown_type);
......
......@@ -22,7 +22,10 @@ macro FastCopy(
// with the src because of custom species constructor. If the types
// of src and result array are the same and they are not sharing the
// same buffer, use memmove.
if (srcKind != destInfo.kind) goto IfSlow;
if (srcKind != destInfo.kind) {
// TODO(v8:11111): Enable the fast branch for RAB / GSAB.
goto IfSlow;
}
if (dest.buffer == src.buffer) {
goto IfSlow;
}
......@@ -63,11 +66,10 @@ transitioning javascript builtin TypedArrayPrototypeSlice(
// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
const src: JSTypedArray =
ValidateTypedArray(context, receiver, kBuiltinNameSlice);
// 3. Let len be O.[[ArrayLength]].
const len: uintptr = src.length;
const len =
ValidateTypedArrayAndGetLength(context, receiver, kBuiltinNameSlice);
const src: JSTypedArray = UnsafeCast<JSTypedArray>(receiver);
// 4. Let relativeStart be ? ToInteger(start).
// 5. If relativeStart < 0, let k be max((len + relativeStart), 0);
......@@ -81,11 +83,11 @@ transitioning javascript builtin TypedArrayPrototypeSlice(
// 7. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
// else let final be min(relativeEnd, len).
const end = arguments[1];
const final: uintptr =
let final: uintptr =
end != Undefined ? ConvertToRelativeIndex(end, len) : len;
// 8. Let count be max(final - k, 0).
const count: uintptr = Unsigned(IntPtrMax(Signed(final - k), 0));
let count: uintptr = Unsigned(IntPtrMax(Signed(final - k), 0));
// 9. Let A be ? TypedArraySpeciesCreate(O, « count »).
const dest: JSTypedArray =
......@@ -93,9 +95,19 @@ transitioning javascript builtin TypedArrayPrototypeSlice(
if (count > 0) {
try {
const srcAttached = typed_array::EnsureAttached(src)
otherwise IfDetached;
FastCopy(srcAttached, dest, k, count) otherwise IfSlow;
const newLength =
LoadJSTypedArrayLengthAndCheckDetached(src) otherwise IfDetached;
// If the backing buffer is a RAB, it's possible that the length has
// decreased since the last time we loaded it.
if (k >= newLength) {
return dest;
}
if (final > newLength) {
final = newLength;
count = Unsigned(IntPtrMax(Signed(final - k), 0));
}
FastCopy(%RawDownCast<AttachedJSTypedArray>(src), dest, k, count)
otherwise IfSlow;
} label IfDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSlice);
} label IfSlow deferred {
......
......@@ -176,6 +176,7 @@ transient type AttachedJSTypedArray extends JSTypedArray;
macro EnsureAttached(array: JSTypedArray): AttachedJSTypedArray
labels Detached {
// TODO(v8:11111): Add OOB check for RAB / GSAB.
if (IsDetachedBuffer(array.buffer)) goto Detached;
return %RawDownCast<AttachedJSTypedArray>(array);
}
......
......@@ -58,6 +58,23 @@ namespace internal {
V(BigUint64, rab_gsab_biguint64, RAB_GSAB_BIGUINT64, uint64_t) \
V(BigInt64, rab_gsab_bigint64, RAB_GSAB_BIGINT64, int64_t)
// Like RAB_GSAB_TYPED_ARRAYS but has an additional parameter for
// for the corresponding non-RAB/GSAB ElementsKind.
#define RAB_GSAB_TYPED_ARRAYS_WITH_NON_RAB_GSAB_ELEMENTS_KIND(V) \
V(RabGsabUint8, rab_gsab_uint8, RAB_GSAB_UINT8, uint8_t, UINT8) \
V(RabGsabInt8, rab_gsab_int8, RAB_GSAB_INT8, int8_t, INT8) \
V(RabGsabUint16, rab_gsab_uint16, RAB_GSAB_UINT16, uint16_t, UINT16) \
V(RabGsabInt16, rab_gsab_int16, RAB_GSAB_INT16, int16_t, INT16) \
V(RabGsabUint32, rab_gsab_uint32, RAB_GSAB_UINT32, uint32_t, UINT32) \
V(RabGsabInt32, rab_gsab_int32, RAB_GSAB_INT32, int32_t, INT32) \
V(RabGsabFloat32, rab_gsab_float32, RAB_GSAB_FLOAT32, float, FLOAT32) \
V(RabGsabFloat64, rab_gsab_float64, RAB_GSAB_FLOAT64, double, FLOAT64) \
V(RabGsabUint8Clamped, rab_gsab_uint8_clamped, RAB_GSAB_UINT8_CLAMPED, \
uint8_t, UINT8_CLAMPED) \
V(RabGsabBigUint64, rab_gsab_biguint64, RAB_GSAB_BIGUINT64, uint64_t, \
BIGUINT64) \
V(RabGsabBigInt64, rab_gsab_bigint64, RAB_GSAB_BIGINT64, int64_t, BIGINT64)
enum ElementsKind : uint8_t {
// The "fast" kind for elements that only contain SMI values. Must be first
// to make it possible to efficiently check maps for this kind.
......
......@@ -3541,7 +3541,7 @@ class TypedElementsAccessor
CHECK(!source.WasDetached());
CHECK(!destination.WasDetached());
DCHECK_LE(start, end);
DCHECK_LE(end, source.length());
DCHECK_LE(end, source.GetLength());
size_t count = end - start;
DCHECK_LE(count, destination.length());
ElementType* dest_data = static_cast<ElementType*>(destination.DataPtr());
......@@ -3559,6 +3559,16 @@ class TypedElementsAccessor
}
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, NON_RAB_GSAB_TYPE) \
case TYPE##_ELEMENTS: { \
ctype* source_data = reinterpret_cast<ctype*>(source.DataPtr()) + start; \
CopyBetweenBackingStores<NON_RAB_GSAB_TYPE##_ELEMENTS, ctype>( \
source_data, dest_data, count, is_shared); \
break; \
}
RAB_GSAB_TYPED_ARRAYS_WITH_NON_RAB_GSAB_ELEMENTS_KIND(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
default:
UNREACHABLE();
break;
......
......@@ -127,9 +127,20 @@ void JSArrayBuffer::Detach(bool force_for_wasm_memory) {
set_was_detached(true);
}
std::shared_ptr<BackingStore> JSArrayBuffer::GetBackingStore() {
if (!extension()) return nullptr;
return extension()->backing_store();
std::shared_ptr<BackingStore> JSArrayBuffer::GetBackingStore() const {
if (!extension()) return nullptr;
return extension()->backing_store();
}
size_t JSArrayBuffer::GetByteLength() const {
if V8_UNLIKELY (is_shared() && is_resizable()) {
// Invariant: byte_length for GSAB is 0 (it needs to be read from the
// BackingStore).
DCHECK_EQ(0, byte_length());
return GetBackingStore()->byte_length(std::memory_order_seq_cst);
}
return byte_length();
}
ArrayBufferExtension* JSArrayBuffer::EnsureExtension() {
......
......@@ -104,7 +104,9 @@ class JSArrayBuffer
// Get a reference to backing store of this array buffer, if there is a
// backing store. Returns nullptr if there is no backing store (e.g. detached
// or a zero-length array buffer).
std::shared_ptr<BackingStore> GetBackingStore();
std::shared_ptr<BackingStore> GetBackingStore() const;
size_t GetByteLength() const;
// Allocates an ArrayBufferExtension for this array buffer, unless it is
// already associated with an extension.
......
......@@ -6,6 +6,8 @@
"use strict";
d8.file.execute('test/mjsunit/typedarray-helpers.js');
function CreateResizableArrayBuffer(byteLength, maxByteLength) {
return new ArrayBuffer(byteLength, {maxByteLength: maxByteLength});
}
......@@ -52,11 +54,11 @@ function growHelper(ab, value) {
assertEquals(0, rab.maxByteLength);
})();
const ctors = [[ArrayBuffer, (b) => b.resizable],
[SharedArrayBuffer, (b) => b.growable]];
const arrayBufferCtors = [[ArrayBuffer, (b) => b.resizable],
[SharedArrayBuffer, (b) => b.growable]];
(function TestOptionsBagNotObject() {
for (let [ctor, resizable] of ctors) {
for (let [ctor, resizable] of arrayBufferCtors) {
const buffer = new ctor(10, 'this is not an options bag');
assertFalse(resizable(buffer));
}
......@@ -66,7 +68,7 @@ const ctors = [[ArrayBuffer, (b) => b.resizable],
let evil = {};
Object.defineProperty(evil, 'maxByteLength',
{get: () => { throw new Error('thrown'); }});
for (let [ctor, resizable] of ctors) {
for (let [ctor, resizable] of arrayBufferCtors) {
let caught = false;
try {
new ctor(10, evil);
......@@ -79,14 +81,14 @@ const ctors = [[ArrayBuffer, (b) => b.resizable],
})();
(function TestMaxByteLengthNonExisting() {
for (let [ctor, resizable] of ctors) {
for (let [ctor, resizable] of arrayBufferCtors) {
const buffer = new ctor(10, {});
assertFalse(resizable(buffer));
}
})();
(function TestMaxByteLengthUndefinedOrNan() {
for (let [ctor, resizable] of ctors) {
for (let [ctor, resizable] of arrayBufferCtors) {
const buffer1 = new ctor(10, {maxByteLength: undefined});
assertFalse(resizable(buffer1));
const buffer2 = new ctor(0, {maxByteLength: NaN});
......@@ -97,7 +99,7 @@ const ctors = [[ArrayBuffer, (b) => b.resizable],
})();
(function TestMaxByteLengthBooleanNullOrString() {
for (let [ctor, resizable] of ctors) {
for (let [ctor, resizable] of arrayBufferCtors) {
const buffer1 = new ctor(0, {maxByteLength: true});
assertTrue(resizable(buffer1));
assertEquals(0, buffer1.byteLength);
......@@ -118,7 +120,7 @@ const ctors = [[ArrayBuffer, (b) => b.resizable],
})();
(function TestMaxByteLengthDouble() {
for (let [ctor, resizable] of ctors) {
for (let [ctor, resizable] of arrayBufferCtors) {
const buffer1 = new ctor(0, {maxByteLength: -0.0});
assertTrue(resizable(buffer1));
assertEquals(0, buffer1.byteLength);
......@@ -138,7 +140,7 @@ const ctors = [[ArrayBuffer, (b) => b.resizable],
(function TestMaxByteLengthThrows() {
const evil = {valueOf: () => { throw new Error('thrown');}};
for (let [ctor, resizable] of ctors) {
for (let [ctor, resizable] of arrayBufferCtors) {
let caught = false;
try {
new ctor(0, {maxByteLength: evil});
......@@ -153,7 +155,7 @@ const ctors = [[ArrayBuffer, (b) => b.resizable],
(function TestByteLengthThrows() {
const evil1 = {valueOf: () => { throw new Error('byteLength throws');}};
const evil2 = {valueOf: () => { throw new Error('maxByteLength throws');}};
for (let [ctor, resizable] of ctors) {
for (let [ctor, resizable] of arrayBufferCtors) {
let caught = false;
try {
new ctor(evil1, {maxByteLength: evil2});
......@@ -544,3 +546,73 @@ const ctors = [[ArrayBuffer, (b) => b.resizable],
assertEquals('ok', w.getMessage());
assertEquals(15, gsab.byteLength);
})();
(function Slice() {
const rab = CreateResizableArrayBuffer(10, 20);
const sliced1 = rab.slice();
assertEquals(10, sliced1.byteLength);
assertTrue(sliced1 instanceof ArrayBuffer);
assertFalse(sliced1 instanceof SharedArrayBuffer);
assertFalse(sliced1.resizable);
const gsab = CreateGrowableSharedArrayBuffer(10, 20);
const sliced2 = gsab.slice();
assertEquals(10, sliced2.byteLength);
assertFalse(sliced2 instanceof ArrayBuffer);
assertTrue(sliced2 instanceof SharedArrayBuffer);
assertFalse(sliced2.growable);
})();
(function SliceSpeciesConstructorReturnsResizable() {
class MyArrayBuffer extends ArrayBuffer {
static get [Symbol.species]() { return MyResizableArrayBuffer; }
}
class MyResizableArrayBuffer extends ArrayBuffer {
constructor(byteLength) {
super(byteLength, {maxByteLength: byteLength * 2});
}
}
const ab = new MyArrayBuffer(20);
const sliced1 = ab.slice();
assertTrue(sliced1.resizable);
class MySharedArrayBuffer extends SharedArrayBuffer {
static get [Symbol.species]() { return MyGrowableSharedArrayBuffer; }
}
class MyGrowableSharedArrayBuffer extends SharedArrayBuffer {
constructor(byteLength) {
super(byteLength, {maxByteLength: byteLength * 2});
}
}
const sab = new MySharedArrayBuffer(20);
const sliced2 = sab.slice();
assertTrue(sliced2.growable);
})();
(function SliceSpeciesConstructorResizes() {
let rab;
let resizeWhenConstructorCalled = false;
class MyArrayBuffer extends ArrayBuffer {
constructor(...params) {
super(...params);
if (resizeWhenConstructorCalled) {
rab.resize(2);
}
}
}
rab = new MyArrayBuffer(4, {maxByteLength: 8});
const taWrite = new Uint8Array(rab);
for (let i = 0; i < 4; ++i) {
taWrite[i] = 1;
}
assertEquals([1, 1, 1, 1], ToNumbers(taWrite));
resizeWhenConstructorCalled = true;
const sliced = rab.slice();
assertEquals(2, rab.byteLength);
assertEquals(4, sliced.byteLength);
assertEquals([1, 1, 0, 0], ToNumbers(new Uint8Array(sliced)));
})();
......@@ -717,3 +717,116 @@ function TestIterationAndGrow(ta, expected, gsab, grow_after,
assertEquals(0, AtHelper(lengthTrackingWithOffset, -1));
}
})();
(function Slice() {
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, i);
}
const fixedLengthSlice = fixedLength.slice();
assertEquals([0, 1, 2, 3], ToNumbers(fixedLengthSlice));
assertTrue(fixedLengthSlice.buffer instanceof ArrayBuffer);
assertFalse(fixedLengthSlice.buffer instanceof SharedArrayBuffer);
assertFalse(fixedLengthSlice.buffer.resizable);
const fixedLengthWithOffsetSlice = fixedLengthWithOffset.slice();
assertEquals([2, 3], ToNumbers(fixedLengthWithOffsetSlice));
assertTrue(fixedLengthWithOffsetSlice.buffer instanceof ArrayBuffer);
assertFalse(fixedLengthWithOffsetSlice.buffer instanceof SharedArrayBuffer);
assertFalse(fixedLengthWithOffsetSlice.buffer.resizable);
const lengthTrackingSlice = lengthTracking.slice();
assertEquals([0, 1, 2, 3], ToNumbers(lengthTrackingSlice));
assertTrue(lengthTrackingSlice.buffer instanceof ArrayBuffer);
assertFalse(lengthTrackingSlice.buffer instanceof SharedArrayBuffer);
assertFalse(lengthTrackingSlice.buffer.resizable);
const lengthTrackingWithOffsetSlice = lengthTrackingWithOffset.slice();
assertEquals([2, 3], ToNumbers(lengthTrackingWithOffsetSlice));
assertTrue(lengthTrackingWithOffsetSlice.buffer instanceof ArrayBuffer);
assertFalse(lengthTrackingWithOffsetSlice.buffer instanceof
SharedArrayBuffer);
assertFalse(lengthTrackingWithOffsetSlice.buffer.resizable);
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
assertEquals([0, 1, 2, 3], ToNumbers(fixedLength.slice()));
assertEquals([2, 3], ToNumbers(fixedLengthWithOffset.slice()));
assertEquals([0, 1, 2, 3, 0, 0], ToNumbers(lengthTracking.slice()));
assertEquals([2, 3, 0, 0], ToNumbers(lengthTrackingWithOffset.slice()));
// Verify that the previously created slices aren't affected by the growing.
assertEquals([0, 1, 2, 3], ToNumbers(fixedLengthSlice));
assertEquals([2, 3], ToNumbers(fixedLengthWithOffsetSlice));
assertEquals([0, 1, 2, 3], ToNumbers(lengthTrackingSlice));
assertEquals([2, 3], ToNumbers(lengthTrackingWithOffsetSlice));
}
})();
(function SliceSpeciesCreateResizes() {
for (let ctor of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const taWrite = new ctor(gsab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 1);
}
let resizeWhenConstructorCalled = false;
class MyArray extends ctor {
constructor(...params) {
super(...params);
if (resizeWhenConstructorCalled) {
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
}
}
};
const fixedLength = new MyArray(gsab, 0, 4);
resizeWhenConstructorCalled = true;
const a = fixedLength.slice();
assertEquals(4, a.length);
assertEquals([1, 1, 1, 1], ToNumbers(a));
assertEquals(6 * ctor.BYTES_PER_ELEMENT, gsab.byteLength);
}
for (let ctor of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const taWrite = new ctor(gsab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 1);
}
let resizeWhenConstructorCalled = false;
class MyArray extends ctor {
constructor(...params) {
super(...params);
if (resizeWhenConstructorCalled) {
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
}
}
};
const lengthTracking = new MyArray(gsab);
resizeWhenConstructorCalled = true;
const a = lengthTracking.slice();
assertEquals(6 * ctor.BYTES_PER_ELEMENT, gsab.byteLength);
// The length of the resulting TypedArray is determined before
// TypedArraySpeciesCreate is called, and it doesn't change.
assertEquals(4, a.length);
assertEquals([1, 1, 1, 1], ToNumbers(a));
}
})();
......@@ -1224,3 +1224,189 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
assertEquals(undefined, AtHelper(lengthTracking, evil));
}
})();
(function Slice() {
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, i);
}
const fixedLengthSlice = fixedLength.slice();
assertEquals([0, 1, 2, 3], ToNumbers(fixedLengthSlice));
assertFalse(fixedLengthSlice.buffer.resizable);
const fixedLengthWithOffsetSlice = fixedLengthWithOffset.slice();
assertEquals([2, 3], ToNumbers(fixedLengthWithOffsetSlice));
assertFalse(fixedLengthWithOffsetSlice.buffer.resizable);
const lengthTrackingSlice = lengthTracking.slice();
assertEquals([0, 1, 2, 3], ToNumbers(lengthTrackingSlice));
assertFalse(lengthTrackingSlice.buffer.resizable);
const lengthTrackingWithOffsetSlice = lengthTrackingWithOffset.slice();
assertEquals([2, 3], ToNumbers(lengthTrackingWithOffsetSlice));
assertFalse(lengthTrackingWithOffsetSlice.buffer.resizable);
// Shrink so that fixed length TAs go out of bounds.
rab.resize(3 * ctor.BYTES_PER_ELEMENT);
assertThrows(() => { fixedLength.slice(); });
assertThrows(() => { fixedLengthWithOffset.slice(); });
assertEquals([0, 1, 2], ToNumbers(lengthTracking.slice()));
assertEquals([2], ToNumbers(lengthTrackingWithOffset.slice()));
// Shrink so that the TAs with offset go out of bounds.
rab.resize(1 * ctor.BYTES_PER_ELEMENT);
assertThrows(() => { fixedLength.slice(); });
assertThrows(() => { fixedLengthWithOffset.slice(); });
assertEquals([0], ToNumbers(lengthTracking.slice()));
assertThrows(() => { lengthTrackingWithOffset.slice(); });
// Shrink to zero.
rab.resize(0);
assertThrows(() => { fixedLength.slice(); });
assertThrows(() => { fixedLengthWithOffset.slice(); });
assertEquals([], ToNumbers(lengthTracking.slice()));
assertThrows(() => { lengthTrackingWithOffset.slice(); });
// Verify that the previously created slices aren't affected by the
// shrinking.
assertEquals([0, 1, 2, 3], ToNumbers(fixedLengthSlice));
assertEquals([2, 3], ToNumbers(fixedLengthWithOffsetSlice));
assertEquals([0, 1, 2, 3], ToNumbers(lengthTrackingSlice));
assertEquals([2, 3], ToNumbers(lengthTrackingWithOffsetSlice));
// Grow so that all TAs are back in-bounds. New memory is zeroed.
rab.resize(6 * ctor.BYTES_PER_ELEMENT);
assertEquals([0, 0, 0, 0], ToNumbers(fixedLength.slice()));
assertEquals([0, 0], ToNumbers(fixedLengthWithOffset.slice()));
assertEquals([0, 0, 0, 0, 0, 0], ToNumbers(lengthTracking.slice()));
assertEquals([0, 0, 0, 0], ToNumbers(lengthTrackingWithOffset.slice()));
}
})();
(function SliceSpeciesCreateResizes() {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
let resizeWhenConstructorCalled = false;
class MyArray extends ctor {
constructor(...params) {
super(...params);
if (resizeWhenConstructorCalled) {
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
}
}
};
const fixedLength = new MyArray(rab, 0, 4);
resizeWhenConstructorCalled = true;
assertThrows(() => { fixedLength.slice(); }, TypeError);
assertEquals(2 * ctor.BYTES_PER_ELEMENT, rab.byteLength);
}
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 1);
}
let resizeWhenConstructorCalled = false;
class MyArray extends ctor {
constructor(...params) {
super(...params);
if (resizeWhenConstructorCalled) {
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
}
}
};
const lengthTracking = new MyArray(rab);
resizeWhenConstructorCalled = true;
const a = lengthTracking.slice();
assertEquals(2 * ctor.BYTES_PER_ELEMENT, rab.byteLength);
// The length of the resulting TypedArray is determined before
// TypedArraySpeciesCreate is called, and it doesn't change.
assertEquals(4, a.length);
assertEquals([1, 1, 0, 0], ToNumbers(a));
}
// Test that the (start, end) parameters are computed based on the original
// length.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 1);
}
let resizeWhenConstructorCalled = false;
class MyArray extends ctor {
constructor(...params) {
super(...params);
if (resizeWhenConstructorCalled) {
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
}
}
};
const lengthTracking = new MyArray(rab);
resizeWhenConstructorCalled = true;
const a = lengthTracking.slice(-3, -1);
assertEquals(2 * ctor.BYTES_PER_ELEMENT, rab.byteLength);
// The length of the resulting TypedArray is determined before
// TypedArraySpeciesCreate is called, and it doesn't change.
assertEquals(2, a.length);
assertEquals([1, 0], ToNumbers(a));
}
// Test where the buffer gets resized "between elements".
{
const rab = CreateResizableArrayBuffer(8, 16);
// Fill the buffer with 1-bits.
const taWrite = new Uint8Array(rab);
for (let i = 0; i < 8; ++i) {
WriteToTypedArray(taWrite, i, 255);
}
let resizeWhenConstructorCalled = false;
class MyArray extends Uint16Array {
constructor(...params) {
super(...params);
if (resizeWhenConstructorCalled) {
// Resize so that the size is not a multiple of the element size.
rab.resize(5);
}
}
};
const lengthTracking = new MyArray(rab);
assertEquals([65535, 65535, 65535, 65535], ToNumbers(lengthTracking));
resizeWhenConstructorCalled = true;
const a = lengthTracking.slice();
assertEquals(5, rab.byteLength);
assertEquals(4, a.length); // The old length is used.
assertEquals(65535, a[0]);
assertEquals(65535, a[1]);
assertEquals(0, a[2]);
assertEquals(0, a[3]);
}
})();
......@@ -304,13 +304,6 @@
'language/module-code/export-expname-binding-index': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=11111
'built-ins/ArrayBuffer/prototype/resize/resize-grow': [FAIL],
'built-ins/ArrayBuffer/prototype/resize/resize-same-size': [FAIL],
'built-ins/ArrayBuffer/prototype/resize/resize-same-size-zero-explicit': [FAIL],
'built-ins/ArrayBuffer/prototype/resize/resize-same-size-zero-implicit': [FAIL],
'built-ins/ArrayBuffer/prototype/resize/resize-shrink': [FAIL],
'built-ins/ArrayBuffer/prototype/resize/resize-shrink-zero-explicit': [FAIL],
'built-ins/ArrayBuffer/prototype/resize/resize-shrink-zero-implicit': [FAIL],
'built-ins/ArrayBuffer/prototype/transfer/*': [FAIL],
'built-ins/ArrayBuffer/prototype/transfer/this-is-sharedarraybuffer': [PASS],
'built-ins/DataView/prototype/byteLength/resizable-array-buffer-auto': [FAIL],
......@@ -368,18 +361,17 @@
'built-ins/TypedArray/prototype/keys/return-abrupt-from-this-out-of-bounds': [FAIL],
'built-ins/TypedArray/prototype/lastIndexOf/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/lastIndexOf/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/map/BigInt/return-abrupt-from-this-out-of-bounds': [FAIL],
'built-ins/TypedArray/prototype/map/return-abrupt-from-this-out-of-bounds': [FAIL],
'built-ins/TypedArray/prototype/map/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/map/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/reduce/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/reduce/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/reduceRight/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/reduceRight/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/reverse/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/reverse/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/set/BigInt/typedarray-arg-set-values-same-buffer-same-type-resized': [FAIL],
'built-ins/TypedArray/prototype/set/typedarray-arg-target-out-of-bounds': [FAIL],
'built-ins/TypedArray/prototype/slice/BigInt/return-abrupt-from-this-out-of-bounds': [FAIL],
'built-ins/TypedArray/prototype/slice/return-abrupt-from-this-out-of-bounds': [FAIL],
'built-ins/TypedArray/prototype/set/BigInt/typedarray-arg-target-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/set/BigInt/typedarray-arg-set-values-same-buffer-same-type-resized': [SKIP],
'built-ins/TypedArray/prototype/set/typedarray-arg-target-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/some/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/some/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/sort/BigInt/return-abrupt-from-this-out-of-bounds': [FAIL],
......
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