1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 14:47:44 +00:00

LibJS: Propagate OOM errors from Intl Abstract Operations

This excludes the PartitionPattern AO as that has a much larger foot-
print and will be handled separately.
This commit is contained in:
Timothy Flynn 2023-01-19 11:39:05 -05:00 committed by Linus Groh
parent ca62aeb6bd
commit bff0e25ebe
14 changed files with 71 additions and 67 deletions

View file

@ -20,16 +20,16 @@
namespace JS::Intl { namespace JS::Intl {
// 6.2.2 IsStructurallyValidLanguageTag ( locale ), https://tc39.es/ecma402/#sec-isstructurallyvalidlanguagetag // 6.2.2 IsStructurallyValidLanguageTag ( locale ), https://tc39.es/ecma402/#sec-isstructurallyvalidlanguagetag
Optional<::Locale::LocaleID> is_structurally_valid_language_tag(StringView locale) ThrowCompletionOr<Optional<::Locale::LocaleID>> is_structurally_valid_language_tag(VM& vm, StringView locale)
{ {
auto contains_duplicate_variant = [](auto& variants) { auto contains_duplicate_variant = [&](auto& variants) -> ThrowCompletionOr<bool> {
if (variants.is_empty()) if (variants.is_empty())
return false; return false;
quick_sort(variants); quick_sort(variants);
for (size_t i = 0; i < variants.size() - 1; ++i) { for (size_t i = 0; i < variants.size() - 1; ++i) {
if (variants[i].equals_ignoring_case(variants[i + 1]).release_value_but_fixme_should_propagate_errors()) if (TRY_OR_THROW_OOM(vm, variants[i].equals_ignoring_case(variants[i + 1])))
return true; return true;
} }
@ -39,18 +39,18 @@ Optional<::Locale::LocaleID> is_structurally_valid_language_tag(StringView local
// IsStructurallyValidLanguageTag returns true if all of the following conditions hold, false otherwise: // IsStructurallyValidLanguageTag returns true if all of the following conditions hold, false otherwise:
// locale can be generated from the EBNF grammar for unicode_locale_id in Unicode Technical Standard #35 LDML § 3.2 Unicode Locale Identifier; // locale can be generated from the EBNF grammar for unicode_locale_id in Unicode Technical Standard #35 LDML § 3.2 Unicode Locale Identifier;
auto locale_id = ::Locale::parse_unicode_locale_id(locale).release_value_but_fixme_should_propagate_errors(); auto locale_id = TRY_OR_THROW_OOM(vm, ::Locale::parse_unicode_locale_id(locale));
if (!locale_id.has_value()) if (!locale_id.has_value())
return {}; return Optional<::Locale::LocaleID> {};
// locale does not use any of the backwards compatibility syntax described in Unicode Technical Standard #35 LDML § 3.3 BCP 47 Conformance; // locale does not use any of the backwards compatibility syntax described in Unicode Technical Standard #35 LDML § 3.3 BCP 47 Conformance;
// https://unicode.org/reports/tr35/#BCP_47_Conformance // https://unicode.org/reports/tr35/#BCP_47_Conformance
if (locale.contains('_') || locale_id->language_id.is_root || !locale_id->language_id.language.has_value()) if (locale.contains('_') || locale_id->language_id.is_root || !locale_id->language_id.language.has_value())
return {}; return Optional<::Locale::LocaleID> {};
// the unicode_language_id within locale contains no duplicate unicode_variant_subtag subtags; and // the unicode_language_id within locale contains no duplicate unicode_variant_subtag subtags; and
if (contains_duplicate_variant(locale_id->language_id.variants)) if (TRY(contains_duplicate_variant(locale_id->language_id.variants)))
return {}; return Optional<::Locale::LocaleID> {};
// if locale contains an extensions* component, that component // if locale contains an extensions* component, that component
Vector<char> unique_keys; Vector<char> unique_keys;
@ -64,15 +64,15 @@ Optional<::Locale::LocaleID> is_structurally_valid_language_tag(StringView local
[](::Locale::OtherExtension const& ext) { return static_cast<char>(to_ascii_lowercase(ext.key)); }); [](::Locale::OtherExtension const& ext) { return static_cast<char>(to_ascii_lowercase(ext.key)); });
if (unique_keys.contains_slow(key)) if (unique_keys.contains_slow(key))
return {}; return Optional<::Locale::LocaleID> {};
unique_keys.append(key); TRY_OR_THROW_OOM(vm, unique_keys.try_append(key));
// if a transformed_extensions component that contains a tlang component is present, then // if a transformed_extensions component that contains a tlang component is present, then
// the tlang component contains no duplicate unicode_variant_subtag subtags. // the tlang component contains no duplicate unicode_variant_subtag subtags.
if (auto* transformed = extension.get_pointer<::Locale::TransformedExtension>()) { if (auto* transformed = extension.get_pointer<::Locale::TransformedExtension>()) {
auto& language = transformed->language; auto& language = transformed->language;
if (language.has_value() && contains_duplicate_variant(language->variants)) if (language.has_value() && TRY(contains_duplicate_variant(language->variants)))
return {}; return Optional<::Locale::LocaleID> {};
} }
} }
@ -80,7 +80,7 @@ Optional<::Locale::LocaleID> is_structurally_valid_language_tag(StringView local
} }
// 6.2.3 CanonicalizeUnicodeLocaleId ( locale ), https://tc39.es/ecma402/#sec-canonicalizeunicodelocaleid // 6.2.3 CanonicalizeUnicodeLocaleId ( locale ), https://tc39.es/ecma402/#sec-canonicalizeunicodelocaleid
DeprecatedString canonicalize_unicode_locale_id(::Locale::LocaleID& locale) ThrowCompletionOr<DeprecatedString> canonicalize_unicode_locale_id(VM& vm, ::Locale::LocaleID& locale)
{ {
// Note: This implementation differs from the spec in how Step 3 is implemented. The spec assumes // Note: This implementation differs from the spec in how Step 3 is implemented. The spec assumes
// the input to this method is a string, and is written such that operations are performed on parts // the input to this method is a string, and is written such that operations are performed on parts
@ -100,13 +100,13 @@ DeprecatedString canonicalize_unicode_locale_id(::Locale::LocaleID& locale)
auto attributes = move(locale_extension.attributes); auto attributes = move(locale_extension.attributes);
for (auto& attribute : attributes) { for (auto& attribute : attributes) {
if (!locale_extension.attributes.contains_slow(attribute)) if (!locale_extension.attributes.contains_slow(attribute))
locale_extension.attributes.append(move(attribute)); TRY_OR_THROW_OOM(vm, locale_extension.attributes.try_append(move(attribute)));
} }
auto keywords = move(locale_extension.keywords); auto keywords = move(locale_extension.keywords);
for (auto& keyword : keywords) { for (auto& keyword : keywords) {
if (!any_of(locale_extension.keywords, [&](auto const& k) { return k.key == keyword.key; })) if (!any_of(locale_extension.keywords, [&](auto const& k) { return k.key == keyword.key; }))
locale_extension.keywords.append(move(keyword)); TRY_OR_THROW_OOM(vm, locale_extension.keywords.try_append(move(keyword)));
} }
break; break;
@ -114,7 +114,7 @@ DeprecatedString canonicalize_unicode_locale_id(::Locale::LocaleID& locale)
// 1. Let localeId be the string locale after performing the algorithm to transform it to canonical syntax per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers. // 1. Let localeId be the string locale after performing the algorithm to transform it to canonical syntax per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers.
// 2. Let localeId be the string localeId after performing the algorithm to transform it to canonical form. // 2. Let localeId be the string localeId after performing the algorithm to transform it to canonical form.
auto locale_id = ::Locale::canonicalize_unicode_locale_id(locale).release_value_but_fixme_should_propagate_errors(); auto locale_id = TRY_OR_THROW_OOM(vm, ::Locale::canonicalize_unicode_locale_id(locale));
VERIFY(locale_id.has_value()); VERIFY(locale_id.has_value());
// 4. Return localeId. // 4. Return localeId.
@ -244,16 +244,17 @@ ThrowCompletionOr<Vector<DeprecatedString>> canonicalize_locale_list(VM& vm, Val
} }
// v. If ! IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception. // v. If ! IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
auto locale_id = is_structurally_valid_language_tag(tag); auto locale_id = TRY(is_structurally_valid_language_tag(vm, tag));
if (!locale_id.has_value()) if (!locale_id.has_value())
return vm.throw_completion<RangeError>(ErrorType::IntlInvalidLanguageTag, tag); return vm.throw_completion<RangeError>(ErrorType::IntlInvalidLanguageTag, tag);
// vi. Let canonicalizedTag be ! CanonicalizeUnicodeLocaleId(tag). // vi. Let canonicalizedTag be ! CanonicalizeUnicodeLocaleId(tag).
auto canonicalized_tag = JS::Intl::canonicalize_unicode_locale_id(*locale_id); // NOTE: We TRY this operation only to propagate OOM errors.
auto canonicalized_tag = TRY(JS::Intl::canonicalize_unicode_locale_id(vm, *locale_id));
// vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen. // vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen.
if (!seen.contains_slow(canonicalized_tag)) if (!seen.contains_slow(canonicalized_tag))
seen.append(move(canonicalized_tag)); TRY_OR_THROW_OOM(vm, seen.try_append(move(canonicalized_tag)));
} }
// d. Increase k by 1. // d. Increase k by 1.
@ -294,14 +295,14 @@ struct MatcherResult {
}; };
// 9.2.3 LookupMatcher ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupmatcher // 9.2.3 LookupMatcher ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupmatcher
static MatcherResult lookup_matcher(Vector<DeprecatedString> const& requested_locales) static ThrowCompletionOr<MatcherResult> lookup_matcher(VM& vm, Vector<DeprecatedString> const& requested_locales)
{ {
// 1. Let result be a new Record. // 1. Let result be a new Record.
MatcherResult result {}; MatcherResult result {};
// 2. For each element locale of requestedLocales, do // 2. For each element locale of requestedLocales, do
for (auto const& locale : requested_locales) { for (auto const& locale : requested_locales) {
auto locale_id = ::Locale::parse_unicode_locale_id(locale).release_value_but_fixme_should_propagate_errors(); auto locale_id = TRY_OR_THROW_OOM(vm, ::Locale::parse_unicode_locale_id(locale));
VERIFY(locale_id.has_value()); VERIFY(locale_id.has_value());
// a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed. // a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed.
@ -337,22 +338,22 @@ static MatcherResult lookup_matcher(Vector<DeprecatedString> const& requested_lo
} }
// 9.2.4 BestFitMatcher ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-bestfitmatcher // 9.2.4 BestFitMatcher ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-bestfitmatcher
static MatcherResult best_fit_matcher(Vector<DeprecatedString> const& requested_locales) static ThrowCompletionOr<MatcherResult> best_fit_matcher(VM& vm, Vector<DeprecatedString> const& requested_locales)
{ {
// The algorithm is implementation dependent, but should produce results that a typical user of the requested locales would // The algorithm is implementation dependent, but should produce results that a typical user of the requested locales would
// perceive as at least as good as those produced by the LookupMatcher abstract operation. // perceive as at least as good as those produced by the LookupMatcher abstract operation.
return lookup_matcher(requested_locales); return lookup_matcher(vm, requested_locales);
} }
// 9.2.6 InsertUnicodeExtensionAndCanonicalize ( locale, extension ), https://tc39.es/ecma402/#sec-insert-unicode-extension-and-canonicalize // 9.2.6 InsertUnicodeExtensionAndCanonicalize ( locale, extension ), https://tc39.es/ecma402/#sec-insert-unicode-extension-and-canonicalize
DeprecatedString insert_unicode_extension_and_canonicalize(::Locale::LocaleID locale, ::Locale::LocaleExtension extension) ThrowCompletionOr<DeprecatedString> insert_unicode_extension_and_canonicalize(VM& vm, ::Locale::LocaleID locale, ::Locale::LocaleExtension extension)
{ {
// Note: This implementation differs from the spec in how the extension is inserted. The spec assumes // Note: This implementation differs from the spec in how the extension is inserted. The spec assumes
// the input to this method is a string, and is written such that operations are performed on parts // the input to this method is a string, and is written such that operations are performed on parts
// of that string. LibUnicode gives us the parsed locale in a structure, so we can mutate that // of that string. LibUnicode gives us the parsed locale in a structure, so we can mutate that
// structure directly. // structure directly.
locale.extensions.append(move(extension)); TRY_OR_THROW_OOM(vm, locale.extensions.try_append(move(extension)));
return JS::Intl::canonicalize_unicode_locale_id(locale); return TRY(JS::Intl::canonicalize_unicode_locale_id(vm, locale));
} }
template<typename T> template<typename T>
@ -376,7 +377,7 @@ static auto& find_key_in_value(T& value, StringView key)
} }
// 9.2.7 ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData ), https://tc39.es/ecma402/#sec-resolvelocale // 9.2.7 ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData ), https://tc39.es/ecma402/#sec-resolvelocale
ThrowCompletionOr<LocaleResult> resolve_locale(Vector<DeprecatedString> const& requested_locales, LocaleOptions const& options, Span<StringView const> relevant_extension_keys) ThrowCompletionOr<LocaleResult> resolve_locale(VM& vm, Vector<DeprecatedString> const& requested_locales, LocaleOptions const& options, Span<StringView const> relevant_extension_keys)
{ {
// 1. Let matcher be options.[[localeMatcher]]. // 1. Let matcher be options.[[localeMatcher]].
auto const& matcher = options.locale_matcher; auto const& matcher = options.locale_matcher;
@ -385,12 +386,14 @@ ThrowCompletionOr<LocaleResult> resolve_locale(Vector<DeprecatedString> const& r
// 2. If matcher is "lookup", then // 2. If matcher is "lookup", then
if (matcher.is_string() && (TRY(matcher.as_string().utf8_string_view()) == "lookup"sv)) { if (matcher.is_string() && (TRY(matcher.as_string().utf8_string_view()) == "lookup"sv)) {
// a. Let r be ! LookupMatcher(availableLocales, requestedLocales). // a. Let r be ! LookupMatcher(availableLocales, requestedLocales).
matcher_result = lookup_matcher(requested_locales); // NOTE: We TRY this operation only to propagate OOM errors.
matcher_result = TRY(lookup_matcher(vm, requested_locales));
} }
// 3. Else, // 3. Else,
else { else {
// a. Let r be ! BestFitMatcher(availableLocales, requestedLocales). // a. Let r be ! BestFitMatcher(availableLocales, requestedLocales).
matcher_result = best_fit_matcher(requested_locales); // NOTE: We TRY this operation only to propagate OOM errors.
matcher_result = TRY(best_fit_matcher(vm, requested_locales));
} }
// 4. Let foundLocale be r.[[locale]]. // 4. Let foundLocale be r.[[locale]].
@ -456,7 +459,7 @@ ThrowCompletionOr<LocaleResult> resolve_locale(Vector<DeprecatedString> const& r
value = requested_value.to_deprecated_string(); value = requested_value.to_deprecated_string();
// ii. Let supportedExtensionAddition be the string-concatenation of "-", key, "-", and value. // ii. Let supportedExtensionAddition be the string-concatenation of "-", key, "-", and value.
supported_extension_addition = ::Locale::Keyword { String::from_utf8(key).release_value_but_fixme_should_propagate_errors(), move(entry.value) }; supported_extension_addition = ::Locale::Keyword { TRY_OR_THROW_OOM(vm, String::from_utf8(key)), move(entry.value) };
} }
} }
// 4. Else if keyLocaleData contains "true", then // 4. Else if keyLocaleData contains "true", then
@ -465,7 +468,7 @@ ThrowCompletionOr<LocaleResult> resolve_locale(Vector<DeprecatedString> const& r
value = "true"sv; value = "true"sv;
// b. Let supportedExtensionAddition be the string-concatenation of "-" and key. // b. Let supportedExtensionAddition be the string-concatenation of "-" and key.
supported_extension_addition = ::Locale::Keyword { String::from_utf8(key).release_value_but_fixme_should_propagate_errors(), {} }; supported_extension_addition = ::Locale::Keyword { TRY_OR_THROW_OOM(vm, String::from_utf8(key)), {} };
} }
break; break;
@ -480,8 +483,8 @@ ThrowCompletionOr<LocaleResult> resolve_locale(Vector<DeprecatedString> const& r
if (options_value.has_value()) { 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. // 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. // 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.
auto options_value_string = String::from_deprecated_string(*options_value).release_value_but_fixme_should_propagate_errors(); auto options_value_string = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(*options_value));
::Locale::canonicalize_unicode_extension_values(key, options_value_string, true).release_value_but_fixme_should_propagate_errors(); TRY_OR_THROW_OOM(vm, ::Locale::canonicalize_unicode_extension_values(key, options_value_string, true));
options_value = options_value_string.to_deprecated_string(); options_value = options_value_string.to_deprecated_string();
// 3. If optionsValue is the empty String, then // 3. If optionsValue is the empty String, then
@ -505,16 +508,16 @@ ThrowCompletionOr<LocaleResult> resolve_locale(Vector<DeprecatedString> const& r
// k. Set supportedExtension to the string-concatenation of supportedExtension and supportedExtensionAddition. // k. Set supportedExtension to the string-concatenation of supportedExtension and supportedExtensionAddition.
if (supported_extension_addition.has_value()) if (supported_extension_addition.has_value())
supported_extension.keywords.append(supported_extension_addition.release_value()); TRY_OR_THROW_OOM(vm, supported_extension.keywords.try_append(supported_extension_addition.release_value()));
} }
// 10. If supportedExtension is not "-u", then // 10. If supportedExtension is not "-u", then
if (!supported_extension.keywords.is_empty()) { if (!supported_extension.keywords.is_empty()) {
auto locale_id = ::Locale::parse_unicode_locale_id(found_locale).release_value_but_fixme_should_propagate_errors(); auto locale_id = TRY_OR_THROW_OOM(vm, ::Locale::parse_unicode_locale_id(found_locale));
VERIFY(locale_id.has_value()); VERIFY(locale_id.has_value());
// a. Set foundLocale to InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedExtension). // a. Set foundLocale to InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedExtension).
found_locale = insert_unicode_extension_and_canonicalize(locale_id.release_value(), move(supported_extension)); found_locale = TRY(insert_unicode_extension_and_canonicalize(vm, locale_id.release_value(), move(supported_extension)));
} }
// 11. Set result.[[locale]] to foundLocale. // 11. Set result.[[locale]] to foundLocale.
@ -525,14 +528,14 @@ ThrowCompletionOr<LocaleResult> resolve_locale(Vector<DeprecatedString> const& r
} }
// 9.2.8 LookupSupportedLocales ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupsupportedlocales // 9.2.8 LookupSupportedLocales ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupsupportedlocales
Vector<DeprecatedString> lookup_supported_locales(Vector<DeprecatedString> const& requested_locales) static ThrowCompletionOr<Vector<DeprecatedString>> lookup_supported_locales(VM& vm, Vector<DeprecatedString> const& requested_locales)
{ {
// 1. Let subset be a new empty List. // 1. Let subset be a new empty List.
Vector<DeprecatedString> subset; Vector<DeprecatedString> subset;
// 2. For each element locale of requestedLocales, do // 2. For each element locale of requestedLocales, do
for (auto const& locale : requested_locales) { for (auto const& locale : requested_locales) {
auto locale_id = ::Locale::parse_unicode_locale_id(locale).release_value_but_fixme_should_propagate_errors(); auto locale_id = TRY_OR_THROW_OOM(vm, ::Locale::parse_unicode_locale_id(locale));
VERIFY(locale_id.has_value()); VERIFY(locale_id.has_value());
// a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed. // a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed.
@ -544,7 +547,7 @@ Vector<DeprecatedString> lookup_supported_locales(Vector<DeprecatedString> const
// c. If availableLocale is not undefined, append locale to the end of subset. // c. If availableLocale is not undefined, append locale to the end of subset.
if (available_locale.has_value()) if (available_locale.has_value())
subset.append(locale); TRY_OR_THROW_OOM(vm, subset.try_append(locale));
} }
// 3. Return subset. // 3. Return subset.
@ -552,7 +555,7 @@ Vector<DeprecatedString> lookup_supported_locales(Vector<DeprecatedString> const
} }
// 9.2.9 BestFitSupportedLocales ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-bestfitsupportedlocales // 9.2.9 BestFitSupportedLocales ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-bestfitsupportedlocales
Vector<DeprecatedString> best_fit_supported_locales(Vector<DeprecatedString> const& requested_locales) static ThrowCompletionOr<Vector<DeprecatedString>> best_fit_supported_locales(VM& vm, Vector<DeprecatedString> const& requested_locales)
{ {
// The BestFitSupportedLocales abstract operation returns the subset of the provided BCP 47 // The BestFitSupportedLocales abstract operation returns the subset of the provided BCP 47
// language priority list requestedLocales for which availableLocales has a matching locale // language priority list requestedLocales for which availableLocales has a matching locale
@ -560,7 +563,7 @@ Vector<DeprecatedString> best_fit_supported_locales(Vector<DeprecatedString> con
// list as in requestedLocales. The steps taken are implementation dependent. // list as in requestedLocales. The steps taken are implementation dependent.
// :yakbrain: // :yakbrain:
return lookup_supported_locales(requested_locales); return lookup_supported_locales(vm, requested_locales);
} }
// 9.2.10 SupportedLocales ( availableLocales, requestedLocales, options ), https://tc39.es/ecma402/#sec-supportedlocales // 9.2.10 SupportedLocales ( availableLocales, requestedLocales, options ), https://tc39.es/ecma402/#sec-supportedlocales
@ -579,12 +582,12 @@ ThrowCompletionOr<Array*> supported_locales(VM& vm, Vector<DeprecatedString> con
// 3. If matcher is "best fit", then // 3. If matcher is "best fit", then
if (TRY(matcher.as_string().utf8_string_view()) == "best fit"sv) { if (TRY(matcher.as_string().utf8_string_view()) == "best fit"sv) {
// a. Let supportedLocales be BestFitSupportedLocales(availableLocales, requestedLocales). // a. Let supportedLocales be BestFitSupportedLocales(availableLocales, requestedLocales).
supported_locales = best_fit_supported_locales(requested_locales); supported_locales = TRY(best_fit_supported_locales(vm, requested_locales));
} }
// 4. Else, // 4. Else,
else { else {
// a. Let supportedLocales be LookupSupportedLocales(availableLocales, requestedLocales). // a. Let supportedLocales be LookupSupportedLocales(availableLocales, requestedLocales).
supported_locales = lookup_supported_locales(requested_locales); supported_locales = TRY(lookup_supported_locales(vm, requested_locales));
} }
// 5. Return CreateArrayFromList(supportedLocales). // 5. Return CreateArrayFromList(supportedLocales).

View file

@ -79,16 +79,14 @@ struct PatternPartitionWithSource : public PatternPartition {
using StringOrBoolean = Variant<StringView, bool>; using StringOrBoolean = Variant<StringView, bool>;
Optional<::Locale::LocaleID> is_structurally_valid_language_tag(StringView locale); ThrowCompletionOr<Optional<::Locale::LocaleID>> is_structurally_valid_language_tag(VM&, StringView locale);
DeprecatedString canonicalize_unicode_locale_id(::Locale::LocaleID& locale); ThrowCompletionOr<DeprecatedString> canonicalize_unicode_locale_id(VM&, ::Locale::LocaleID& locale);
bool is_well_formed_currency_code(StringView currency); bool is_well_formed_currency_code(StringView currency);
bool is_well_formed_unit_identifier(StringView unit_identifier); bool is_well_formed_unit_identifier(StringView unit_identifier);
ThrowCompletionOr<Vector<DeprecatedString>> canonicalize_locale_list(VM&, Value locales); ThrowCompletionOr<Vector<DeprecatedString>> canonicalize_locale_list(VM&, Value locales);
Optional<DeprecatedString> best_available_locale(StringView locale); Optional<DeprecatedString> best_available_locale(StringView locale);
DeprecatedString insert_unicode_extension_and_canonicalize(::Locale::LocaleID locale_id, ::Locale::LocaleExtension extension); ThrowCompletionOr<DeprecatedString> insert_unicode_extension_and_canonicalize(VM&, ::Locale::LocaleID locale_id, ::Locale::LocaleExtension extension);
ThrowCompletionOr<LocaleResult> resolve_locale(Vector<DeprecatedString> const& requested_locales, LocaleOptions const& options, Span<StringView const> relevant_extension_keys); ThrowCompletionOr<LocaleResult> resolve_locale(VM&, Vector<DeprecatedString> const& requested_locales, LocaleOptions const& options, Span<StringView const> relevant_extension_keys);
Vector<DeprecatedString> lookup_supported_locales(Vector<DeprecatedString> const& requested_locales);
Vector<DeprecatedString> best_fit_supported_locales(Vector<DeprecatedString> const& requested_locales);
ThrowCompletionOr<Array*> supported_locales(VM&, Vector<DeprecatedString> const& requested_locales, Value options); ThrowCompletionOr<Array*> supported_locales(VM&, Vector<DeprecatedString> const& requested_locales, Value options);
ThrowCompletionOr<Object*> coerce_options_to_object(VM&, Value options); ThrowCompletionOr<Object*> coerce_options_to_object(VM&, Value options);
ThrowCompletionOr<StringOrBoolean> get_string_or_boolean_option(VM&, Object const& options, PropertyKey const& property, Span<StringView const> values, StringOrBoolean true_value, StringOrBoolean falsy_value, StringOrBoolean fallback); ThrowCompletionOr<StringOrBoolean> get_string_or_boolean_option(VM&, Object const& options, PropertyKey const& property, Span<StringView const> values, StringOrBoolean true_value, StringOrBoolean falsy_value, StringOrBoolean fallback);

View file

@ -75,7 +75,7 @@ static ThrowCompletionOr<Collator*> initialize_collator(VM& vm, Collator& collat
auto relevant_extension_keys = Collator::relevant_extension_keys(); auto relevant_extension_keys = Collator::relevant_extension_keys();
// 19. Let r be ResolveLocale(%Collator%.[[AvailableLocales]], requestedLocales, opt, relevantExtensionKeys, localeData). // 19. Let r be ResolveLocale(%Collator%.[[AvailableLocales]], requestedLocales, opt, relevantExtensionKeys, localeData).
auto result = TRY(resolve_locale(requested_locales, opt, relevant_extension_keys)); auto result = TRY(resolve_locale(vm, requested_locales, opt, relevant_extension_keys));
// 20. Set collator.[[Locale]] to r.[[locale]]. // 20. Set collator.[[Locale]] to r.[[locale]].
collator.set_locale(move(result.locale)); collator.set_locale(move(result.locale));

View file

@ -144,7 +144,7 @@ ThrowCompletionOr<DateTimeFormat*> initialize_date_time_format(VM& vm, DateTimeF
// 16. Let localeData be %DateTimeFormat%.[[LocaleData]]. // 16. Let localeData be %DateTimeFormat%.[[LocaleData]].
// 17. Let r be ResolveLocale(%DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]], localeData). // 17. Let r be ResolveLocale(%DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]], localeData).
auto result = TRY(resolve_locale(requested_locales, opt, DateTimeFormat::relevant_extension_keys())); auto result = TRY(resolve_locale(vm, requested_locales, opt, DateTimeFormat::relevant_extension_keys()));
// 18. Set dateTimeFormat.[[Locale]] to r.[[locale]]. // 18. Set dateTimeFormat.[[Locale]] to r.[[locale]].
date_time_format.set_locale(move(result.locale)); date_time_format.set_locale(move(result.locale));

View file

@ -110,12 +110,13 @@ ThrowCompletionOr<Value> canonical_code_for_display_names(VM& vm, DisplayNames::
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "language"sv); return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "language"sv);
// b. If IsStructurallyValidLanguageTag(code) is false, throw a RangeError exception. // b. If IsStructurallyValidLanguageTag(code) is false, throw a RangeError exception.
auto locale_id = is_structurally_valid_language_tag(code); auto locale_id = TRY(is_structurally_valid_language_tag(vm, code));
if (!locale_id.has_value()) if (!locale_id.has_value())
return vm.throw_completion<RangeError>(ErrorType::IntlInvalidLanguageTag, code); return vm.throw_completion<RangeError>(ErrorType::IntlInvalidLanguageTag, code);
// c. Return ! CanonicalizeUnicodeLocaleId(code). // c. Return ! CanonicalizeUnicodeLocaleId(code).
auto canonicalized_tag = JS::Intl::canonicalize_unicode_locale_id(*locale_id); // NOTE: We TRY this operation only to propagate OOM errors.
auto canonicalized_tag = TRY(JS::Intl::canonicalize_unicode_locale_id(vm, *locale_id));
return PrimitiveString::create(vm, move(canonicalized_tag)); return PrimitiveString::create(vm, move(canonicalized_tag));
} }

View file

@ -76,7 +76,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DisplayNamesConstructor::construct(Funct
opt.locale_matcher = matcher; opt.locale_matcher = matcher;
// 10. Let r be ResolveLocale(%DisplayNames%.[[AvailableLocales]], requestedLocales, opt, %DisplayNames%.[[RelevantExtensionKeys]]). // 10. Let r be ResolveLocale(%DisplayNames%.[[AvailableLocales]], requestedLocales, opt, %DisplayNames%.[[RelevantExtensionKeys]]).
auto result = TRY(resolve_locale(requested_locales, opt, {})); auto result = TRY(resolve_locale(vm, requested_locales, opt, {}));
// 11. Let style be ? GetOption(options, "style", string, « "narrow", "short", "long" », "long"). // 11. Let style be ? GetOption(options, "style", string, « "narrow", "short", "long" », "long").
auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "narrow"sv, "short"sv, "long"sv }, "long"sv)); auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "narrow"sv, "short"sv, "long"sv }, "long"sv));

