Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (264536 => 264537)
--- trunk/Source/_javascript_Core/ChangeLog 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/ChangeLog 2020-07-17 22:05:30 UTC (rev 264537)
@@ -1,3 +1,42 @@
+2020-07-17 Yusuke Suzuki <[email protected]>
+
+ [JSC] Clean up resolveLocale
+ https://bugs.webkit.org/show_bug.cgi?id=214446
+
+ Reviewed by Darin Adler.
+
+ Introduce RelevantExtensionKey and optimize resolveLocale implementation which avoids using HashMap for input and output.
+ We instead use std::array<T, numberOfRelevantExtensionKeys> since # of RelevantExtensionKeys is only 6.
+
+ For input option, we use std::array<Optional<String>, numberOfRelevantExtensionKeys> since this distinguish non-set-option and null String().
+ For output extension values, we simply use std::array<String, numberOfRelevantExtensionKeys>.
+
+ * runtime/IntlCollator.cpp:
+ (JSC::IntlCollator::sortLocaleData):
+ (JSC::IntlCollator::searchLocaleData):
+ (JSC::IntlCollator::initializeCollator):
+ * runtime/IntlCollator.h:
+ * runtime/IntlDateTimeFormat.cpp:
+ (JSC::IntlDateTimeFormat::localeData):
+ (JSC::IntlDateTimeFormat::initializeDateTimeFormat):
+ * runtime/IntlDateTimeFormat.h:
+ * runtime/IntlNumberFormat.cpp:
+ (JSC::IntlNumberFormat::localeData):
+ (JSC::IntlNumberFormat::initializeNumberFormat):
+ * runtime/IntlNumberFormat.h:
+ * runtime/IntlObject.cpp:
+ (JSC::relevantExtensionKeyString):
+ (JSC::resolveLocale):
+ * runtime/IntlObject.h:
+ * runtime/IntlPluralRules.cpp:
+ (JSC::IntlPluralRules::localeData):
+ (JSC::IntlPluralRules::initializePluralRules):
+ * runtime/IntlPluralRules.h:
+ * runtime/IntlRelativeTimeFormat.cpp:
+ (JSC::IntlRelativeTimeFormat::localeData):
+ (JSC::IntlRelativeTimeFormat::initializeRelativeTimeFormat):
+ * runtime/IntlRelativeTimeFormat.h:
+
2020-07-17 Xan López <[email protected]>
Math.max() can yield the wrong result for max(0, -0).
Modified: trunk/Source/_javascript_Core/runtime/IntlCollator.cpp (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlCollator.cpp 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlCollator.cpp 2020-07-17 22:05:30 UTC (rev 264537)
@@ -39,10 +39,6 @@
const ClassInfo IntlCollator::s_info = { "Object", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(IntlCollator) };
namespace IntlCollatorInternal {
-constexpr const char* relevantExtensionKeys[3] = { "co", "kf", "kn" };
-constexpr size_t collationIndex = 0;
-constexpr size_t caseFirstIndex = 1;
-constexpr size_t numericIndex = 2;
constexpr bool verbose = false;
}
@@ -85,12 +81,12 @@
visitor.append(thisObject->m_boundCompare);
}
-Vector<String> IntlCollator::sortLocaleData(const String& locale, size_t keyIndex)
+Vector<String> IntlCollator::sortLocaleData(const String& locale, RelevantExtensionKey key)
{
// 9.1 Internal slots of Service Constructors & 10.2.3 Internal slots (ECMA-402 2.0)
Vector<String> keyLocaleData;
- switch (keyIndex) {
- case IntlCollatorInternal::collationIndex: {
+ switch (key) {
+ case RelevantExtensionKey::Co: {
// 10.2.3 "The first element of [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co must be null for all locale values."
keyLocaleData.append({ });
@@ -119,13 +115,13 @@
}
break;
}
- case IntlCollatorInternal::caseFirstIndex:
+ case RelevantExtensionKey::Kf:
keyLocaleData.reserveInitialCapacity(3);
keyLocaleData.uncheckedAppend("false"_s);
keyLocaleData.uncheckedAppend("lower"_s);
keyLocaleData.uncheckedAppend("upper"_s);
break;
- case IntlCollatorInternal::numericIndex:
+ case RelevantExtensionKey::Kn:
keyLocaleData.reserveInitialCapacity(2);
keyLocaleData.uncheckedAppend("false"_s);
keyLocaleData.uncheckedAppend("true"_s);
@@ -136,23 +132,23 @@
return keyLocaleData;
}
-Vector<String> IntlCollator::searchLocaleData(const String&, size_t keyIndex)
+Vector<String> IntlCollator::searchLocaleData(const String&, RelevantExtensionKey key)
{
// 9.1 Internal slots of Service Constructors & 10.2.3 Internal slots (ECMA-402 2.0)
Vector<String> keyLocaleData;
- switch (keyIndex) {
- case IntlCollatorInternal::collationIndex:
+ switch (key) {
+ case RelevantExtensionKey::Co:
// 10.2.3 "The first element of [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co must be null for all locale values."
keyLocaleData.reserveInitialCapacity(1);
keyLocaleData.append({ });
break;
- case IntlCollatorInternal::caseFirstIndex:
+ case RelevantExtensionKey::Kf:
keyLocaleData.reserveInitialCapacity(3);
keyLocaleData.uncheckedAppend("false"_s);
keyLocaleData.uncheckedAppend("lower"_s);
keyLocaleData.uncheckedAppend("upper"_s);
break;
- case IntlCollatorInternal::numericIndex:
+ case RelevantExtensionKey::Kn:
keyLocaleData.reserveInitialCapacity(2);
keyLocaleData.uncheckedAppend("false"_s);
keyLocaleData.uncheckedAppend("true"_s);
@@ -183,7 +179,7 @@
auto localeData = (m_usage == Usage::Sort) ? sortLocaleData : searchLocaleData;
- HashMap<String, String> opt;
+ ResolveLocaleOptions localeOptions;
LocaleMatcher localeMatcher = intlOption<LocaleMatcher>(globalObject, options, vm.propertyNames->localeMatcher, { { "lookup"_s, LocaleMatcher::Lookup }, { "best fit"_s, LocaleMatcher::BestFit } }, "localeMatcher must be either \"lookup\" or \"best fit\""_s, LocaleMatcher::BestFit);
RETURN_IF_EXCEPTION(scope, void());
@@ -191,27 +187,27 @@
TriState numeric = intlBooleanOption(globalObject, options, vm.propertyNames->numeric);
RETURN_IF_EXCEPTION(scope, void());
if (numeric != TriState::Indeterminate)
- opt.add("kn"_s, numeric == TriState::True ? "true"_s : "false"_s);
+ localeOptions[static_cast<unsigned>(RelevantExtensionKey::Kn)] = String(numeric == TriState::True ? "true"_s : "false"_s);
String caseFirstOption = intlStringOption(globalObject, options, vm.propertyNames->caseFirst, { "upper", "lower", "false" }, "caseFirst must be either \"upper\", \"lower\", or \"false\"", nullptr);
RETURN_IF_EXCEPTION(scope, void());
if (!caseFirstOption.isNull())
- opt.add("kf"_s, caseFirstOption);
+ localeOptions[static_cast<unsigned>(RelevantExtensionKey::Kf)] = caseFirstOption;
auto& availableLocales = intlCollatorAvailableLocales();
- auto result = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, opt, IntlCollatorInternal::relevantExtensionKeys, WTF_ARRAY_LENGTH(IntlCollatorInternal::relevantExtensionKeys), localeData);
+ auto resolved = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, localeOptions, { RelevantExtensionKey::Co, RelevantExtensionKey::Kf, RelevantExtensionKey::Kn }, localeData);
- m_locale = result.get("locale"_s);
+ m_locale = resolved.locale;
if (m_locale.isEmpty()) {
throwTypeError(globalObject, scope, "failed to initialize Collator due to invalid locale"_s);
return;
}
- const String& collation = result.get("co"_s);
+ const String& collation = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Co)];
m_collation = collation.isNull() ? "default"_s : collation;
- m_numeric = result.get("kn"_s) == "true";
+ m_numeric = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Kn)] == "true"_s;
- const String& caseFirstString = result.get("kf"_s);
+ const String& caseFirstString = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Kf)];
if (caseFirstString == "lower")
m_caseFirst = CaseFirst::Lower;
else if (caseFirstString == "upper")
@@ -236,7 +232,7 @@
// searchLocaleData filters out "co" unicode extension. However, we need to pass "co" to ICU when Usage::Search is specified.
// So we need to pass "co" unicode extension through locale. Since the other relevant extensions are handled via ucol_setAttribute,
// we can just use dataLocale
- dataLocaleWithExtensions = makeString(result.get("dataLocale"_s), "-u-co-search").utf8();
+ dataLocaleWithExtensions = makeString(resolved.dataLocale, "-u-co-search").utf8();
break;
}
dataLogLnIf(IntlCollatorInternal::verbose, "dataLocaleWithExtensions:(", dataLocaleWithExtensions, ")");
Modified: trunk/Source/_javascript_Core/runtime/IntlCollator.h (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlCollator.h 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlCollator.h 2020-07-17 22:05:30 UTC (rev 264537)
@@ -31,6 +31,8 @@
namespace JSC {
+enum class RelevantExtensionKey : uint8_t;
+
class JSBoundFunction;
class IntlCollator final : public JSNonFinalObject {
@@ -67,8 +69,8 @@
void finishCreation(VM&);
static void visitChildren(JSCell*, SlotVisitor&);
- static Vector<String> sortLocaleData(const String&, size_t);
- static Vector<String> searchLocaleData(const String&, size_t);
+ static Vector<String> sortLocaleData(const String&, RelevantExtensionKey);
+ static Vector<String> searchLocaleData(const String&, RelevantExtensionKey);
enum class Usage : uint8_t { Sort, Search };
enum class Sensitivity : uint8_t { Base, Accent, Case, Variant };
Modified: trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp 2020-07-17 22:05:30 UTC (rev 264537)
@@ -44,10 +44,6 @@
const ClassInfo IntlDateTimeFormat::s_info = { "Object", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(IntlDateTimeFormat) };
namespace IntlDateTimeFormatInternal {
-constexpr const char* relevantExtensionKeys[3] = { "ca", "hc", "nu" };
-constexpr size_t calendarIndex = 0;
-constexpr size_t hourCycleIndex = 1;
-constexpr size_t numberingSystemIndex = 2;
}
void IntlDateTimeFormat::UDateFormatDeleter::operator()(UDateFormat* dateFormat) const
@@ -163,11 +159,11 @@
return canonical;
}
-Vector<String> IntlDateTimeFormat::localeData(const String& locale, size_t keyIndex)
+Vector<String> IntlDateTimeFormat::localeData(const String& locale, RelevantExtensionKey key)
{
Vector<String> keyLocaleData;
- switch (keyIndex) {
- case IntlDateTimeFormatInternal::calendarIndex: {
+ switch (key) {
+ case RelevantExtensionKey::Ca: {
UErrorCode status = U_ZERO_ERROR;
UEnumeration* calendars = ucal_getKeywordValuesForLocale("calendar", locale.utf8().data(), false, &status);
ASSERT(U_SUCCESS(status));
@@ -188,7 +184,7 @@
uenum_close(calendars);
break;
}
- case IntlDateTimeFormatInternal::hourCycleIndex:
+ case RelevantExtensionKey::Hc:
// Null default so we know to use 'j' in pattern.
keyLocaleData.append(String());
keyLocaleData.append("h11"_s);
@@ -196,7 +192,7 @@
keyLocaleData.append("h23"_s);
keyLocaleData.append("h24"_s);
break;
- case IntlDateTimeFormatInternal::numberingSystemIndex:
+ case RelevantExtensionKey::Nu:
keyLocaleData = numberingSystemsForLocale(locale);
break;
default:
@@ -419,7 +415,7 @@
JSObject* options = toDateTimeOptionsAnyDate(globalObject, originalOptions);
RETURN_IF_EXCEPTION(scope, void());
- HashMap<String, String> opt;
+ ResolveLocaleOptions localeOptions;
LocaleMatcher localeMatcher = intlOption<LocaleMatcher>(globalObject, options, vm.propertyNames->localeMatcher, { { "lookup"_s, LocaleMatcher::Lookup }, { "best fit"_s, LocaleMatcher::BestFit } }, "localeMatcher must be either \"lookup\" or \"best fit\""_s, LocaleMatcher::BestFit);
RETURN_IF_EXCEPTION(scope, void());
@@ -431,7 +427,7 @@
throwRangeError(globalObject, scope, "calendar is not a well-formed calendar value"_s);
return;
}
- opt.add("ca"_s, calendar);
+ localeOptions[static_cast<unsigned>(RelevantExtensionKey::Ca)] = calendar;
}
String numberingSystem = intlStringOption(globalObject, options, vm.propertyNames->numberingSystem, { }, nullptr, nullptr);
@@ -441,7 +437,7 @@
throwRangeError(globalObject, scope, "numberingSystem is not a well-formed numbering system value"_s);
return;
}
- opt.add("nu"_s, numberingSystem);
+ localeOptions[static_cast<unsigned>(RelevantExtensionKey::Nu)] = numberingSystem;
}
TriState hour12 = intlBooleanOption(globalObject, options, vm.propertyNames->hour12);
@@ -454,20 +450,20 @@
// Set hour12 here to simplify hour logic later.
hour12 = triState(hourCycle == "h11" || hourCycle == "h12");
if (!hourCycle.isNull())
- opt.add("hc"_s, hourCycle);
+ localeOptions[static_cast<unsigned>(RelevantExtensionKey::Hc)] = hourCycle;
} else
- opt.add("hc"_s, String());
+ localeOptions[static_cast<unsigned>(RelevantExtensionKey::Hc)] = String();
const HashSet<String>& availableLocales = intlDateTimeFormatAvailableLocales();
- HashMap<String, String> resolved = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, opt, IntlDateTimeFormatInternal::relevantExtensionKeys, WTF_ARRAY_LENGTH(IntlDateTimeFormatInternal::relevantExtensionKeys), localeData);
+ auto resolved = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, localeOptions, { RelevantExtensionKey::Ca, RelevantExtensionKey::Hc, RelevantExtensionKey::Nu }, localeData);
- m_locale = resolved.get(vm.propertyNames->locale.string());
+ m_locale = resolved.locale;
if (m_locale.isEmpty()) {
throwTypeError(globalObject, scope, "failed to initialize DateTimeFormat due to invalid locale"_s);
return;
}
- m_calendar = resolved.get("ca"_s);
+ m_calendar = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Ca)];
if (m_calendar == "gregorian")
m_calendar = "gregory"_s;
else if (m_calendar == "islamicc")
@@ -475,9 +471,9 @@
else if (m_calendar == "ethioaa")
m_calendar = "ethiopic-amete-alem"_s;
- m_hourCycle = resolved.get("hc"_s);
- m_numberingSystem = resolved.get("nu"_s);
- CString dataLocaleWithExtensions = makeString(resolved.get("dataLocale"_s), "-u-ca-", m_calendar, "-nu-", m_numberingSystem).utf8();
+ m_hourCycle = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Hc)];
+ m_numberingSystem = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Nu)];
+ CString dataLocaleWithExtensions = makeString(resolved.dataLocale, "-u-ca-", m_calendar, "-nu-", m_numberingSystem).utf8();
JSValue tzValue = options->get(globalObject, vm.propertyNames->timeZone);
RETURN_IF_EXCEPTION(scope, void());
Modified: trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.h (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.h 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.h 2020-07-17 22:05:30 UTC (rev 264537)
@@ -30,6 +30,8 @@
namespace JSC {
+enum class RelevantExtensionKey : uint8_t;
+
class JSBoundFunction;
class IntlDateTimeFormat final : public JSNonFinalObject {
@@ -67,7 +69,7 @@
void finishCreation(VM&);
static void visitChildren(JSCell*, SlotVisitor&);
- static Vector<String> localeData(const String&, size_t);
+ static Vector<String> localeData(const String&, RelevantExtensionKey);
enum class Weekday : uint8_t { None, Narrow, Short, Long };
enum class Era : uint8_t { None, Narrow, Short, Long };
Modified: trunk/Source/_javascript_Core/runtime/IntlNumberFormat.cpp (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlNumberFormat.cpp 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlNumberFormat.cpp 2020-07-17 22:05:30 UTC (rev 264537)
@@ -41,7 +41,6 @@
const ClassInfo IntlNumberFormat::s_info = { "Object", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(IntlNumberFormat) };
namespace IntlNumberFormatInternal {
-constexpr const char* relevantExtensionKeys[1] = { "nu" };
constexpr bool verbose = false;
}
@@ -89,10 +88,10 @@
visitor.append(thisObject->m_boundFormat);
}
-Vector<String> IntlNumberFormat::localeData(const String& locale, size_t keyIndex)
+Vector<String> IntlNumberFormat::localeData(const String& locale, RelevantExtensionKey key)
{
// 9.1 Internal slots of Service Constructors & 11.2.3 Internal slots (ECMA-402 2.0)
- ASSERT_UNUSED(keyIndex, !keyIndex); // The index of the extension key "nu" in relevantExtensionKeys is 0.
+ ASSERT_UNUSED(key, key == RelevantExtensionKey::Nu);
return numberingSystemsForLocale(locale);
}
@@ -171,7 +170,7 @@
RETURN_IF_EXCEPTION(scope, void());
}
- HashMap<String, String> opt;
+ ResolveLocaleOptions localeOptions;
LocaleMatcher localeMatcher = intlOption<LocaleMatcher>(globalObject, options, vm.propertyNames->localeMatcher, { { "lookup"_s, LocaleMatcher::Lookup }, { "best fit"_s, LocaleMatcher::BestFit } }, "localeMatcher must be either \"lookup\" or \"best fit\""_s, LocaleMatcher::BestFit);
RETURN_IF_EXCEPTION(scope, void());
@@ -183,19 +182,19 @@
throwRangeError(globalObject, scope, "numberingSystem is not a well-formed numbering system value"_s);
return;
}
- opt.add("nu"_s, numberingSystem);
+ localeOptions[static_cast<unsigned>(RelevantExtensionKey::Nu)] = numberingSystem;
}
auto& availableLocales = intlNumberFormatAvailableLocales();
- auto result = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, opt, IntlNumberFormatInternal::relevantExtensionKeys, WTF_ARRAY_LENGTH(IntlNumberFormatInternal::relevantExtensionKeys), localeData);
+ auto resolved = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, localeOptions, { RelevantExtensionKey::Nu }, localeData);
- m_locale = result.get("locale"_s);
+ m_locale = resolved.locale;
if (m_locale.isEmpty()) {
throwTypeError(globalObject, scope, "failed to initialize NumberFormat due to invalid locale"_s);
return;
}
- m_numberingSystem = result.get("nu"_s);
+ m_numberingSystem = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Nu)];
m_style = intlOption<Style>(globalObject, options, vm.propertyNames->style, { { "decimal"_s, Style::Decimal }, { "percent"_s, Style::Percent }, { "currency"_s, Style::Currency } }, "style must be either \"decimal\", \"percent\", or \"currency\""_s, Style::Decimal);
RETURN_IF_EXCEPTION(scope, void());
@@ -292,7 +291,7 @@
ASSERT_NOT_REACHED();
}
- CString dataLocaleWithExtensions = makeString(result.get("dataLocale"_s), "-u-nu-", m_numberingSystem).utf8();
+ CString dataLocaleWithExtensions = makeString(resolved.dataLocale, "-u-nu-", m_numberingSystem).utf8();
dataLogLnIf(IntlNumberFormatInternal::verbose, "dataLocaleWithExtensions:(", dataLocaleWithExtensions , ")");
UErrorCode status = U_ZERO_ERROR;
Modified: trunk/Source/_javascript_Core/runtime/IntlNumberFormat.h (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlNumberFormat.h 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlNumberFormat.h 2020-07-17 22:05:30 UTC (rev 264537)
@@ -32,6 +32,7 @@
namespace JSC {
class JSBoundFunction;
+enum class RelevantExtensionKey : uint8_t;
class IntlNumberFormat final : public JSNonFinalObject {
public:
@@ -71,7 +72,7 @@
void finishCreation(VM&);
static void visitChildren(JSCell*, SlotVisitor&);
- static Vector<String> localeData(const String&, size_t);
+ static Vector<String> localeData(const String&, RelevantExtensionKey);
enum class Style : uint8_t { Decimal, Percent, Currency };
enum class CurrencyDisplay : uint8_t { Code, Symbol, Name };
Modified: trunk/Source/_javascript_Core/runtime/IntlObject.cpp (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlObject.cpp 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlObject.cpp 2020-07-17 22:05:30 UTC (rev 264537)
@@ -629,8 +629,21 @@
subtags.append(extension.substring(valueStart, extensionLength - valueStart));
}
-HashMap<String, String> resolveLocale(JSGlobalObject* globalObject, const HashSet<String>& availableLocales, const Vector<String>& requestedLocales, LocaleMatcher localeMatcher, const HashMap<String, String>& options, const char* const relevantExtensionKeys[], size_t relevantExtensionKeyCount, Vector<String> (*localeData)(const String&, size_t))
+constexpr ASCIILiteral relevantExtensionKeyString(RelevantExtensionKey key)
{
+ switch (key) {
+#define JSC_RETURN_INTL_RELEVANT_EXTENSION_KEYS(lowerName, capitalizedName) \
+ case RelevantExtensionKey::capitalizedName: \
+ return #lowerName ""_s;
+ JSC_INTL_RELEVANT_EXTENSION_KEYS(JSC_RETURN_INTL_RELEVANT_EXTENSION_KEYS)
+#undef JSC_RETURN_INTL_RELEVANT_EXTENSION_KEYS
+ }
+ ASSERT_UNDER_CONSTEXPR_CONTEXT(0);
+ return ASCIILiteral::null();
+}
+
+ResolvedLocale resolveLocale(JSGlobalObject* globalObject, const HashSet<String>& availableLocales, const Vector<String>& requestedLocales, LocaleMatcher localeMatcher, const ResolveLocaleOptions& options, std::initializer_list<RelevantExtensionKey> relevantExtensionKeys, Vector<String> (*localeData)(const String&, RelevantExtensionKey))
+{
// ResolveLocale (availableLocales, requestedLocales, options, relevantExtensionKeys, localeData)
// https://tc39.github.io/ecma402/#sec-resolvelocale
@@ -644,13 +657,13 @@
if (!matcherResult.extension.isNull())
unicodeExtensionSubTags(matcherResult.extension, extensionSubtags);
- HashMap<String, String> result;
- result.add("dataLocale"_s, foundLocale);
+ ResolvedLocale resolved;
+ resolved.dataLocale = foundLocale;
String supportedExtension = "-u"_s;
- for (size_t keyIndex = 0; keyIndex < relevantExtensionKeyCount; ++keyIndex) {
- const char* key = relevantExtensionKeys[keyIndex];
- Vector<String> keyLocaleData = localeData(foundLocale, keyIndex);
+ for (RelevantExtensionKey key : relevantExtensionKeys) {
+ ASCIILiteral keyString = relevantExtensionKeyString(key);
+ Vector<String> keyLocaleData = localeData(foundLocale, key);
ASSERT(!keyLocaleData.isEmpty());
String value = keyLocaleData[0];
@@ -657,43 +670,40 @@
String supportedExtensionAddition;
if (!extensionSubtags.isEmpty()) {
- size_t keyPos = extensionSubtags.find(key);
+ size_t keyPos = extensionSubtags.find(keyString);
if (keyPos != notFound) {
if (keyPos + 1 < extensionSubtags.size() && extensionSubtags[keyPos + 1].length() > 2) {
const String& requestedValue = extensionSubtags[keyPos + 1];
if (keyLocaleData.contains(requestedValue)) {
value = requestedValue;
- supportedExtensionAddition = makeString('-', key, '-', value);
+ supportedExtensionAddition = makeString('-', keyString, '-', value);
}
} else if (keyLocaleData.contains("true"_s)) {
value = "true"_s;
- supportedExtensionAddition = makeString('-', key);
+ supportedExtensionAddition = makeString('-', keyString);
}
}
}
- HashMap<String, String>::const_iterator iterator = options.find(key);
- if (iterator != options.end()) {
- const String& optionsValue = iterator->value;
+ if (auto optionsValue = options[static_cast<unsigned>(key)]) {
// Undefined should not get added to the options, it won't displace the extension.
// Null will remove the extension.
- if ((optionsValue.isNull() || keyLocaleData.contains(optionsValue)) && optionsValue != value) {
- value = optionsValue;
+ if ((optionsValue->isNull() || keyLocaleData.contains(*optionsValue)) && *optionsValue != value) {
+ value = optionsValue.value();
supportedExtensionAddition = String();
}
}
- result.add(key, value);
+ resolved.extensions[static_cast<unsigned>(key)] = value;
supportedExtension.append(supportedExtensionAddition);
}
if (supportedExtension.length() > 2) {
- String preExtension = foundLocale.substring(0, matcherResult.extensionIndex);
- String postExtension = foundLocale.substring(matcherResult.extensionIndex);
- foundLocale = preExtension + supportedExtension + postExtension;
+ StringView foundLocaleView(foundLocale);
+ foundLocale = makeString(foundLocaleView.substring(0, matcherResult.extensionIndex), supportedExtension, foundLocaleView.substring(matcherResult.extensionIndex));
}
- result.add("locale"_s, foundLocale);
- return result;
+ resolved.locale = WTFMove(foundLocale);
+ return resolved;
}
static JSArray* lookupSupportedLocales(JSGlobalObject* globalObject, const HashSet<String>& availableLocales, const Vector<String>& requestedLocales)
Modified: trunk/Source/_javascript_Core/runtime/IntlObject.h (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlObject.h 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlObject.h 2020-07-17 22:05:30 UTC (rev 264537)
@@ -39,6 +39,23 @@
BestFit,
};
+#define JSC_INTL_RELEVANT_EXTENSION_KEYS(macro) \
+ macro(ca, Ca) /* calendar */ \
+ macro(co, Co) /* collation */ \
+ macro(hc, Hc) /* hour-cycle */ \
+ macro(kf, Kf) /* case-first */ \
+ macro(kn, Kn) /* numeric */ \
+ macro(nu, Nu) /* numbering-system */ \
+
+enum class RelevantExtensionKey : uint8_t {
+#define JSC_DECLARE_INTL_RELEVANT_EXTENSION_KEYS(lowerName, capitalizedName) capitalizedName,
+JSC_INTL_RELEVANT_EXTENSION_KEYS(JSC_DECLARE_INTL_RELEVANT_EXTENSION_KEYS)
+#undef JSC_DECLARE_INTL_RELEVANT_EXTENSION_KEYS
+};
+#define JSC_COUNT_INTL_RELEVANT_EXTENSION_KEYS(lowerName, capitalizedName) + 1
+static constexpr uint8_t numberOfRelevantExtensionKeys = 0 JSC_INTL_RELEVANT_EXTENSION_KEYS(JSC_COUNT_INTL_RELEVANT_EXTENSION_KEYS);
+#undef JSC_COUNT_INTL_RELEVANT_EXTENSION_KEYS
+
class IntlObject final : public JSNonFinalObject {
public:
using Base = JSNonFinalObject;
@@ -76,7 +93,16 @@
Vector<char, 32> localeIDBufferForLanguageTag(const CString&);
String languageTagForLocaleID(const char*, bool isImmortal = false);
Vector<String> canonicalizeLocaleList(JSGlobalObject*, JSValue locales);
-HashMap<String, String> resolveLocale(JSGlobalObject*, const HashSet<String>& availableLocales, const Vector<String>& requestedLocales, LocaleMatcher, const HashMap<String, String>& options, const char* const relevantExtensionKeys[], size_t relevantExtensionKeyCount, Vector<String> (*localeData)(const String&, size_t));
+
+using ResolveLocaleOptions = std::array<Optional<String>, numberOfRelevantExtensionKeys>;
+using RelevantExtensions = std::array<String, numberOfRelevantExtensionKeys>;
+struct ResolvedLocale {
+ String locale;
+ String dataLocale;
+ RelevantExtensions extensions;
+};
+
+ResolvedLocale resolveLocale(JSGlobalObject*, const HashSet<String>& availableLocales, const Vector<String>& requestedLocales, LocaleMatcher, const ResolveLocaleOptions&, std::initializer_list<RelevantExtensionKey> relevantExtensionKeys, Vector<String> (*localeData)(const String&, RelevantExtensionKey));
JSValue supportedLocales(JSGlobalObject*, const HashSet<String>& availableLocales, const Vector<String>& requestedLocales, JSValue options);
String removeUnicodeLocaleExtension(const String& locale);
String bestAvailableLocale(const HashSet<String>& availableLocales, const String& requestedLocale);
Modified: trunk/Source/_javascript_Core/runtime/IntlPluralRules.cpp (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlPluralRules.cpp 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlPluralRules.cpp 2020-07-17 22:05:30 UTC (rev 264537)
@@ -86,7 +86,7 @@
Base::visitChildren(thisObject, visitor);
}
-Vector<String> IntlPluralRules::localeData(const String&, size_t)
+Vector<String> IntlPluralRules::localeData(const String&, RelevantExtensionKey)
{
return { };
}
@@ -108,13 +108,13 @@
RETURN_IF_EXCEPTION(scope, void());
}
- HashMap<String, String> localeOpt;
+ ResolveLocaleOptions localeOptions;
LocaleMatcher localeMatcher = intlOption<LocaleMatcher>(globalObject, options, vm.propertyNames->localeMatcher, { { "lookup"_s, LocaleMatcher::Lookup }, { "best fit"_s, LocaleMatcher::BestFit } }, "localeMatcher must be either \"lookup\" or \"best fit\""_s, LocaleMatcher::BestFit);
RETURN_IF_EXCEPTION(scope, void());
const HashSet<String>& availableLocales = intlPluralRulesAvailableLocales();
- HashMap<String, String> resolved = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, localeOpt, nullptr, 0, localeData);
- m_locale = resolved.get(vm.propertyNames->locale.string());
+ auto resolved = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, localeOptions, { }, localeData);
+ m_locale = resolved.locale;
if (m_locale.isEmpty()) {
throwTypeError(globalObject, scope, "failed to initialize PluralRules due to invalid locale"_s);
return;
Modified: trunk/Source/_javascript_Core/runtime/IntlPluralRules.h (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlPluralRules.h 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlPluralRules.h 2020-07-17 22:05:30 UTC (rev 264537)
@@ -31,6 +31,8 @@
namespace JSC {
+enum class RelevantExtensionKey : uint8_t;
+
class IntlPluralRules final : public JSNonFinalObject {
public:
using Base = JSNonFinalObject;
@@ -62,7 +64,7 @@
void finishCreation(VM&);
static void visitChildren(JSCell*, SlotVisitor&);
- static Vector<String> localeData(const String&, size_t);
+ static Vector<String> localeData(const String&, RelevantExtensionKey);
enum class Type : bool { Cardinal, Ordinal };
Modified: trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.cpp (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.cpp 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.cpp 2020-07-17 22:05:30 UTC (rev 264537)
@@ -37,7 +37,6 @@
const ClassInfo IntlRelativeTimeFormat::s_info = { "Object", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(IntlRelativeTimeFormat) };
namespace IntlRelativeTimeFormatInternal {
-constexpr const char* relevantExtensionKeys[1] = { "nu" };
}
void IntlRelativeTimeFormat::URelativeDateTimeFormatterDeleter::operator()(URelativeDateTimeFormatter* relativeDateTimeFormatter) const
@@ -83,10 +82,9 @@
Base::visitChildren(thisObject, visitor);
}
-Vector<String> IntlRelativeTimeFormat::localeData(const String& locale, size_t keyIndex)
+Vector<String> IntlRelativeTimeFormat::localeData(const String& locale, RelevantExtensionKey key)
{
- // The index of the extension key "nu" in relevantExtensionKeys is 0.
- ASSERT_UNUSED(keyIndex, !keyIndex);
+ ASSERT_UNUSED(key, key == RelevantExtensionKey::Nu);
return numberingSystemsForLocale(locale);
}
@@ -107,7 +105,7 @@
RETURN_IF_EXCEPTION(scope, void());
}
- HashMap<String, String> opt;
+ ResolveLocaleOptions localeOptions;
LocaleMatcher localeMatcher = intlOption<LocaleMatcher>(globalObject, options, vm.propertyNames->localeMatcher, { { "lookup"_s, LocaleMatcher::Lookup }, { "best fit"_s, LocaleMatcher::BestFit } }, "localeMatcher must be either \"lookup\" or \"best fit\""_s, LocaleMatcher::BestFit);
RETURN_IF_EXCEPTION(scope, void());
@@ -118,19 +116,19 @@
throwRangeError(globalObject, scope, "numberingSystem is not a well-formed numbering system value"_s);
return;
}
- opt.add("nu"_s, numberingSystem);
+ localeOptions[static_cast<unsigned>(RelevantExtensionKey::Nu)] = numberingSystem;
}
const HashSet<String>& availableLocales = intlRelativeTimeFormatAvailableLocales();
- HashMap<String, String> resolved = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, opt, IntlRelativeTimeFormatInternal::relevantExtensionKeys, WTF_ARRAY_LENGTH(IntlRelativeTimeFormatInternal::relevantExtensionKeys), localeData);
- m_locale = resolved.get(vm.propertyNames->locale.string());
+ auto resolved = resolveLocale(globalObject, availableLocales, requestedLocales, localeMatcher, localeOptions, { RelevantExtensionKey::Nu }, localeData);
+ m_locale = resolved.locale;
if (m_locale.isEmpty()) {
throwTypeError(globalObject, scope, "failed to initialize RelativeTimeFormat due to invalid locale"_s);
return;
}
- m_numberingSystem = resolved.get("nu"_s);
- CString dataLocaleWithExtensions = makeString(resolved.get("dataLocale"_s), "-u-nu-", m_numberingSystem).utf8();
+ m_numberingSystem = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Nu)];
+ CString dataLocaleWithExtensions = makeString(resolved.dataLocale, "-u-nu-", m_numberingSystem).utf8();
m_style = intlOption<Style>(globalObject, options, vm.propertyNames->style, { { "long"_s, Style::Long }, { "short"_s, Style::Short }, { "narrow"_s, Style::Narrow } }, "style must be either \"long\", \"short\", or \"narrow\""_s, Style::Long);
RETURN_IF_EXCEPTION(scope, void());
Modified: trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.h (264536 => 264537)
--- trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.h 2020-07-17 22:01:45 UTC (rev 264536)
+++ trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.h 2020-07-17 22:05:30 UTC (rev 264537)
@@ -62,7 +62,7 @@
void finishCreation(VM&);
static void visitChildren(JSCell*, SlotVisitor&);
- static Vector<String> localeData(const String&, size_t);
+ static Vector<String> localeData(const String&, RelevantExtensionKey);
String formatInternal(JSGlobalObject*, double, StringView unit) const;