Bug 1165052 - Part 11: Use ArraySpeciesCreate in Array.prototype.slice. r=efaust
☠☠ backed out by b38df48d4638 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Sat, 05 Mar 2016 18:57:53 +0900
changeset 290616 e682faee5bff7c99ed7dc045f67e5324525d396e
parent 290615 5d13efb13fec2a7843d65c27c9862bd5e51c457d
child 290617 5e108d752209e5dc17c91775974d0b82b7ff2f93
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
bugs1165052
milestone48.0a1
Bug 1165052 - Part 11: Use ArraySpeciesCreate in Array.prototype.slice. r=efaust
js/src/jsarray.cpp
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -901,17 +901,17 @@ js::IsWrappedArrayConstructor(JSContext*
 
         *result = IsArrayConstructor(obj);
     } else {
         *result = false;
     }
     return true;
 }
 
-/* static */ bool
+static bool
 IsArraySpecies(JSContext* cx, HandleObject origArray)
 {
     RootedValue ctor(cx);
     if (!GetPropertyPure(cx, origArray, NameToId(cx->names().constructor), ctor.address()))
         return false;
 
     if (!IsArrayConstructor(ctor))
         return false;
@@ -920,17 +920,17 @@ IsArraySpecies(JSContext* cx, HandleObje
     RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
     JSFunction* getter;
     if (!GetGetterPure(cx, ctorObj, speciesId, &getter))
         return false;
 
     return IsSelfHostedFunctionWithName(getter, cx->names().ArraySpecies);
 }
 
-/* static */ bool
+static bool
 ArraySpeciesCreate(JSContext* cx, HandleObject origArray, uint32_t length, MutableHandleObject arr)
 {
     RootedId createId(cx, NameToId(cx->names().ArraySpeciesCreate));
     RootedFunction create(cx, JS::GetSelfHostedFunction(cx, "ArraySpeciesCreate", createId, 2));
 
     FixedInvokeArgs<2> args(cx);
 
     args.setCallee(ObjectValue(*create));
@@ -2821,45 +2821,20 @@ NormalizeSliceTerm(T value, uint32_t len
         if (value < 0)
             return 0;
     } else if (double(value) > double(length)) {
         return length;
     }
     return uint32_t(value);
 }
 
-bool
-js::array_slice(JSContext* cx, unsigned argc, Value* vp)
+static bool
+ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t begin, uint32_t end,
+                   MutableHandleObject arr)
 {
-    AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.slice");
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    uint32_t length;
-    if (!GetLengthProperty(cx, obj, &length))
-        return false;
-
-    uint32_t begin = 0;
-    uint32_t end = length;
-    if (args.length() > 0) {
-        double d;
-        if (!ToInteger(cx, args[0], &d))
-            return false;
-        begin = NormalizeSliceTerm(d, length);
-
-        if (args.hasDefined(1)) {
-            if (!ToInteger(cx, args[1], &d))
-                return false;
-            end = NormalizeSliceTerm(d, length);
-        }
-    }
-
     if (begin > end)
         begin = end;
 
     if (!ObjectMayHaveExtraIndexedProperties(obj)) {
         size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
         size_t count = 0;
         if (initlen > begin)
             count = Min<size_t>(initlen - begin, end - begin);
@@ -2869,42 +2844,134 @@ js::array_slice(JSContext* cx, unsigned 
             return false;
         SetAnyBoxedOrUnboxedArrayLength(cx, narr, end - begin);
 
         if (count) {
             DebugOnly<DenseElementResult> result =
                 CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
             MOZ_ASSERT(result.value == DenseElementResult::Success);
         }
-        args.rval().setObject(*narr);
+        arr.set(narr);
         return true;
     }
 
     RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
     if (!narr)
         return false;
 
     if (js::GetElementsOp op = obj->getOps()->getElements) {
         ElementAdder adder(cx, narr, end - begin, ElementAdder::CheckHasElemPreserveHoles);
         if (!op(cx, obj, begin, end, &adder))
             return false;
 
-        args.rval().setObject(*narr);
+        arr.set(narr);
         return true;
     }
 
     if (obj->isNative() && obj->isIndexed() && end - begin > 1000) {
         if (!SliceSparse(cx, obj, begin, end, narr))
             return false;
     } else {
         if (!SliceSlowly(cx, obj, obj, begin, end, narr))
             return false;
     }
 
-    args.rval().setObject(*narr);
+    arr.set(narr);
+    return true;
+}
+
+/* ES 2016 draft Mar 25, 2016 22.1.3.23. */
+bool
+js::array_slice(JSContext* cx, unsigned argc, Value* vp)
+{
+    AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.slice");
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Step 1. */
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return false;
+
+    /* Step 2. */
+    uint32_t length;
+    if (!GetLengthProperty(cx, obj, &length))
+        return false;
+
+    uint32_t k = 0;
+    uint32_t final = length;
+    if (args.length() > 0) {
+        double d;
+        /* Step 3. */
+        if (!ToInteger(cx, args[0], &d))
+            return false;
+
+        /* Step 4. */
+        k = NormalizeSliceTerm(d, length);
+
+        if (args.hasDefined(1)) {
+            /* Step 5. */
+            if (!ToInteger(cx, args[1], &d))
+                return false;
+
+            /* Step 6. */
+            final = NormalizeSliceTerm(d, length);
+        }
+    }
+
+    /* Step 7. */
+    uint32_t count = final > k ? final - k : 0;
+
+    RootedObject arr(cx);
+    if (IsArraySpecies(cx, obj)) {
+        /* Steps 8-11: Optimized for ordinary array. */
+        if (!ArraySliceOrdinary(cx, obj, length, k, final, &arr))
+            return false;
+
+        /* Step 12. */
+        args.rval().setObject(*arr);
+        return true;
+    }
+
+    /* Step 8. */
+    if (!ArraySpeciesCreate(cx, obj, count, &arr))
+        return false;
+
+    /* Step 9. */
+    uint32_t n = 0;
+
+    /* Step 10. */
+    RootedValue kValue(cx);
+    while (k < final) {
+        if (!CheckForInterrupt(cx))
+            return false;
+
+        /* Steps 10.a-b, and 10.c.i. */
+        bool kNotPresent;
+        if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
+            return false;
+
+        /* Step 10.c. */
+        if (!kNotPresent) {
+            /* Steps 10.c.ii. */
+            if (!DefineElement(cx, arr, n, kValue))
+                return false;
+        }
+        /* Step 10.d. */
+        k++;
+
+        /* Step 10.e. */
+        n++;
+    }
+
+    /* Step 11. */
+    if (!SetLengthProperty(cx, arr, n))
+        return false;
+
+    /* Step 12. */
+    args.rval().setObject(*arr);
     return true;
 }
 
 template <JSValueType Type>
 DenseElementResult
 ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result)
 {
     int32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
@@ -2932,17 +2999,17 @@ ArraySliceDenseKernel(JSContext* cx, JSO
 
 DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel,
                              JSContext*, JSObject*, int32_t, int32_t, JSObject*);
 
 JSObject*
 js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end,
                       HandleObject result)
 {
-    if (result) {
+    if (result && IsArraySpecies(cx, obj)) {
         ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result);
         DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
         MOZ_ASSERT(rv != DenseElementResult::Incomplete);
         return rv == DenseElementResult::Success ? result : nullptr;
     }
 
     // Slower path if the JIT wasn't able to allocate an object inline.
     JS::AutoValueArray<4> argv(cx);