LCOV - code coverage report
Current view: top level - src/objects - js-plural-rules.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 60 108 55.6 %
Date: 2019-04-19 Functions: 6 10 60.0 %

          Line data    Source code
       1             : // Copyright 2018 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #ifndef V8_INTL_SUPPORT
       6             : #error Internationalization is expected to be enabled.
       7             : #endif  // V8_INTL_SUPPORT
       8             : 
       9             : #include "src/objects/js-plural-rules.h"
      10             : 
      11             : #include "src/isolate-inl.h"
      12             : #include "src/objects/intl-objects.h"
      13             : #include "src/objects/js-plural-rules-inl.h"
      14             : #include "unicode/decimfmt.h"
      15             : #include "unicode/locid.h"
      16             : #include "unicode/numfmt.h"
      17             : #include "unicode/plurrule.h"
      18             : 
      19             : namespace v8 {
      20             : namespace internal {
      21             : 
      22             : namespace {
      23             : 
      24          45 : bool CreateICUPluralRules(Isolate* isolate, const icu::Locale& icu_locale,
      25             :                           JSPluralRules::Type type,
      26             :                           std::unique_ptr<icu::PluralRules>* pl,
      27             :                           std::unique_ptr<icu::DecimalFormat>* nf) {
      28             :   // Make formatter from options. Numbering system is added
      29             :   // to the locale as Unicode extension (if it was specified at all).
      30          45 :   UErrorCode status = U_ZERO_ERROR;
      31             : 
      32             :   UPluralType icu_type = UPLURAL_TYPE_CARDINAL;
      33          45 :   if (type == JSPluralRules::Type::ORDINAL) {
      34             :     icu_type = UPLURAL_TYPE_ORDINAL;
      35             :   } else {
      36          36 :     CHECK_EQ(JSPluralRules::Type::CARDINAL, type);
      37             :   }
      38             : 
      39             :   std::unique_ptr<icu::PluralRules> plural_rules(
      40          45 :       icu::PluralRules::forLocale(icu_locale, icu_type, status));
      41          45 :   if (U_FAILURE(status)) {
      42             :     return false;
      43             :   }
      44          45 :   CHECK_NOT_NULL(plural_rules.get());
      45             : 
      46             :   std::unique_ptr<icu::DecimalFormat> number_format(
      47             :       static_cast<icu::DecimalFormat*>(
      48          45 :           icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status)));
      49          45 :   if (U_FAILURE(status)) {
      50             :     return false;
      51             :   }
      52          45 :   CHECK_NOT_NULL(number_format.get());
      53             : 
      54             :   *pl = std::move(plural_rules);
      55             :   *nf = std::move(number_format);
      56             : 
      57             :   return true;
      58             : }
      59             : 
      60          45 : void InitializeICUPluralRules(
      61             :     Isolate* isolate, const icu::Locale& icu_locale, JSPluralRules::Type type,
      62             :     std::unique_ptr<icu::PluralRules>* plural_rules,
      63             :     std::unique_ptr<icu::DecimalFormat>* number_format) {
      64             :   bool success = CreateICUPluralRules(isolate, icu_locale, type, plural_rules,
      65          45 :                                       number_format);
      66          45 :   if (!success) {
      67             :     // Remove extensions and try again.
      68           0 :     icu::Locale no_extension_locale(icu_locale.getBaseName());
      69             :     success = CreateICUPluralRules(isolate, no_extension_locale, type,
      70           0 :                                    plural_rules, number_format);
      71             : 
      72           0 :     if (!success) {
      73           0 :       FATAL("Failed to create ICU PluralRules, are ICU data files missing?");
      74             :     }
      75             :   }
      76             : 
      77          45 :   CHECK_NOT_NULL((*plural_rules).get());
      78          45 :   CHECK_NOT_NULL((*number_format).get());
      79          45 : }
      80             : 
      81             : }  // namespace
      82             : 
      83           0 : Handle<String> JSPluralRules::TypeAsString() const {
      84           0 :   switch (type()) {
      85             :     case Type::CARDINAL:
      86             :       return GetReadOnlyRoots().cardinal_string_handle();
      87             :     case Type::ORDINAL:
      88             :       return GetReadOnlyRoots().ordinal_string_handle();
      89             :     case Type::COUNT:
      90           0 :       UNREACHABLE();
      91             :   }
      92           0 : }
      93             : 
      94             : // static
      95          45 : MaybeHandle<JSPluralRules> JSPluralRules::Initialize(
      96             :     Isolate* isolate, Handle<JSPluralRules> plural_rules,
      97             :     Handle<Object> locales, Handle<Object> options_obj) {
      98             :   plural_rules->set_flags(0);
      99             :   // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
     100             :   Maybe<std::vector<std::string>> maybe_requested_locales =
     101          45 :       Intl::CanonicalizeLocaleList(isolate, locales);
     102          45 :   MAYBE_RETURN(maybe_requested_locales, Handle<JSPluralRules>());
     103             :   std::vector<std::string> requested_locales =
     104          45 :       maybe_requested_locales.FromJust();
     105             : 
     106             :   // 2. If options is undefined, then
     107          45 :   if (options_obj->IsUndefined(isolate)) {
     108             :     // 2. a. Let options be ObjectCreate(null).
     109          27 :     options_obj = isolate->factory()->NewJSObjectWithNullProto();
     110             :   } else {
     111             :     // 3. Else
     112             :     // 3. a. Let options be ? ToObject(options).
     113          36 :     ASSIGN_RETURN_ON_EXCEPTION(
     114             :         isolate, options_obj,
     115             :         Object::ToObject(isolate, options_obj, "Intl.PluralRules"),
     116             :         JSPluralRules);
     117             :   }
     118             : 
     119             :   // At this point, options_obj can either be a JSObject or a JSProxy only.
     120          45 :   Handle<JSReceiver> options = Handle<JSReceiver>::cast(options_obj);
     121             : 
     122             :   // 5. Let matcher be ? GetOption(options, "localeMatcher", "string",
     123             :   // « "lookup", "best fit" », "best fit").
     124             :   // 6. Set opt.[[localeMatcher]] to matcher.
     125             :   Maybe<Intl::MatcherOption> maybe_locale_matcher =
     126          45 :       Intl::GetLocaleMatcher(isolate, options, "Intl.PluralRules");
     127          45 :   MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSPluralRules>());
     128             :   Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
     129             : 
     130             :   // 7. Let t be ? GetOption(options, "type", "string", « "cardinal",
     131             :   // "ordinal" », "cardinal").
     132             :   Maybe<Type> maybe_type = Intl::GetStringOption<Type>(
     133             :       isolate, options, "type", "Intl.PluralRules", {"cardinal", "ordinal"},
     134         135 :       {Type::CARDINAL, Type::ORDINAL}, Type::CARDINAL);
     135          45 :   MAYBE_RETURN(maybe_type, MaybeHandle<JSPluralRules>());
     136             :   Type type = maybe_type.FromJust();
     137             : 
     138             :   // 8. Set pluralRules.[[Type]] to t.
     139          45 :   plural_rules->set_type(type);
     140             : 
     141             :   // Note: The spec says we should do ResolveLocale after performing
     142             :   // SetNumberFormatDigitOptions but we need the locale to create all
     143             :   // the ICU data structures.
     144             :   //
     145             :   // This isn't observable so we aren't violating the spec.
     146             : 
     147             :   // 11. Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]],
     148             :   // requestedLocales, opt, %PluralRules%.[[RelevantExtensionKeys]],
     149             :   // localeData).
     150             :   Intl::ResolvedLocale r =
     151             :       Intl::ResolveLocale(isolate, JSPluralRules::GetAvailableLocales(),
     152         135 :                           requested_locales, matcher, {});
     153             : 
     154             :   // 12. Set pluralRules.[[Locale]] to the value of r.[[locale]].
     155             :   Handle<String> locale_str =
     156          45 :       isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
     157          45 :   plural_rules->set_locale(*locale_str);
     158             : 
     159          45 :   std::unique_ptr<icu::PluralRules> icu_plural_rules;
     160          45 :   std::unique_ptr<icu::DecimalFormat> icu_decimal_format;
     161             :   InitializeICUPluralRules(isolate, r.icu_locale, type, &icu_plural_rules,
     162          45 :                            &icu_decimal_format);
     163          45 :   CHECK_NOT_NULL(icu_plural_rules.get());
     164          45 :   CHECK_NOT_NULL(icu_decimal_format.get());
     165             : 
     166             :   // 9. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3).
     167             :   Maybe<bool> done = Intl::SetNumberFormatDigitOptions(
     168          45 :       isolate, icu_decimal_format.get(), options, 0, 3);
     169          45 :   MAYBE_RETURN(done, MaybeHandle<JSPluralRules>());
     170             : 
     171             :   Handle<Managed<icu::PluralRules>> managed_plural_rules =
     172             :       Managed<icu::PluralRules>::FromUniquePtr(isolate, 0,
     173          90 :                                                std::move(icu_plural_rules));
     174          45 :   plural_rules->set_icu_plural_rules(*managed_plural_rules);
     175             : 
     176             :   Handle<Managed<icu::DecimalFormat>> managed_decimal_format =
     177             :       Managed<icu::DecimalFormat>::FromUniquePtr(isolate, 0,
     178          90 :                                                  std::move(icu_decimal_format));
     179          45 :   plural_rules->set_icu_decimal_format(*managed_decimal_format);
     180             : 
     181             :   // 13. Return pluralRules.
     182          45 :   return plural_rules;
     183             : }
     184             : 
     185         378 : MaybeHandle<String> JSPluralRules::ResolvePlural(
     186             :     Isolate* isolate, Handle<JSPluralRules> plural_rules, double number) {
     187             :   icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw();
     188         378 :   CHECK_NOT_NULL(icu_plural_rules);
     189             : 
     190             :   icu::DecimalFormat* icu_decimal_format =
     191             :       plural_rules->icu_decimal_format()->raw();
     192         378 :   CHECK_NOT_NULL(icu_decimal_format);
     193             : 
     194             :   // Currently, PluralRules doesn't implement all the options for rounding that
     195             :   // the Intl spec provides; format and parse the number to round to the
     196             :   // appropriate amount, then apply PluralRules.
     197             :   //
     198             :   // TODO(littledan): If a future ICU version supports an extended API to avoid
     199             :   // this step, then switch to that API. Bug thread:
     200             :   // http://bugs.icu-project.org/trac/ticket/12763
     201         378 :   icu::UnicodeString rounded_string;
     202         378 :   icu_decimal_format->format(number, rounded_string);
     203             : 
     204         756 :   icu::Formattable formattable;
     205         378 :   UErrorCode status = U_ZERO_ERROR;
     206         378 :   icu_decimal_format->parse(rounded_string, formattable, status);
     207         378 :   CHECK(U_SUCCESS(status));
     208             : 
     209         378 :   double rounded = formattable.getDouble(status);
     210         378 :   CHECK(U_SUCCESS(status));
     211             : 
     212         756 :   icu::UnicodeString result = icu_plural_rules->select(rounded);
     213             :   return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
     214        1134 :       reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
     215             : }
     216             : 
     217             : namespace {
     218             : 
     219           0 : void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
     220             :                                   Handle<Object> value, const char* key) {
     221           0 :   Handle<String> key_str = isolate->factory()->NewStringFromAsciiChecked(key);
     222             : 
     223             :   // This is a brand new JSObject that shouldn't already have the same
     224             :   // key so this shouldn't fail.
     225           0 :   CHECK(JSReceiver::CreateDataProperty(isolate, options, key_str, value,
     226             :                                        Just(kDontThrow))
     227             :             .FromJust());
     228           0 : }
     229             : 
     230           0 : void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
     231             :                                   int value, const char* key) {
     232             :   Handle<Smi> value_smi(Smi::FromInt(value), isolate);
     233           0 :   CreateDataPropertyForOptions(isolate, options, value_smi, key);
     234           0 : }
     235             : 
     236             : }  // namespace
     237             : 
     238           0 : Handle<JSObject> JSPluralRules::ResolvedOptions(
     239             :     Isolate* isolate, Handle<JSPluralRules> plural_rules) {
     240             :   Handle<JSObject> options =
     241           0 :       isolate->factory()->NewJSObject(isolate->object_function());
     242             : 
     243             :   Handle<String> locale_value(plural_rules->locale(), isolate);
     244           0 :   CreateDataPropertyForOptions(isolate, options, locale_value, "locale");
     245             : 
     246           0 :   CreateDataPropertyForOptions(isolate, options, plural_rules->TypeAsString(),
     247           0 :                                "type");
     248             : 
     249             :   icu::DecimalFormat* icu_decimal_format =
     250             :       plural_rules->icu_decimal_format()->raw();
     251           0 :   CHECK_NOT_NULL(icu_decimal_format);
     252             : 
     253             :   // This is a safe upcast as icu::DecimalFormat inherits from
     254             :   // icu::NumberFormat.
     255             :   icu::NumberFormat* icu_number_format =
     256             :       static_cast<icu::NumberFormat*>(icu_decimal_format);
     257             : 
     258           0 :   int min_int_digits = icu_number_format->getMinimumIntegerDigits();
     259             :   CreateDataPropertyForOptions(isolate, options, min_int_digits,
     260           0 :                                "minimumIntegerDigits");
     261             : 
     262           0 :   int min_fraction_digits = icu_number_format->getMinimumFractionDigits();
     263             :   CreateDataPropertyForOptions(isolate, options, min_fraction_digits,
     264           0 :                                "minimumFractionDigits");
     265             : 
     266           0 :   int max_fraction_digits = icu_number_format->getMaximumFractionDigits();
     267             :   CreateDataPropertyForOptions(isolate, options, max_fraction_digits,
     268           0 :                                "maximumFractionDigits");
     269             : 
     270           0 :   if (icu_decimal_format->areSignificantDigitsUsed()) {
     271             :     int min_significant_digits =
     272           0 :         icu_decimal_format->getMinimumSignificantDigits();
     273             :     CreateDataPropertyForOptions(isolate, options, min_significant_digits,
     274           0 :                                  "minimumSignificantDigits");
     275             : 
     276             :     int max_significant_digits =
     277           0 :         icu_decimal_format->getMaximumSignificantDigits();
     278             :     CreateDataPropertyForOptions(isolate, options, max_significant_digits,
     279           0 :                                  "maximumSignificantDigits");
     280             :   }
     281             : 
     282             :   // 6. Let pluralCategories be a List of Strings representing the
     283             :   // possible results of PluralRuleSelect for the selected locale pr.
     284             :   icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw();
     285           0 :   CHECK_NOT_NULL(icu_plural_rules);
     286             : 
     287           0 :   UErrorCode status = U_ZERO_ERROR;
     288             :   std::unique_ptr<icu::StringEnumeration> categories(
     289           0 :       icu_plural_rules->getKeywords(status));
     290           0 :   CHECK(U_SUCCESS(status));
     291           0 :   int32_t count = categories->count(status);
     292           0 :   CHECK(U_SUCCESS(status));
     293             : 
     294             :   Handle<FixedArray> plural_categories =
     295           0 :       isolate->factory()->NewFixedArray(count);
     296           0 :   for (int32_t i = 0; i < count; i++) {
     297           0 :     const icu::UnicodeString* category = categories->snext(status);
     298           0 :     CHECK(U_SUCCESS(status));
     299           0 :     if (category == nullptr) break;
     300             : 
     301             :     std::string keyword;
     302             :     Handle<String> value = isolate->factory()->NewStringFromAsciiChecked(
     303           0 :         category->toUTF8String(keyword).data());
     304           0 :     plural_categories->set(i, *value);
     305             :   }
     306             : 
     307             :   // 7. Perform ! CreateDataProperty(options, "pluralCategories",
     308             :   // CreateArrayFromList(pluralCategories)).
     309             :   Handle<JSArray> plural_categories_value =
     310             :       isolate->factory()->NewJSArrayWithElements(plural_categories);
     311           0 :   CreateDataPropertyForOptions(isolate, options, plural_categories_value,
     312           0 :                                "pluralCategories");
     313             : 
     314           0 :   return options;
     315             : }
     316             : 
     317         131 : const std::set<std::string>& JSPluralRules::GetAvailableLocales() {
     318             :   // TODO(ftang): For PluralRules, filter out locales that
     319             :   // don't support PluralRules.
     320             :   // PluralRules is missing an appropriate getAvailableLocales method,
     321             :   // so we should filter from all locales, but it's not clear how; see
     322             :   // https://ssl.icu-project.org/trac/ticket/12756
     323         176 :   return Intl::GetAvailableLocalesForLocale();
     324             : }
     325             : 
     326             : }  // namespace internal
     327      122036 : }  // namespace v8

Generated by: LCOV version 1.10