// 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 <map>
|
#include <memory>
|
#include <string>
|
|
#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>
|
JSRelativeTimeFormat::InitializeRelativeTimeFormat(
|
Isolate* isolate, Handle<JSRelativeTimeFormat> relative_time_format_holder,
|
Handle<Object> input_locales, Handle<Object> input_options) {
|
Factory* factory = isolate->factory();
|
relative_time_format_holder->set_flags(0);
|
// 4. If options is undefined, then
|
Handle<JSReceiver> 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<JSObject> r;
|
ASSIGN_RETURN_ON_EXCEPTION(isolate, r,
|
Intl::ResolveLocale(isolate, "relativetimeformat",
|
input_locales, options),
|
JSRelativeTimeFormat);
|
Handle<Object> locale_obj =
|
JSObject::GetDataProperty(r, factory->locale_string());
|
Handle<String> 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<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.RelativeTimeFormat", &style_str);
|
Style style_enum = Style::LONG;
|
MAYBE_RETURN(maybe_found_style, MaybeHandle<JSRelativeTimeFormat>());
|
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<char[]> numeric_str = nullptr;
|
std::vector<const char*> numeric_values = {"always", "auto"};
|
Maybe<bool> 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<JSRelativeTimeFormat>());
|
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<char[]> 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<icu::RelativeDateTimeFormatter>> managed_formatter =
|
Managed<icu::RelativeDateTimeFormatter>::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<JSObject> JSRelativeTimeFormat::ResolvedOptions(
|
Isolate* isolate, Handle<JSRelativeTimeFormat> 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->numeric_string(),
|
format_holder->NumericAsString(), NONE);
|
return result;
|
}
|
|
icu::RelativeDateTimeFormatter* JSRelativeTimeFormat::UnpackFormatter(
|
Handle<JSRelativeTimeFormat> holder) {
|
return Managed<icu::RelativeDateTimeFormatter>::cast(holder->formatter())
|
->raw();
|
}
|
|
Handle<String> 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<String> 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
|