summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartok <martok@martoks-place.de>2023-06-11 00:54:39 +0200
committerMartok <martok@martoks-place.de>2023-06-30 00:01:27 +0200
commit371ae1639af8cd5d737bfb41f598ffbc5d405786 (patch)
tree19e1eedb466fbb7de282018bb3881f16afbe72d0
parent33ec3d04f26eaa76d67b330621e0758dcf3e2c5d (diff)
downloaduxp-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.cpp4
-rw-r--r--js/src/builtin/intl/Collator.js118
-rw-r--r--js/src/builtin/intl/CommonFunctions.cpp3
-rw-r--r--js/src/builtin/intl/CommonFunctions.js48
-rw-r--r--js/src/builtin/intl/DateTimeFormat.cpp66
-rw-r--r--js/src/builtin/intl/DateTimeFormat.h10
-rw-r--r--js/src/builtin/intl/DateTimeFormat.js10
-rw-r--r--js/src/builtin/intl/NumberFormat.js7
-rw-r--r--js/src/builtin/intl/PluralRules.js13
-rw-r--r--js/src/vm/SelfHosting.cpp1
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),