View file

@ -62,7 +62,7 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of)
break; break;
} }
if (auto locale = is_structurally_valid_language_tag(code_string); locale.has_value()) if (auto locale = TRY(is_structurally_valid_language_tag(vm, code_string)); locale.has_value())
formatted_result = ::Locale::format_locale_for_display(display_names->locale(), locale.release_value()); formatted_result = ::Locale::format_locale_for_display(display_names->locale(), locale.release_value());
break; break;
case DisplayNames::Type::Region: case DisplayNames::Type::Region:

View file

@ -77,7 +77,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
opt.nu = numbering_system.is_undefined() ? Optional<DeprecatedString>() : TRY(numbering_system.as_string().deprecated_string()); opt.nu = numbering_system.is_undefined() ? Optional<DeprecatedString>() : TRY(numbering_system.as_string().deprecated_string());
// 9. Let r be ResolveLocale(%DurationFormat%.[[AvailableLocales]], requestedLocales, opt, %DurationFormat%.[[RelevantExtensionKeys]], %DurationFormat%.[[LocaleData]]). // 9. Let r be ResolveLocale(%DurationFormat%.[[AvailableLocales]], requestedLocales, opt, %DurationFormat%.[[RelevantExtensionKeys]], %DurationFormat%.[[LocaleData]]).
auto result = TRY(resolve_locale(requested_locales, opt, DurationFormat::relevant_extension_keys())); auto result = TRY(resolve_locale(vm, requested_locales, opt, DurationFormat::relevant_extension_keys()));
// 10. Let locale be r.[[locale]]. // 10. Let locale be r.[[locale]].
auto locale = move(result.locale); auto locale = move(result.locale);

