Bug 1333844 - Create helper method to call ICU string conversion methods. r=Waldo
authorAndré Bargull <andre.bargull@gmail.com>
Tue, 21 Feb 2017 12:35:37 -0800
changeset 344293 583e4b713f186cf37f9e981e67479a7543efe89f
parent 344292 246d39385cd3ac5273bb9d6b184a0005c3791079
child 344294 0bdfccec471f1890341d185e9a84a1235a9c638c
push id31406
push userkwierso@gmail.com
push dateWed, 22 Feb 2017 23:01:18 +0000
treeherdermozilla-central@32dcdde1fc64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1333844
milestone54.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 1333844 - Create helper method to call ICU string conversion methods. r=Waldo
js/src/builtin/Intl.cpp
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -931,16 +931,41 @@ class ScopedICUObject
         ptr_ = nullptr;
         return tmp;
     }
 };
 
 // The inline capacity we use for the char16_t Vectors.
 static const size_t INITIAL_CHAR_BUFFER_SIZE = 32;
 
+template <typename ICUStringFunction>
+static JSString*
+Call(JSContext* cx, const ICUStringFunction& strFn)
+{
+    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
+    MOZ_ALWAYS_TRUE(chars.resize(INITIAL_CHAR_BUFFER_SIZE));
+
+    UErrorCode status = U_ZERO_ERROR;
+    int32_t size = strFn(Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE, &status);
+    if (status == U_BUFFER_OVERFLOW_ERROR) {
+        MOZ_ASSERT(size >= 0);
+        if (!chars.resize(size_t(size)))
+            return nullptr;
+        status = U_ZERO_ERROR;
+        strFn(Char16ToUChar(chars.begin()), size, &status);
+    }
+    if (U_FAILURE(status)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+        return nullptr;
+    }
+
+    MOZ_ASSERT(size >= 0);
+    return NewStringCopyN<CanGC>(cx, chars.begin(), size_t(size));
+}
+
 
 /******************** Collator ********************/
 
 const ClassOps CollatorObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
@@ -1795,85 +1820,45 @@ NewUNumberFormat(JSContext* cx, Handle<N
         unum_setAttribute(nf, UNUM_MAX_FRACTION_DIGITS, uMaximumFractionDigits);
     }
     unum_setAttribute(nf, UNUM_GROUPING_USED, uUseGrouping);
     unum_setAttribute(nf, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
 
     return toClose.forget();
 }
 
-using FormattedNumberChars = Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE>;
-
-static bool
+static JSString*
 PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double* x,
-                       UFieldPositionIterator* fpositer, FormattedNumberChars& formattedChars)
+                       UFieldPositionIterator* fpositer)
 {
     // PartitionNumberPattern doesn't consider -0.0 to be negative.
     if (IsNegativeZero(*x))
         *x = 0.0;
 
-    MOZ_ASSERT(formattedChars.length() == 0,
-               "formattedChars must initially be empty");
-    MOZ_ALWAYS_TRUE(formattedChars.resize(INITIAL_CHAR_BUFFER_SIZE));
-
-    UErrorCode status = U_ZERO_ERROR;
-
 #if !defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS)
     MOZ_ASSERT(fpositer == nullptr,
                "shouldn't be requesting field information from an ICU that "
                "can't provide it");
 #endif
 
-    int32_t resultSize;
+    return Call(cx, [nf, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
 #if defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS)
-    resultSize =
-        unum_formatDoubleForFields(nf, *x,
-                                   Char16ToUChar(formattedChars.begin()), INITIAL_CHAR_BUFFER_SIZE,
-                                   fpositer, &status);
+        return unum_formatDoubleForFields(nf, *x, chars, size, fpositer, status);
 #else
-    resultSize =
-        unum_formatDouble(nf, *x, Char16ToUChar(formattedChars.begin()), INITIAL_CHAR_BUFFER_SIZE,
-                          nullptr, &status);
-#endif // defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS)
-    if (status == U_BUFFER_OVERFLOW_ERROR) {
-        MOZ_ASSERT(resultSize >= 0);
-        if (!formattedChars.resize(size_t(resultSize)))
-            return false;
-        status = U_ZERO_ERROR;
-#ifdef DEBUG
-        int32_t size =
+        return unum_formatDouble(nf, *x, chars, size, nullptr, status);
 #endif
-#if defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS)
-            unum_formatDoubleForFields(nf, *x, Char16ToUChar(formattedChars.begin()), resultSize,
-                                       fpositer, &status);
-#else
-            unum_formatDouble(nf, *x, Char16ToUChar(formattedChars.begin()), resultSize,
-                              nullptr, &status);
-#endif // defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS)
-        MOZ_ASSERT(size == resultSize);
-    }
-    if (U_FAILURE(status)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
-        return false;
-    }
-
-    MOZ_ASSERT(resultSize >= 0);
-    return formattedChars.resize(size_t(resultSize));
+    });
 }
 
 static bool
 intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result)
 {
     // Passing null for |fpositer| will just not compute partition information,
     // letting us common up all ICU number-formatting code.
-    FormattedNumberChars chars(cx);
-    if (!PartitionNumberPattern(cx, nf, &x, nullptr, chars))
-        return false;
-
-    JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), chars.length());
+    JSString* str = PartitionNumberPattern(cx, nf, &x, nullptr);
     if (!str)
         return false;
 
     result.setString(str);
     return true;
 }
 
 using FieldType = ImmutablePropertyNamePtr JSAtomState::*;
