[builtins] Improve performance of array.prototype.filter and map.
BUG=
Review-Url: https://codereview.chromium.org/2775503006
Cr-Commit-Position: refs/heads/master@{#44793}
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 8653ce9..97da2d1 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -3860,14 +3860,6 @@
void Genesis::InitializeGlobal_experimental_fast_array_builtins() {
if (!FLAG_experimental_fast_array_builtins) return;
{
- Handle<JSFunction> array_constructor(native_context()->array_function());
- Handle<Object> array_prototype(array_constructor->prototype(), isolate());
- // Insert experimental fast Array builtins here.
- InstallOneBuiltinFunction(array_prototype, "filter",
- Builtins::kArrayFilter);
- InstallOneBuiltinFunction(array_prototype, "map", Builtins::kArrayMap);
- }
- {
Handle<Object> typed_array_prototype(
native_context()->typed_array_prototype(), isolate());
// Insert experimental fast TypedArray builtins here.
@@ -4355,6 +4347,12 @@
// Add forEach to the context.
native_context()->set_array_for_each_iterator(*forEach);
+ // Install Array.prototype.filter
+ InstallArrayBuiltinFunction(proto, "filter", Builtins::kArrayFilter, 2);
+
+ // Install Array.prototype.map
+ InstallArrayBuiltinFunction(proto, "map", Builtins::kArrayMap, 2);
+
// Install Array.prototype.every
InstallArrayBuiltinFunction(proto, "every", Builtins::kArrayEvery, 2);
diff --git a/src/builtins/builtins-array-gen.cc b/src/builtins/builtins-array-gen.cc
index aad31db..21269fd 100644
--- a/src/builtins/builtins-array-gen.cc
+++ b/src/builtins/builtins-array-gen.cc
@@ -137,13 +137,58 @@
BIND(&true_continue);
// iii. If selected is true, then...
{
- // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue).
- CallRuntime(Runtime::kCreateDataProperty, context(), a(), to_.value(),
- k_value);
+ Label after_work(this, &to_);
+ Node* kind = nullptr;
- // 2. Increase to by 1.
- to_.Bind(NumberInc(to_.value()));
- Goto(&false_continue);
+ // If a() is a JSArray, we can have a fast path.
+ Label fast(this);
+ Label runtime(this);
+ Label object_push_pre(this), object_push(this), double_push(this);
+ BranchIfFastJSArray(a(), context(), FastJSArrayAccessMode::ANY_ACCESS,
+ &fast, &runtime);
+
+ BIND(&fast);
+ {
+ kind = EnsureArrayPushable(a(), &runtime);
+ GotoIf(IsElementsKindGreaterThan(kind, FAST_HOLEY_SMI_ELEMENTS),
+ &object_push_pre);
+
+ BuildAppendJSArray(FAST_SMI_ELEMENTS, a(), k_value, &runtime);
+ Goto(&after_work);
+ }
+
+ BIND(&object_push_pre);
+ {
+ Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_ELEMENTS),
+ &double_push, &object_push);
+ }
+
+ BIND(&object_push);
+ {
+ BuildAppendJSArray(FAST_ELEMENTS, a(), k_value, &runtime);
+ Goto(&after_work);
+ }
+
+ BIND(&double_push);
+ {
+ BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, a(), k_value, &runtime);
+ Goto(&after_work);
+ }
+
+ BIND(&runtime);
+ {
+ // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue).
+ CallRuntime(Runtime::kCreateDataProperty, context(), a(), to_.value(),
+ k_value);
+ Goto(&after_work);
+ }
+
+ BIND(&after_work);
+ {
+ // 2. Increase to by 1.
+ to_.Bind(NumberInc(to_.value()));
+ Goto(&false_continue);
+ }
}
BIND(&false_continue);
return a();
@@ -160,8 +205,58 @@
Node* mappedValue = CallJS(CodeFactory::Call(isolate()), context(),
callbackfn(), this_arg(), k_value, k, o());
- // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
- CallRuntime(Runtime::kCreateDataProperty, context(), a(), k, mappedValue);
+ Label finished(this);
+ Node* kind = nullptr;
+ Node* elements = nullptr;
+
+ // If a() is a JSArray, we can have a fast path.
+ // mode is SMI_PARAMETERS because k has tagged representation.
+ ParameterMode mode = SMI_PARAMETERS;
+ Label fast(this);
+ Label runtime(this);
+ Label object_push_pre(this), object_push(this), double_push(this);
+ BranchIfFastJSArray(a(), context(), FastJSArrayAccessMode::ANY_ACCESS,
+ &fast, &runtime);
+
+ BIND(&fast);
+ {
+ kind = EnsureArrayPushable(a(), &runtime);
+ elements = LoadElements(a());
+ GotoIf(IsElementsKindGreaterThan(kind, FAST_HOLEY_SMI_ELEMENTS),
+ &object_push_pre);
+ TryStoreArrayElement(FAST_SMI_ELEMENTS, mode, &runtime, elements, k,
+ mappedValue);
+ Goto(&finished);
+ }
+
+ BIND(&object_push_pre);
+ {
+ Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_ELEMENTS), &double_push,
+ &object_push);
+ }
+
+ BIND(&object_push);
+ {
+ TryStoreArrayElement(FAST_ELEMENTS, mode, &runtime, elements, k,
+ mappedValue);
+ Goto(&finished);
+ }
+
+ BIND(&double_push);
+ {
+ TryStoreArrayElement(FAST_DOUBLE_ELEMENTS, mode, &runtime, elements, k,
+ mappedValue);
+ Goto(&finished);
+ }
+
+ BIND(&runtime);
+ {
+ // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
+ CallRuntime(Runtime::kCreateDataProperty, context(), a(), k, mappedValue);
+ Goto(&finished);
+ }
+
+ BIND(&finished);
return a();
}
@@ -575,7 +670,7 @@
Node* o_map = LoadMap(o());
Node* bit_field2 = LoadMapBitField2(o_map);
Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
- Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)),
+ Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_ELEMENTS),
&maybe_double_elements, &fast_elements);
ParameterMode mode = OptimalParameterMode();
@@ -591,8 +686,8 @@
}
BIND(&maybe_double_elements);
- Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)),
- slow, &fast_double_elements);
+ Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_DOUBLE_ELEMENTS), slow,
+ &fast_double_elements);
BIND(&fast_double_elements);
{
@@ -644,39 +739,13 @@
BIND(&fast);
{
- // Disallow pushing onto prototypes. It might be the JSArray prototype.
- // Disallow pushing onto non-extensible objects.
- Comment("Disallow pushing onto prototypes");
- Node* map = LoadMap(receiver);
- Node* bit_field2 = LoadMapBitField2(map);
- int mask = static_cast<int>(Map::IsPrototypeMapBits::kMask) |
- (1 << Map::kIsExtensible);
- Node* test = Word32And(bit_field2, Int32Constant(mask));
- GotoIf(Word32NotEqual(test, Int32Constant(1 << Map::kIsExtensible)),
- &runtime);
-
- // Disallow pushing onto arrays in dictionary named property mode. We need
- // to figure out whether the length property is still writable.
- Comment("Disallow pushing onto arrays in dictionary named property mode");
- GotoIf(IsDictionaryMap(map), &runtime);
-
- // Check whether the length property is writable. The length property is the
- // only default named property on arrays. It's nonconfigurable, hence is
- // guaranteed to stay the first property.
- Node* descriptors = LoadMapDescriptors(map);
- Node* details =
- LoadFixedArrayElement(descriptors, DescriptorArray::ToDetailsIndex(0));
- GotoIf(IsSetSmi(details, PropertyDetails::kAttributesReadOnlyMask),
- &runtime);
-
arg_index.Bind(IntPtrConstant(0));
- kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
-
- GotoIf(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_SMI_ELEMENTS)),
+ kind = EnsureArrayPushable(receiver, &runtime);
+ GotoIf(IsElementsKindGreaterThan(kind, FAST_HOLEY_SMI_ELEMENTS),
&object_push_pre);
- Node* new_length = BuildAppendJSArray(FAST_SMI_ELEMENTS, context, receiver,
- args, arg_index, &smi_transition);
+ Node* new_length = BuildAppendJSArray(FAST_SMI_ELEMENTS, receiver, args,
+ arg_index, &smi_transition);
args.PopAndReturn(new_length);
}
@@ -708,22 +777,21 @@
BIND(&object_push_pre);
{
- Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)),
- &double_push, &object_push);
+ Branch(IsElementsKindGreaterThan(kind, FAST_HOLEY_ELEMENTS), &double_push,
+ &object_push);
}
BIND(&object_push);
{
- Node* new_length = BuildAppendJSArray(FAST_ELEMENTS, context, receiver,
- args, arg_index, &default_label);
+ Node* new_length = BuildAppendJSArray(FAST_ELEMENTS, receiver, args,
+ arg_index, &default_label);
args.PopAndReturn(new_length);
}
BIND(&double_push);
{
- Node* new_length =
- BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, context, receiver, args,
- arg_index, &double_transition);
+ Node* new_length = BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, receiver, args,
+ arg_index, &double_transition);
args.PopAndReturn(new_length);
}
diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc
index 2a01133..5d4531f 100644
--- a/src/code-stub-assembler.cc
+++ b/src/code-stub-assembler.cc
@@ -1523,8 +1523,53 @@
return StoreNoWriteBarrier(rep, object, offset, value);
}
-Node* CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* context,
- Node* array,
+Node* CodeStubAssembler::EnsureArrayPushable(Node* receiver, Label* bailout) {
+ // Disallow pushing onto prototypes. It might be the JSArray prototype.
+ // Disallow pushing onto non-extensible objects.
+ Comment("Disallow pushing onto prototypes");
+ Node* map = LoadMap(receiver);
+ Node* bit_field2 = LoadMapBitField2(map);
+ int mask = static_cast<int>(Map::IsPrototypeMapBits::kMask) |
+ (1 << Map::kIsExtensible);
+ Node* test = Word32And(bit_field2, Int32Constant(mask));
+ GotoIf(Word32NotEqual(test, Int32Constant(1 << Map::kIsExtensible)), bailout);
+
+ // Disallow pushing onto arrays in dictionary named property mode. We need
+ // to figure out whether the length property is still writable.
+ Comment("Disallow pushing onto arrays in dictionary named property mode");
+ GotoIf(IsDictionaryMap(map), bailout);
+
+ // Check whether the length property is writable. The length property is the
+ // only default named property on arrays. It's nonconfigurable, hence is
+ // guaranteed to stay the first property.
+ Node* descriptors = LoadMapDescriptors(map);
+ Node* details =
+ LoadFixedArrayElement(descriptors, DescriptorArray::ToDetailsIndex(0));
+ GotoIf(IsSetSmi(details, PropertyDetails::kAttributesReadOnlyMask), bailout);
+
+ Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
+ return kind;
+}
+
+void CodeStubAssembler::PossiblyGrowElementsCapacity(
+ ParameterMode mode, ElementsKind kind, Node* array, Node* length,
+ Variable* var_elements, Node* growth, Label* bailout) {
+ Label fits(this, var_elements);
+ Node* capacity =
+ TaggedToParameter(LoadFixedArrayBaseLength(var_elements->value()), mode);
+ // length and growth nodes are already in a ParameterMode appropriate
+ // representation.
+ Node* new_length = IntPtrOrSmiAdd(growth, length, mode);
+ GotoIfNot(IntPtrOrSmiGreaterThan(new_length, capacity, mode), &fits);
+ Node* new_capacity = CalculateNewElementsCapacity(new_length, mode);
+ var_elements->Bind(GrowElementsCapacity(array, var_elements->value(), kind,
+ kind, capacity, new_capacity, mode,
+ bailout));
+ Goto(&fits);
+ BIND(&fits);
+}
+
+Node* CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* array,
CodeStubArguments& args,
Variable& arg_index,
Label* bailout) {
@@ -1536,46 +1581,22 @@
VARIABLE(var_length, OptimalParameterRepresentation(),
TaggedToParameter(LoadJSArrayLength(array), mode));
VARIABLE(var_elements, MachineRepresentation::kTagged, LoadElements(array));
- Node* capacity =
- TaggedToParameter(LoadFixedArrayBaseLength(var_elements.value()), mode);
// Resize the capacity of the fixed array if it doesn't fit.
- Label fits(this, &var_elements);
Node* first = arg_index.value();
- Node* growth = IntPtrSub(args.GetLength(), first);
- Node* new_length =
- IntPtrOrSmiAdd(WordToParameter(growth, mode), var_length.value(), mode);
- GotoIfNot(IntPtrOrSmiGreaterThan(new_length, capacity, mode), &fits);
- Node* new_capacity = CalculateNewElementsCapacity(new_length, mode);
- var_elements.Bind(GrowElementsCapacity(array, var_elements.value(), kind,
- kind, capacity, new_capacity, mode,
- &pre_bailout));
- Goto(&fits);
- BIND(&fits);
- Node* elements = var_elements.value();
+ Node* growth = WordToParameter(IntPtrSub(args.GetLength(), first), mode);
+ PossiblyGrowElementsCapacity(mode, kind, array, var_length.value(),
+ &var_elements, growth, &pre_bailout);
// Push each argument onto the end of the array now that there is enough
// capacity.
CodeStubAssembler::VariableList push_vars({&var_length}, zone());
+ Node* elements = var_elements.value();
args.ForEach(
push_vars,
[this, kind, mode, elements, &var_length, &pre_bailout](Node* arg) {
- if (IsFastSmiElementsKind(kind)) {
- GotoIf(TaggedIsNotSmi(arg), &pre_bailout);
- } else if (IsFastDoubleElementsKind(kind)) {
- GotoIfNotNumber(arg, &pre_bailout);
- }
- if (IsFastDoubleElementsKind(kind)) {
- Node* double_value = ChangeNumberToFloat64(arg);
- StoreFixedDoubleArrayElement(elements, var_length.value(),
- Float64SilenceNaN(double_value), mode);
- } else {
- WriteBarrierMode barrier_mode = IsFastSmiElementsKind(kind)
- ? SKIP_WRITE_BARRIER
- : UPDATE_WRITE_BARRIER;
- StoreFixedArrayElement(elements, var_length.value(), arg,
- barrier_mode, 0, mode);
- }
+ TryStoreArrayElement(kind, mode, &pre_bailout, elements,
+ var_length.value(), arg);
Increment(var_length, 1, mode);
},
first, nullptr);
@@ -1600,6 +1621,49 @@
return var_tagged_length.value();
}
+void CodeStubAssembler::TryStoreArrayElement(ElementsKind kind,
+ ParameterMode mode, Label* bailout,
+ Node* elements, Node* index,
+ Node* value) {
+ if (IsFastSmiElementsKind(kind)) {
+ GotoIf(TaggedIsNotSmi(value), bailout);
+ } else if (IsFastDoubleElementsKind(kind)) {
+ GotoIfNotNumber(value, bailout);
+ }
+ if (IsFastDoubleElementsKind(kind)) {
+ Node* double_value = ChangeNumberToFloat64(value);
+ StoreFixedDoubleArrayElement(elements, index,
+ Float64SilenceNaN(double_value), mode);
+ } else {
+ WriteBarrierMode barrier_mode =
+ IsFastSmiElementsKind(kind) ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER;
+ StoreFixedArrayElement(elements, index, value, barrier_mode, 0, mode);
+ }
+}
+
+void CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* array,
+ Node* value, Label* bailout) {
+ Comment("BuildAppendJSArray: %s", ElementsKindToString(kind));
+ ParameterMode mode = OptimalParameterMode();
+ VARIABLE(var_length, OptimalParameterRepresentation(),
+ TaggedToParameter(LoadJSArrayLength(array), mode));
+ VARIABLE(var_elements, MachineRepresentation::kTagged, LoadElements(array));
+
+ // Resize the capacity of the fixed array if it doesn't fit.
+ Node* growth = IntPtrOrSmiConstant(1, mode);
+ PossiblyGrowElementsCapacity(mode, kind, array, var_length.value(),
+ &var_elements, growth, bailout);
+
+ // Push each argument onto the end of the array now that there is enough
+ // capacity.
+ TryStoreArrayElement(kind, mode, bailout, var_elements.value(),
+ var_length.value(), value);
+ Increment(var_length, 1, mode);
+
+ Node* length = ParameterToTagged(var_length.value(), mode);
+ StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length);
+}
+
Node* CodeStubAssembler::AllocateHeapNumber(MutableMode mode) {
Node* result = Allocate(HeapNumber::kSize, kNone);
Heap::RootListIndex heap_map_index =
@@ -8485,6 +8549,11 @@
return Word32Equal(holey_elements, Int32Constant(1));
}
+Node* CodeStubAssembler::IsElementsKindGreaterThan(
+ Node* target_kind, ElementsKind reference_kind) {
+ return Int32GreaterThan(target_kind, Int32Constant(reference_kind));
+}
+
Node* CodeStubAssembler::IsDebugActive() {
Node* is_debug_active = Load(
MachineType::Uint8(),
diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h
index 3e20e6f..74c23c6 100644
--- a/src/code-stub-assembler.h
+++ b/src/code-stub-assembler.h
@@ -501,9 +501,23 @@
Node* object, Node* index, Node* value,
ParameterMode parameter_mode = INTPTR_PARAMETERS);
- Node* BuildAppendJSArray(ElementsKind kind, Node* context, Node* array,
+ // EnsureArrayPushable verifies that receiver is:
+ // 1. Is not a prototype.
+ // 2. Is not a dictionary.
+ // 3. Has a writeable length property.
+ // It returns ElementsKind as a node for further division into cases.
+ Node* EnsureArrayPushable(Node* receiver, Label* bailout);
+
+ void TryStoreArrayElement(ElementsKind kind, ParameterMode mode,
+ Label* bailout, Node* elements, Node* index,
+ Node* value);
+ // Consumes args into the array, and returns tagged new length.
+ Node* BuildAppendJSArray(ElementsKind kind, Node* array,
CodeStubArguments& args, Variable& arg_index,
Label* bailout);
+ // Pushes value onto the end of array.
+ void BuildAppendJSArray(ElementsKind kind, Node* array, Node* value,
+ Label* bailout);
void StoreFieldsNoWriteBarrier(Node* start_address, Node* end_address,
Node* value);
@@ -663,6 +677,14 @@
Node* capacity, Node* new_capacity,
ParameterMode mode, Label* bailout);
+ // Given a need to grow by |growth|, allocate an appropriate new capacity
+ // if necessary, and return a new elements FixedArray object. Label |bailout|
+ // is followed for allocation failure.
+ void PossiblyGrowElementsCapacity(ParameterMode mode, ElementsKind kind,
+ Node* array, Node* length,
+ Variable* var_elements, Node* growth,
+ Label* bailout);
+
// Allocation site manipulation
void InitializeAllocationMemento(Node* base_allocation,
int base_allocation_size,
@@ -750,6 +772,8 @@
// ElementsKind helpers:
Node* IsFastElementsKind(Node* elements_kind);
Node* IsHoleyFastElementsKind(Node* elements_kind);
+ Node* IsElementsKindGreaterThan(Node* target_kind,
+ ElementsKind reference_kind);
// String helpers.
// Load a character from a String (might flatten a ConsString).
diff --git a/src/js/array.js b/src/js/array.js
index ddcc0e3..188f21c 100644
--- a/src/js/array.js
+++ b/src/js/array.js
@@ -1000,57 +1000,6 @@
return InnerArraySort(array, length, comparefn);
}
-
-// The following functions cannot be made efficient on sparse arrays while
-// preserving the semantics, since the calls to the receiver function can add
-// or delete elements from the array.
-function InnerArrayFilter(f, receiver, array, length, result) {
- var result_length = 0;
- for (var i = 0; i < length; i++) {
- if (i in array) {
- var element = array[i];
- if (%_Call(f, receiver, element, i, array)) {
- %CreateDataProperty(result, result_length, element);
- result_length++;
- }
- }
- }
- return result;
-}
-
-
-
-function ArrayFilter(f, receiver) {
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
-
- // Pull out the length so that modifications to the length in the
- // loop will not affect the looping and side effects are visible.
- var array = TO_OBJECT(this);
- var length = TO_LENGTH(array.length);
- if (!IS_CALLABLE(f)) throw %make_type_error(kCalledNonCallable, f);
- var result = ArraySpeciesCreate(array, 0);
- return InnerArrayFilter(f, receiver, array, length, result);
-}
-
-function ArrayMap(f, receiver) {
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
-
- // Pull out the length so that modifications to the length in the
- // loop will not affect the looping and side effects are visible.
- var array = TO_OBJECT(this);
- var length = TO_LENGTH(array.length);
- if (!IS_CALLABLE(f)) throw %make_type_error(kCalledNonCallable, f);
- var result = ArraySpeciesCreate(array, length);
- for (var i = 0; i < length; i++) {
- if (i in array) {
- var element = array[i];
- %CreateDataProperty(result, i, %_Call(f, receiver, element, i, array));
- }
- }
- return result;
-}
-
-
function ArrayLastIndexOf(element, index) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
@@ -1381,8 +1330,6 @@
"slice", getFunction("slice", ArraySlice, 2),
"splice", getFunction("splice", ArraySplice, 2),
"sort", getFunction("sort", ArraySort),
- "filter", getFunction("filter", ArrayFilter, 1),
- "map", getFunction("map", ArrayMap, 1),
"indexOf", getFunction("indexOf", null, 1),
"lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
"copyWithin", getFunction("copyWithin", ArrayCopyWithin, 2),
@@ -1439,7 +1386,6 @@
to.ArrayPush = ArrayPush;
to.ArrayToString = ArrayToString;
to.ArrayValues = IteratorFunctions.values,
- to.InnerArrayFilter = InnerArrayFilter;
to.InnerArrayFind = InnerArrayFind;
to.InnerArrayFindIndex = InnerArrayFindIndex;
to.InnerArrayJoin = InnerArrayJoin;
diff --git a/src/js/typedarray.js b/src/js/typedarray.js
index 32e88b3..f7377dc 100644
--- a/src/js/typedarray.js
+++ b/src/js/typedarray.js
@@ -20,7 +20,6 @@
var GlobalArrayBuffer = global.ArrayBuffer;
var GlobalArrayBufferPrototype = GlobalArrayBuffer.prototype;
var GlobalObject = global.Object;
-var InnerArrayFilter;
var InnerArrayFind;
var InnerArrayFindIndex;
var InnerArrayJoin;
@@ -57,7 +56,6 @@
ArrayValues = from.ArrayValues;
GetIterator = from.GetIterator;
GetMethod = from.GetMethod;
- InnerArrayFilter = from.InnerArrayFilter;
InnerArrayFind = from.InnerArrayFind;
InnerArrayFindIndex = from.InnerArrayFindIndex;
InnerArrayJoin = from.InnerArrayJoin;
@@ -415,6 +413,23 @@
}
%FunctionSetLength(TypedArrayForEach, 1);
+// The following functions cannot be made efficient on sparse arrays while
+// preserving the semantics, since the calls to the receiver function can add
+// or delete elements from the array.
+function InnerTypedArrayFilter(f, receiver, array, length, result) {
+ var result_length = 0;
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ if (%_Call(f, receiver, element, i, array)) {
+ %CreateDataProperty(result, result_length, element);
+ result_length++;
+ }
+ }
+ }
+ return result;
+}
+
// ES6 draft 07-15-13, section 22.2.3.9
function TypedArrayFilter(f, thisArg) {
@@ -423,7 +438,7 @@
var length = %_TypedArrayGetLength(this);
if (!IS_CALLABLE(f)) throw %make_type_error(kCalledNonCallable, f);
var result = new InternalArray();
- InnerArrayFilter(f, thisArg, this, length, result);
+ InnerTypedArrayFilter(f, thisArg, this, length, result);
var captured = result.length;
var output = TypedArraySpeciesCreate(this, captured);
for (var i = 0; i < captured; i++) {
diff --git a/test/cctest/test-code-stub-assembler.cc b/test/cctest/test-code-stub-assembler.cc
index 4af612f..cd7c9a3 100644
--- a/test/cctest/test-code-stub-assembler.cc
+++ b/test/cctest/test-code-stub-assembler.cc
@@ -1807,9 +1807,8 @@
Variable arg_index(this, MachineType::PointerRepresentation());
Label bailout(this);
arg_index.Bind(IntPtrConstant(0));
- Node* length = BuildAppendJSArray(
- kind_, HeapConstant(Handle<HeapObject>(isolate->context(), isolate)),
- HeapConstant(array), args, arg_index, &bailout);
+ Node* length = BuildAppendJSArray(kind_, HeapConstant(array), args,
+ arg_index, &bailout);
Return(length);
Bind(&bailout);
diff --git a/test/mjsunit/stack-traces.js b/test/mjsunit/stack-traces.js
index 9522178..33552bb 100644
--- a/test/mjsunit/stack-traces.js
+++ b/test/mjsunit/stack-traces.js
@@ -264,7 +264,7 @@
}
-testTrace("testArrayNative", testArrayNative, ["Array.map (native)"]);
+testTrace("testArrayNative", testArrayNative, ["Array.map"]);
testTrace("testNested", testNested, ["at one", "at two", "at three"]);
testTrace("testMethodNameInference", testMethodNameInference, ["at Foo.bar"]);
testTrace("testImplicitConversion", testImplicitConversion, ["at Nirk.valueOf"]);