/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/objects/js-plural-rules.cc
434 строки
17 KB
Michaël Zasso
deps: update V8 to 14.3.127.12
13 ноя 2025, 17:08
Не верифицирован
13 ноя 2025, 17:08
53379f3
Код
Авторство
О чём код?
// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_INTL_SUPPORT #error Internationalization is expected to be enabled. #endif // V8_INTL_SUPPORT #include "src/objects/js-plural-rules.h" #include "src/execution/isolate-inl.h" #include "src/objects/intl-objects.h" #include "src/objects/js-number-format.h" #include "src/objects/js-plural-rules-inl.h" #include "src/objects/managed-inl.h" #include "src/objects/option-utils.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" #include "unicode/locid.h" #include "unicode/numberformatter.h" #include "unicode/numberrangeformatter.h" #include "unicode/plurrule.h" #include "unicode/unumberformatter.h" #pragma GCC diagnostic pop namespace v8 { namespace internal { namespace { bool CreateICUPluralRules(Isolate* isolate, const icu::Locale& icu_locale, JSPluralRules::Type type, std::unique_ptr<icu::PluralRules>* pl) { // Make formatter from options. Numbering system is added // to the locale as Unicode extension (if it was specified at all). UErrorCode status = U_ZERO_ERROR; UPluralType icu_type = UPLURAL_TYPE_CARDINAL; if (type == JSPluralRules::Type::ORDINAL) { icu_type = UPLURAL_TYPE_ORDINAL; } else { DCHECK_EQ(JSPluralRules::Type::CARDINAL, type); } std::unique_ptr<icu::PluralRules> plural_rules( icu::PluralRules::forLocale(icu_locale, icu_type, status)); if (U_FAILURE(status)) { return false; } DCHECK_NOT_NULL(plural_rules.get()); *pl = std::move(plural_rules); return true; } } // namespace Handle<String> JSPluralRules::TypeAsString(Isolate* isolate) const { switch (type()) { case Type::CARDINAL: return isolate->factory()->cardinal_string(); case Type::ORDINAL: return isolate->factory()->ordinal_string(); } UNREACHABLE(); } // static MaybeDirectHandle<JSPluralRules> JSPluralRules::New( Isolate* isolate, DirectHandle<Map> map, DirectHandle<Object> locales, DirectHandle<Object> options_obj, const char* service) { // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). Maybe<std::vector<std::string>> maybe_requested_locales = Intl::CanonicalizeLocaleList(isolate, locales); MAYBE_RETURN(maybe_requested_locales, DirectHandle<JSPluralRules>()); std::vector<std::string> requested_locales = maybe_requested_locales.FromJust(); // 2. Set options to ? CoerceOptionsToObject(options). DirectHandle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, CoerceOptionsToObject(isolate, options_obj, service)); // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", // « "lookup", "best fit" », "best fit"). // 6. Set opt.[[localeMatcher]] to matcher. Maybe<Intl::MatcherOption> maybe_locale_matcher = Intl::GetLocaleMatcher(isolate, options, service); MAYBE_RETURN(maybe_locale_matcher, MaybeDirectHandle<JSPluralRules>()); Intl::MatcherOption matcher = maybe_locale_matcher.FromJust(); // 7. Let t be ? GetOption(options, "type", "string", « "cardinal", // "ordinal" », "cardinal"). Maybe<Type> maybe_type = GetStringOption<Type>( isolate, options, isolate->factory()->type_string(), service, std::to_array<const std::string_view>({"cardinal", "ordinal"}), std::array{Type::CARDINAL, Type::ORDINAL}, Type::CARDINAL); MAYBE_RETURN(maybe_type, MaybeDirectHandle<JSPluralRules>()); Type type = maybe_type.FromJust(); Intl::Notation notation = Intl::Notation::STANDARD; // 9. Let notation be ? GetOption(options, "notation", "string", « // "standard", "scientific", "engineering", "compact" », "standard"). ASSIGN_RETURN_ON_EXCEPTION( isolate, notation, GetStringOption<Intl::Notation>( isolate, options, isolate->factory()->notation_string(), service, std::to_array<const std::string_view>( {"standard", "scientific", "engineering", "compact"}), std::array{Intl::Notation::STANDARD, Intl::Notation::SCIENTIFIC, Intl::Notation::ENGINEERING, Intl::Notation::COMPACT}, Intl::Notation::STANDARD)); // 10. Set pluralRules.[[Notation]] to notation. // 28. Let compactDisplay be ? GetOption(options, "compactDisplay", // "string", « "short", "long" », "short"). Maybe<Intl::CompactDisplay> maybe_compact_display = GetStringOption<Intl::CompactDisplay>( isolate, options, isolate->factory()->compactDisplay_string(), service, std::to_array<const std::string_view>({"short", "long"}), std::array{Intl::CompactDisplay::SHORT, Intl::CompactDisplay::LONG}, Intl::CompactDisplay::SHORT); MAYBE_RETURN(maybe_compact_display, MaybeDirectHandle<JSPluralRules>()); Intl::CompactDisplay compact_display = maybe_compact_display.FromJust(); // Note: The spec says we should do ResolveLocale after performing // SetNumberFormatDigitOptions but we need the locale to create all // the ICU data structures. // // This isn't observable so we aren't violating the spec. // 11. Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]], // requestedLocales, opt, %PluralRules%.[[RelevantExtensionKeys]], // localeData). Intl::ResolvedLocale r; if (!Intl::ResolveLocale(isolate, JSPluralRules::GetAvailableLocales(), requested_locales, matcher, {}) .To(&r)) { THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError)); } DirectHandle<String> locale_str = isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str()); icu::Locale icu_locale = r.icu_locale; icu::number::UnlocalizedNumberFormatter settings = icu::number::UnlocalizedNumberFormatter().roundingMode(UNUM_ROUND_HALFUP); std::unique_ptr<icu::PluralRules> icu_plural_rules; bool success = CreateICUPluralRules(isolate, r.icu_locale, type, &icu_plural_rules); if (!success || icu_plural_rules == nullptr) { // Remove extensions and try again. icu::Locale no_extension_locale(icu_locale.getBaseName()); success = CreateICUPluralRules(isolate, no_extension_locale, type, &icu_plural_rules); icu_locale = no_extension_locale; if (!success || icu_plural_rules == nullptr) { THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError)); } } // The default notation in ICU is Simple, which mapped from STANDARD // so we can skip setting it. if (notation != Intl::Notation::STANDARD) { settings = settings.notation(Intl::ToICUNotation(notation, compact_display)); } // 11. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3, // notation). Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options = Intl::SetNumberFormatDigitOptions( isolate, options, 0, 3, notation == Intl::Notation::COMPACT, service); MAYBE_RETURN(maybe_digit_options, MaybeDirectHandle<JSPluralRules>()); Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust(); settings = JSNumberFormat::SetDigitOptionsToFormatter(settings, digit_options); icu::number::LocalizedNumberFormatter icu_number_formatter = settings.locale(icu_locale); DirectHandle<Managed<icu::PluralRules>> managed_plural_rules = Managed<icu::PluralRules>::From(isolate, 0, std::move(icu_plural_rules)); DirectHandle<Managed<icu::number::LocalizedNumberFormatter>> managed_number_formatter = Managed<icu::number::LocalizedNumberFormatter>::From( isolate, 0, std::make_shared<icu::number::LocalizedNumberFormatter>( icu_number_formatter)); // Now all properties are ready, so we can allocate the result object. DirectHandle<JSPluralRules> plural_rules = Cast<JSPluralRules>( isolate->factory()->NewFastOrSlowJSObjectFromMap(map)); DisallowGarbageCollection no_gc; plural_rules->set_flags(0); // 8. Set pluralRules.[[Type]] to t. plural_rules->set_type(type); // 12. Set pluralRules.[[Locale]] to the value of r.[[locale]]. plural_rules->set_locale(*locale_str); plural_rules->set_icu_plural_rules(*managed_plural_rules); plural_rules->set_icu_number_formatter(*managed_number_formatter); // 13. Return pluralRules. return plural_rules; } MaybeDirectHandle<String> JSPluralRules::ResolvePlural( Isolate* isolate, DirectHandle<JSPluralRules> plural_rules, double number) { icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw(); DCHECK_NOT_NULL(icu_plural_rules); icu::number::LocalizedNumberFormatter* fmt = plural_rules->icu_number_formatter()->raw(); DCHECK_NOT_NULL(fmt); UErrorCode status = U_ZERO_ERROR; icu::number::FormattedNumber formatted_number = fmt->formatDouble(number, status); DCHECK(U_SUCCESS(status)); icu::UnicodeString result = icu_plural_rules->select(formatted_number, status); DCHECK(U_SUCCESS(status)); return Intl::ToString(isolate, result); } MaybeDirectHandle<String> JSPluralRules::ResolvePluralRange( Isolate* isolate, DirectHandle<JSPluralRules> plural_rules, double x, double y) { icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw(); DCHECK_NOT_NULL(icu_plural_rules); Maybe<icu::number::LocalizedNumberRangeFormatter> maybe_range_formatter = JSNumberFormat::GetRangeFormatter( isolate, plural_rules->locale(), *plural_rules->icu_number_formatter()->raw()); MAYBE_RETURN(maybe_range_formatter, MaybeDirectHandle<String>()); icu::number::LocalizedNumberRangeFormatter nrfmt = maybe_range_formatter.FromJust(); UErrorCode status = U_ZERO_ERROR; icu::number::FormattedNumberRange formatted = nrfmt.formatFormattableRange( icu::Formattable(x), icu::Formattable(y), status); DCHECK(U_SUCCESS(status)); icu::UnicodeString result = icu_plural_rules->select(formatted, status); DCHECK(U_SUCCESS(status)); return Intl::ToString(isolate, result); } namespace { void CreateDataPropertyForOptions(Isolate* isolate, DirectHandle<JSObject> options, DirectHandle<Object> value, const char* key) { DirectHandle<String> key_str = isolate->factory()->NewStringFromAsciiChecked(key); // This is a brand new JSObject that shouldn't already have the same // key so this shouldn't fail. Maybe<bool> maybe = JSReceiver::CreateDataProperty(isolate, options, key_str, value, Just(kDontThrow)); DCHECK(maybe.FromJust()); USE(maybe); } void CreateDataPropertyForOptions(Isolate* isolate, DirectHandle<JSObject> options, int value, const char* key) { DirectHandle<Smi> value_smi(Smi::FromInt(value), isolate); CreateDataPropertyForOptions(isolate, options, value_smi, key); } } // namespace DirectHandle<JSObject> JSPluralRules::ResolvedOptions( Isolate* isolate, DirectHandle<JSPluralRules> plural_rules) { DirectHandle<JSObject> options = isolate->factory()->NewJSObject(isolate->object_function()); DirectHandle<String> locale_value(plural_rules->locale(), isolate); CreateDataPropertyForOptions(isolate, options, locale_value, "locale"); CreateDataPropertyForOptions(isolate, options, plural_rules->TypeAsString(isolate), "type"); UErrorCode status = U_ZERO_ERROR; icu::number::LocalizedNumberFormatter* icu_number_formatter = plural_rules->icu_number_formatter()->raw(); icu::UnicodeString skeleton = icu_number_formatter->toSkeleton(status); DCHECK(U_SUCCESS(status)); Factory* factory = isolate->factory(); Intl::Notation notation = Intl::NotationFromSkeleton(skeleton); CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->notation_string(), Intl::NotationAsString(isolate, notation), Just(kDontThrow)) .FromJust()); if (notation == Intl::Notation::COMPACT) { // https://github.com/tc39/ecma402/issues/1031 CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->compactDisplay_string(), Intl::CompactDisplayString(isolate, skeleton), Just(kDontThrow)) .FromJust()); } CreateDataPropertyForOptions( isolate, options, JSNumberFormat::MinimumIntegerDigitsFromSkeleton(skeleton), "minimumIntegerDigits"); int32_t min = 0, max = 0; if (JSNumberFormat::SignificantDigitsFromSkeleton(skeleton, &min, &max)) { CreateDataPropertyForOptions(isolate, options, min, "minimumSignificantDigits"); CreateDataPropertyForOptions(isolate, options, max, "maximumSignificantDigits"); } else { JSNumberFormat::FractionDigitsFromSkeleton(skeleton, &min, &max); CreateDataPropertyForOptions(isolate, options, min, "minimumFractionDigits"); CreateDataPropertyForOptions(isolate, options, max, "maximumFractionDigits"); } // 6. Let pluralCategories be a List of Strings containing all possible // results of PluralRuleSelect for the selected locale pr.[[Locale]], sorted // according to the following order: "zero", "one", "two", "few", "many", // "other". icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw(); DCHECK_NOT_NULL(icu_plural_rules); std::unique_ptr<icu::StringEnumeration> categories( icu_plural_rules->getKeywords(status)); DCHECK(U_SUCCESS(status)); int32_t count = categories->count(status); DCHECK(U_SUCCESS(status)); DirectHandle<FixedArray> plural_categories = factory->NewFixedArray(count); const std::vector<const char*> kCategories = {"zero", "one", "two", "few", "many", "other"}; int32_t index = 0; std::for_each(kCategories.cbegin(), kCategories.cend(), [&](const char* val) { categories->reset(status); DCHECK(U_SUCCESS(status)); for (int32_t i = 0; i < count; i++) { int32_t len; const char* cat = categories->next(&len, status); DCHECK(U_SUCCESS(status)); if (cat == nullptr) break; if (std::strcmp(val, cat) == 0) { DirectHandle<String> value_string = factory->NewStringFromAsciiChecked(val); plural_categories->set(index++, *value_string); break; } } }); DCHECK(count == index); // 7. Perform ! CreateDataProperty(options, "pluralCategories", // CreateArrayFromList(pluralCategories)). DirectHandle<JSArray> plural_categories_value = factory->NewJSArrayWithElements(plural_categories); CreateDataPropertyForOptions(isolate, options, plural_categories_value, "pluralCategories"); CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->roundingIncrement_string(), JSNumberFormat::RoundingIncrement(isolate, skeleton), Just(kDontThrow)) .FromJust()); CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->roundingMode_string(), JSNumberFormat::RoundingModeString(isolate, skeleton), Just(kDontThrow)) .FromJust()); CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->roundingPriority_string(), JSNumberFormat::RoundingPriorityString(isolate, skeleton), Just(kDontThrow)) .FromJust()); CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->trailingZeroDisplay_string(), JSNumberFormat::TrailingZeroDisplayString(isolate, skeleton), Just(kDontThrow)) .FromJust()); return options; } namespace { class PluralRulesAvailableLocales { public: PluralRulesAvailableLocales() { UErrorCode status = U_ZERO_ERROR; std::unique_ptr<icu::StringEnumeration> locales( icu::PluralRules::getAvailableLocales(status)); DCHECK(U_SUCCESS(status)); int32_t len = 0; const char* locale = nullptr; while ((locale = locales->next(&len, status)) != nullptr && U_SUCCESS(status)) { std::string str(locale); if (len > 3) { std::replace(str.begin(), str.end(), '_', '-'); } set_.insert(std::move(str)); } } const std::set<std::string>& Get() const { return set_; } private: std::set<std::string> set_; }; } // namespace const std::set<std::string>& JSPluralRules::GetAvailableLocales() { static base::LazyInstance<PluralRulesAvailableLocales>::type available_locales = LAZY_INSTANCE_INITIALIZER; return available_locales.Pointer()->Get(); } } // namespace internal } // namespace v8