Bug 1165052 - Part 12: Use ArraySpeciesCreate in Array.prototype.splice. r=efaust,evilpie
☠☠ backed out by 696987b80ad4 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Wed, 23 Dec 2015 13:30:58 +0900
changeset 290617 5e108d752209e5dc17c91775974d0b82b7ff2f93
parent 290616 e682faee5bff7c99ed7dc045f67e5324525d396e
child 290618 52a142a57bf629018a0b16e85d75450e1699c0da
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust, evilpie
bugs1165052
milestone48.0a1
Bug 1165052 - Part 12: Use ArraySpeciesCreate in Array.prototype.splice. r=efaust,evilpie
js/src/jit-test/tests/basic/splice-check-steps.js
js/src/jsarray.cpp
--- a/js/src/jit-test/tests/basic/splice-check-steps.js
+++ b/js/src/jit-test/tests/basic/splice-check-steps.js
@@ -74,44 +74,47 @@ function check_splice_proxy(arr, expecte
 
     return result;
 }
 
 // Shrinking array
 check_splice_proxy(
         [10,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," +
         "del-5,del-4,del-3," +
         "set-length",
         {},
         [3,4,5],
         [10,1,2],
         0, 3
 );
 
 // Growing array
 check_splice_proxy(
         [11,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-5,get-5,set-9,has-4,get-4,set-8,has-3,get-3,set-7," +
         "set-0,set-1,set-2,set-3,set-4,set-5,set-6," +
         "set-length",
         {},
         [9,9,9,9,9,9,9,3,4,5],
         [11,1,2],
         0, 3, 9, 9, 9, 9, 9, 9, 9
 );
 
 // Same sized array
 check_splice_proxy(
         [12,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "set-0,set-1,set-2," +
         "set-length",
         {},
         [9,9,9,3,4,5],
         [12,1,2],
         0, 3, 9, 9, 9
 );
@@ -132,151 +135,163 @@ check_splice_proxy(
         undefined,
         0, 3, 9, 9, 9
 );
 
 // Step 9b: fail when [[HasProperty]]
 check_splice_proxy(
         [14,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1",
         {has: '1'},
         [14,1,2,3,4,5],
         undefined,
         0, 3, 9, 9, 9
 );
 
 // Step 9c(i): fail when [[Get]]
 check_splice_proxy(
         [15,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1",
         {get: '1'},
         [15,1,2,3,4,5],
         undefined,
         0, 3, 9, 9, 9
 );
 
 // Step 12b(iii): fail when [[HasProperty]]
 check_splice_proxy(
         [16,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-3,get-3,set-0,has-4",
         {has: '4'},
         [3,1,2,3,4,5],
         undefined,
         0, 3
 );
 
 
 // Step 12b(iv)1: fail when [[Get]]
 check_splice_proxy(
         [17,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-3,get-3,set-0,has-4,get-4",
         {get: '4'},
         [3,1,2,3,4,5],
         undefined,
         0, 3
 );
 
 
 // Step 12b(iv)2: fail when [[Put]]
 check_splice_proxy(
         [18,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-3,get-3,set-0,has-4,get-4,set-1",
         {set: '1'},
         [3,1,2,3,4,5],
         undefined,
         0, 3
 );
 
 // Step 12b(v)1: fail when [[Delete]]
 check_splice_proxy(
         [19,1,2,3,,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-3,get-3,set-0,has-4,del-1",
         {del: '1'},
         [3,1,2,3,,5],
         undefined,
         0, 3
 );
 
 // Step 12d(i): fail when [[Delete]]
 check_splice_proxy(
         [20,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," +
         "del-5,del-4",
         {del: '4'},
         [3,4,5,3,4],
         undefined,
         0, 3
 );
 
 // Step 13b(iii): fail when [[HasProperty]]
 check_splice_proxy(
         [21,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-5,get-5,set-8,has-4",
         {has: '4'},
         [21,1,2,3,4,5,,,5],
         undefined,
         0, 3, 9,9,9,9,9,9
 );
 
 
 // Step 13b(iv)1: fail when [[Get]]
 check_splice_proxy(
         [22,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-5,get-5,set-8,has-4,get-4",
         {get: '4'},
         [22,1,2,3,4,5,,,5],
         undefined,
         0, 3, 9,9,9,9,9,9
 );
 
 
 // Step 13b(iv)2: fail when [[Put]]
 check_splice_proxy(
         [23,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-5,get-5,set-8,has-4,get-4,set-7",
         {set: '7'},
         [23,1,2,3,4,5,,,5],
         undefined,
         0, 3, 9,9,9,9,9,9
 );
 
 // Step 13b(v)1: fail when [[Delete]]
 check_splice_proxy(
         [24,1,2,3,,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-5,get-5,set-8,has-4,del-7",
         {del: '7'},
         [24,1,2,3,,5,,,5],
         undefined,
         0, 3, 9,9,9,9,9,9
 );
 
 // Step 15b: fail when [[Put]]
 check_splice_proxy(
         [25,1,2,3,4,5],
         "get-length," +
+        "get-constructor," +
         "has-0,get-0,has-1,get-1,has-2,get-2," +
         "has-5,get-5,set-8,has-4,get-4,set-7,has-3,get-3,set-6," +
         "set-0,set-1,set-2",
         {set: '2'},
         [9,9,2,3,4,5,3,4,5],
         undefined,
         0, 3, 9,9,9,9,9,9
 );
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2437,151 +2437,201 @@ CanOptimizeForDenseStorage(HandleObject 
      * Now watch out for getters and setters along the prototype chain or in
      * other indexed properties on the object.  (Note that non-writable length
      * is subsumed by the initializedLength comparison.)
      */
     return !ObjectMayHaveExtraIndexedProperties(arr) &&
            startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr);
 }
 
-/* ES5 15.4.4.12. */
+/* ES 2016 draft Mar 25, 2016 22.1.3.26. */
 static bool
 array_splice(JSContext* cx, unsigned argc, Value* vp)
 {
     return array_splice_impl(cx, argc, vp, true);
 }
 
+static inline bool
+ArraySpliceCopy(JSContext* cx, HandleObject arr, HandleObject obj,
+                uint32_t actualStart, uint32_t actualDeleteCount)
+{
+    /* Steps 14, 15, 15.e. */
+    RootedValue fromValue(cx);
+    for (uint32_t k = 0; k < actualDeleteCount; k++) {
+        /* Step 15.a (implicit). */
+
+        if (!CheckForInterrupt(cx))
+            return false;
+
+        /* Steps 15.b-c, 15.d.i-ii. */
+        bool hole;
+        if (!GetElement(cx, obj, actualStart + k, &hole, &fromValue))
+            return false;
+
+        /* Step 15.d. */
+        if (!hole) {
+            /* Step 15.d.iii-iv. */
+            if (!DefineElement(cx, arr, k, fromValue))
+                return false;
+        }
+    }
+
+    /* Steps 16-17. */
+    return SetLengthProperty(cx, arr, actualDeleteCount);
+}
+
 bool
 js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
 {
     AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.splice");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
-    /* Steps 3-4. */
+    /* Step 2. */
     uint32_t len;
     if (!GetLengthProperty(cx, obj, &len))
         return false;
 
-    /* Step 5. */
+    /* Step 3. */
     double relativeStart;
     if (!ToInteger(cx, args.get(0), &relativeStart))
         return false;
 
-    /* Step 6. */
+    /* Step 4. */
     uint32_t actualStart;
     if (relativeStart < 0)
         actualStart = Max(len + relativeStart, 0.0);
     else
         actualStart = Min(relativeStart, double(len));
 
-    /* Step 7. */
+    /* Step 5. */
     uint32_t actualDeleteCount;
-    if (args.length() != 1) {
+    if (args.length() == 0) {
+        /* Step 5.b. */
+        actualDeleteCount = 0;
+    } else if (args.length() == 1) {
+        /* Step 6.b. */
+        actualDeleteCount = len - actualStart;
+    } else {
+        /* Steps 7.b. */
         double deleteCountDouble;
-        RootedValue cnt(cx, args.length() >= 2 ? args[1] : Int32Value(0));
+        RootedValue cnt(cx, args[1]);
         if (!ToInteger(cx, cnt, &deleteCountDouble))
             return false;
+
+        /* Step 7.c. */
         actualDeleteCount = Min(Max(deleteCountDouble, 0.0), double(len - actualStart));
-    } else {
-        /*
-         * Non-standard: if start was specified but deleteCount was omitted,
-         * delete to the end of the array.  See bug 668024 for discussion.
-         */
-        actualDeleteCount = len - actualStart;
     }
 
+    /* Step 8 (implicit). */
+
     MOZ_ASSERT(len - actualStart >= actualDeleteCount);
 
-    /* Steps 2, 8-9. */
     RootedObject arr(cx);
-    if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
-        if (returnValueIsUsed) {
+    if (IsArraySpecies(cx, obj)) {
+        if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
+            if (returnValueIsUsed) {
+                /* Step 9. */
+                arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, actualDeleteCount);
+                if (!arr)
+                    return false;
+
+                /* Steps 10-11. */
+                DebugOnly<DenseElementResult> result =
+                    CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
+                MOZ_ASSERT(result.value == DenseElementResult::Success);
+
+                /* Step 12 (implicit). */
+            }
+        } else {
+            /* Step 9. */
             arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, actualDeleteCount);
             if (!arr)
                 return false;
-            DebugOnly<DenseElementResult> result =
-                CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
-            MOZ_ASSERT(result.value == DenseElementResult::Success);
+
+            /* Steps 10-12. */
+            if (!ArraySpliceCopy(cx, arr, obj, actualStart, actualDeleteCount))
+                return false;
         }
     } else {
-        arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, actualDeleteCount);
-        if (!arr)
+        /* Steps 9. */
+        if (!ArraySpeciesCreate(cx, obj, actualDeleteCount, &arr))
             return false;
 
-        RootedValue fromValue(cx);
-        for (uint32_t k = 0; k < actualDeleteCount; k++) {
-            bool hole;
-            if (!CheckForInterrupt(cx) ||
-                !GetElement(cx, obj, actualStart + k, &hole, &fromValue) ||
-                (!hole && !DefineElement(cx, arr, k, fromValue)))
-            {
-                return false;
-            }
-        }
+        /* Steps 10-12. */
+        if (!ArraySpliceCopy(cx, arr, obj, actualStart, actualDeleteCount))
+            return false;
     }
 
-    /* Step 11. */
+    /* Step 14. */
     uint32_t itemCount = (args.length() >= 2) ? (args.length() - 2) : 0;
 
     if (itemCount < actualDeleteCount) {
-        /* Step 12: the array is being shrunk. */
+        /* Step 15: the array is being shrunk. */
         uint32_t sourceIndex = actualStart + actualDeleteCount;
         uint32_t targetIndex = actualStart + itemCount;
         uint32_t finalLength = len - actualDeleteCount + itemCount;
 
         if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
-            /* Steps 12(a)-(b). */
+            /* Steps 15.a-b. */
             DenseElementResult result =
                 MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
                                                    len - sourceIndex);
             MOZ_ASSERT(result != DenseElementResult::Incomplete);
             if (result == DenseElementResult::Failure)
                 return false;
 
-            /* Steps 12(c)-(d). */
+            /* Steps 15.c-d. */
             SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
         } else {
             /*
              * This is all very slow if the length is very large. We don't yet
              * have the ability to iterate in sorted order, so we just do the
              * pessimistic thing and let CheckForInterrupt handle the
              * fallout.
              */
 
-            /* Steps 12(a)-(b). */
+            /* Steps 15.a-b. */
             RootedValue fromValue(cx);
             for (uint32_t from = sourceIndex, to = targetIndex; from < len; from++, to++) {
+                /* Steps 15.b.i-ii (implicit). */
+
                 if (!CheckForInterrupt(cx))
                     return false;
 
+                /* Steps 15.b.iii, 15.b.iv.1. */
                 bool hole;
                 if (!GetElement(cx, obj, from, &hole, &fromValue))
                     return false;
+
+                /* Steps 15.b.iv. */
                 if (hole) {
+                    /* Steps 15.b.v.1. */
                     if (!DeletePropertyOrThrow(cx, obj, to))
                         return false;
                 } else {
+                    /* Step 15.b.iv.2. */
                     if (!SetArrayElement(cx, obj, to, fromValue))
                         return false;
                 }
             }
 
-            /* Steps 12(c)-(d). */
+            /* Steps 15.c-d. */
             for (uint32_t k = len; k > finalLength; k--) {
+                /* Steps 15.d.i-ii. */
                 if (!DeletePropertyOrThrow(cx, obj, k - 1))
                     return false;
             }
         }
     } else if (itemCount > actualDeleteCount) {
-        /* Step 13. */
+        /* Step 16. */
 
         /*
          * Optimize only if the array is already dense and we can extend it to
          * its new length.  It would be wrong to extend the elements here for a
          * number of reasons.
          *
          * First, this could cause us to fall into the fast-path below.  This
          * would cause elements to be moved into places past the non-writable
@@ -2616,57 +2666,67 @@ js::array_splice_impl(JSContext* cx, uns
             DenseElementResult result =
                 MoveAnyBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount,
                                                    actualStart + actualDeleteCount,
                                                    len - (actualStart + actualDeleteCount));
             MOZ_ASSERT(result != DenseElementResult::Incomplete);
             if (result == DenseElementResult::Failure)
                 return false;
 
-            /* Steps 12(c)-(d). */
+            /* Steps 16.a-b. */
             SetAnyBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount);
         } else {
             RootedValue fromValue(cx);
             for (double k = len - actualDeleteCount; k > actualStart; k--) {
                 if (!CheckForInterrupt(cx))
                     return false;
 
+                /* Step 16.b.i. */
                 double from = k + actualDeleteCount - 1;
+
+                /* Step 16.b.ii. */
                 double to = k + itemCount - 1;
 
+                /* Steps 16.b.iii, 16.b.iv.1. */
                 bool hole;
                 if (!GetElement(cx, obj, from, &hole, &fromValue))
                     return false;
 
+                /* Steps 16.b.iv. */
                 if (hole) {
+                    /* Step 16.b.v.1. */
                     if (!DeletePropertyOrThrow(cx, obj, to))
                         return false;
                 } else {
+                    /* Step 16.b.iv.2. */
                     if (!SetArrayElement(cx, obj, to, fromValue))
                         return false;
                 }
             }
         }
     }
 
-    /* Step 10. */
+    /* Step 13 (reordered). */
     Value* items = args.array() + 2;
 
-    /* Steps 14-15. */
+    /* Steps 17-18. */
     for (uint32_t k = actualStart, i = 0; i < itemCount; i++, k++) {
+        /* Step 18.a (implicit). */
+
+        /* Step 18.b. */
         if (!SetArrayElement(cx, obj, k, HandleValue::fromMarkedLocation(&items[i])))
             return false;
     }
 
-    /* Step 16. */
+    /* Step 19. */
     double finalLength = double(len) - actualDeleteCount + itemCount;
     if (!SetLengthProperty(cx, obj, finalLength))
         return false;
 
-    /* Step 17. */
+    /* Step 20. */
     if (returnValueIsUsed)
         args.rval().setObject(*arr);
 
     return true;
 }
 
 struct SortComparatorIndexes
 {