@@ -1955,28 +1940,24 @@ intl_FormatNumberToParts(JSContext* cx, 
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
     MOZ_ASSERT(fpositer);
     ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer);
 
-    FormattedNumberChars chars(cx);
-    if (!PartitionNumberPattern(cx, nf, &x, fpositer, chars))
+    RootedString overallResult(cx, PartitionNumberPattern(cx, nf, &x, fpositer));
+    if (!overallResult)
         return false;
 
     RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
     if (!partsArray)
         return false;
 
-    RootedString overallResult(cx, NewStringCopyN<CanGC>(cx, chars.begin(), chars.length()));
-    if (!overallResult)
-        return false;
-
     // First, vacuum up fields in the overall formatted string.
 
     struct Field
     {
         uint32_t begin;
         uint32_t end;
         FieldType type;
 
@@ -2222,17 +2203,17 @@ intl_FormatNumberToParts(JSContext* cx, 
     };
 
     // Finally, generate the result array.
     size_t lastEndIndex = 0;
     uint32_t partIndex = 0;
     RootedObject singlePart(cx);
     RootedValue propVal(cx);
 
-    PartGenerator gen(cx, fields, chars.length());
+    PartGenerator gen(cx, fields, overallResult->length());
     do {
         bool hasPart;
         Part part;
         if (!gen.nextPart(&hasPart, &part))
             return false;
 
         if (!hasPart)
             break;
@@ -2262,17 +2243,17 @@ intl_FormatNumberToParts(JSContext* cx, 
         propVal.setObject(*singlePart);
         if (!DefineElement(cx, partsArray, partIndex, propVal))
             return false;
 
         lastEndIndex = endIndex;
         partIndex++;
     } while (true);
 
-    MOZ_ASSERT(lastEndIndex == chars.length(),
+    MOZ_ASSERT(lastEndIndex == overallResult->length(),
                "result array must partition the entire string");
 
     result.setObject(*partsArray);
     return true;
 }
 
 #endif // defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS)
 
@@ -2888,80 +2869,42 @@ js::intl_canonicalizeTimeZone(JSContext*
     }
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, timeZone))
         return false;
 
     mozilla::Range<const char16_t> tzchars = stableChars.twoByteRange();
 
-    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
-    if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
-        return false;
-
-    UBool* isSystemID = nullptr;
-    UErrorCode status = U_ZERO_ERROR;
-    int32_t size = ucal_getCanonicalTimeZoneID(Char16ToUChar(tzchars.begin().get()),
-                                               tzchars.length(), Char16ToUChar(chars.begin()),
-                                               INITIAL_CHAR_BUFFER_SIZE, isSystemID, &status);
-    if (status == U_BUFFER_OVERFLOW_ERROR) {
-        MOZ_ASSERT(size >= 0);
-        if (!chars.resize(size_t(size)))
-            return false;
-        status = U_ZERO_ERROR;
-        ucal_getCanonicalTimeZoneID(Char16ToUChar(tzchars.begin().get()), tzchars.length(),
-                                    Char16ToUChar(chars.begin()), size, isSystemID, &status);
-    }
-    if (U_FAILURE(status)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
-        return false;
-    }
-
-    MOZ_ASSERT(size >= 0);
-    JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size_t(size));
+    JSString* str = Call(cx, [&tzchars](UChar* chars, uint32_t size, UErrorCode* status) {
+        return ucal_getCanonicalTimeZoneID(Char16ToUChar(tzchars.begin().get()), tzchars.length(),
+                                           chars, size, nullptr, status);
+    });
     if (!str)
         return false;
