summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS
diff options
context:
space:
mode:
authorTimothy Flynn <trflynn89@pm.me>2021-09-10 10:01:41 -0400
committerLinus Groh <mail@linusgroh.de>2021-09-11 11:05:50 +0100
commita1954262f8d404eeb341a0ca791867407cc07b57 (patch)
treea85ca58ee6ed5ce28d789374f164b24b55103bc1 /Userland/Libraries/LibJS
parente6334cb856dde5d1642c21d236fee01cd46f6303 (diff)
downloadserenity-a1954262f8d404eeb341a0ca791867407cc07b57.zip
LibJS: Handle Unicode locale extensions in the ResolveLocale operation
Currently, all callers of ResolveLocale invoke the operation with an empty [[RelevantExtensionKeys]] slot, so the block of the method that deals with those keys was unimplemented. This implements that block now to prepare for Intl.NumberFormat which has a [[RelevantExtensionKeys]]. Note that the find_key_in_value() method is a simple VERIFY_NOT_REACHED in just this commit until the Intl.NumberFormat's keys are handled in its implementation.
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r--Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp157
-rw-r--r--Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h3
2 files changed, 120 insertions, 40 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp
index 5c4a7373f2..15c717669e 100644
--- a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp
@@ -325,8 +325,15 @@ String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale, Unico
return JS::Intl::canonicalize_unicode_locale_id(locale);
}
+template<typename T>
+static auto& find_key_in_value(T& value, StringView key)
+{
+ // If you hit this point, you must add any missing keys from [[RelevantExtensionKeys]] to LocaleOptions and LocaleResult.
+ VERIFY_NOT_REACHED();
+}
+
// 9.2.7 ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData ), https://tc39.es/ecma402/#sec-resolvelocale
-LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptions const& options, [[maybe_unused]] Vector<StringView> relevant_extension_keys)
+LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptions const& options, Vector<StringView> const& relevant_extension_keys)
{
// 1. Let matcher be options.[[localeMatcher]].
auto const& matcher = options.locale_matcher;
@@ -350,49 +357,121 @@ LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptio
LocaleResult result {};
// 6. Set result.[[dataLocale]] to foundLocale.
+ result.data_locale = found_locale;
- // FIXME: Currently, the only caller to this method has an empty [[RelevantExtensionKeys]] internal slot,
- // so this block isn't testable. When a caller has a non-empty slot, implement the below steps.
- //
// 7. If r has an [[extension]] field, then
- // a. Let components be ! UnicodeExtensionComponents(r.[[extension]]).
- // b. Let keywords be components.[[Keywords]].
+ Vector<Unicode::Keyword> keywords;
+ for (auto& extension : matcher_result.extensions) {
+ if (!extension.has<Unicode::LocaleExtension>())
+ continue;
+
+ // a. Let components be ! UnicodeExtensionComponents(r.[[extension]]).
+ auto& components = extension.get<Unicode::LocaleExtension>();
+ // b. Let keywords be components.[[Keywords]].
+ keywords = move(components.keywords);
+
+ break;
+ }
+
// 8. Let supportedExtension be "-u".
+ Unicode::LocaleExtension supported_extension {};
+
// 9. For each element key of relevantExtensionKeys, do
- // a. Let foundLocaleData be localeData.[[<foundLocale>]].
- // b. Assert: Type(foundLocaleData) is Record.
- // c. Let keyLocaleData be foundLocaleData.[[<key>]].
- // d. Assert: Type(keyLocaleData) is List.
- // e. Let value be keyLocaleData[0].
- // f. Assert: Type(value) is either String or Null.
- // g. Let supportedExtensionAddition be "".
- // h. If r has an [[extension]] field, then
- // i. If keywords contains an element whose [[Key]] is the same as key, then
- // 1. Let entry be the element of keywords whose [[Key]] is the same as key.
- // 2. Let requestedValue be entry.[[Value]].
- // 3. If requestedValue is not the empty String, then
- // a. If keyLocaleData contains requestedValue, then
- // i. Let value be requestedValue.
- // ii. Let supportedExtensionAddition be the string-concatenation of "-", key, "-", and value.
- // 4. Else if keyLocaleData contains "true", then
- // a. Let value be "true".
- // b. Let supportedExtensionAddition be the string-concatenation of "-" and key.
- // i. If options has a field [[<key>]], then
- // i. Let optionsValue be options.[[<key>]].
- // ii. Assert: Type(optionsValue) is either String, Undefined, or Null.
- // iii. If Type(optionsValue) is String, then
- // 1. Let optionsValue be the string optionsValue after performing the algorithm steps to transform Unicode extension values to canonical syntax per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.
- // 2. Let optionsValue be the string optionsValue after performing the algorithm steps to replace Unicode extension values with their canonical form per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.
- // 3. If optionsValue is the empty String, then
- // a. Let optionsValue be "true".
- // iv. If keyLocaleData contains optionsValue, then
- // 1. If SameValue(optionsValue, value) is false, then
- // a. Let value be optionsValue.
- // b. Let supportedExtensionAddition be "".
- // j. Set result.[[<key>]] to value.
- // k. Append supportedExtensionAddition to supportedExtension.
+ for (auto const& key : relevant_extension_keys) {
+ // a. Let foundLocaleData be localeData.[[<foundLocale>]].
+ // b. Assert: Type(foundLocaleData) is Record.
+ // c. Let keyLocaleData be foundLocaleData.[[<key>]].
+ // d. Assert: Type(keyLocaleData) is List.
+ auto key_locale_data = Unicode::get_locale_key_mapping(found_locale, key);
+
+ // e. Let value be keyLocaleData[0].
+ // f. Assert: Type(value) is either String or Null.
+ Optional<String> value;
+ if (!key_locale_data.is_empty())
+ value = key_locale_data[0];
+
+ // g. Let supportedExtensionAddition be "".
+ Optional<Unicode::Keyword> supported_extension_addition {};
+
+ // h. If r has an [[extension]] field, then
+ for (auto& entry : keywords) {
+ // i. If keywords contains an element whose [[Key]] is the same as key, then
+ if (entry.key != key)
+ continue;
+
+ // 1. Let entry be the element of keywords whose [[Key]] is the same as key.
+ // 2. Let requestedValue be entry.[[Value]].
+ auto requested_value = entry.value;
+
+ // 3. If requestedValue is not the empty String, then
+ if (!requested_value.is_empty()) {
+ // a. If keyLocaleData contains requestedValue, then
+ if (key_locale_data.contains_slow(requested_value)) {
+ // i. Let value be requestedValue.
+ value = move(requested_value);
+
+ // ii. Let supportedExtensionAddition be the string-concatenation of "-", key, "-", and value.
+ supported_extension_addition = Unicode::Keyword { key, move(entry.value) };
+ }
+ }
+ // 4. Else if keyLocaleData contains "true", then
+ else if (key_locale_data.contains_slow("true"sv)) {
+ // a. Let value be "true".
+ value = "true"sv;
+
+ // b. Let supportedExtensionAddition be the string-concatenation of "-" and key.
+ supported_extension_addition = Unicode::Keyword { key, {} };
+ }
+
+ break;
+ }
+
+ // i. If options has a field [[<key>]], then
+ // i. Let optionsValue be options.[[<key>]].
+ // ii. Assert: Type(optionsValue) is either String, Undefined, or Null.
+ auto options_value = find_key_in_value(options, key);
+
+ // iii. If Type(optionsValue) is String, then
+ if (options_value.has_value()) {
+ // 1. Let optionsValue be the string optionsValue after performing the algorithm steps to transform Unicode extension values to canonical syntax per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.
+ // 2. Let optionsValue be the string optionsValue after performing the algorithm steps to replace Unicode extension values with their canonical form per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.
+ Unicode::canonicalize_unicode_extension_values(key, *options_value, true);
+
+ // 3. If optionsValue is the empty String, then
+ if (options_value->is_empty()) {
+ // a. Let optionsValue be "true".
+ options_value = "true"sv;
+ }
+ }
+
+ // iv. If keyLocaleData contains optionsValue, then
+ if (options_value.has_value() && key_locale_data.contains_slow(*options_value)) {
+ // 1. If SameValue(optionsValue, value) is false, then
+ if (options_value != value) {
+ // a. Let value be optionsValue.
+ value = move(options_value);
+
+ // b. Let supportedExtensionAddition be "".
+ supported_extension_addition.clear();
+ }
+ }
+
+ // j. Set result.[[<key>]] to value.
+ find_key_in_value(result, key) = move(value);
+
+ // k. Append supportedExtensionAddition to supportedExtension.
+ if (supported_extension_addition.has_value())
+ supported_extension.keywords.append(supported_extension_addition.release_value());
+ }
+
// 10. If the number of elements in supportedExtension is greater than 2, then
- // a. Let foundLocale be InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedExtension).
+ if (!supported_extension.keywords.is_empty()) {
+ auto locale_id = Unicode::parse_unicode_locale_id(found_locale);
+ VERIFY(locale_id.has_value());
+
+ // a. Let foundLocale be InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedExtension).
+ found_locale = insert_unicode_extension_and_canonicalize(locale_id.release_value(), move(supported_extension));
+ }
// 11. Set result.[[locale]] to foundLocale.
result.locale = move(found_locale);
diff --git a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h
index b2c7167c26..64b6a4d449 100644
--- a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h
+++ b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h
@@ -24,6 +24,7 @@ struct LocaleOptions {
struct LocaleResult {
String locale;
+ String data_locale;
};
struct PatternPartition {
@@ -42,7 +43,7 @@ Object* coerce_options_to_object(GlobalObject& global_object, Value options);
Value get_option(GlobalObject& global_object, Value options, PropertyName const& property, Value::Type type, Vector<StringView> const& values, Fallback fallback);
Vector<PatternPartition> partition_pattern(StringView pattern);
String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale_id, Unicode::LocaleExtension extension);
-LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptions const& options, Vector<StringView> relevant_extension_keys);
+LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptions const& options, Vector<StringView> const& relevant_extension_keys);
Value canonical_code_for_display_names(GlobalObject&, DisplayNames::Type type, StringView code);
}