View file

@ -71,7 +71,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> ListFormatConstructor::construct(Functio
// 8. Let localeData be %ListFormat%.[[LocaleData]]. // 8. Let localeData be %ListFormat%.[[LocaleData]].
// 9. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]], requestedLocales, opt, %ListFormat%.[[RelevantExtensionKeys]], localeData). // 9. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]], requestedLocales, opt, %ListFormat%.[[RelevantExtensionKeys]], localeData).
auto result = TRY(resolve_locale(requested_locales, opt, {})); auto result = TRY(resolve_locale(vm, requested_locales, opt, {}));
// 10. Set listFormat.[[Locale]] to r.[[locale]]. // 10. Set listFormat.[[Locale]] to r.[[locale]].
list_format->set_locale(move(result.locale)); list_format->set_locale(move(result.locale));

View file

@ -47,7 +47,7 @@ static ThrowCompletionOr<DeprecatedString> apply_options_to_tag(VM& vm, StringVi
// 2. Assert: Type(options) is Object. // 2. Assert: Type(options) is Object.
// 3. If ! IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception. // 3. If ! IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
auto locale_id = is_structurally_valid_language_tag(tag); auto locale_id = TRY(is_structurally_valid_language_tag(vm, tag));
if (!locale_id.has_value()) if (!locale_id.has_value())
return vm.throw_completion<RangeError>(ErrorType::IntlInvalidLanguageTag, tag); return vm.throw_completion<RangeError>(ErrorType::IntlInvalidLanguageTag, tag);
@ -67,7 +67,8 @@ static ThrowCompletionOr<DeprecatedString> apply_options_to_tag(VM& vm, StringVi
auto region = TRY(get_string_option(vm, options, vm.names.region, ::Locale::is_unicode_region_subtag)); auto region = TRY(get_string_option(vm, options, vm.names.region, ::Locale::is_unicode_region_subtag));
// 10. Set tag to ! CanonicalizeUnicodeLocaleId(tag). // 10. Set tag to ! CanonicalizeUnicodeLocaleId(tag).
auto canonicalized_tag = JS::Intl::canonicalize_unicode_locale_id(*locale_id); // NOTE: We TRY this operation only to propagate OOM errors.
auto canonicalized_tag = TRY(JS::Intl::canonicalize_unicode_locale_id(vm, *locale_id));
// 11. Assert: tag matches the unicode_locale_id production. // 11. Assert: tag matches the unicode_locale_id production.
locale_id = TRY_OR_THROW_OOM(vm, ::Locale::parse_unicode_locale_id(canonicalized_tag)); locale_id = TRY_OR_THROW_OOM(vm, ::Locale::parse_unicode_locale_id(canonicalized_tag));
@ -102,11 +103,11 @@ static ThrowCompletionOr<DeprecatedString> apply_options_to_tag(VM& vm, StringVi
// 16. Set tag to tag with the substring corresponding to the unicode_language_id production replaced by the string languageId. // 16. Set tag to tag with the substring corresponding to the unicode_language_id production replaced by the string languageId.
// 17. Return ! CanonicalizeUnicodeLocaleId(tag). // 17. Return ! CanonicalizeUnicodeLocaleId(tag).
return JS::Intl::canonicalize_unicode_locale_id(*locale_id); return JS::Intl::canonicalize_unicode_locale_id(vm, *locale_id);
} }
// 14.1.3 ApplyUnicodeExtensionToTag ( tag, options, relevantExtensionKeys ), https://tc39.es/ecma402/#sec-apply-unicode-extension-to-tag // 14.1.3 ApplyUnicodeExtensionToTag ( tag, options, relevantExtensionKeys ), https://tc39.es/ecma402/#sec-apply-unicode-extension-to-tag
static LocaleAndKeys apply_unicode_extension_to_tag(StringView tag, LocaleAndKeys options, Span<StringView const> relevant_extension_keys) static ThrowCompletionOr<LocaleAndKeys> apply_unicode_extension_to_tag(VM& vm, StringView tag, LocaleAndKeys options, Span<StringView const> relevant_extension_keys)
{ {
// 1. Assert: Type(tag) is String. // 1. Assert: Type(tag) is String.
// 2. Assert: tag matches the unicode_locale_id production. // 2. Assert: tag matches the unicode_locale_id production.
@ -207,7 +208,7 @@ static LocaleAndKeys apply_unicode_extension_to_tag(StringView tag, LocaleAndKey
// 9. If newExtension is not the empty String, then // 9. If newExtension is not the empty String, then
if (!new_extension.attributes.is_empty() || !new_extension.keywords.is_empty()) { if (!new_extension.attributes.is_empty() || !new_extension.keywords.is_empty()) {
// a. Let locale be ! InsertUnicodeExtensionAndCanonicalize(locale, newExtension). // a. Let locale be ! InsertUnicodeExtensionAndCanonicalize(locale, newExtension).
locale = String::from_deprecated_string(insert_unicode_extension_and_canonicalize(locale_id.release_value(), move(new_extension))).release_value_but_fixme_should_propagate_errors(); locale = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(TRY(insert_unicode_extension_and_canonicalize(vm, locale_id.release_value(), move(new_extension)))));
} }
// 10. Set result.[[locale]] to locale. // 10. Set result.[[locale]] to locale.
@ -323,7 +324,8 @@ ThrowCompletionOr<NonnullGCPtr<Object>> LocaleConstructor::construct(FunctionObj
opt.nu = TRY(get_string_option(vm, *options, vm.names.numberingSystem, ::Locale::is_type_identifier)); opt.nu = TRY(get_string_option(vm, *options, vm.names.numberingSystem, ::Locale::is_type_identifier));
// 29. Let r be ! ApplyUnicodeExtensionToTag(tag, opt, relevantExtensionKeys). // 29. Let r be ! ApplyUnicodeExtensionToTag(tag, opt, relevantExtensionKeys).
auto result = apply_unicode_extension_to_tag(tag, move(opt), relevant_extension_keys); // NOTE: We TRY this operation only to propagate OOM errors.
auto result = TRY(apply_unicode_extension_to_tag(vm, tag, move(opt), relevant_extension_keys));
// 30. Set locale.[[Locale]] to r.[[locale]]. // 30. Set locale.[[Locale]] to r.[[locale]].
locale->set_locale(result.locale.to_deprecated_string()); locale->set_locale(result.locale.to_deprecated_string());

View file

@ -112,7 +112,7 @@ ThrowCompletionOr<NumberFormat*> initialize_number_format(VM& vm, NumberFormat&
// 9. Let localeData be %NumberFormat%.[[LocaleData]]. // 9. Let localeData be %NumberFormat%.[[LocaleData]].
// 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], localeData). // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], localeData).
auto result = TRY(resolve_locale(requested_locales, opt, NumberFormat::relevant_extension_keys())); auto result = TRY(resolve_locale(vm, requested_locales, opt, NumberFormat::relevant_extension_keys()));
// 11. Set numberFormat.[[Locale]] to r.[[locale]]. // 11. Set numberFormat.[[Locale]] to r.[[locale]].
number_format.set_locale(move(result.locale)); number_format.set_locale(move(result.locale));

View file

@ -101,7 +101,7 @@ ThrowCompletionOr<PluralRules*> initialize_plural_rules(VM& vm, PluralRules& plu
// 9. Let localeData be %PluralRules%.[[LocaleData]]. // 9. Let localeData be %PluralRules%.[[LocaleData]].
// 10. Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]], requestedLocales, opt, %PluralRules%.[[RelevantExtensionKeys]], localeData). // 10. Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]], requestedLocales, opt, %PluralRules%.[[RelevantExtensionKeys]], localeData).
auto result = TRY(resolve_locale(requested_locales, opt, {})); auto result = TRY(resolve_locale(vm, requested_locales, opt, {}));
// 11. Set pluralRules.[[Locale]] to r.[[locale]]. // 11. Set pluralRules.[[Locale]] to r.[[locale]].
plural_rules.set_locale(move(result.locale)); plural_rules.set_locale(move(result.locale));

View file

@ -110,7 +110,7 @@ ThrowCompletionOr<RelativeTimeFormat*> initialize_relative_time_format(VM& vm, R
// 9. Let localeData be %RelativeTimeFormat%.[[LocaleData]]. // 9. Let localeData be %RelativeTimeFormat%.[[LocaleData]].
// 10. Let r be ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %RelativeTimeFormat%.[[RelevantExtensionKeys]], localeData). // 10. Let r be ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %RelativeTimeFormat%.[[RelevantExtensionKeys]], localeData).
auto result = TRY(resolve_locale(requested_locales, opt, RelativeTimeFormat::relevant_extension_keys())); auto result = TRY(resolve_locale(vm, requested_locales, opt, RelativeTimeFormat::relevant_extension_keys()));
// 11. Let locale be r.[[locale]]. // 11. Let locale be r.[[locale]].
auto locale = move(result.locale); auto locale = move(result.locale);

View file

@ -72,7 +72,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> SegmenterConstructor::construct(Function
// 9. Let localeData be %Segmenter%.[[LocaleData]]. // 9. Let localeData be %Segmenter%.[[LocaleData]].
// 10. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]], requestedLocales, opt, %Segmenter%.[[RelevantExtensionKeys]], localeData). // 10. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]], requestedLocales, opt, %Segmenter%.[[RelevantExtensionKeys]], localeData).
auto result = TRY(resolve_locale(requested_locales, opt, {})); auto result = TRY(resolve_locale(vm, requested_locales, opt, {}));
// 11. Set segmenter.[[Locale]] to r.[[locale]]. // 11. Set segmenter.[[Locale]] to r.[[locale]].
segmenter->set_locale(move(result.locale)); segmenter->set_locale(move(result.locale));