Bug 1319740 - Parametrize ComputeSingleDisplayName based on the character type of the key string, and iterate through the string using iterators, not using null-termination. r=arai
authorJeff Walden <jwalden@mit.edu>
Fri, 02 Dec 2016 14:02:36 -0800
changeset 326533 e70365b0720ce124090edf22adeeee0935dbf433
parent 326532 97aef98c5230cad867e6073d76c6a6e935e84e9f
child 326534 d37fc752b0af25f53e32d893739228a6cfc3b60b
push id31106
push userkwierso@gmail.com
push dateTue, 20 Dec 2016 19:42:03 +0000
treeherdermozilla-central@7083c0d30e75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1319740
milestone53.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 1319740 - Parametrize ComputeSingleDisplayName based on the character type of the key string, and iterate through the string using iterators, not using null-termination. r=arai
js/src/builtin/Intl.cpp
js/src/tests/Intl/getDisplayNames.js
mfbt/RangedPtr.h
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -46,17 +46,18 @@
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::IsFinite;
 using mozilla::IsNegativeZero;
 using mozilla::MakeScopeExit;
 using mozilla::PodCopy;
-
+using mozilla::Range;
+using mozilla::RangedPtr;
 
 /*
  * Pervasive note: ICU functions taking a UErrorCode in/out parameter always
  * test that parameter before doing anything, and will return immediately if
  * the value indicates that a failure occurred in a prior ICU call,
  * without doing anything else. See
  * http://userguide.icu-project.org/design#TOC-Error-Handling
  */
@@ -3011,205 +3012,232 @@ js::intl_GetCalendarInfo(JSContext* cx, 
 
     if (!DefineProperty(cx, info, cx->names().weekendEnd, weekendEnd))
         return false;
 
     args.rval().setObject(*info);
     return true;
 }
 
-template<size_t N>
-inline bool
-MatchPart(const char** pattern, const char (&part)[N])
+static void
+ReportBadKey(JSContext* cx, const Range<const JS::Latin1Char>& range)
+{
+    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY,
+                               range.begin().get());
+}
+
+static void
+ReportBadKey(JSContext* cx, const Range<const char16_t>& range)
 {
-    if (strncmp(*pattern, part, N - 1))
-        return false;
-
-    *pattern += N - 1;
+    JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY,
+                           range.begin().get());
+}
+
+template<typename ConstChar>
+static bool
+MatchPart(RangedPtr<ConstChar> iter, const RangedPtr<ConstChar> end,
+          const char* part, size_t partlen)
+{
+    for (size_t i = 0; i < partlen; iter++, i++) {
+        if (iter == end || *iter != part[i])
+            return false;
+    }
+
     return true;
 }
 
-static bool
-MatchSlash(JSContext* cx, const JSAutoByteString& pattern, const char** iter)
+template<typename ConstChar, size_t N>
+inline bool
+MatchPart(RangedPtr<ConstChar>* iter, const RangedPtr<ConstChar> end, const char (&part)[N])
 {
-    if (MOZ_LIKELY(**iter == '/')) {
-        *iter += 1;
-        return true;
-    }
-
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
-    return false;
+    if (!MatchPart(*iter, end, part, N - 1))
+        return false;
+
+    *iter += N - 1;
+    return true;
 }
 
 enum class DisplayNameStyle
 {
     Narrow,
     Short,
     Long,
 };
 
