[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/+/3066941 Reviewed-by: Shu-yu Guo <syg@chromium.org> Commit-Queue: Shu-yu Guo <syg@chromium.org> Cr-Commit-Position: refs/heads/main@{#76611}
This commit is contained in:
parent
9cc414068e
commit
3926d6cde4
@ -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],
|
||||
|
Loading…
Reference in New Issue
Block a user