mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-22 23:27:30 +09:00
Issue #2259 - Improve ResolveLocale performance when initializing the default Intl objects
Based-on: m-c 1365650
This commit is contained in:
parent
e3dbf7c5e7
commit
e923c48f4f
10 changed files with 189 additions and 91 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 |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);
|
||||
}
|
||||
|
||||
if (intl_isUpperCaseFirst(actualLocale))
|
||||
return ["upper", "false", "lower"];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
RootedValue element(cx);
|
||||
if (!DefaultCalendar(cx, locale, &element))
|
||||
return false;
|
||||
|
||||
// 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, 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);
|
||||
|
|
@ -341,6 +349,20 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,6 +76,16 @@ intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
|||
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 )
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -446,9 +446,12 @@ function getNumberingSystems(locale) {
|
|||
}
|
||||
|
||||
|
||||
function numberFormatLocaleData(locale) {
|
||||
function numberFormatLocaleData() {
|
||||
return {
|
||||
nu: getNumberingSystems(locale)
|
||||
nu: getNumberingSystems,
|
||||
default: {
|
||||
nu: intl_numberingSystem,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue