[builtins] Implement Array#findFromLast and friends

This proposal reached Stage 3 at the July 2021 TC39.

https://github.com/tc39/proposal-array-find-from-last

Bug: v8:11990
Change-Id: I1364b46b7ed4bc56e4b3024d14bde799f9878b5a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3037160
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76007}
This commit is contained in:
legendecas 2021-07-24 16:22:43 +08:00 committed by V8 LUCI CQ
parent 5e4e0126f1
commit d63ca69c22
14 changed files with 1019 additions and 22 deletions

View File

@ -617,6 +617,8 @@ filegroup(
"src/builtins/array-filter.tq",
"src/builtins/array-find.tq",
"src/builtins/array-findindex.tq",
"src/builtins/array-findlast.tq",
"src/builtins/array-findlastindex.tq",
"src/builtins/array-foreach.tq",
"src/builtins/array-from.tq",
"src/builtins/array-isarray.tq",
@ -718,6 +720,8 @@ filegroup(
"src/builtins/typed-array-filter.tq",
"src/builtins/typed-array-find.tq",
"src/builtins/typed-array-findindex.tq",
"src/builtins/typed-array-findlast.tq",
"src/builtins/typed-array-findlastindex.tq",
"src/builtins/typed-array-foreach.tq",
"src/builtins/typed-array-from.tq",
"src/builtins/typed-array-keys.tq",

View File

@ -1413,6 +1413,8 @@ torque_files = [
"src/builtins/array-filter.tq",
"src/builtins/array-find.tq",
"src/builtins/array-findindex.tq",
"src/builtins/array-findlast.tq",
"src/builtins/array-findlastindex.tq",
"src/builtins/array-foreach.tq",
"src/builtins/array-from.tq",
"src/builtins/array-isarray.tq",
@ -1514,6 +1516,8 @@ torque_files = [
"src/builtins/typed-array-filter.tq",
"src/builtins/typed-array-find.tq",
"src/builtins/typed-array-findindex.tq",
"src/builtins/typed-array-findlast.tq",
"src/builtins/typed-array-findlastindex.tq",
"src/builtins/typed-array-foreach.tq",
"src/builtins/typed-array-from.tq",
"src/builtins/typed-array-keys.tq",

View File

@ -0,0 +1,110 @@
// Copyright 2021 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.
namespace array {
// https://tc39.es/proposal-array-find-from-last/index.html#sec-array.prototype.findlast
transitioning builtin ArrayFindLastLoopContinuation(implicit context: Context)(
predicate: Callable, thisArg: JSAny, o: JSReceiver,
initialK: Number): JSAny {
// 5. Repeat, while k >= 0
for (let k: Number = initialK; k >= 0; k--) {
// 5a. Let Pk be ! ToString(𝔽(k)).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 5b. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
// 5c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
const testResult: JSAny = Call(context, predicate, thisArg, value, k, o);
// 5d. If testResult is true, return kValue.
if (ToBoolean(testResult)) {
return value;
}
// 5e. Set k to k - 1. (done by the loop).
}
// 6. Return undefined.
return Undefined;
}
// https://tc39.es/proposal-array-find-from-last/index.html#sec-array.prototype.findlast
transitioning macro FastArrayFindLast(implicit context: Context)(
o: JSReceiver, len: Number, predicate: Callable, thisArg: JSAny): JSAny
labels Bailout(Number) {
const smiLen = Cast<Smi>(len) otherwise goto Bailout(len - 1);
// 4. Let k be len - 1.
let k: Smi = smiLen - 1;
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// 5. Repeat, while k ≥ 0
// Build a fast loop over the smi array.
for (; k >= 0; k--) {
fastOW.Recheck() otherwise goto Bailout(k);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
// 5a. Let Pk be ! ToString(𝔽(k)).
// k is guaranteed to be a positive integer, hence there is no need to
// cast ToString for LoadElementOrUndefined.
// 5b. Let kValue be ? Get(O, Pk).
const value: JSAny = fastOW.LoadElementOrUndefined(k);
// 5c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
const testResult: JSAny =
Call(context, predicate, thisArg, value, k, fastOW.Get());
// 5d. If testResult is true, return kValue.
if (ToBoolean(testResult)) {
return value;
}
// 5e. Set k to k - 1. (done by the loop).
}
// 6. Return undefined.
return Undefined;
}
// https://tc39.es/proposal-array-find-from-last/index.html#sec-array.prototype.findlast
transitioning javascript builtin
ArrayPrototypeFindLast(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.findLast');
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? LengthOfArrayLike(O).
const len: Number = GetLengthProperty(o);
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NotCallableError;
}
const predicate = Cast<Callable>(arguments[0]) otherwise NotCallableError;
// If a thisArg parameter is provided, it will be used as the this value for
// each invocation of predicate. If it is not provided, undefined is used
// instead.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
return FastArrayFindLast(o, len, predicate, thisArg)
otherwise Bailout;
} label Bailout(k: Number) deferred {
return ArrayFindLastLoopContinuation(predicate, thisArg, o, k);
}
} label NotCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -0,0 +1,111 @@
// Copyright 2021 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.
namespace array {
// https://tc39.es/proposal-array-find-from-last/index.html#sec-array.prototype.findlastindex
transitioning builtin ArrayFindLastIndexLoopContinuation(
implicit context: Context)(
predicate: Callable, thisArg: JSAny, o: JSReceiver,
initialK: Number): Number {
// 5. Repeat, while k >= 0
for (let k: Number = initialK; k >= 0; k--) {
// 5a. Let Pk be ! ToString(𝔽(k)).
// k is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
// 5b. Let kValue be ? Get(O, Pk).
const value: JSAny = GetProperty(o, k);
// 5c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
const testResult: JSAny = Call(context, predicate, thisArg, value, k, o);
// 5d. If testResult is true, return 𝔽(k).
if (ToBoolean(testResult)) {
return k;
}
// 5e. Set k to k - 1. (done by the loop).
}
// 6. Return -1𝔽.
return Convert<Smi>(-1);
}
// https://tc39.es/proposal-array-find-from-last/index.html#sec-array.prototype.findlastindex
transitioning macro FastArrayFindLastIndex(implicit context: Context)(
o: JSReceiver, len: Number, predicate: Callable, thisArg: JSAny): Number
labels Bailout(Number) {
const smiLen = Cast<Smi>(len) otherwise goto Bailout(len - 1);
// 4. Let k be len - 1.
let k: Smi = smiLen - 1;
const fastO = Cast<FastJSArray>(o) otherwise goto Bailout(k);
let fastOW = NewFastJSArrayWitness(fastO);
// 5. Repeat, while k ≥ 0
// Build a fast loop over the smi array.
for (; k >= 0; k--) {
fastOW.Recheck() otherwise goto Bailout(k);
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= fastOW.Get().length) goto Bailout(k);
// 5a. Let Pk be ! ToString(𝔽(k)).
// k is guaranteed to be a positive integer, hence there is no need to
// cast ToString for LoadElementOrUndefined.
// 5b. Let kValue be ? Get(O, Pk).
const value: JSAny = fastOW.LoadElementOrUndefined(k);
// 5c. Let testResult be ! ToBoolean(? Call(predicate, thisArg, « kValue,
// 𝔽(k), O »)).
const testResult: JSAny =
Call(context, predicate, thisArg, value, k, fastOW.Get());
// 5d. If testResult is true, return 𝔽(k).
if (ToBoolean(testResult)) {
return k;
}
// 5e. Set k to k - 1. (done by the loop).
}
// 6. Return -1𝔽.
return -1;
}
// https://tc39.es/proposal-array-find-from-last/index.html#sec-array.prototype.findlastindex
transitioning javascript builtin
ArrayPrototypeFindLastIndex(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
RequireObjectCoercible(receiver, 'Array.prototype.findLastIndex');
// 1. Let O be ? ToObject(this value).
const o: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? LengthOfArrayLike(O).
const len: Number = GetLengthProperty(o);
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto NotCallableError;
}
const predicate = Cast<Callable>(arguments[0]) otherwise NotCallableError;
// If a thisArg parameter is provided, it will be used as the this value for
// each invocation of predicate. If it is not provided, undefined is used
// instead.
const thisArg: JSAny = arguments[1];
// Special cases.
try {
return FastArrayFindLastIndex(o, len, predicate, thisArg)
otherwise Bailout;
} label Bailout(k: Number) deferred {
return ArrayFindLastIndexLoopContinuation(predicate, thisArg, o, k);
}
} label NotCallableError deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
}
}
}

