[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"]);