mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:42:43 +00:00 
			
		
		
		
	 c3abb1396c
			
		
	
	
		c3abb1396c
		
	
	
	
	
		
			
			First, this adds an overload of PrimitiveString::create for StringView. This overload will throw an OOM completion if creating a String fails. This is not only a bit more convenient, but it also ensures at compile time that all PrimitiveString::create(string_view) invocations will be handled as String and OOM-aware. Next, this wraps all invocations to PrimitiveString::create(string_view) with MUST_OR_THROW_OOM. A small PrimitiveString::create(DeprecatedFlyString) overload also had to be added to disambiguate between the StringView and DeprecatedString overloads.
		
			
				
	
	
		
			191 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <LibJS/Runtime/GlobalObject.h>
 | |
| #include <LibJS/Runtime/Intl/AbstractOperations.h>
 | |
| #include <LibJS/Runtime/Intl/DisplayNames.h>
 | |
| 
 | |
| namespace JS::Intl {
 | |
| 
 | |
| // 12 DisplayNames Objects, https://tc39.es/ecma402/#intl-displaynames-objects
 | |
| DisplayNames::DisplayNames(Object& prototype)
 | |
|     : Object(ConstructWithPrototypeTag::Tag, prototype)
 | |
| {
 | |
| }
 | |
| 
 | |
| void DisplayNames::set_type(StringView type)
 | |
| {
 | |
|     if (type == "language"sv)
 | |
|         m_type = Type::Language;
 | |
|     else if (type == "region"sv)
 | |
|         m_type = Type::Region;
 | |
|     else if (type == "script"sv)
 | |
|         m_type = Type::Script;
 | |
|     else if (type == "currency"sv)
 | |
|         m_type = Type::Currency;
 | |
|     else if (type == "calendar"sv)
 | |
|         m_type = Type::Calendar;
 | |
|     else if (type == "dateTimeField"sv)
 | |
|         m_type = Type::DateTimeField;
 | |
|     else
 | |
|         VERIFY_NOT_REACHED();
 | |
| }
 | |
| 
 | |
| StringView DisplayNames::type_string() const
 | |
| {
 | |
|     switch (m_type) {
 | |
|     case Type::Language:
 | |
|         return "language"sv;
 | |
|     case Type::Region:
 | |
|         return "region"sv;
 | |
|     case Type::Script:
 | |
|         return "script"sv;
 | |
|     case Type::Currency:
 | |
|         return "currency"sv;
 | |
|     case Type::Calendar:
 | |
|         return "calendar"sv;
 | |
|     case Type::DateTimeField:
 | |
|         return "dateTimeField"sv;
 | |
|     default:
 | |
|         VERIFY_NOT_REACHED();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void DisplayNames::set_fallback(StringView fallback)
 | |
| {
 | |
|     if (fallback == "none"sv)
 | |
|         m_fallback = Fallback::None;
 | |
|     else if (fallback == "code"sv)
 | |
|         m_fallback = Fallback::Code;
 | |
|     else
 | |
|         VERIFY_NOT_REACHED();
 | |
| }
 | |
| 
 | |
| StringView DisplayNames::fallback_string() const
 | |
| {
 | |
|     switch (m_fallback) {
 | |
|     case Fallback::None:
 | |
|         return "none"sv;
 | |
|     case Fallback::Code:
 | |
|         return "code"sv;
 | |
|     default:
 | |
|         VERIFY_NOT_REACHED();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void DisplayNames::set_language_display(StringView language_display)
 | |
| {
 | |
|     if (language_display == "dialect"sv)
 | |
|         m_language_display = LanguageDisplay::Dialect;
 | |
|     else if (language_display == "standard"sv)
 | |
|         m_language_display = LanguageDisplay::Standard;
 | |
|     else
 | |
|         VERIFY_NOT_REACHED();
 | |
| }
 | |
| 
 | |
| StringView DisplayNames::language_display_string() const
 | |
| {
 | |
|     VERIFY(m_language_display.has_value());
 | |
| 
 | |
|     switch (*m_language_display) {
 | |
|     case LanguageDisplay::Dialect:
 | |
|         return "dialect"sv;
 | |
|     case LanguageDisplay::Standard:
 | |
|         return "standard"sv;
 | |
|     default:
 | |
|         VERIFY_NOT_REACHED();
 | |
|     }
 | |
| }
 | |
| 
 | |
| // 12.5.1 CanonicalCodeForDisplayNames ( type, code ), https://tc39.es/ecma402/#sec-canonicalcodefordisplaynames
 | |
| ThrowCompletionOr<Value> canonical_code_for_display_names(VM& vm, DisplayNames::Type type, StringView code)
 | |
| {
 | |
|     // 1. If type is "language", then
 | |
|     if (type == DisplayNames::Type::Language) {
 | |
|         // a. If code does not match the unicode_language_id production, throw a RangeError exception.
 | |
|         if (!TRY_OR_THROW_OOM(vm, ::Locale::parse_unicode_language_id(code)).has_value())
 | |
|             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "language"sv);
 | |
| 
 | |
|         // b. If IsStructurallyValidLanguageTag(code) is false, throw a RangeError exception.
 | |
|         auto locale_id = MUST_OR_THROW_OOM(is_structurally_valid_language_tag(vm, code));
 | |
|         if (!locale_id.has_value())
 | |
|             return vm.throw_completion<RangeError>(ErrorType::IntlInvalidLanguageTag, code);
 | |
| 
 | |
|         // c. Return ! CanonicalizeUnicodeLocaleId(code).
 | |
|         auto canonicalized_tag = MUST_OR_THROW_OOM(canonicalize_unicode_locale_id(vm, *locale_id));
 | |
|         return PrimitiveString::create(vm, move(canonicalized_tag));
 | |
|     }
 | |
| 
 | |
|     // 2. If type is "region", then
 | |
|     if (type == DisplayNames::Type::Region) {
 | |
|         // a. If code does not match the unicode_region_subtag production, throw a RangeError exception.
 | |
|         if (!::Locale::is_unicode_region_subtag(code))
 | |
|             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "region"sv);
 | |
| 
 | |
|         // b. Return the ASCII-uppercase of code.
 | |
|         return PrimitiveString::create(vm, code.to_uppercase_string());
 | |
|     }
 | |
| 
 | |
|     // 3. If type is "script", then
 | |
|     if (type == DisplayNames::Type::Script) {
 | |
|         // a. If code does not match the unicode_script_subtag production, throw a RangeError exception.
 | |
|         if (!::Locale::is_unicode_script_subtag(code))
 | |
|             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "script"sv);
 | |
| 
 | |
|         // Assert: The length of code is 4, and every code unit of code represents an ASCII letter (0x0041 through 0x005A and 0x0061 through 0x007A, both inclusive).
 | |
|         VERIFY(code.length() == 4);
 | |
|         VERIFY(all_of(code, is_ascii_alpha));
 | |
| 
 | |
|         // c. Let first be the ASCII-uppercase of the substring of code from 0 to 1.
 | |
|         // d. Let rest be the ASCII-lowercase of the substring of code from 1.
 | |
|         // e. Return the string-concatenation of first and rest.
 | |
|         return PrimitiveString::create(vm, code.to_titlecase_string());
 | |
|     }
 | |
| 
 | |
|     // 4. If type is "calendar", then
 | |
|     if (type == DisplayNames::Type::Calendar) {
 | |
|         // a. If code does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
 | |
|         if (!::Locale::is_type_identifier(code))
 | |
|             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "calendar"sv);
 | |
| 
 | |
|         // b. If code uses any of the backwards compatibility syntax described in Unicode Technical Standard #35 LDML § 3.3 BCP 47 Conformance, throw a RangeError exception.
 | |
|         if (code.contains('_'))
 | |
|             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "calendar"sv);
 | |
| 
 | |
|         // c. Return the ASCII-lowercase of code.
 | |
|         return PrimitiveString::create(vm, code.to_lowercase_string());
 | |
|     }
 | |
| 
 | |
|     // 5. If type is "dateTimeField", then
 | |
|     if (type == DisplayNames::Type::DateTimeField) {
 | |
|         // a. If the result of IsValidDateTimeFieldCode(code) is false, throw a RangeError exception.
 | |
|         if (!is_valid_date_time_field_code(code))
 | |
|             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "dateTimeField"sv);
 | |
| 
 | |
|         // b. Return code.
 | |
|         return MUST_OR_THROW_OOM(PrimitiveString::create(vm, code));
 | |
|     }
 | |
| 
 | |
|     // 6. Assert: type is "currency".
 | |
|     VERIFY(type == DisplayNames::Type::Currency);
 | |
| 
 | |
|     // 7. If ! IsWellFormedCurrencyCode(code) is false, throw a RangeError exception.
 | |
|     if (!is_well_formed_currency_code(code))
 | |
|         return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, code, "currency"sv);
 | |
| 
 | |
|     // 8. Return the ASCII-uppercase of code.
 | |
|     return PrimitiveString::create(vm, code.to_uppercase_string());
 | |
| }
 | |
| 
 | |
| // 12.5.2 IsValidDateTimeFieldCode ( field ), https://tc39.es/ecma402/#sec-isvaliddatetimefieldcode
 | |
| bool is_valid_date_time_field_code(StringView field)
 | |
| {
 | |
|     // 1. If field is listed in the Code column of Table 9, return true.
 | |
|     // 2. Return false.
 | |
|     return field.is_one_of("era"sv, "year"sv, "quarter"sv, "month"sv, "weekOfYear"sv, "weekday"sv, "day"sv, "dayPeriod"sv, "hour"sv, "minute"sv, "second"sv, "timeZoneName"sv);
 | |
| }
 | |
| 
 | |
| }
 |