View File

@ -0,0 +1,112 @@
// Copyright 2021 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.
#include 'src/builtins/builtins-typed-array-gen.h'
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) {
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.
// 6b. Let kValue be ! Get(O, Pk).
const value: JSAny = witness.Load(k);
// 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, 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;
}
// https://tc39.es/proposal-array-find-from-last/index.html#sec-%typedarray%.prototype.findlast
transitioning javascript builtin
TypedArrayPrototypeFindLast(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg
try {
// 1. Let O be the this value.
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
// 2. Perform ? ValidateTypedArray(O).
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
// 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);
}
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFindLast);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFindLast);
}
}
}

View File

@ -0,0 +1,115 @@
// Copyright 2021 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.
#include 'src/builtins/builtins-typed-array-gen.h'
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) {
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.
// 6b. Let kValue be ! Get(O, Pk).
const value: JSAny = witness.Load(k);
// 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, value, indexNumber, witness.GetStable());
// 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 javascript builtin
TypedArrayPrototypeFindLastIndex(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// arguments[0] = callback
// arguments[1] = thisArg.
try {
// 1. Let O be the this value.
const array: JSTypedArray = Cast<JSTypedArray>(receiver)
otherwise NotTypedArray;
// 2. Perform ? ValidateTypedArray(O).
const uarray = typed_array::EnsureAttached(array) otherwise IsDetached;
// 4. If IsCallable(predicate) is false, throw a TypeError exception.
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);
}
} label NotCallable deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFindLastIndex);
} label IsDetached deferred {
ThrowTypeError(
MessageTemplate::kDetachedOperation, kBuiltinNameFindLastIndex);
}
}
}

