[builtins] Port Array.p.{find,findIndex} to CSA

- Removes JS implementation and InnerArrayFind/InnerArrayFindIndex
- Adds TFJ, with TFS for slow continuation path

Some quick benchmarks show ~2x improvement for unoptimized code
and up to 16% improvement against optimized code (diminishes with
larger arrays as iterating dominates).

https://github.com/peterwmwong/v8-perf/blob/master/array-find-findIndex/README.md

Bug: chromium:791045, v8:1956, v8:5049, v8:7165
Change-Id: Ie16252ed495bbd91fe548b16d5ef6764de791a50
Reviewed-on: https://chromium-review.googlesource.com/804704
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49851}
This commit is contained in:
peterwmwong 2017-12-04 22:14:36 -06:00 committed by Commit Bot
parent cc07ac73a4
commit 99b5f699ab
8 changed files with 333 additions and 95 deletions

View File

@ -1679,6 +1679,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
DONT_ENUM);
SimpleInstallFunction(proto, "concat", Builtins::kArrayConcat, 1, false);
SimpleInstallFunction(proto, "find", Builtins::kArrayPrototypeFind, 1,
false);
SimpleInstallFunction(proto, "findIndex",
Builtins::kArrayPrototypeFindIndex, 1, false);
SimpleInstallFunction(proto, "pop", Builtins::kFastArrayPop, 0, false);
SimpleInstallFunction(proto, "push", Builtins::kFastArrayPush, 1, false);
SimpleInstallFunction(proto, "shift", Builtins::kFastArrayShift, 0, false);

View File

