// 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-relative-time-format.h" #include #include #include #include "src/heap/factory.h" #include "src/isolate.h" #include "src/objects-inl.h" #include "src/objects/intl-objects.h" #include "src/objects/js-relative-time-format-inl.h" #include "src/objects/managed.h" #include "unicode/numfmt.h" #include "unicode/reldatefmt.h" namespace v8 { namespace internal { namespace { UDateRelativeDateTimeFormatterStyle getIcuStyle( JSRelativeTimeFormat::Style style) { switch (style) { case JSRelativeTimeFormat::Style::LONG: return UDAT_STYLE_LONG; case JSRelativeTimeFormat::Style::SHORT: return UDAT_STYLE_SHORT; case JSRelativeTimeFormat::Style::NARROW: return UDAT_STYLE_NARROW; case JSRelativeTimeFormat::Style::COUNT: UNREACHABLE(); } } } // namespace JSRelativeTimeFormat::Style JSRelativeTimeFormat::getStyle(const char* str) { if (strcmp(str, "long") == 0) return JSRelativeTimeFormat::Style::LONG; if (strcmp(str, "short") == 0) return JSRelativeTimeFormat::Style::SHORT; if (strcmp(str, "narrow") == 0) return JSRelativeTimeFormat::Style::NARROW; UNREACHABLE(); } JSRelativeTimeFormat::Numeric JSRelativeTimeFormat::getNumeric( const char* str) { if (strcmp(str, "auto") == 0) return JSRelativeTimeFormat::Numeric::AUTO; if (strcmp(str, "always") == 0) return JSRelativeTimeFormat::Numeric::ALWAYS; UNREACHABLE(); } MaybeHandle JSRelativeTimeFormat::InitializeRelativeTimeFormat( Isolate* isolate, Handle relative_time_format_holder, Handle input_locales, Handle input_options) { Factory* factory = isolate->factory(); relative_time_format_holder->set_flags(0); // 4. If options is undefined, then Handle options; if (input_options->IsUndefined(isolate)) { // a. Let options be ObjectCreate(null). options = isolate->factory()->NewJSObjectWithNullProto(); // 5. Else } else { // a. Let options be ? ToObject(options). ASSIGN_RETURN_ON_EXCEPTION(isolate, options, Object::ToObject(isolate, input_options), JSRelativeTimeFormat); } // 10. Let r be ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]], // requestedLocales, opt, // %RelativeTimeFormat%.[[RelevantExtensionKeys]], // localeData). Handle r; ASSIGN_RETURN_ON_EXCEPTION(isolate, r, Intl::ResolveLocale(isolate, "relativetimeformat", input_locales, options), JSRelativeTimeFormat); Handle locale_obj = JSObject::GetDataProperty(r, factory->locale_string()); Handle locale; ASSIGN_RETURN_ON_EXCEPTION(isolate, locale, Object::ToString(isolate, locale_obj), JSRelativeTimeFormat); // 11. Let locale be r.[[Locale]]. // 12. Set relativeTimeFormat.[[Locale]] to locale. relative_time_format_holder->set_locale(*locale); // 14. Let s be ? GetOption(options, "style", "string", // «"long", "short", "narrow"», "long"). std::unique_ptr style_str = nullptr; std::vector style_values = {"long", "short", "narrow"}; Maybe maybe_found_style = Intl::GetStringOption(isolate, options, "style", style_values, "Intl.RelativeTimeFormat", &style_str); Style style_enum = Style::LONG; MAYBE_RETURN(maybe_found_style, MaybeHandle()); if (maybe_found_style.FromJust()) { DCHECK_NOT_NULL(style_str.get()); style_enum = getStyle(style_str.get()); } // 15. Set relativeTimeFormat.[[Style]] to s. relative_time_format_holder->set_style(style_enum); // 16. Let numeric be ? GetOption(options, "numeric", "string", // «"always", "auto"», "always"). std::unique_ptr numeric_str = nullptr; std::vector numeric_values = {"always", "auto"}; Maybe maybe_found_numeric = Intl::GetStringOption(isolate, options, "numeric", numeric_values, "Intl.RelativeTimeFormat", &numeric_str); Numeric numeric_enum = Numeric::ALWAYS; MAYBE_RETURN(maybe_found_numeric, MaybeHandle()); if (maybe_found_numeric.FromJust()) { DCHECK_NOT_NULL(numeric_str.get()); numeric_enum = getNumeric(numeric_str.get()); } // 17. Set relativeTimeFormat.[[Numeric]] to numeric. relative_time_format_holder->set_numeric(numeric_enum); std::unique_ptr locale_name = locale->ToCString(); icu::Locale icu_locale(locale_name.get()); UErrorCode status = U_ZERO_ERROR; // 25. Let relativeTimeFormat.[[NumberFormat]] be // ? Construct(%NumberFormat%, « nfLocale, nfOptions »). icu::NumberFormat* number_format = icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status); if (U_FAILURE(status)) { delete number_format; FATAL("Failed to create ICU number format, are ICU data files missing?"); } CHECK_NOT_NULL(number_format); // Change UDISPCTX_CAPITALIZATION_NONE to other values if // ECMA402 later include option to change capitalization. // Ref: https://github.com/tc39/proposal-intl-relative-time/issues/11 icu::RelativeDateTimeFormatter* icu_formatter = new icu::RelativeDateTimeFormatter(icu_locale, number_format, getIcuStyle(style_enum), UDISPCTX_CAPITALIZATION_NONE, status); if (U_FAILURE(status)) { delete icu_formatter; FATAL( "Failed to create ICU relative date time formatter, are ICU data files " "missing?"); } CHECK_NOT_NULL(icu_formatter); Handle> managed_formatter = Managed::FromRawPtr(isolate, 0, icu_formatter); // 30. Set relativeTimeFormat.[[InitializedRelativeTimeFormat]] to true. relative_time_format_holder->set_formatter(*managed_formatter); // 31. Return relativeTimeFormat. return relative_time_format_holder; } Handle JSRelativeTimeFormat::ResolvedOptions( Isolate* isolate, Handle format_holder) { Factory* factory = isolate->factory(); Handle result = factory->NewJSObject(isolate->object_function()); Handle 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->numeric_string(), format_holder->NumericAsString(), NONE); return result; } icu::RelativeDateTimeFormatter* JSRelativeTimeFormat::UnpackFormatter( Handle holder) { return Managed::cast(holder->formatter()) ->raw(); } Handle JSRelativeTimeFormat::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 JSRelativeTimeFormat::NumericAsString() const { switch (numeric()) { case Numeric::ALWAYS: return GetReadOnlyRoots().always_string_handle(); case Numeric::AUTO: return GetReadOnlyRoots().auto_string_handle(); case Numeric::COUNT: UNREACHABLE(); } } } // namespace internal } // namespace v8