+
     args.rval().setString(str);
     return true;
 }
 
 bool
 js::intl_defaultTimeZone(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
 
     // The current default might be stale, because JS::ResetTimeZone() doesn't
     // immediately update ICU's default time zone. So perform an update if
     // needed.
     js::ResyncICUDefaultTimeZone();
 
-    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
-    if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
-        return false;
-
-    UErrorCode status = U_ZERO_ERROR;
-    int32_t size = ucal_getDefaultTimeZone(Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
-                                           &status);
-    if (status == U_BUFFER_OVERFLOW_ERROR) {
-        MOZ_ASSERT(size >= 0);
-        if (!chars.resize(size_t(size)))
-            return false;
-        status = U_ZERO_ERROR;
-        ucal_getDefaultTimeZone(Char16ToUChar(chars.begin()), size, &status);
-    }
-    if (U_FAILURE(status)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
-        return false;
-    }
-
-    MOZ_ASSERT(size >= 0);
-    JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size_t(size));
+    JSString* str = Call(cx, ucal_getDefaultTimeZone);
     if (!str)
         return false;
+
     args.rval().setString(str);
     return true;
 }
 
 bool
 js::intl_defaultTimeZoneOffset(JSContext* cx, unsigned argc, Value* vp) {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
@@ -2998,50 +2941,33 @@ js::intl_patternForSkeleton(JSContext* c
     JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     AutoStableStringChars skeleton(cx);
     if (!skeleton.initTwoByte(cx, args[1].toString()))
         return false;
 
-    mozilla::Range<const char16_t> skeletonChars = skeleton.twoByteRange();
+    mozilla::Range<const char16_t> skelChars = skeleton.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
     UDateTimePatternGenerator* gen = udatpg_open(icuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
     ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
 
-    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
-    if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
-        return false;
-
-    int32_t size = udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.begin().get()),
-                                         skeletonChars.length(), Char16ToUChar(chars.begin()),
-                                         INITIAL_CHAR_BUFFER_SIZE, &status);
-    if (status == U_BUFFER_OVERFLOW_ERROR) {
-        MOZ_ASSERT(size >= 0);
-        if (!chars.resize(size))
-            return false;
-        status = U_ZERO_ERROR;
-        udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.begin().get()),
-                              skeletonChars.length(), Char16ToUChar(chars.begin()), size, &status);
-    }
-    if (U_FAILURE(status)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
-        return false;
-    }
-
-    MOZ_ASSERT(size >= 0);
-    JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
+    JSString* str = Call(cx, [gen, &skelChars](UChar* chars, uint32_t size, UErrorCode* status) {
+        return udatpg_getBestPattern(gen, Char16ToUChar(skelChars.begin().get()),
+                                     skelChars.length(), chars, size, status);
+    });
     if (!str)
         return false;
+
     args.rval().setString(str);
     return true;
 }
 
 /**
  * Returns a new UDateFormat with the locale and date-time formatting options
  * of the given DateTimeFormat.
  */
