diff options
author | Martok <martok@martoks-place.de> | 2023-06-11 00:54:39 +0200 |
---|---|---|
committer | Martok <martok@martoks-place.de> | 2023-06-30 00:01:27 +0200 |
commit | 371ae1639af8cd5d737bfb41f598ffbc5d405786 (patch) | |
tree | 19e1eedb466fbb7de282018bb3881f16afbe72d0 | |
parent | 33ec3d04f26eaa76d67b330621e0758dcf3e2c5d (diff) | |
download | uxp-371ae1639af8cd5d737bfb41f598ffbc5d405786.tar.gz |
Issue #2259 - Improve ResolveLocale performance when initializing the default Intl objects
Based-on: m-c 1365650
-rw-r--r-- | js/src/builtin/intl/Collator.cpp | 4 | ||||
-rw-r--r-- | js/src/builtin/intl/Collator.js | 118 | ||||
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.cpp | 3 | ||||
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.js | 48 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.cpp | 66 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.h | 10 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.js | 10 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.js | 7 | ||||
-rw-r--r-- | js/src/builtin/intl/PluralRules.js | 13 | ||||
-rw-r--r-- | js/src/vm/SelfHosting.cpp | 1 |
10 files changed, 189 insertions, 91 deletions
diff --git a/js/src/builtin/intl/Collator.cpp b/js/src/builtin/intl/Collator.cpp index ea04b4ee3a..ad95a5712c 100644 --- a/js/src/builtin/intl/Collator.cpp +++ b/js/src/builtin/intl/Collator.cpp @@ -233,7 +233,6 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp) if (!DefineElement(cx, collations, index++, NullHandleValue))
return false;
- RootedString jscollation(cx);
RootedValue element(cx);
for (uint32_t i = 0; i < count; i++) {
@@ -251,8 +250,7 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp) continue;
// ICU returns old-style keyword values; map them to BCP 47 equivalents.
- jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
-
+ JSString* jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
if (!jscollation)
return false;
element = StringValue(jscollation);
diff --git a/js/src/builtin/intl/Collator.js b/js/src/builtin/intl/Collator.js index eba09d3c83..ee6ea9a9b8 100644 --- a/js/src/builtin/intl/Collator.js +++ b/js/src/builtin/intl/Collator.js @@ -82,15 +82,11 @@ function resolveCollatorInternals(lazyCollatorData) // Steps 21-22.
var s = lazyCollatorData.rawSensitivity;
if (s === undefined) {
- if (collatorIsSorting) {
- // Step 21.a.
- s = "variant";
- } else {
- // Step 21.b.
- var dataLocale = r.dataLocale;
- var dataLocaleData = localeData(dataLocale);
- s = dataLocaleData.sensitivity;
- }
+ // In theory the default sensitivity for the "search" collator is
+ // locale dependent; in reality the CLDR/ICU default strength is
+ // always tertiary. Therefore use "variant" as the default value for
+ // both collation modes.
+ s = "variant";
}
internalProps.sensitivity = s;
@@ -263,49 +259,96 @@ var collatorInternalProperties = { /**
- * Returns the default caseFirst values for the given locale and usage. The
- * first element in the returned array denotes the default value per ES2017
- * Intl, 9.1 Internal slots of Service Constructors.
+ * Returns the actual locale used when a collator for |locale| is constructed.
*/
-function collatorCaseFirst(locale, usage) {
+function collatorActualLocale(locale) {
assert(typeof locale === "string", "locale should be string");
- assert(usage === "sort" || usage === "search", "invalid usage option");
-
- if (usage === "sort") {
- // If |locale| is the default locale (e.g. da-DK), but only supported
- // through a fallback (da), we need to get the actual locale before we
- // can call intl_isUpperCaseFirst. Also see BestAvailableLocaleHelper.
- var availableLocales = callFunction(collatorInternalProperties.availableLocales,
- collatorInternalProperties);
- var actualLocale = BestAvailableLocaleIgnoringDefault(availableLocales, locale);
-
- if (intl_isUpperCaseFirst(actualLocale))
- return ["upper", "false", "lower"];
- }
+
+ // If |locale| is the default locale (e.g. da-DK), but only supported
+ // through a fallback (da), we need to get the actual locale before we
+ // can call intl_isUpperCaseFirst. Also see BestAvailableLocaleHelper.
+ var availableLocales = callFunction(collatorInternalProperties.availableLocales,
+ collatorInternalProperties);
+ return BestAvailableLocaleIgnoringDefault(availableLocales, locale);
+}
+
+
+/**
+ * Returns the default caseFirst values for the given locale. The first
+ * element in the returned array denotes the default value per ES2017 Intl,
+ * 9.1 Internal slots of Service Constructors.
+ */
+function collatorSortCaseFirst(locale) {
+ var actualLocale = collatorActualLocale(locale);
+ if (intl_isUpperCaseFirst(actualLocale))
+ return ["upper", "false", "lower"];
// Default caseFirst values for all other languages.
return ["false", "lower", "upper"];
}
-function collatorSortLocaleData(locale) {
+/**
+ * Returns the default caseFirst value for the given locale.
+ */
+function collatorSortCaseFirstDefault(locale) {
+ var actualLocale = collatorActualLocale(locale);
+ if (intl_isUpperCaseFirst(actualLocale))
+ return "upper";
+
+ // Default caseFirst value for all other languages.
+ return "false";
+}
+
+function collatorSortLocaleData() {
+ /* eslint-disable object-shorthand */
return {
- co: intl_availableCollations(locale),
- kn: ["false", "true"],
- kf: collatorCaseFirst(locale, "sort"),
+ co: intl_availableCollations,
+ kn: function() {
+ return ["false", "true"];
+ },
+ kf: collatorSortCaseFirst,
+ default: {
+ co: function() {
+ // The first element of the collations array must be |null|
+ // per ES2017 Intl, 10.2.3 Internal Slots.
+ return null;
+ },
+ kn: function() {
+ return "false";
+ },
+ kf: collatorSortCaseFirstDefault,
+ }
};
+ /* eslint-enable object-shorthand */
}
-function collatorSearchLocaleData(locale) {
+function collatorSearchLocaleData() {
+ /* eslint-disable object-shorthand */
return {
- co: [null],
- kn: ["false", "true"],
- kf: collatorCaseFirst(locale, "search"),
- // In theory the default sensitivity is locale dependent;
- // in reality the CLDR/ICU default strength is always tertiary.
- sensitivity: "variant"
+ co: function() {
+ return [null];
+ },
+ kn: function() {
+ return ["false", "true"];
+ },
+ kf: function() {
+ return ["false", "lower", "upper"];
+ },
+ default: {
+ co: function() {
+ return null;
+ },
+ kn: function() {
+ return "false";
+ },
+ kf: function() {
+ return "false";
+ },
+ }
};
+ /* eslint-enable object-shorthand */
}
@@ -354,6 +397,7 @@ function Intl_Collator_compare_get() { // Step 2.
return internals.boundCompare;
}
+_SetCanonicalName(Intl_Collator_compare_get, "get compare");
/**
diff --git a/js/src/builtin/intl/CommonFunctions.cpp b/js/src/builtin/intl/CommonFunctions.cpp index be07ae4de7..e8723f3cfe 100644 --- a/js/src/builtin/intl/CommonFunctions.cpp +++ b/js/src/builtin/intl/CommonFunctions.cpp @@ -92,7 +92,6 @@ js::intl::GetAvailableLocales(JSContext* cx, CountAvailable countAvailable, return false;
uint32_t count = countAvailable();
- RootedValue t(cx, BooleanValue(true));
for (uint32_t i = 0; i < count; i++) {
const char* locale = getAvailable(i);
auto lang = DuplicateString(cx, locale);
@@ -104,7 +103,7 @@ js::intl::GetAvailableLocales(JSContext* cx, CountAvailable countAvailable, RootedAtom a(cx, Atomize(cx, lang.get(), strlen(lang.get())));
if (!a)
return false;
- if (!DefineProperty(cx, locales, a->asPropertyName(), t, nullptr, nullptr,
+ if (!DefineProperty(cx, locales, a->asPropertyName(), TrueHandleValue, nullptr, nullptr,
JSPROP_ENUMERATE))
{
return false;
diff --git a/js/src/builtin/intl/CommonFunctions.js b/js/src/builtin/intl/CommonFunctions.js index 59fbeaea3b..cf5a615721 100644 --- a/js/src/builtin/intl/CommonFunctions.js +++ b/js/src/builtin/intl/CommonFunctions.js @@ -767,8 +767,6 @@ function BestAvailableLocaleIgnoringDefault(availableLocales, locale) { return BestAvailableLocaleHelper(availableLocales, locale, false); } -var noRelevantExtensionKeys = []; - /** * Compares a BCP 47 language priority list against the set of locales in * availableLocales and determines the best available language to meet the @@ -925,21 +923,17 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte // Step 8. var supportedExtension = "-u"; + // In this implementation, localeData is a function, not an object. + var localeDataProvider = localeData(); + // Steps 9-12. - var i = 0; - var len = relevantExtensionKeys.length; - while (i < len) { + for (var i = 0; i < relevantExtensionKeys.length; i++) { // Steps 12.a-c. var key = relevantExtensionKeys[i]; - // In this implementation, localeData is a function, not an object. - var foundLocaleData = localeData(foundLocale); - var keyLocaleData = foundLocaleData[key]; - - // Locale data provides default value. - // Step 12.d. - var value = keyLocaleData[0]; - assert(typeof value === "string" || value === null, "unexpected locale data value"); + // Steps 12.b-d (The locale data is only computed when needed). + var keyLocaleData = undefined; + var value = undefined; // Locale tag may override. @@ -958,6 +952,9 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte // Step 12.f.ii. if (requestedValue !== undefined) { + // Steps 12.b-c. + keyLocaleData = callFunction(localeDataProvider[key], null, foundLocale); + // Step 12.f.ii.1. if (requestedValue !== "") { // Step 12.f.ii.1.a. @@ -983,18 +980,29 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte var optionsValue = options[key]; // Step 12.g, 12.gg.ii. - if (optionsValue !== undefined && - optionsValue !== value && - callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1) - { - value = optionsValue; - supportedExtensionAddition = ""; + if (optionsValue !== undefined && optionsValue !== value) { + // Steps 12.b-c. + if (keyLocaleData === undefined) + keyLocaleData = callFunction(localeDataProvider[key], null, foundLocale); + + if (callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1) { + value = optionsValue; + supportedExtensionAddition = ""; + } + } + + // Locale data provides default value. + if (value === undefined) { + // Steps 12.b-d. + value = keyLocaleData === undefined + ? callFunction(localeDataProvider.default[key], null, foundLocale) + : keyLocaleData[0]; } // Steps 12.h-j. + assert(typeof value === "string" || value === null, "unexpected locale data value"); result[key] = value; supportedExtension += supportedExtensionAddition; - i++; } // Step 13. diff --git a/js/src/builtin/intl/DateTimeFormat.cpp b/js/src/builtin/intl/DateTimeFormat.cpp index c649c3c416..9b0a10f1c3 100644 --- a/js/src/builtin/intl/DateTimeFormat.cpp +++ b/js/src/builtin/intl/DateTimeFormat.cpp @@ -241,6 +241,30 @@ js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp return true;
}
+static bool
+DefaultCalendar(JSContext* cx, const JSAutoByteString& locale, MutableHandleValue rval)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
+
+ // This correctly handles nullptr |cal| when opening failed.
+ ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
+
+ const char* calendar = ucal_getType(cal, &status);
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+
+ // ICU returns old-style keyword values; map them to BCP 47 equivalents
+ JSString* str = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
+ if (!str)
+ return false;
+
+ rval.setString(str);
+ return true;
+}
+
struct CalendarAlias
{
const char* const calendar;
@@ -269,31 +293,15 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp) uint32_t index = 0;
// We need the default calendar for the locale as the first result.
- UErrorCode status = U_ZERO_ERROR;
- RootedString jscalendar(cx);
- {
- UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
-
- // This correctly handles nullptr |cal| when opening failed.
- ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
-
- const char* calendar = ucal_getType(cal, &status);
- if (U_FAILURE(status)) {
- intl::ReportInternalError(cx);
- return false;
- }
-
- // ICU returns old-style keyword values; map them to BCP 47 equivalents
- jscalendar = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
- if (!jscalendar)
- return false;
- }
+ RootedValue element(cx);
+ if (!DefaultCalendar(cx, locale, &element))
+ return false;
- RootedValue element(cx, StringValue(jscalendar));
if (!DefineElement(cx, calendars, index++, element))
return false;
// Now get the calendars that "would make a difference", i.e., not the default.
+ UErrorCode status = U_ZERO_ERROR;
UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
@@ -317,7 +325,7 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp) // ICU returns old-style keyword values; map them to BCP 47 equivalents
calendar = uloc_toUnicodeLocaleType("ca", calendar);
- jscalendar = JS_NewStringCopyZ(cx, calendar);
+ JSString* jscalendar = JS_NewStringCopyZ(cx, calendar);
if (!jscalendar)
return false;
element = StringValue(jscalendar);
@@ -327,7 +335,7 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp) // ICU doesn't return calendar aliases, append them here.
for (const auto& calendarAlias : calendarAliases) {
if (StringsAreEqual(calendar, calendarAlias.calendar)) {
- jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
+ JSString* jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
if (!jscalendar)
return false;
element = StringValue(jscalendar);
@@ -342,6 +350,20 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp) }
bool
+js::intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 1);
+ MOZ_ASSERT(args[0].isString());
+
+ JSAutoByteString locale(cx, args[0].toString());
+ if (!locale)
+ return false;
+
+ return DefaultCalendar(cx, locale, args.rval());
+}
+
+bool
js::intl_IsValidTimeZoneName(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
diff --git a/js/src/builtin/intl/DateTimeFormat.h b/js/src/builtin/intl/DateTimeFormat.h index db468269dc..b4a6a218de 100644 --- a/js/src/builtin/intl/DateTimeFormat.h +++ b/js/src/builtin/intl/DateTimeFormat.h @@ -77,6 +77,16 @@ extern MOZ_MUST_USE bool intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp);
/**
+ * Returns the calendar type identifier per Unicode Technical Standard 35,
+ * Unicode Locale Data Markup Language, for the default calendar for the given
+ * locale.
+ *
+ * Usage: calendar = intl_defaultCalendar(locale)
+ */
+extern MOZ_MUST_USE bool
+intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp);
+
+/**
* 6.4.1 IsValidTimeZoneName ( timeZone )
*
* Verifies that the given string is a valid time zone name. If it is a valid
diff --git a/js/src/builtin/intl/DateTimeFormat.js b/js/src/builtin/intl/DateTimeFormat.js index 6057251ca8..79bfbd4f0e 100644 --- a/js/src/builtin/intl/DateTimeFormat.js +++ b/js/src/builtin/intl/DateTimeFormat.js @@ -793,10 +793,14 @@ var dateTimeFormatInternalProperties = { };
-function dateTimeFormatLocaleData(locale) {
+function dateTimeFormatLocaleData() {
return {
- ca: intl_availableCalendars(locale),
- nu: getNumberingSystems(locale)
+ ca: intl_availableCalendars,
+ nu: getNumberingSystems,
+ default: {
+ ca: intl_defaultCalendar,
+ nu: intl_numberingSystem,
+ }
};
}
diff --git a/js/src/builtin/intl/NumberFormat.js b/js/src/builtin/intl/NumberFormat.js index fca1bdb8ae..bba78d7a0d 100644 --- a/js/src/builtin/intl/NumberFormat.js +++ b/js/src/builtin/intl/NumberFormat.js @@ -446,9 +446,12 @@ function getNumberingSystems(locale) { }
-function numberFormatLocaleData(locale) {
+function numberFormatLocaleData() {
return {
- nu: getNumberingSystems(locale)
+ nu: getNumberingSystems,
+ default: {
+ nu: intl_numberingSystem,
+ }
};
}
diff --git a/js/src/builtin/intl/PluralRules.js b/js/src/builtin/intl/PluralRules.js index 997fa4db7d..1e138a8830 100644 --- a/js/src/builtin/intl/PluralRules.js +++ b/js/src/builtin/intl/PluralRules.js @@ -10,6 +10,7 @@ * Spec: ECMAScript 402 API, PluralRules, 1.3.3.
*/
var pluralRulesInternalProperties = {
+ localeData: pluralRulesLocaleData,
_availableLocales: null,
availableLocales: function()
{
@@ -20,9 +21,17 @@ var pluralRulesInternalProperties = { locales = intl_PluralRules_availableLocales();
addSpecialMissingLanguageTags(locales);
return (this._availableLocales = locales);
- }
+ },
+ relevantExtensionKeys: [],
};
+
+function pluralRulesLocaleData() {
+ // PluralRules don't support any extension keys.
+ return {};
+}
+
+
/**
* Compute an internal properties object from |lazyPluralRulesData|.
*/
@@ -39,7 +48,7 @@ function resolvePluralRulesInternals(lazyPluralRulesData) { const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules),
lazyPluralRulesData.requestedLocales,
lazyPluralRulesData.opt,
- noRelevantExtensionKeys, undefined);
+ PluralRules.relevantExtensionKeys, PluralRules.localeData);
// Step 14.
internalProps.locale = r.locale;
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 0717bfd490..ebb95c83e9 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -2467,6 +2467,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0), JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2,0), JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0), + JS_FN("intl_defaultCalendar", intl_defaultCalendar, 1,0), JS_FN("intl_defaultTimeZone", intl_defaultTimeZone, 0,0), JS_FN("intl_defaultTimeZoneOffset", intl_defaultTimeZoneOffset, 0,0), JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0), |