Bug 1410937 - Directly initialize result arrays in String.prototype.split specializations. r=jandem
authorAndré Bargull <andre.bargull@gmail.com>
Tue, 24 Oct 2017 01:05:50 +0900
changeset 388065 40e33c4b41fd68d572725d03705433a81a6b65ce
parent 388064 1f17f122eb7c0a1a9d6f08bc55796c7ce8d954fe
child 388066 0faad69400fac3a8cb9340e820380a5fca11d57f
push id32740
push useracraciun@mozilla.com
push dateWed, 25 Oct 2017 09:30:59 +0000
treeherdermozilla-central@e56ae7213756 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1410937
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1410937 - Directly initialize result arrays in String.prototype.split specializations. r=jandem
js/src/jsstr.cpp
js/src/vm/SelfHosting.cpp
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2879,16 +2879,46 @@ js::str_replace_string_raw(JSContext* cx
         if (!repl)
             return nullptr;
     } else if (string->isRope()) {
         return BuildFlatRopeReplacement(cx, string, repl, match, patternLength);
     }
     return BuildFlatReplacement(cx, string, repl, match, patternLength);
 }
 
+static ArrayObject*
+NewFullyAllocatedStringArray(JSContext* cx, HandleObjectGroup group, uint32_t length)
+{
+    ArrayObject* array = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
+    if (!array)
+        return nullptr;
+
+    // Only string values will be added to this array. Inform TI early about
+    // the element type, so we can directly initialize all elements using
+    // NativeObject::initDenseElement() instead of the slightly more expensive
+    // NativeObject::initDenseElementWithType() method.
+    // Since this function is never called to create a zero-length array, it's
+    // always necessary and correct to call AddTypePropertyId here.
+    MOZ_ASSERT(length > 0);
+    AddTypePropertyId(cx, array, JSID_VOID, TypeSet::StringType());
+
+    return array;
+}
+
+static ArrayObject*
+SingleElementStringArray(JSContext* cx, HandleObjectGroup group, HandleLinearString str)
+{
+    ArrayObject* array = NewFullyAllocatedStringArray(cx, group, 1);
+    if (!array)
+        return nullptr;
+    array->setDenseInitializedLength(1);
+    array->initDenseElement(0, StringValue(str));
+    return array;
+}
+
 // ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
 static ArrayObject*
 SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearString sep,
             HandleObjectGroup group)
 {
     size_t strLength = str->length();
     size_t sepLength = sep->length();
     MOZ_ASSERT(sepLength != 0);
@@ -2898,18 +2928,17 @@ SplitHelper(JSContext* cx, HandleLinearS
         // Step 12.a.
         int match = StringMatch(str, sep, 0);
 
         // Step 12.b.
         if (match != -1)
             return NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
 
         // Steps 12.c-e.
-        RootedValue v(cx, StringValue(str));
-        return NewCopiedArrayTryUseGroup(cx, group, v.address(), 1);
+        return SingleElementStringArray(cx, group, str);
     }
 
     // Step 3 (reordered).
     AutoValueVector splits(cx);
 
     // Step 8 (reordered).
     size_t lastEndIndex = 0;
 
@@ -2986,72 +3015,77 @@ static ArrayObject*
 CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group)
 {
     size_t strLength = str->length();
     if (strLength == 0)
         return NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
 
     js::StaticStrings& staticStrings = cx->staticStrings();
     uint32_t resultlen = (limit < strLength ? limit : strLength);
-
-    AutoValueVector splits(cx);
-    if (!splits.reserve(resultlen))
+    MOZ_ASSERT(limit > 0 && resultlen > 0,
+               "Neither limit nor strLength is zero, so resultlen is greater than zero.");
+
+    RootedArrayObject splits(cx, NewFullyAllocatedStringArray(cx, group, resultlen));
+    if (!splits)
         return nullptr;
+    splits->ensureDenseInitializedLength(cx, 0, resultlen);
 
     for (size_t i = 0; i < resultlen; ++i) {
         JSString* sub = staticStrings.getUnitStringForElement(cx, str, i);
         if (!sub)
             return nullptr;
-        splits.infallibleAppend(StringValue(sub));
+        splits->initDenseElement(i, StringValue(sub));
     }
 
-    return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
+    return splits;
 }
 
 template <typename TextChar>
 static MOZ_ALWAYS_INLINE ArrayObject*
 SplitSingleCharHelper(JSContext* cx, HandleLinearString str, const TextChar* text,
                       uint32_t textLen, char16_t patCh, HandleObjectGroup group)
 {
     // Count the number of occurrences of patCh within text.
     uint32_t count = 0;
     for (size_t index = 0; index < textLen; index++) {
         if (static_cast<char16_t>(text[index]) == patCh)
             count++;
     }
 
     // Handle zero-occurrence case - return input string in an array.
-    if (count == 0) {
-        RootedValue strValue(cx, StringValue(str.get()));
-        return NewCopiedArrayTryUseGroup(cx, group, &strValue.get(), 1);
-    }
-
-    // Reserve memory for substring values.
-    AutoValueVector splits(cx);
-    if (!splits.reserve(count + 1))
+    if (count == 0)
+        return SingleElementStringArray(cx, group, str);
+
+    // Create the result array for the substring values.
+    RootedArrayObject splits(cx, NewFullyAllocatedStringArray(cx, group, count + 1));
+    if (!splits)
         return nullptr;
+    splits->ensureDenseInitializedLength(cx, 0, count + 1);
 
     // Add substrings.
+    uint32_t splitsIndex = 0;
     size_t lastEndIndex = 0;
     for (size_t index = 0; index < textLen; index++) {
         if (static_cast<char16_t>(text[index]) == patCh) {
             size_t subLength = size_t(index - lastEndIndex);
             JSString* sub = NewDependentString(cx, str, lastEndIndex, subLength);
-            if (!sub || !splits.append(StringValue(sub)))
+            if (!sub)
                 return nullptr;
+            splits->initDenseElement(splitsIndex++, StringValue(sub));
             lastEndIndex = index + 1;
         }
     }
 
     // Add substring for tail of string (after last match).
     JSString* sub = NewDependentString(cx, str, lastEndIndex, textLen - lastEndIndex);
-    if (!sub || !splits.append(StringValue(sub)))
+    if (!sub)
         return nullptr;
-
-    return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
+    splits->initDenseElement(splitsIndex++, StringValue(sub));
+
+    return splits;
 }
 
 // ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
 static ArrayObject*
 SplitSingleCharHelper(JSContext* cx, HandleLinearString str, char16_t ch, HandleObjectGroup group)
 {
     // Step 12.
     size_t strLength = str->length();
@@ -3066,16 +3100,18 @@ SplitSingleCharHelper(JSContext* cx, Han
     return SplitSingleCharHelper(cx, str, linearChars.twoByteChars(), strLength, ch, group);
 }
 
 // ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
 ArrayObject*
 js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep,
                      uint32_t limit)
 {
+    MOZ_ASSERT(limit > 0, "Only called for strictly positive limit.");
+
     RootedLinearString linearStr(cx, str->ensureLinear(cx));
     if (!linearStr)
         return nullptr;
 
     RootedLinearString linearSep(cx, sep->ensureLinear(cx));
     if (!linearSep)
         return nullptr;
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1716,16 +1716,17 @@ intrinsic_StringSplitStringLimit(JSConte
     MOZ_ASSERT(args.length() == 3);
 
     RootedString string(cx, args[0].toString());
     RootedString sep(cx, args[1].toString());
 
     // args[2] should be already in UInt32 range, but it could be double typed,
     // because of Ion optimization.
     uint32_t limit = uint32_t(args[2].toNumber());
+    MOZ_ASSERT(limit > 0, "Zero limit case is already handled in self-hosted code.");
 
     RootedObjectGroup group(cx, ObjectGroupCompartment::getStringSplitStringGroup(cx));
     if (!group)
         return false;
 
     JSObject* aobj = str_split_string(cx, group, string, sep, limit);
     if (!aobj)
         return false;