View File

@ -283,7 +283,8 @@ DEFINE_BOOL(harmony_shipping, true, "enable all shipped harmony features")
"harmony weak references with FinalizationRegistry.prototype.cleanupSome") \
V(harmony_import_assertions, "harmony import assertions") \
V(harmony_rab_gsab, \
"harmony ResizableArrayBuffer / GrowableSharedArrayBuffer")
"harmony ResizableArrayBuffer / GrowableSharedArrayBuffer") \
V(harmony_array_find_last, "harmony array find last helpers")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V)

View File

@ -4405,6 +4405,39 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_more_timezone)
#undef EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE
void Genesis::InitializeGlobal_harmony_array_find_last() {
if (!FLAG_harmony_array_find_last) return;
{
Handle<JSFunction> array_function(native_context()->array_function(),
isolate());
Handle<JSObject> array_prototype(
JSObject::cast(array_function->instance_prototype()), isolate());
SimpleInstallFunction(isolate_, array_prototype, "findLast",
Builtin::kArrayPrototypeFindLast, 1, false);
SimpleInstallFunction(isolate_, array_prototype, "findLastIndex",
Builtin::kArrayPrototypeFindLastIndex, 1, false);
Handle<JSObject> unscopables = Handle<JSObject>::cast(
JSObject::GetProperty(isolate(), array_prototype,
isolate()->factory()->unscopables_symbol())
.ToHandleChecked());
InstallTrueValuedProperty(isolate_, unscopables, "findLast");
InstallTrueValuedProperty(isolate_, unscopables, "findLastIndex");
}
{
Handle<JSObject> prototype(native_context()->typed_array_prototype(),
isolate());
SimpleInstallFunction(isolate_, prototype, "findLast",
Builtin::kTypedArrayPrototypeFindLast, 1, false);
SimpleInstallFunction(isolate_, prototype, "findLastIndex",
Builtin::kTypedArrayPrototypeFindLastIndex, 1, false);
}
}
void Genesis::InitializeGlobal_harmony_object_has_own() {
if (!FLAG_harmony_object_has_own) return;

View File

@ -0,0 +1,15 @@
// Copyright 2021 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-array-find-last
var findLast = 'local findLast';
var findLastIndex = 'local findLastIndex';
var array = [];
with (array) {
assertEquals('local findLast', findLast);
assertEquals('local findLastIndex', findLastIndex);
}

View File

@ -0,0 +1,21 @@
// Copyright 2021 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-array-find-last
(function () {
var array = [,];
function findLast() {
return array.findLast(v => v > 0);
}
assertEquals(findLast(), undefined);
array.__proto__.push(6);
assertEquals(findLast(), 6);
array = [6, -1, 5];
assertEquals(findLast(), 5);
})();

View File

@ -0,0 +1,21 @@
// Copyright 2021 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-array-find-last
(function () {
var array = [,];
function findLastIndex() {
return array.findLastIndex(v => v > 0);
}
assertEquals(findLastIndex(), -1);
array.__proto__.push(6);
assertEquals(findLastIndex(), 0);
array = [6, -1, 5];
assertEquals(findLastIndex(), 2);
})();

View File

@ -0,0 +1,226 @@
// Copyright 2021 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: --allow-natives-syntax --harmony-array-find-last
var typedArrayConstructors = [
Uint8Array,
Int8Array,
Uint16Array,
Int16Array,
Uint32Array,
Int32Array,
Uint8ClampedArray,
Float32Array,
Float64Array];
for (var constructor of typedArrayConstructors) {
assertEquals(1, constructor.prototype.findLast.length);
var a = new constructor([21, 22, 23, 24]);
assertEquals(undefined, a.findLast(function() { return false; }));
assertEquals(24, a.findLast(function() { return true; }));
assertEquals(undefined, a.findLast(function(val) { return 121 === val; }));
assertEquals(24, a.findLast(function(val) { return 24 === val; }));
assertEquals(23, a.findLast(function(val) { return 23 === val; }), null);
assertEquals(22, a.findLast(function(val) { return 22 === val; }), undefined);
//
// Test predicate is not called when array is empty
//
(function() {
var a = new constructor([]);
var l = -1;
var o = -1;
var v = -1;
var k = -1;
a.findLast(function(val, key, obj) {
o = obj;
l = obj.length;
v = val;
k = key;
return false;
});
assertEquals(-1, l);
assertEquals(-1, o);
assertEquals(-1, v);
assertEquals(-1, k);
})();
//
// Test predicate is called with correct arguments
//
(function() {
var a = new constructor([5]);
var l = -1;
var o = -1;
var v = -1;
var k = -1;
var found = a.findLast(function(val, key, obj) {
o = obj;
l = obj.length;
v = val;
k = key;
return false;
});
assertArrayEquals(a, o);
assertEquals(a.length, l);
assertEquals(5, v);
assertEquals(0, k);
assertEquals(undefined, found);
})();
//
// Test predicate is called array.length times
//
(function() {
var a = new constructor([1, 2, 3, 4, 5]);
var l = 0;
var found = a.findLast(function() {
l++;
return false;
});
assertEquals(a.length, l);
assertEquals(undefined, found);
})();
//
// Test array modifications
//
(function() {
a = new constructor([1, 2, 3]);
found = a.findLast(function(val, key) { a[key] = ++val; return false; });
assertArrayEquals([2, 3, 4], a);
assertEquals(3, a.length);
assertEquals(undefined, found);
})();
//
// Test thisArg
//
(function() {
// Test String as a thisArg
var found = new constructor([1, 2, 3]).findLast(function(val, key) {
return this.charAt(Number(key)) === String(val);
}, "321");
assertEquals(2, found);
// Test object as a thisArg
var thisArg = {
elementAt: function(key) {
return this[key];
}
};
Array.prototype.push.apply(thisArg, [3, 2, 1]);
found = new constructor([1, 2, 3]).findLast(function(val, key) {
return this.elementAt(key) === val;
}, thisArg);
assertEquals(2, found);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
new constructor([1, 2]).findLast(function() { a.push(this) }, "");
assertTrue(a[0] !== a[1]);
// Do not create a new object otherwise.
a = [];
new constructor([1, 2]).findLast(function() { a.push(this) }, {});
assertEquals(a[0], a[1]);
// In strict mode primitive values should not be coerced to an object.
a = [];
new constructor([1, 2]).findLast(function() { 'use strict'; a.push(this); }, "");
assertEquals("", a[0]);
assertEquals(a[0], a[1]);
})();
// Test exceptions
assertThrows('constructor.prototype.findLast.call(null, function() { })',
TypeError);
assertThrows('constructor.prototype.findLast.call(undefined, function() { })',
TypeError);
assertThrows('constructor.prototype.findLast.apply(null, function() { }, [])',
TypeError);
assertThrows('constructor.prototype.findLast.apply(undefined, function() { }, [])',
TypeError);
assertThrows('constructor.prototype.findLast.apply([], function() { }, [])',
TypeError);
assertThrows('constructor.prototype.findLast.apply({}, function() { }, [])',
TypeError);
assertThrows('constructor.prototype.findLast.apply("", function() { }, [])',
TypeError);
assertThrows('new constructor([]).findLast(null)', TypeError);
assertThrows('new constructor([]).findLast(undefined)', TypeError);
assertThrows('new constructor([]).findLast(0)', TypeError);
assertThrows('new constructor([]).findLast(true)', TypeError);
assertThrows('new constructor([]).findLast(false)', TypeError);
assertThrows('new constructor([]).findLast("")', TypeError);
assertThrows('new constructor([]).findLast({})', TypeError);
assertThrows('new constructor([]).findLast([])', TypeError);
assertThrows('new constructor([]).findLast(/\d+/)', TypeError);
// Shadowing length doesn't affect findLast, unlike Array.prototype.findLast
a = new constructor([1, 2]);
Object.defineProperty(a, 'length', {value: 1});
var x = 0;
assertEquals(a.findLast(function(elt) { x += elt; return false; }), undefined);
assertEquals(x, 3);
assertEquals(Array.prototype.findLast.call(a,
function(elt) { x += elt; return false; }), undefined);
assertEquals(x, 4);
// Detached Operation
var tmp = {
[Symbol.toPrimitive]() {
assertUnreachable("Parameter should not be processed when " +
"array.[[ViewedArrayBuffer]] is detached.");
return 0;
}
};
var array = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
%ArrayBufferDetach(array.buffer);
assertThrows(() => array.findLast(tmp), TypeError);
//
// Test detaching in predicate.
//
(function() {
var array = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var values = [];
assertEquals(array.findLast((value) => {
values.push(value);
if (value === 5) {
%ArrayBufferDetach(array.buffer);
}
}), undefined);
assertEquals(values, [10, 9, 8, 7, 6, 5, undefined, undefined, undefined, undefined]);
var array = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
assertEquals(array.findLast((value, idx) => {
if (value !== undefined) {
%ArrayBufferDetach(array.buffer);
}
return idx === 0;
}), undefined);
})();
}

View File

@ -0,0 +1,224 @@
// Copyright 2021 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: --allow-natives-syntax --harmony-array-find-last
var typedArrayConstructors = [
Uint8Array,
Int8Array,
Uint16Array,
Int16Array,
Uint32Array,
Int32Array,
Uint8ClampedArray,
Float32Array,
Float64Array];
for (var constructor of typedArrayConstructors) {
assertEquals(1, constructor.prototype.findLastIndex.length);
var a = new constructor([21, 22, 23, 24]);
assertEquals(-1, a.findLastIndex(function() { return false; }));
assertEquals(-1, a.findLastIndex(function(val) { return 121 === val; }));
assertEquals(3, a.findLastIndex(function() { return true; }));
assertEquals(1, a.findLastIndex(function(val) { return 22 === val; }), undefined);
assertEquals(2, a.findLastIndex(function(val) { return 23 === val; }), null);
assertEquals(3, a.findLastIndex(function(val) { return 24 === val; }));
//
// Test predicate is not called when array is empty
//
(function() {
var a = new constructor([]);
var l = -1;
var o = -1;
var v = -1;
var k = -1;
a.findLastIndex(function(val, key, obj) {
o = obj;
l = obj.length;
v = val;
k = key;
return false;
});
assertEquals(-1, l);
assertEquals(-1, o);
assertEquals(-1, v);
assertEquals(-1, k);
})();
//
// Test predicate is called with correct arguments
//
(function() {
var a = new constructor([5]);
var l = -1;
var o = -1;
var v = -1;
var k = -1;
var index = a.findLastIndex(function(val, key, obj) {
o = obj;
l = obj.length;
v = val;
k = key;
return false;
});
assertArrayEquals(a, o);
assertEquals(a.length, l);
assertEquals(5, v);
assertEquals(0, k);
assertEquals(-1, index);
})();
//
// Test predicate is called array.length times
//
(function() {
var a = new constructor([1, 2, 3, 4, 5]);
var l = 0;
a.findLastIndex(function() {
l++;
return false;
});
assertEquals(a.length, l);
})();
//
// Test array modifications
//
(function() {
a = new constructor([1, 2, 3]);
a.findLastIndex(function(val, key) { a[key] = ++val; return false; });
assertArrayEquals([2, 3, 4], a);
assertEquals(3, a.length);
})();
//
// Test thisArg
//
(function() {
// Test String as a thisArg
var index = new constructor([1, 2, 3]).findLastIndex(function(val, key) {
return this.charAt(Number(key)) === String(val);
}, "321");
assertEquals(1, index);
// Test object as a thisArg
var thisArg = {
elementAt: function(key) {
return this[key];
}
};
Array.prototype.push.apply(thisArg, [3, 2, 1]);
index = new constructor([1, 2, 3]).findLastIndex(function(val, key) {
return this.elementAt(key) === val;
}, thisArg);
assertEquals(1, index);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
new constructor([1, 2]).findLastIndex(function() { a.push(this) }, "");
assertTrue(a[0] !== a[1]);
// Do not create a new object otherwise.
a = [];
new constructor([1, 2]).findLastIndex(function() { a.push(this) }, {});
assertEquals(a[0], a[1]);
// In strict mode primitive values should not be coerced to an object.
a = [];
new constructor([1, 2]).findLastIndex(function() { 'use strict'; a.push(this); }, "");
assertEquals("", a[0]);
assertEquals(a[0], a[1]);
})();
// Test exceptions
assertThrows('constructor.prototype.findLastIndex.call(null, function() { })',
TypeError);
assertThrows('constructor.prototype.findLastIndex.call(undefined, function() { })',
TypeError);
assertThrows('constructor.prototype.findLastIndex.apply(null, function() { }, [])',
TypeError);
assertThrows('constructor.prototype.findLastIndex.apply(undefined, function() { }, [])',
TypeError);
assertThrows('constructor.prototype.findLastIndex.apply([], function() { }, [])',
TypeError);
assertThrows('constructor.prototype.findLastIndex.apply({}, function() { }, [])',
TypeError);
assertThrows('constructor.prototype.findLastIndex.apply("", function() { }, [])',
TypeError);
assertThrows('new constructor([]).findLastIndex(null)', TypeError);
assertThrows('new constructor([]).findLastIndex(undefined)', TypeError);
assertThrows('new constructor([]).findLastIndex(0)', TypeError);
assertThrows('new constructor([]).findLastIndex(true)', TypeError);
assertThrows('new constructor([]).findLastIndex(false)', TypeError);
assertThrows('new constructor([]).findLastIndex("")', TypeError);
assertThrows('new constructor([]).findLastIndex({})', TypeError);
assertThrows('new constructor([]).findLastIndex([])', TypeError);
assertThrows('new constructor([]).findLastIndex(/\d+/)', TypeError);
// Shadowing length doesn't affect findLastIndex, unlike Array.prototype.findLastIndex
a = new constructor([1, 2]);
Object.defineProperty(a, 'length', {value: 1});
var x = 0;
assertEquals(a.findLastIndex(function(elt) { x += elt; return false; }), -1);
assertEquals(x, 3);
assertEquals(Array.prototype.findLastIndex.call(a,
function(elt) { x += elt; return false; }), -1);
assertEquals(x, 4);
// Detached Operation
var tmp = {
[Symbol.toPrimitive]() {
assertUnreachable("Parameter should not be processed when " +
"array.[[ViewedArrayBuffer]] is detached.");
return 0;
}
};
var array = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
%ArrayBufferDetach(array.buffer);
assertThrows(() => array.findLastIndex(tmp), TypeError);
//
// Test detaching in predicate.
//
(function() {
var array = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var values = [];
assertEquals(array.findLastIndex((value, idx) => {
values.push(value);
if (value === 5) {
%ArrayBufferDetach(array.buffer);
}
}), -1);
assertEquals(values, [10, 9, 8, 7, 6, 5, undefined, undefined, undefined, undefined]);
var array = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
assertEquals(array.findLastIndex((value, idx) => {
if (value !== undefined) {
%ArrayBufferDetach(array.buffer);
}
return idx === 0;
}), 0);
})();
}

View File

@ -475,27 +475,27 @@ KNOWN_OBJECTS = {
("old_space", 0x029b5): "StringSplitCache",
("old_space", 0x02dbd): "RegExpMultipleCache",
("old_space", 0x031c5): "BuiltinsConstantsTable",
("old_space", 0x035d5): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x035f9): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x0361d): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x03641): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x03665): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x03689): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x036ad): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x036d1): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x036f5): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x03719): "PromiseAllResolveElementSharedFun",
("old_space", 0x0373d): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x03761): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x03785): "PromiseAnyRejectElementSharedFun",
("old_space", 0x037a9): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x037cd): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x037f1): "PromiseCatchFinallySharedFun",
("old_space", 0x03815): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x03839): "PromiseThenFinallySharedFun",
("old_space", 0x0385d): "PromiseThrowerFinallySharedFun",
("old_space", 0x03881): "PromiseValueThunkFinallySharedFun",
("old_space", 0x038a5): "ProxyRevokeSharedFun",
("old_space", 0x035e5): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x03609): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x0362d): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x03651): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x03675): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x03699): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x036bd): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x036e1): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x03705): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x03729): "PromiseAllResolveElementSharedFun",
("old_space", 0x0374d): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x03771): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x03795): "PromiseAnyRejectElementSharedFun",
("old_space", 0x037b9): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x037dd): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x03801): "PromiseCatchFinallySharedFun",
("old_space", 0x03825): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x03849): "PromiseThenFinallySharedFun",
("old_space", 0x0386d): "PromiseThrowerFinallySharedFun",
("old_space", 0x03891): "PromiseValueThunkFinallySharedFun",
("old_space", 0x038b5): "ProxyRevokeSharedFun",
}
# Lower 32 bits of first page addresses for various heap spaces.