diff --git a/js/src/builtin/intl/DateTimeFormat.cpp b/js/src/builtin/intl/DateTimeFormat.cpp index 21c9f8f3a750..6da700d14b06 100644 --- a/js/src/builtin/intl/DateTimeFormat.cpp +++ b/js/src/builtin/intl/DateTimeFormat.cpp @@ -548,14 +548,12 @@ bool js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp) { mozilla::Range skelChars = skeleton.twoByteRange(); - UErrorCode status = U_ZERO_ERROR; + SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref(); UDateTimePatternGenerator* gen = - udatpg_open(IcuLocale(locale.get()), &status); - if (U_FAILURE(status)) { - intl::ReportInternalError(cx); + sharedIntlData.getDateTimePatternGenerator(cx, locale.get()); + if (!gen) { return false; } - ScopedICUObject toClose(gen); JSString* str = CallICU( cx, [gen, &skelChars](UChar* chars, uint32_t size, UErrorCode* status) { diff --git a/js/src/builtin/intl/SharedIntlData.cpp b/js/src/builtin/intl/SharedIntlData.cpp index 3ab3e9522487..720e8a270a59 100644 --- a/js/src/builtin/intl/SharedIntlData.cpp +++ b/js/src/builtin/intl/SharedIntlData.cpp @@ -13,6 +13,7 @@ #include "mozilla/TextUtils.h" #include +#include #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" @@ -395,6 +396,39 @@ bool js::intl::SharedIntlData::isUpperCaseFirst(JSContext* cx, return true; } +void js::intl::DateTimePatternGeneratorDeleter::operator()( + UDateTimePatternGenerator* ptr) { + udatpg_close(ptr); +} + +UDateTimePatternGenerator* +js::intl::SharedIntlData::getDateTimePatternGenerator(JSContext* cx, + const char* locale) { + // Return the cached instance if the requested locale matches the locale + // of the cached generator. + if (dateTimePatternGeneratorLocale && + StringsAreEqual(dateTimePatternGeneratorLocale.get(), locale)) { + return dateTimePatternGenerator.get(); + } + + UErrorCode status = U_ZERO_ERROR; + UniqueUDateTimePatternGenerator gen(udatpg_open(IcuLocale(locale), &status)); + if (U_FAILURE(status)) { + intl::ReportInternalError(cx); + return nullptr; + } + + JS::UniqueChars localeCopy = js::DuplicateString(cx, locale); + if (!localeCopy) { + return nullptr; + } + + dateTimePatternGenerator = std::move(gen); + dateTimePatternGeneratorLocale = std::move(localeCopy); + + return dateTimePatternGenerator.get(); +} + void js::intl::SharedIntlData::destroyInstance() { availableTimeZones.clearAndCompact(); ianaZonesTreatedAsLinksByICU.clearAndCompact(); @@ -418,5 +452,6 @@ size_t js::intl::SharedIntlData::sizeOfExcludingThis( ianaZonesTreatedAsLinksByICU.shallowSizeOfExcludingThis(mallocSizeOf) + ianaLinksCanonicalizedDifferentlyByICU.shallowSizeOfExcludingThis( mallocSizeOf) + - upperCaseFirstLocales.shallowSizeOfExcludingThis(mallocSizeOf); + upperCaseFirstLocales.shallowSizeOfExcludingThis(mallocSizeOf) + + mallocSizeOf(dateTimePatternGeneratorLocale.get()); } diff --git a/js/src/builtin/intl/SharedIntlData.h b/js/src/builtin/intl/SharedIntlData.h index cb3f2b9b0c57..93dec5aef2bd 100644 --- a/js/src/builtin/intl/SharedIntlData.h +++ b/js/src/builtin/intl/SharedIntlData.h @@ -8,6 +8,7 @@ #define builtin_intl_SharedIntlData_h #include "mozilla/MemoryReporting.h" +#include "mozilla/UniquePtr.h" #include @@ -19,10 +20,17 @@ #include "js/Utility.h" #include "vm/StringType.h" +using UDateTimePatternGenerator = void*; + namespace js { namespace intl { +class DateTimePatternGeneratorDeleter { + public: + void operator()(UDateTimePatternGenerator* ptr); +}; + /** * Stores Intl data which can be shared across compartments (but not contexts). * @@ -203,6 +211,22 @@ class SharedIntlData { bool isUpperCaseFirst(JSContext* cx, JS::Handle locale, bool* isUpperFirst); + private: + using UniqueUDateTimePatternGenerator = + mozilla::UniquePtr; + + UniqueUDateTimePatternGenerator dateTimePatternGenerator; + JS::UniqueChars dateTimePatternGeneratorLocale; + + public: + /** + * Wrapper around |udatpg_open| to return a possibly cached generator + * instance. The returned pointer must not be closed via |udatpg_close|. + */ + UDateTimePatternGenerator* getDateTimePatternGenerator(JSContext* cx, + const char* locale); + public: void destroyInstance();