Commit 3926d6cd authored by legendecas's avatar legendecas Committed by V8 LUCI CQ

[builtins] typed array detaching in builtin iterations

%TypedArray.prototype% methods that receive a user callback
fn should not break in the mid-way of the iteration when the
backing array buffer was been detached. Instead, the iteration
should continue with the value set to undefined.

Notably, %TypedArray.prototype%.filter was throwing when the
backing buffer was detached during iteration. This should not
throw now.

Refs: https://github.com/tc39/ecma262/pull/2164
Bug: v8:4895
Change-Id: Ia7fab63264c8148a11f8f123b43c7b3ee0893300
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3066941Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76611}
parent 9cc41406
......@@ -45,13 +45,13 @@ void ArrayBuiltinsAssembler::TypedArrayMapResultGenerator() {
// See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map.
TNode<Object> ArrayBuiltinsAssembler::TypedArrayMapProcessor(
TNode<Object> k_value, TNode<UintPtrT> k) {
// 8. c. Let mapped_value be ? Call(callbackfn, T, « kValue, k, O »).
// 7c. Let mapped_value be ? Call(callbackfn, T, « kValue, k, O »).
TNode<Number> k_number = ChangeUintPtrToTagged(k);
TNode<Object> mapped_value =
Call(context(), callbackfn(), this_arg(), k_value, k_number, o());
Label fast(this), slow(this), done(this), detached(this, Label::kDeferred);
// 8. d. Perform ? Set(A, Pk, mapped_value, true).
// 7d. Perform ? Set(A, Pk, mapped_value, true).
// Since we know that A is a TypedArray, this always ends up in
// #sec-integer-indexed-exotic-objects-set-p-v-receiver and then
// tc39.github.io/ecma262/#sec-integerindexedelementset .
......@@ -59,9 +59,9 @@ TNode<Object> ArrayBuiltinsAssembler::TypedArrayMapProcessor(
BIND(&fast);
// #sec-integerindexedelementset
// 5. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let
// 2. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let
// numValue be ? ToBigInt(v).
// 6. Otherwise, let numValue be ? ToNumber(value).
// 3. Otherwise, let numValue be ? ToNumber(value).
TNode<Object> num_value;
if (source_elements_kind_ == BIGINT64_ELEMENTS ||
source_elements_kind_ == BIGUINT64_ELEMENTS) {
......@@ -175,24 +175,15 @@ void ArrayBuiltinsAssembler::GenerateIteratingTypedArrayBuiltinBody(
size_t i = 0;
for (auto it = labels.begin(); it != labels.end(); ++i, ++it) {
BIND(&*it);
Label done(this);
source_elements_kind_ = static_cast<ElementsKind>(elements_kinds[i]);
// TODO(turbofan): Silently cancelling the loop on buffer detachment is a
// spec violation. Should go to &throw_detached and throw a TypeError
// instead.
VisitAllTypedArrayElements(array_buffer, processor, &done, direction,
typed_array);
Goto(&done);
// No exception, return success
BIND(&done);
VisitAllTypedArrayElements(array_buffer, processor, direction, typed_array);
ReturnFromBuiltin(a_.value());
}
}
void ArrayBuiltinsAssembler::VisitAllTypedArrayElements(
TNode<JSArrayBuffer> array_buffer, const CallResultProcessor& processor,
Label* detached, ForEachDirection direction,
TNode<JSTypedArray> typed_array) {
ForEachDirection direction, TNode<JSTypedArray> typed_array) {
VariableList list({&a_, &k_}, zone());
TNode<UintPtrT> start = UintPtrConstant(0);
......@@ -208,12 +199,28 @@ void ArrayBuiltinsAssembler::VisitAllTypedArrayElements(
BuildFastLoop<UintPtrT>(
list, start, end,
[&](TNode<UintPtrT> index) {
GotoIf(IsDetachedBuffer(array_buffer), detached);
TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array);
TNode<Numeric> value = LoadFixedTypedArrayElementAsTagged(
data_ptr, index, source_elements_kind_);
k_ = index;
a_ = processor(this, value, index);
TVARIABLE(Object, value);
Label detached(this, Label::kDeferred);
Label process(this);
GotoIf(IsDetachedBuffer(array_buffer), &detached);
{
TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array);
value = LoadFixedTypedArrayElementAsTagged(data_ptr, index,
source_elements_kind_);
Goto(&process);
}
BIND(&detached);
{
value = UndefinedConstant();
Goto(&process);
}
BIND(&process);
{
k_ = index;
a_ = processor(this, value.value(), index);
}
},
incr, advance_mode);
}
......
......@@ -104,7 +104,7 @@ class ArrayBuiltinsAssembler : public CodeStubAssembler {
private:
void VisitAllTypedArrayElements(TNode<JSArrayBuffer> array_buffer,
const CallResultProcessor& processor,
Label* detached, ForEachDirection direction,
ForEachDirection direction,
TNode<JSTypedArray> typed_array);
TNode<Object> callbackfn_;
......
......@@ -7,24 +7,43 @@
namespace typed_array {
const kBuiltinNameEvery: constexpr string = '%TypedArray%.prototype.every';
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every
transitioning macro EveryAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Boolean {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
// 6. Repeat, while k < len
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// 6a. Let Pk be ! ToString(𝔽(k)).
// There is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
// kValue must be undefined when the buffer is detached.
let value: JSAny;
try {
witness.Recheck() otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
// 6c. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue,
// 𝔽(k), O »)).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
// 6d. If testResult is false, return false.
if (!ToBoolean(result)) {
return False;
}
// 6e. Set k to k + 1. (done by the loop).
}
// 7. Return true.
return True;
}
......
......@@ -38,11 +38,15 @@ transitioning javascript builtin TypedArrayPrototypeFilter(
// 8. Let captured be 0.
// 9. Repeat, while k < len
for (let k: uintptr = 0; k < len; k++) {
witness.Recheck() otherwise IsDetached;
let value: JSAny;
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
const value: JSAny = witness.Load(k);
try {
witness.Recheck() otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
// c. Let selected be ToBoolean(? Call(callbackfn, T, « kValue, k, O
// »)).
......@@ -57,7 +61,7 @@ transitioning javascript builtin TypedArrayPrototypeFilter(
// ii. Increase captured by 1.
if (ToBoolean(selected)) kept.Push(value);
// e.Increase k by 1.
// e. Increase k by 1. (done by the loop)
}
// 10. Let A be ? TypedArraySpeciesCreate(O, captured).
......
......@@ -7,24 +7,45 @@
namespace typed_array {
const kBuiltinNameFind: constexpr string = '%TypedArray%.prototype.find';
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.find
transitioning macro FindAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
array: typed_array::AttachedJSTypedArray, predicate: Callable,
thisArg: JSAny): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
// 6. Repeat, while k < len
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// 6a. Let Pk be ! ToString(𝔽(k)).
// There is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
// kValue must be undefined when the buffer is detached.
let value: JSAny;
try {
witness.Recheck() otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
// 6c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
context, predicate, thisArg, value, Convert<Number>(k),
witness.GetStable());
// 6d. If testResult is true, return kValue.
if (ToBoolean(result)) {
return value;
}
// 6e. Set k to k + 1. (done by the loop).
}
// 7. Return undefined.
return Undefined;
}
......@@ -39,9 +60,9 @@ TypedArrayPrototypeFind(
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const predicate = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return FindAllElements(uarray, callbackfn, thisArg);
return FindAllElements(uarray, predicate, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
......
......@@ -9,19 +9,33 @@ const kBuiltinNameFindIndex: constexpr string =
'%TypedArray%.prototype.findIndex';
transitioning macro FindIndexAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
array: typed_array::AttachedJSTypedArray, predicate: Callable,
thisArg: JSAny): Number {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
// 6. Repeat, while k < len
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// 6a. Let Pk be ! ToString(𝔽(k)).
// There is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
// kValue must be undefined when the buffer is detached.
let value: JSAny;
try {
witness.Recheck() otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
// 6c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const indexNumber: Number = Convert<Number>(k);
const result = Call(
context, callbackfn, thisArg, value, indexNumber, witness.GetStable());
context, predicate, thisArg, value, indexNumber, witness.GetStable());
if (ToBoolean(result)) {
return indexNumber;
}
......@@ -40,9 +54,9 @@ TypedArrayPrototypeFindIndex(
otherwise NotTypedArray;
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const predicate = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return FindIndexAllElements(uarray, callbackfn, thisArg);
return FindIndexAllElements(uarray, predicate, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
......
......@@ -8,56 +8,28 @@ namespace typed_array {
const kBuiltinNameFindLast: constexpr string =
'%TypedArray%.prototype.findLast';
// Continuation part of
// https://tc39.es/proposal-array-find-from-last/index.html#sec-%typedarray%.prototype.findlast
// when array buffer was detached.
transitioning builtin FindLastAllElementsDetachedContinuation(
implicit context: Context)(
array: JSTypedArray, predicate: Callable, thisArg: JSAny,
initialK: Number): JSAny {
// 6. Repeat, while k ≥ 0
for (let k: Number = initialK; k >= 0; k--) {
// 6a. Let Pk be ! ToString(𝔽(k)).
// there is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
// kValue must be undefined when the buffer was detached.
// 6c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result =
Call(context, predicate, thisArg, Undefined, Convert<Number>(k), array);
// 6d. If testResult is true, return kValue.
if (ToBoolean(result)) {
return Undefined;
}
// 6e. Set k to k - 1. (done by the loop).
}
// 7. Return undefined.
return Undefined;
}
// https://tc39.es/proposal-array-find-from-last/index.html#sec-%typedarray%.prototype.findlast
transitioning macro FindLastAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, predicate: Callable,
thisArg: JSAny): JSAny labels
Bailout(Number) {
thisArg: JSAny): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// 3. Let len be O.[[ArrayLength]].
const length: uintptr = witness.Get().length;
// 5. Let k be len - 1.
// 6. Repeat, while k ≥ 0
for (let k: uintptr = length; k-- > 0;) {
witness.Recheck() otherwise goto Bailout(Convert<Number>(k));
// 6a. Let Pk be ! ToString(𝔽(k)).
// there is no need to cast ToString to load elements.
// There is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
const value: JSAny = witness.Load(k);
// kValue must be undefined when the buffer was detached.
let value: JSAny;
try {
witness.Recheck() otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
// 6c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
......@@ -94,13 +66,7 @@ TypedArrayPrototypeFindLast(
// 4. If IsCallable(predicate) is false, throw a TypeError exception.
const predicate = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
try {
return FindLastAllElements(uarray, predicate, thisArg)
otherwise Bailout;
} label Bailout(k: Number) deferred {
return FindLastAllElementsDetachedContinuation(
uarray, predicate, thisArg, k);
}
return FindLastAllElements(uarray, predicate, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
......
......@@ -8,57 +8,28 @@ namespace typed_array {
const kBuiltinNameFindLastIndex: constexpr string =
'%TypedArray%.prototype.findIndexLast';
// Continuation part of
// https://tc39.es/proposal-array-find-from-last/index.html#sec-%typedarray%.prototype.findlastindex
// when array buffer was detached.
transitioning builtin FindLastIndexAllElementsDetachedContinuation(
implicit context: Context)(
array: JSTypedArray, predicate: Callable, thisArg: JSAny,
initialK: Number): Number {
// 6. Repeat, while k ≥ 0
for (let k: Number = initialK; k >= 0; k--) {
// 6a. Let Pk be ! ToString(𝔽(k)).
// there is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
// kValue must be undefined when the buffer was detached.
// 6c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const indexNumber: Number = Convert<Number>(k);
const result =
Call(context, predicate, thisArg, Undefined, indexNumber, array);
// 6d. If testResult is true, return 𝔽(k).
if (ToBoolean(result)) {
return indexNumber;
}
// 6e. Set k to k - 1. (done by the loop).
}
// 7. Return -1𝔽.
return -1;
}
// https://tc39.es/proposal-array-find-from-last/index.html#sec-%typedarray%.prototype.findlastindex
transitioning macro FindLastIndexAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, predicate: Callable,
thisArg: JSAny): Number labels
Bailout(Number) {
thisArg: JSAny): Number {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// 3. Let len be O.[[ArrayLength]].
const length: uintptr = witness.Get().length;
// 5. Let k be len - 1.
// 6. Repeat, while k ≥ 0
for (let k: uintptr = length; k-- > 0;) {
witness.Recheck() otherwise goto Bailout(Convert<Number>(k));
// 6a. Let Pk be ! ToString(𝔽(k)).
// there is no need to cast ToString to load elements.
// There is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
const value: JSAny = witness.Load(k);
// kValue must be undefined when the buffer was detached.
let value: JSAny;
try {
witness.Recheck() otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
// 6c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
......@@ -96,13 +67,7 @@ TypedArrayPrototypeFindLastIndex(
const predicate = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
try {
return FindLastIndexAllElements(uarray, predicate, thisArg)
otherwise Bailout;
} label Bailout(k: Number) deferred {
return FindLastIndexAllElementsDetachedContinuation(
uarray, predicate, thisArg, k);
}
return FindLastIndexAllElements(uarray, predicate, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
......
......@@ -12,16 +12,33 @@ transitioning macro ForEachAllElements(implicit context: Context)(
thisArg: JSAny): Undefined {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
// 6. Repeat, while k < len
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// 6a. Let Pk be ! ToString(𝔽(k)).
// There is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
// kValue must be undefined when the buffer is detached.
let value: JSAny;
try {
witness.Recheck() otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
// 6c. Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
// 6d. Set k to k + 1. (done by the loop).
}
// 7. Return undefined.
return Undefined;
}
......
......@@ -12,11 +12,17 @@ transitioning macro ReduceAllElements(implicit context: Context)(
initialValue: JSAny|TheHole): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
let accumulator = initialValue;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
let value: JSAny;
try {
witness.Recheck()
otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
typeswitch (accumulator) {
case (TheHole): {
accumulator = value;
......
......@@ -8,6 +8,7 @@ namespace typed_array {
const kBuiltinNameReduceRight: constexpr string =
'%TypedArray%.prototype.reduceRight';
// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduceright
transitioning macro ReduceRightAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
initialValue: JSAny|TheHole): JSAny {
......@@ -15,9 +16,14 @@ transitioning macro ReduceRightAllElements(implicit context: Context)(
const length: uintptr = witness.Get().length;
let accumulator = initialValue;
for (let k: uintptr = length; k-- > 0;) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
let value: JSAny;
try {
witness.Recheck()
otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
typeswitch (accumulator) {
case (TheHole): {
accumulator = value;
......
......@@ -7,24 +7,45 @@
namespace typed_array {
const kBuiltinNameSome: constexpr string = '%TypedArray%.prototype.some';
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.some
transitioning macro SomeAllElements(implicit context: Context)(
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Boolean {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
const length: uintptr = witness.Get().length;
// 6. Repeat, while k < len
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
// 6a. Let Pk be ! ToString(𝔽(k)).
// There is no need to cast ToString to load elements.
// 6b. Let kValue be ! Get(O, Pk).
// kValue must be undefined when the buffer is detached.
let value: JSAny;
try {
witness.Recheck() otherwise goto IsDetached;
value = witness.Load(k);
} label IsDetached deferred {
value = Undefined;
}
// 6c. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue,
// 𝔽(k), O »)).
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
// 6d. If testResult is true, return true.
if (ToBoolean(result)) {
return True;
}
// 6e. Set k to k + 1. (done by the loop).
}
// 7. Return false.
return False;
}
......@@ -41,6 +62,7 @@ TypedArrayPrototypeSome(
const callbackfn = Cast<Callable>(arguments[0]) otherwise NotCallable;
const thisArg = arguments[1];
return SomeAllElements(uarray, callbackfn, thisArg);
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
......
......@@ -82,18 +82,16 @@ function TestTypedArrayForEach(constructor) {
CheckWrapping({}, Object);
// Detaching the buffer backing the typed array mid-way should
// still make .forEach() finish, and the array should keep being
// still make .every() finish, and the array should keep being
// empty after detaching it.
count = 0;
a = new constructor(3);
result = a.every(function (n, index, array) {
assertFalse(array[index] === undefined); // don't get here if detached
if (count > 0) %ArrayBufferDetach(array.buffer);
array[index] = n + 1;
count++;
return count > 1 ? array[index] === undefined : true;
if (count > 1) %ArrayBufferDetach(array.buffer);
return count > 2 ? n === undefined : true;
});
assertEquals(2, count);
assertEquals(3, count);
assertEquals(true, result);
CheckTypedArrayIsDetached(a);
assertEquals(undefined, a[0]);
......
......@@ -19,10 +19,22 @@ function TestTypedArrayFilter(constructor) {
assertEquals(1, constructor.prototype.filter.length);
// Throw type error if source array is detached while executing a callback
let ta1 = new constructor(10);
assertThrows(() =>
ta1.filter(() => %ArrayBufferDetach(ta1.buffer))
, TypeError);
let ta1 = new constructor(4);
let seen = [];
let result = ta1.filter((val, idx) => {
if (idx === 0) {
%ArrayBufferDetach(ta1.buffer);
}
seen.push(val);
return idx < 3;
});
assertArrayEquals(seen, [0, undefined, undefined, undefined]);
// https://tc39.es/ecma262/#sec-setvalueinbuffer
// undefined values should be converted to numerics.
const expectedResult = [Float32Array, Float64Array].includes(constructor) ?
[0, NaN, NaN] :
[0, 0, 0];
assertArrayEquals(result, expectedResult);
// A new typed array should be created after finishing callbacks
var speciesCreated = 0;
......
......@@ -85,20 +85,18 @@ function TestTypedArrayForEach(constructor) {
assertEquals(42, a[1]);
// Detaching the buffer backing the typed array mid-way should
// still make .forEach() finish, but exiting early due to the missing
// elements, and the array should keep being empty after detaching it.
// TODO(dehrenberg): According to the ES6 spec, accessing or testing
// for members on a detached TypedArray should throw, so really this
// should throw in the third iteration. However, this behavior matches
// the Khronos spec.
// still make .forEach() finish, but the first argument of the callback
// should be undefined value, and the array should keep being empty after
// detaching it.
a = new constructor(3);
count = 0;
a.forEach(function (n, index, array) {
if (count > 0) %ArrayBufferDetach(array.buffer);
if (count > 1) assertTrue(n === undefined);
array[index] = n + 1;
count++;
});
assertEquals(2, count);
assertEquals(3, count);
CheckTypedArrayIsDetached(a);
assertEquals(undefined, a[0]);
......
......@@ -69,28 +69,8 @@
# https://code.google.com/p/v8/issues/detail?id=10958
'language/module-code/eval-gtbndng-indirect-faux-assertion': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=4895
# Some TypedArray methods throw due to the same bug, from Get
'built-ins/TypedArray/prototype/every/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/find/predicate-may-detach-buffer': [FAIL],
'built-ins/TypedArray/prototype/find/BigInt/predicate-may-detach-buffer': [FAIL],
'built-ins/TypedArray/prototype/findIndex/predicate-may-detach-buffer': [FAIL],
'built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer': [FAIL],
'built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/map/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/map/BigInt/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/reduce/BigInt/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/reduceRight/BigInt/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/set/array-arg-primitive-toobject': [FAIL],
'built-ins/TypedArray/prototype/set/BigInt/array-arg-primitive-toobject': [FAIL],
'built-ins/TypedArray/prototype/some/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/some/BigInt/callbackfn-detachbuffer': [FAIL],
# DataView functions should also throw on detached buffers
'built-ins/DataView/detached-buffer': [FAIL],
'built-ins/DataView/prototype/byteLength/detached-buffer': [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