+template<typename ConstChar>
 static JSString*
 ComputeSingleDisplayName(JSContext* cx, UDateFormat* fmt, UDateTimePatternGenerator* dtpg,
                          DisplayNameStyle style,
                          Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE>& chars,
-                         const JSAutoByteString& pattern)
+                         const Range<ConstChar>& pattern)
 {
-    const char* pat = pattern.ptr();
-
-    if (!MatchPart(&pat, "dates")) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+    RangedPtr<ConstChar> iter = pattern.begin();
+    const RangedPtr<ConstChar> end = pattern.end();
+
+    auto MatchSlash = [cx, pattern, &iter, end]() {
+        if (MOZ_LIKELY(iter != end && *iter == '/')) {
+            iter++;
+            return true;
+        }
+
+        ReportBadKey(cx, pattern);
+        return false;
+    };
+
+    if (!MatchPart(&iter, end, "dates")) {
+        ReportBadKey(cx, pattern);
         return nullptr;
     }
 
-    if (!MatchSlash(cx, pattern, &pat))
+    if (!MatchSlash())
         return nullptr;
 
-    if (MatchPart(&pat, "fields")) {
-        if (!MatchSlash(cx, pattern, &pat))
+    if (MatchPart(&iter, end, "fields")) {
+        if (!MatchSlash())
             return nullptr;
 
         UDateTimePatternField fieldType;
 
-        if (MatchPart(&pat, "year")) {
+        if (MatchPart(&iter, end, "year")) {
             fieldType = UDATPG_YEAR_FIELD;
-        } else if (MatchPart(&pat, "month")) {
+        } else if (MatchPart(&iter, end, "month")) {
             fieldType = UDATPG_MONTH_FIELD;
-        } else if (MatchPart(&pat, "week")) {
+        } else if (MatchPart(&iter, end, "week")) {
             fieldType = UDATPG_WEEK_OF_YEAR_FIELD;
-        } else if (MatchPart(&pat, "day")) {
+        } else if (MatchPart(&iter, end, "day")) {
             fieldType = UDATPG_DAY_FIELD;
         } else {
-            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+            ReportBadKey(cx, pattern);
             return nullptr;
         }
 
         // This part must be the final part with no trailing data.
-        if (*pat != '\0') {
-            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+        if (iter != end) {
+            ReportBadKey(cx, pattern);
             return nullptr;
         }
 
         int32_t resultSize;
         const UChar* value = udatpg_getAppendItemName(dtpg, fieldType, &resultSize);
         MOZ_ASSERT(resultSize >= 0);
 
         return NewStringCopyN<CanGC>(cx, UCharToChar16(value), size_t(resultSize));
     }
 
-    if (MatchPart(&pat, "gregorian")) {
-        if (!MatchSlash(cx, pattern, &pat))
+    if (MatchPart(&iter, end, "gregorian")) {
+        if (!MatchSlash())
             return nullptr;
 
         UDateFormatSymbolType symbolType;
         int32_t index;
 
-        if (MatchPart(&pat, "months")) {
-            if (!MatchSlash(cx, pattern, &pat))
+        if (MatchPart(&iter, end, "months")) {
+            if (!MatchSlash())
                 return nullptr;
 
             switch (style) {
               case DisplayNameStyle::Narrow:
                 symbolType = UDAT_STANDALONE_NARROW_MONTHS;
                 break;
 
               case DisplayNameStyle::Short:
                 symbolType = UDAT_STANDALONE_SHORT_MONTHS;
                 break;
 
               case DisplayNameStyle::Long:
                 symbolType = UDAT_STANDALONE_MONTHS;
                 break;
             }
 
-            if (MatchPart(&pat, "january")) {
+            if (MatchPart(&iter, end, "january")) {
                 index = UCAL_JANUARY;
-            } else if (MatchPart(&pat, "february")) {
+            } else if (MatchPart(&iter, end, "february")) {
                 index = UCAL_FEBRUARY;
-            } else if (MatchPart(&pat, "march")) {
+            } else if (MatchPart(&iter, end, "march")) {
                 index = UCAL_MARCH;
-            } else if (MatchPart(&pat, "april")) {
+            } else if (MatchPart(&iter, end, "april")) {
                 index = UCAL_APRIL;
-            } else if (MatchPart(&pat, "may")) {
+            } else if (MatchPart(&iter, end, "may")) {
                 index = UCAL_MAY;
-            } else if (MatchPart(&pat, "june")) {
+            } else if (MatchPart(&iter, end, "june")) {
                 index = UCAL_JUNE;
-            } else if (MatchPart(&pat, "july")) {
+            } else if (MatchPart(&iter, end, "july")) {
                 index = UCAL_JULY;
-            } else if (MatchPart(&pat, "august")) {
+            } else if (MatchPart(&iter, end, "august")) {
                 index = UCAL_AUGUST;
-            } else if (MatchPart(&pat, "september")) {
+            } else if (MatchPart(&iter, end, "september")) {
                 index = UCAL_SEPTEMBER;
-            } else if (MatchPart(&pat, "october")) {
+            } else if (MatchPart(&iter, end, "october")) {
                 index = UCAL_OCTOBER;
-            } else if (MatchPart(&pat, "november")) {
+            } else if (MatchPart(&iter, end, "november")) {
                 index = UCAL_NOVEMBER;
-            } else if (MatchPart(&pat, "december")) {
+            } else if (MatchPart(&iter, end, "december")) {
                 index = UCAL_DECEMBER;
             } else {
-                JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+                ReportBadKey(cx, pattern);
                 return nullptr;
             }
-        } else if (MatchPart(&pat, "weekdays")) {
-            if (!MatchSlash(cx, pattern, &pat))
+        } else if (MatchPart(&iter, end, "weekdays")) {
+            if (!MatchSlash())
                 return nullptr;
 
             switch (style) {
               case DisplayNameStyle::Narrow:
                 symbolType = UDAT_STANDALONE_NARROW_WEEKDAYS;
                 break;
 
               case DisplayNameStyle::Short:
                 symbolType = UDAT_STANDALONE_SHORT_WEEKDAYS;
                 break;
 
               case DisplayNameStyle::Long:
                 symbolType = UDAT_STANDALONE_WEEKDAYS;
                 break;
             }
 
-            if (MatchPart(&pat, "monday")) {
+            if (MatchPart(&iter, end, "monday")) {
                 index = UCAL_MONDAY;
-            } else if (MatchPart(&pat, "tuesday")) {
+            } else if (MatchPart(&iter, end, "tuesday")) {
                 index = UCAL_TUESDAY;
-            } else if (MatchPart(&pat, "wednesday")) {
+            } else if (MatchPart(&iter, end, "wednesday")) {
                 index = UCAL_WEDNESDAY;
-            } else if (MatchPart(&pat, "thursday")) {
+            } else if (MatchPart(&iter, end, "thursday")) {
                 index = UCAL_THURSDAY;
-            } else if (MatchPart(&pat, "friday")) {
+            } else if (MatchPart(&iter, end, "friday")) {
                 index = UCAL_FRIDAY;
-            } else if (MatchPart(&pat, "saturday")) {
+            } else if (MatchPart(&iter, end, "saturday")) {
                 index = UCAL_SATURDAY;
-            } else if (MatchPart(&pat, "sunday")) {
+            } else if (MatchPart(&iter, end, "sunday")) {
                 index = UCAL_SUNDAY;
             } else {
-                JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+                ReportBadKey(cx, pattern);
                 return nullptr;
             }
-        } else if (MatchPart(&pat, "dayperiods")) {
-            if (!MatchSlash(cx, pattern, &pat))
+        } else if (MatchPart(&iter, end, "dayperiods")) {
+            if (!MatchSlash())
                 return nullptr;
 
             symbolType = UDAT_AM_PMS;
 
-            if (MatchPart(&pat, "am")) {
+            if (MatchPart(&iter, end, "am")) {
                 index = UCAL_AM;
-            } else if (MatchPart(&pat, "pm")) {
+            } else if (MatchPart(&iter, end, "pm")) {
                 index = UCAL_PM;
             } else {
-                JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+                ReportBadKey(cx, pattern);
                 return nullptr;
             }
         } else {
-            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+            ReportBadKey(cx, pattern);
             return nullptr;
         }
 
         // This part must be the final part with no trailing data.
-        if (*pat != '\0') {
-            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+        if (iter != end) {
+            ReportBadKey(cx, pattern);
             return nullptr;
         }
 
         UErrorCode status = U_ZERO_ERROR;
         int32_t resultSize =
             udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()),
                             INITIAL_CHAR_BUFFER_SIZE, &status);
         if (status == U_BUFFER_OVERFLOW_ERROR) {
@@ -3222,17 +3250,17 @@ ComputeSingleDisplayName(JSContext* cx, 
         if (U_FAILURE(status)) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
             return nullptr;
         }
 
         return NewStringCopyN<CanGC>(cx, chars.begin(), resultSize);
     }
 
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+    ReportBadKey(cx, pattern);
     return nullptr;
 }
 
 bool
 js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
@@ -3291,32 +3319,36 @@ js::intl_ComputeDisplayNames(JSContext* 
     }
     ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg);
 
     Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
     if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
         return false;
 
     // 5. For each element of keys,
-    JSAutoByteString pattern;
     RootedString keyValStr(cx);
     RootedValue v(cx);
     for (uint32_t i = 0; i < keys->length(); i++) {
         if (!GetElement(cx, keys, keys, i, &v))
             return false;
 
-        pattern.clear();
-
         keyValStr = v.toString();
-        if (!pattern.encodeUtf8(cx, keyValStr))
+
+        AutoStableStringChars stablePatternChars(cx);
+        if (!stablePatternChars.init(cx, keyValStr))
             return false;
 
         // 5.a. Perform an implementation dependent algorithm to map a key to a
         //      corresponding display name.
-        JSString* displayName = ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle, chars, pattern);
+        JSString* displayName =
+            stablePatternChars.isLatin1()
+            ? ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle, chars,
+                                       stablePatternChars.latin1Range())
+            : ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle, chars,
+                                       stablePatternChars.twoByteRange());
         if (!displayName)
             return false;
 
         // 5.b. Append the result string to result.
         v.setString(displayName);
         if (!DefineElement(cx, result, i, v))
             return false;
     }
--- a/js/src/tests/Intl/getDisplayNames.js
+++ b/js/src/tests/Intl/getDisplayNames.js
@@ -212,23 +212,31 @@ const rangeErrorKeys = [
   ['datesEXTRA'],
   ['dates/fieldsEXTRA'],
   ['dates/gregorianEXTRA'],
   ['dates/gregorian/monthsEXTRA'],
   ['dates/gregorian/weekdaysEXTRA'],
   ['dates/fields/dayperiods/amEXTRA'],
   ['dates/gregori\u1161n/months/january'],
   ["dates/fields/year/"],
+  ["dates/fields/year\0"],
   ["dates/fields/month/"],
+  ["dates/fields/month\0"],
   ["dates/fields/week/"],
+  ["dates/fields/week\0"],
   ["dates/fields/day/"],
+  ["dates/fields/day\0"],
   ["dates/gregorian/months/january/"],
+  ["dates/gregorian/months/january\0"],
   ["dates/gregorian/weekdays/saturday/"],
+  ["dates/gregorian/weekdays/saturday\0"],
   ["dates/gregorian/dayperiods/am/"],
+  ["dates/gregorian/dayperiods/am\0"],
   ["dates/fields/months/january/"],
+  ["dates/fields/months/january\0"],
 ];
 
 for (let keys of rangeErrorKeys) {
   assertThrowsInstanceOf(() => {
     gDN('en-US', {
       keys
     });
   }, RangeError);
--- a/mfbt/RangedPtr.h
+++ b/mfbt/RangedPtr.h
@@ -279,14 +279,13 @@ public:
   size_t operator-(const RangedPtr<T>& aOther) const
   {
     MOZ_ASSERT(mPtr >= aOther.mPtr);
     return PointerRangeSize(aOther.mPtr, mPtr);
   }
 
 private:
   RangedPtr() = delete;
-  T* operator&() = delete;
 };
 
 } /* namespace mozilla */
 
 #endif /* mozilla_RangedPtr_h */