| // 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-list-format.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "src/heap/factory.h" |
| #include "src/isolate.h" |
| #include "src/objects-inl.h" |
| #include "src/objects/intl-objects.h" |
| #include "src/objects/js-list-format-inl.h" |
| #include "unicode/listformatter.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| const char* kStandard = "standard"; |
| const char* kOr = "or"; |
| const char* kUnit = "unit"; |
| const char* kStandardShort = "standard-short"; |
| const char* kUnitShort = "unit-short"; |
| const char* kUnitNarrow = "unit-narrow"; |
| |
| const char* GetIcuStyleString(JSListFormat::Style style, |
| JSListFormat::Type type) { |
| switch (type) { |
| case JSListFormat::Type::CONJUNCTION: |
| switch (style) { |
| case JSListFormat::Style::LONG: |
| return kStandard; |
| case JSListFormat::Style::SHORT: |
| return kStandardShort; |
| case JSListFormat::Style::NARROW: |
| // Currently, ListFormat::createInstance on "standard-narrow" will |
| // fail so we use "standard-short" here. |
| // See https://unicode.org/cldr/trac/ticket/11254 |
| // TODO(ftang): change to return kStandardNarrow; after the above |
| // issue fixed in CLDR/ICU. |
| // CLDR bug: https://unicode.org/cldr/trac/ticket/11254 |
| // ICU bug: https://unicode-org.atlassian.net/browse/ICU-20014 |
| return kStandardShort; |
| case JSListFormat::Style::COUNT: |
| UNREACHABLE(); |
| } |
| case JSListFormat::Type::DISJUNCTION: |
| switch (style) { |
| // Currently, ListFormat::createInstance on "or-short" and "or-narrow" |
| // will fail so we use "or" here. |
| // See https://unicode.org/cldr/trac/ticket/11254 |
| // TODO(ftang): change to return kOr, kOrShort or kOrNarrow depend on |
| // style after the above issue fixed in CLDR/ICU. |
| // CLDR bug: https://unicode.org/cldr/trac/ticket/11254 |
| // ICU bug: https://unicode-org.atlassian.net/browse/ICU-20014 |
| case JSListFormat::Style::LONG: |
| case JSListFormat::Style::SHORT: |
| case JSListFormat::Style::NARROW: |
| return kOr; |
| case JSListFormat::Style::COUNT: |
| UNREACHABLE(); |
| } |
| case JSListFormat::Type::UNIT: |
| switch (style) { |
| case JSListFormat::Style::LONG: |
| return kUnit; |
| case JSListFormat::Style::SHORT: |
| return kUnitShort; |
| case JSListFormat::Style::NARROW: |
| return kUnitNarrow; |
| case JSListFormat::Style::COUNT: |
| UNREACHABLE(); |
| } |
| case JSListFormat::Type::COUNT: |
| UNREACHABLE(); |
| } |
| } |
| |
| } // namespace |
| |
| JSListFormat::Style get_style(const char* str) { |
| switch (str[0]) { |
| case 'n': |
| if (strcmp(&str[1], "arrow") == 0) return JSListFormat::Style::NARROW; |
| break; |
| case 'l': |
| if (strcmp(&str[1], "ong") == 0) return JSListFormat::Style::LONG; |
| break; |
| case 's': |
| if (strcmp(&str[1], "hort") == 0) return JSListFormat::Style::SHORT; |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| JSListFormat::Type get_type(const char* str) { |
| switch (str[0]) { |
| case 'c': |
| if (strcmp(&str[1], "onjunction") == 0) |
| return JSListFormat::Type::CONJUNCTION; |
| break; |
| case 'd': |
| if (strcmp(&str[1], "isjunction") == 0) |
| return JSListFormat::Type::DISJUNCTION; |
| break; |
| case 'u': |
| if (strcmp(&str[1], "nit") == 0) return JSListFormat::Type::UNIT; |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| MaybeHandle<JSListFormat> JSListFormat::InitializeListFormat( |
| Isolate* isolate, Handle<JSListFormat> list_format_holder, |
| Handle<Object> input_locales, Handle<Object> input_options) { |
| Factory* factory = isolate->factory(); |
| list_format_holder->set_flags(0); |
| |
| Handle<JSReceiver> options; |
| // 2. If options is undefined, then |
| if (input_options->IsUndefined(isolate)) { |
| // a. Let options be ObjectCreate(null). |
| options = isolate->factory()->NewJSObjectWithNullProto(); |
| // 3. Else |
| } else { |
| // a. Let options be ? ToObject(options). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, options, |
| Object::ToObject(isolate, input_options), |
| JSListFormat); |
| } |
| |
| // 5. Let t be GetOption(options, "type", "string", «"conjunction", |
| // "disjunction", "unit"», "conjunction"). |
| std::unique_ptr<char[]> type_str = nullptr; |
| std::vector<const char*> type_values = {"conjunction", "disjunction", "unit"}; |
| Maybe<bool> maybe_found_type = Intl::GetStringOption( |
| isolate, options, "type", type_values, "Intl.ListFormat", &type_str); |
| Type type_enum = Type::CONJUNCTION; |
| MAYBE_RETURN(maybe_found_type, MaybeHandle<JSListFormat>()); |
| if (maybe_found_type.FromJust()) { |
| DCHECK_NOT_NULL(type_str.get()); |
| type_enum = get_type(type_str.get()); |
| } |
| // 6. Set listFormat.[[Type]] to t. |
| list_format_holder->set_type(type_enum); |
| |
| // 7. Let s be ? GetOption(options, "style", "string", |
| // «"long", "short", "narrow"», "long"). |
| std::unique_ptr<char[]> style_str = nullptr; |
| std::vector<const char*> style_values = {"long", "short", "narrow"}; |
| Maybe<bool> maybe_found_style = Intl::GetStringOption( |
| isolate, options, "style", style_values, "Intl.ListFormat", &style_str); |
| Style style_enum = Style::LONG; |
| MAYBE_RETURN(maybe_found_style, MaybeHandle<JSListFormat>()); |
| if (maybe_found_style.FromJust()) { |
| DCHECK_NOT_NULL(style_str.get()); |
| style_enum = get_style(style_str.get()); |
| } |
| // 15. Set listFormat.[[Style]] to s. |
| list_format_holder->set_style(style_enum); |
| |
| // 10. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]], |
| // requestedLocales, opt, undefined, localeData). |
| Handle<JSObject> r; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, r, |
| Intl::ResolveLocale(isolate, "listformat", input_locales, options), |
| JSListFormat); |
| |
| Handle<Object> locale_obj = |
| JSObject::GetDataProperty(r, factory->locale_string()); |
| Handle<String> locale; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, locale, Object::ToString(isolate, locale_obj), JSListFormat); |
| |
| // 18. Set listFormat.[[Locale]] to the value of r.[[Locale]]. |
| list_format_holder->set_locale(*locale); |
| |
| std::unique_ptr<char[]> locale_name = locale->ToCString(); |
| icu::Locale icu_locale(locale_name.get()); |
| UErrorCode status = U_ZERO_ERROR; |
| icu::ListFormatter* formatter = icu::ListFormatter::createInstance( |
| icu_locale, GetIcuStyleString(style_enum, type_enum), status); |
| if (U_FAILURE(status)) { |
| delete formatter; |
| FATAL("Failed to create ICU list formatter, are ICU data files missing?"); |
| } |
| CHECK_NOT_NULL(formatter); |
| |
| Handle<Managed<icu::ListFormatter>> managed_formatter = |
| Managed<icu::ListFormatter>::FromRawPtr(isolate, 0, formatter); |
| |
| list_format_holder->set_formatter(*managed_formatter); |
| return list_format_holder; |
| } |
| |
| Handle<JSObject> JSListFormat::ResolvedOptions( |
| Isolate* isolate, Handle<JSListFormat> format_holder) { |
| Factory* factory = isolate->factory(); |
| Handle<JSObject> result = factory->NewJSObject(isolate->object_function()); |
| Handle<String> locale(format_holder->locale(), isolate); |
| JSObject::AddProperty(isolate, result, factory->locale_string(), locale, |
| NONE); |
| JSObject::AddProperty(isolate, result, factory->style_string(), |
| format_holder->StyleAsString(), NONE); |
| JSObject::AddProperty(isolate, result, factory->type_string(), |
| format_holder->TypeAsString(), NONE); |
| return result; |
| } |
| |
| icu::ListFormatter* JSListFormat::UnpackFormatter(Isolate* isolate, |
| Handle<JSListFormat> holder) { |
| return Managed<icu::ListFormatter>::cast(holder->formatter())->raw(); |
| } |
| |
| Handle<String> JSListFormat::StyleAsString() const { |
| switch (style()) { |
| case Style::LONG: |
| return GetReadOnlyRoots().long_string_handle(); |
| case Style::SHORT: |
| return GetReadOnlyRoots().short_string_handle(); |
| case Style::NARROW: |
| return GetReadOnlyRoots().narrow_string_handle(); |
| case Style::COUNT: |
| UNREACHABLE(); |
| } |
| } |
| |
| Handle<String> JSListFormat::TypeAsString() const { |
| switch (type()) { |
| case Type::CONJUNCTION: |
| return GetReadOnlyRoots().conjunction_string_handle(); |
| case Type::DISJUNCTION: |
| return GetReadOnlyRoots().disjunction_string_handle(); |
| case Type::UNIT: |
| return GetReadOnlyRoots().unit_string_handle(); |
| case Type::COUNT: |
| UNREACHABLE(); |
| } |
| } |
| |
| } // namespace internal |
| } // namespace v8 |