@ -31,6 +31,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
typedef std::function<void(ArrayBuiltinCodeStubAssembler* masm)>
PostLoopAction;
enum class MissingPropertyMode { kSkip, kUseUndefined };
void FindResultGenerator() { a_.Bind(UndefinedConstant()); }
Node* FindProcessor(Node* k_value, Node* k) {
@ -383,6 +385,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
const char* name, const BuiltinResultGenerator& generator,
const CallResultProcessor& processor, const PostLoopAction& action,
const Callable& slow_case_continuation,
MissingPropertyMode missing_property_mode,
ForEachDirection direction = ForEachDirection::kForward) {
Label non_array(this), array_changes(this, {&k_, &a_, &to_});
@ -439,7 +442,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
generator(this);
HandleFastElements(processor, action, &fully_spec_compliant_, direction);
HandleFastElements(processor, action, &fully_spec_compliant_, direction,
missing_property_mode);
BIND(&fully_spec_compliant_);
@ -550,6 +554,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
void GenerateIteratingArrayBuiltinLoopContinuation(
const CallResultProcessor& processor, const PostLoopAction& action,
MissingPropertyMode missing_property_mode,
ForEachDirection direction = ForEachDirection::kForward) {
Label loop(this, {&k_, &a_, &to_});
Label after_loop(this);
@ -572,12 +577,14 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
// index in the range [0, 2^32-1).
CSA_ASSERT(this, IsNumberArrayIndex(k()));
// b. Let kPresent be HasProperty(O, Pk).
// c. ReturnIfAbrupt(kPresent).
Node* k_present = HasProperty(o(), k(), context(), kHasProperty);
if (missing_property_mode == MissingPropertyMode::kSkip) {
// b. Let kPresent be HasProperty(O, Pk).
// c. ReturnIfAbrupt(kPresent).
Node* k_present = HasProperty(o(), k(), context(), kHasProperty);
// d. If kPresent is true, then
GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element);
// d. If kPresent is true, then
GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element);
}
// i. Let kValue be Get(O, Pk).
// ii. ReturnIfAbrupt(kValue).
@ -655,7 +662,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
void VisitAllFastElementsOneKind(ElementsKind kind,
const CallResultProcessor& processor,
Label* array_changed, ParameterMode mode,
ForEachDirection direction) {
ForEachDirection direction,
MissingPropertyMode missing_property_mode) {
Comment("begin VisitAllFastElementsOneKind");
VARIABLE(original_map, MachineRepresentation::kTagged);
original_map.Bind(LoadMap(o()));
@ -670,7 +678,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
list, start, end,
[=, &original_map](Node* index) {
k_.Bind(ParameterToTagged(index, mode));
Label one_element_done(this), hole_element(this);
Label one_element_done(this), hole_element(this),
process_element(this);
// Check if o's map has changed during the callback. If so, we have to
// fall back to the slower spec implementation for the rest of the
@ -693,24 +702,32 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
? FixedArray::kHeaderSize
: (FixedArray::kHeaderSize - kHeapObjectTag);
Node* offset = ElementOffsetFromIndex(index, kind, mode, base_size);
Node* value = nullptr;
VARIABLE(value, MachineRepresentation::kTagged);
if (kind == PACKED_ELEMENTS) {
value = LoadObjectField(elements, offset);
GotoIf(WordEqual(value, TheHoleConstant()), &hole_element);
value.Bind(LoadObjectField(elements, offset));
GotoIf(WordEqual(value.value(), TheHoleConstant()), &hole_element);
} else {
Node* double_value =
LoadDoubleWithHoleCheck(elements, offset, &hole_element);
value = AllocateHeapNumberWithValue(double_value);
value.Bind(AllocateHeapNumberWithValue(double_value));
}
a_.Bind(processor(this, value, k()));
Goto(&one_element_done);
Goto(&process_element);
BIND(&hole_element);
// Check if o's prototype change unexpectedly has elements after the
// callback in the case of a hole.
BranchIfPrototypesHaveNoElements(o_map, &one_element_done,
array_changed);
if (missing_property_mode == MissingPropertyMode::kSkip) {
// Check if o's prototype change unexpectedly has elements after
// the callback in the case of a hole.
BranchIfPrototypesHaveNoElements(o_map, &one_element_done,
array_changed);
} else {
value.Bind(UndefinedConstant());
Goto(&process_element);
}
BIND(&process_element);
{
a_.Bind(processor(this, value.value(), k()));
Goto(&one_element_done);
}
BIND(&one_element_done);
},
1, mode, advance_mode);
@ -719,7 +736,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
void HandleFastElements(const CallResultProcessor& processor,
const PostLoopAction& action, Label* slow,
ForEachDirection direction) {
ForEachDirection direction,
MissingPropertyMode missing_property_mode) {
Label switch_on_elements_kind(this), fast_elements(this),
maybe_double_elements(this), fast_double_elements(this);
@ -742,7 +760,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
BIND(&fast_elements);
{
VisitAllFastElementsOneKind(PACKED_ELEMENTS, processor, slow, mode,
direction);
direction, missing_property_mode);
action(this);
@ -757,7 +775,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
BIND(&fast_double_elements);
{
VisitAllFastElementsOneKind(PACKED_DOUBLE_ELEMENTS, processor, slow, mode,
direction);
direction, missing_property_mode);
action(this);
@ -1619,6 +1637,95 @@ TF_BUILTIN(CloneFastJSArray, ArrayBuiltinCodeStubAssembler) {
Return(CloneFastJSArray(context, array, mode));
}
TF_BUILTIN(ArrayFindLoopContinuation, ArrayBuiltinCodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* array = Parameter(Descriptor::kArray);
Node* object = Parameter(Descriptor::kObject);
Node* initial_k = Parameter(Descriptor::kInitialK);
Node* len = Parameter(Descriptor::kLength);
Node* to = Parameter(Descriptor::kTo);
InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
this_arg, array, object, initial_k,
len, to);
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::FindProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
}
// ES #sec-get-%typedarray%.prototype.find
TF_BUILTIN(ArrayPrototypeFind, ArrayBuiltinCodeStubAssembler) {
Node* argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
CodeStubArguments args(this, argc);
Node* context = Parameter(BuiltinDescriptor::kContext);
Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
Node* receiver = args.GetReceiver();
Node* callbackfn = args.GetOptionalArgumentValue(0);
Node* this_arg = args.GetOptionalArgumentValue(1);
InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg,
new_target, argc);
GenerateIteratingArrayBuiltinBody(
"Array.prototype.find",
&ArrayBuiltinCodeStubAssembler::FindResultGenerator,
&ArrayBuiltinCodeStubAssembler::FindProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(), Builtins::kArrayFindLoopContinuation),
MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
}
TF_BUILTIN(ArrayFindIndexLoopContinuation, ArrayBuiltinCodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
Node* this_arg = Parameter(Descriptor::kThisArg);
Node* array = Parameter(Descriptor::kArray);
Node* object = Parameter(Descriptor::kObject);
Node* initial_k = Parameter(Descriptor::kInitialK);
Node* len = Parameter(Descriptor::kLength);
Node* to = Parameter(Descriptor::kTo);
InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
this_arg, array, object, initial_k,
len, to);
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::FindIndexProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
}
// ES #sec-get-%typedarray%.prototype.findIndex
TF_BUILTIN(ArrayPrototypeFindIndex, ArrayBuiltinCodeStubAssembler) {
Node* argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
CodeStubArguments args(this, argc);
Node* context = Parameter(BuiltinDescriptor::kContext);
Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
Node* receiver = args.GetReceiver();
Node* callbackfn = args.GetOptionalArgumentValue(0);
Node* this_arg = args.GetOptionalArgumentValue(1);
InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg,
new_target, argc);
GenerateIteratingArrayBuiltinBody(
"Array.prototype.findIndex",
&ArrayBuiltinCodeStubAssembler::FindIndexResultGenerator,
&ArrayBuiltinCodeStubAssembler::FindIndexProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(),
Builtins::kArrayFindIndexLoopContinuation),
MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
}
// ES #sec-get-%typedarray%.prototype.find
TF_BUILTIN(TypedArrayPrototypeFind, ArrayBuiltinCodeStubAssembler) {
Node* argc =
@ -1678,7 +1785,8 @@ TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::ForEachProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
MissingPropertyMode::kSkip);
}
TF_BUILTIN(ArrayForEachLoopEagerDeoptContinuation,
@ -1731,8 +1839,8 @@ TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::ForEachResultGenerator,
&ArrayBuiltinCodeStubAssembler::ForEachProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(),
Builtins::kArrayForEachLoopContinuation));
Builtins::CallableFor(isolate(), Builtins::kArrayForEachLoopContinuation),
MissingPropertyMode::kSkip);
}
TF_BUILTIN(TypedArrayPrototypeForEach, ArrayBuiltinCodeStubAssembler) {
@ -1772,7 +1880,8 @@ TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::SomeProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
MissingPropertyMode::kSkip);
}
TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) {
@ -1793,7 +1902,8 @@ TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::SomeResultGenerator,
&ArrayBuiltinCodeStubAssembler::SomeProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(), Builtins::kArraySomeLoopContinuation));
Builtins::CallableFor(isolate(), Builtins::kArraySomeLoopContinuation),
MissingPropertyMode::kSkip);
}
TF_BUILTIN(TypedArrayPrototypeSome, ArrayBuiltinCodeStubAssembler) {
@ -1833,7 +1943,8 @@ TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::EveryProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
MissingPropertyMode::kSkip);
}
TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) {
@ -1854,7 +1965,8 @@ TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::EveryResultGenerator,
&ArrayBuiltinCodeStubAssembler::EveryProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(), Builtins::kArrayEveryLoopContinuation));
Builtins::CallableFor(isolate(), Builtins::kArrayEveryLoopContinuation),
MissingPropertyMode::kSkip);
}
TF_BUILTIN(TypedArrayPrototypeEvery, ArrayBuiltinCodeStubAssembler) {
@ -1894,7 +2006,8 @@ TF_BUILTIN(ArrayReduceLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::ReduceProcessor,
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction);
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction,
MissingPropertyMode::kSkip);
}
TF_BUILTIN(ArrayReduce, ArrayBuiltinCodeStubAssembler) {
@ -1915,7 +2028,8 @@ TF_BUILTIN(ArrayReduce, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::ReduceResultGenerator,
&ArrayBuiltinCodeStubAssembler::ReduceProcessor,
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction,
Builtins::CallableFor(isolate(), Builtins::kArrayReduceLoopContinuation));
Builtins::CallableFor(isolate(), Builtins::kArrayReduceLoopContinuation),
MissingPropertyMode::kSkip);
}
TF_BUILTIN(TypedArrayPrototypeReduce, ArrayBuiltinCodeStubAssembler) {
@ -1956,7 +2070,7 @@ TF_BUILTIN(ArrayReduceRightLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::ReduceProcessor,
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction,
ForEachDirection::kReverse);
MissingPropertyMode::kSkip, ForEachDirection::kReverse);
}
TF_BUILTIN(ArrayReduceRight, ArrayBuiltinCodeStubAssembler) {
@ -1979,7 +2093,7 @@ TF_BUILTIN(ArrayReduceRight, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction,
Builtins::CallableFor(isolate(),
Builtins::kArrayReduceRightLoopContinuation),
ForEachDirection::kReverse);
MissingPropertyMode::kSkip, ForEachDirection::kReverse);
}
TF_BUILTIN(TypedArrayPrototypeReduceRight, ArrayBuiltinCodeStubAssembler) {
@ -2020,7 +2134,8 @@ TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::FilterProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
MissingPropertyMode::kSkip);
}
TF_BUILTIN(ArrayFilterLoopEagerDeoptContinuation,
@ -2101,7 +2216,8 @@ TF_BUILTIN(ArrayFilter, ArrayBuiltinCodeStubAssembler) {
&ArrayBuiltinCodeStubAssembler::FilterResultGenerator,
&ArrayBuiltinCodeStubAssembler::FilterProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(), Builtins::kArrayFilterLoopContinuation));
Builtins::CallableFor(isolate(), Builtins::kArrayFilterLoopContinuation),
MissingPropertyMode::kSkip);
}
TF_BUILTIN(ArrayMapLoopContinuation, ArrayBuiltinCodeStubAssembler) {
@ -2121,7 +2237,8 @@ TF_BUILTIN(ArrayMapLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::SpecCompliantMapProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
MissingPropertyMode::kSkip);
}
TF_BUILTIN(ArrayMapLoopEagerDeoptContinuation, ArrayBuiltinCodeStubAssembler) {
@ -2182,7 +2299,8 @@ TF_BUILTIN(ArrayMap, ArrayBuiltinCodeStubAssembler) {
"Array.prototype.map", &ArrayBuiltinCodeStubAssembler::MapResultGenerator,
&ArrayBuiltinCodeStubAssembler::FastMapProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
Builtins::CallableFor(isolate(), Builtins::kArrayMapLoopContinuation));
Builtins::CallableFor(isolate(), Builtins::kArrayMapLoopContinuation),
MissingPropertyMode::kSkip);
}
TF_BUILTIN(TypedArrayPrototypeMap, ArrayBuiltinCodeStubAssembler) {

View File

@ -316,6 +316,15 @@ namespace internal {
TFJ(ArrayReduceRight, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.entries */ \
TFJ(ArrayPrototypeEntries, 0) \
/* ES6 #sec-array.prototype.find */ \
TFS(ArrayFindLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \
TFJ(ArrayPrototypeFind, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.findIndex */ \
TFS(ArrayFindIndexLoopContinuation, kReceiver, kCallbackFn, kThisArg, \
kArray, kObject, kInitialK, kLength, kTo) \
TFJ(ArrayPrototypeFindIndex, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.keys */ \
TFJ(ArrayPrototypeKeys, 0) \
/* ES6 #sec-array.prototype.values */ \

View File

@ -522,6 +522,8 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kArrayPrototypeValues:
case Builtins::kArrayIncludes:
case Builtins::kArrayPrototypeEntries:
case Builtins::kArrayPrototypeFind:
case Builtins::kArrayPrototypeFindIndex:
case Builtins::kArrayPrototypeKeys:
case Builtins::kArrayForEach:
case Builtins::kArrayEvery:

View File

@ -1118,64 +1118,6 @@ DEFINE_METHOD_LEN(
);
function InnerArrayFind(predicate, thisArg, array, length) {
if (!IS_CALLABLE(predicate)) {
throw %make_type_error(kCalledNonCallable, predicate);
}
for (var i = 0; i < length; i++) {
var element = array[i];
if (%_Call(predicate, thisArg, element, i, array)) {
return element;
}
}
return;
}
// ES6 draft 07-15-13, section 15.4.3.23
DEFINE_METHOD_LEN(
GlobalArray.prototype,
find(predicate, thisArg) {
var array = TO_OBJECT(this);
var length = TO_INTEGER(array.length);
return InnerArrayFind(predicate, thisArg, array, length);
},
1 /* Set function length */
);
function InnerArrayFindIndex(predicate, thisArg, array, length) {
if (!IS_CALLABLE(predicate)) {
throw %make_type_error(kCalledNonCallable, predicate);
}
for (var i = 0; i < length; i++) {
var element = array[i];
if (%_Call(predicate, thisArg, element, i, array)) {
return i;
}
}
return -1;
}
// ES6 draft 07-15-13, section 15.4.3.24
DEFINE_METHOD_LEN(
GlobalArray.prototype,
findIndex(predicate, thisArg) {
var array = TO_OBJECT(this);
var length = TO_INTEGER(array.length);
return InnerArrayFindIndex(predicate, thisArg, array, length);
},
1 /* Set function length */
);
// ES6, draft 04-05-14, section 22.1.3.6
DEFINE_METHOD_LEN(
GlobalArray.prototype,

View File

@ -73,6 +73,31 @@
assertEquals(3, count);
for (var i in a) assertEquals(2, a[i]);
// Skip over missing properties.
a = {
"0": 0,
"2": 2,
length: 3
};
var received = [];
assertArrayEquals([2],
Array.prototype.filter.call(a, function(n) {
received.push(n);
return n == 2;
}));
assertArrayEquals([0, 2], received);
// Modify array prototype
a = [0, , 2];
received = [];
assertArrayEquals([2],
Array.prototype.filter.call(a, function(n) {
a.__proto__ = null;
received.push(n);
return n == 2;
}));
assertArrayEquals([0, 2], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
@ -131,6 +156,26 @@
a.forEach(function(n) { count++; });
assertEquals(1, count);
// Skip over missing properties.
a = {
"0": 0,
"2": 2,
length: 3
};
var received = [];
Array.prototype.forEach.call(a, function(n) { received.push(n); });
assertArrayEquals([0, 2], received);
// Modify array prototype
a = [0, , 2];
received = [];
Array.prototype.forEach.call(a, function(n) {
a.__proto__ = null;
received.push(n);
return n == 2;
});
assertArrayEquals([0, 2], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
@ -194,6 +239,31 @@
assertTrue(a.every(function(n) { count++; return n == 2; }));
assertEquals(2, count);
// Skip over missing properties.
a = {
"0": 2,
"2": 2,
length: 3
};
var received = [];
assertTrue(
Array.prototype.every.call(a, function(n) {
received.push(n);
return n == 2;
}));
assertArrayEquals([2, 2], received);
// Modify array prototype
a = [2, , 2];
received = [];
assertTrue(
Array.prototype.every.call(a, function(n) {
a.__proto__ = null;
received.push(n);
return n == 2;
}));
assertArrayEquals([2, 2], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
@ -252,6 +322,31 @@
a = a.map(function(n) { return 2*n; });
for (var i in a) assertEquals(4, a[i]);
// Skip over missing properties.
a = {
"0": 1,
"2": 2,
length: 3
};
var received = [];
assertArrayEquals([2, , 4],
Array.prototype.map.call(a, function(n) {
received.push(n);
return n * 2;
}));
assertArrayEquals([1, 2], received);
// Modify array prototype
a = [1, , 2];
received = [];
assertArrayEquals([2, , 4],
Array.prototype.map.call(a, function(n) {
a.__proto__ = null;
received.push(n);
return n * 2;
}));
assertArrayEquals([1, 2], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];

View File

@ -233,6 +233,40 @@ assertEquals(22, a.find(function(val) { return 22 === val; }), undefined);
})();
//
// Test predicate is called for missing properties
//
(function() {
const obj = {
"0": 0,
"2": 2,
length: 3
};
const received = [];
const predicate = (v) => { received.push(v); return false; };
const found = Array.prototype.find.call(obj, predicate);
assertEquals(undefined, found);
assertArrayEquals([0, undefined, 2], received);
})();
//
// Test predicate modifying array prototype
//
(function() {
const a = [0, , 2];
const received = [];
const predicate = (v) => {
a.__proto__ = null;
received.push(v);
return false;
};
const found = Array.prototype.find.call(a, predicate);
assertEquals(undefined, found);
assertArrayEquals([0, undefined, 2], received);
})();
//
// Test thisArg
//

View File

@ -233,6 +233,40 @@ assertEquals(3, a.findIndex(function(val) { return 24 === val; }));
})();
//
// Test predicate is called for missing properties
//
(function() {
const obj = {
"0": 0,
"2": 2,
length: 3
};
const received = [];
const predicate = (v) => { received.push(v); return false; };
const found = Array.prototype.findIndex.call(obj, predicate);
assertEquals(-1, found);
assertArrayEquals([0, undefined, 2], received);
})();
//
// Test predicate modifying array prototype
//
(function() {
const a = [0, , 2];
const received = [];
const predicate = (v) => {
a.__proto__ = null;
received.push(v);
return false;
};
const found = Array.prototype.findIndex.call(a, predicate);
assertEquals(-1, found);
assertArrayEquals([0, undefined, 2], received);
})();
//
// Test thisArg
//