@@ -3105,41 +3031,23 @@ NewUDateFormat(JSContext* cx, Handle<Dat
 static bool
 intl_FormatDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue result)
 {
     if (!IsFinite(x)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
         return false;
     }
 
-    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
-    if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
-        return false;
-    UErrorCode status = U_ZERO_ERROR;
-    int32_t size = udat_format(df, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
-                               nullptr, &status);
-    if (status == U_BUFFER_OVERFLOW_ERROR) {
-        MOZ_ASSERT(size >= 0);
-        if (!chars.resize(size))
-            return false;
-        status = U_ZERO_ERROR;
-        udat_format(df, x, Char16ToUChar(chars.begin()), size, nullptr, &status);
-    }
-    if (U_FAILURE(status)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
-        return false;
-    }
-
-    MOZ_ASSERT(size >= 0);
-    JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
+    JSString* str = Call(cx, [df, x](UChar* chars, int32_t size, UErrorCode* status) {
+        return udat_format(df, x, chars, size, nullptr, status);
+    });
     if (!str)
         return false;
 
     result.setString(str);
-
     return true;
 }
 
 static FieldType
 GetFieldTypeForFormatField(UDateFormatField fieldName)
 {
     // See intl/icu/source/i18n/unicode/udat.h for a detailed field list.  This
     // switch is deliberately exhaustive: cases might have to be added/removed
@@ -3227,58 +3135,41 @@ GetFieldTypeForFormatField(UDateFormatFi
 static bool
 intl_FormatToPartsDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue result)
 {
     if (!IsFinite(x)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
         return false;
     }
 
-    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
-    if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
-        return false;
-
     UErrorCode status = U_ZERO_ERROR;
     UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
     ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer);
 
-    int32_t resultSize =
-        udat_formatForFields(df, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
-                             fpositer, &status);
-    if (status == U_BUFFER_OVERFLOW_ERROR) {
-        MOZ_ASSERT(resultSize >= 0);
-        if (!chars.resize(resultSize))
-            return false;
-        status = U_ZERO_ERROR;
-        udat_formatForFields(df, x, Char16ToUChar(chars.begin()), resultSize, fpositer, &status);
-    }
-    if (U_FAILURE(status)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+    RootedString overallResult(cx);
+    overallResult = Call(cx, [df, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
+        return udat_formatForFields(df, x, chars, size, fpositer, status);
+    });
+    if (!overallResult)
         return false;
-    }
 
     RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
     if (!partsArray)
         return false;
 
-    MOZ_ASSERT(resultSize >= 0);
-    if (resultSize == 0) {
+    if (overallResult->length() == 0) {
         // An empty string contains no parts, so avoid extra work below.
         result.setObject(*partsArray);
         return true;
     }
 
-    RootedString overallResult(cx, NewStringCopyN<CanGC>(cx, chars.begin(), resultSize));
-    if (!overallResult)
-        return false;
-
     size_t lastEndIndex = 0;
 
     uint32_t partIndex = 0;
     RootedObject singlePart(cx);
     RootedValue partType(cx);
     RootedValue val(cx);
 
     auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex) {
@@ -3625,36 +3516,19 @@ js::intl_SelectPluralRule(JSContext* cx,
     UPluralRules* pr = uplrules_openForType(icuLocale(locale.ptr()), category, &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
     ScopedICUObject<UPluralRules, uplrules_close> closePluralRules(pr);
 
-    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
-    if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
-        return false;
-
-    int32_t size = uplrules_select(pr, y, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
-                                   &status);
-    if (status == U_BUFFER_OVERFLOW_ERROR) {
-        MOZ_ASSERT(size >= 0);
-        if (!chars.resize(size))
-            return false;
-        status = U_ZERO_ERROR;
-        uplrules_select(pr, y, Char16ToUChar(chars.begin()), size, &status);
-    }
-    if (U_FAILURE(status)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
-        return false;
-    }
-
-    MOZ_ASSERT(size >= 0);
-    JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
+    JSString* str = Call(cx, [pr, y](UChar* chars, int32_t size, UErrorCode* status) {
+        return uplrules_select(pr, y, chars, size, status);
+    });
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
 bool
@@ -4045,35 +3919,19 @@ ComputeSingleDisplayName(JSContext* cx, 
         }
 
         // This part must be the final part with no trailing data.
         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) {
-            MOZ_ASSERT(resultSize >= 0);
-            if (!chars.resize(resultSize))
-                return nullptr;
-            status = U_ZERO_ERROR;
-            udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()),
-                            resultSize, &status);
-        }
-        if (U_FAILURE(status)) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
-            return nullptr;
-        }
-
-        MOZ_ASSERT(resultSize >= 0);
-        return NewStringCopyN<CanGC>(cx, chars.begin(), resultSize);
+        return Call(cx, [fmt, symbolType, index](UChar* chars, int32_t size, UErrorCode* status) {
+            return udat_getSymbols(fmt, symbolType, index, chars, size, status);
+        });
     }
 
     ReportBadKey(cx, pattern);
     return nullptr;
 }
 
 bool
 js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)