• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-relative-time-format.h"
10 
11 #include <map>
12 #include <memory>
13 #include <string>
14 
15 #include "src/heap/factory.h"
16 #include "src/isolate.h"
17 #include "src/objects-inl.h"
18 #include "src/objects/intl-objects.h"
19 #include "src/objects/js-relative-time-format-inl.h"
20 #include "src/objects/managed.h"
21 #include "unicode/numfmt.h"
22 #include "unicode/reldatefmt.h"
23 
24 namespace v8 {
25 namespace internal {
26 
27 namespace {
getIcuStyle(JSRelativeTimeFormat::Style style)28 UDateRelativeDateTimeFormatterStyle getIcuStyle(
29     JSRelativeTimeFormat::Style style) {
30   switch (style) {
31     case JSRelativeTimeFormat::Style::LONG:
32       return UDAT_STYLE_LONG;
33     case JSRelativeTimeFormat::Style::SHORT:
34       return UDAT_STYLE_SHORT;
35     case JSRelativeTimeFormat::Style::NARROW:
36       return UDAT_STYLE_NARROW;
37     case JSRelativeTimeFormat::Style::COUNT:
38       UNREACHABLE();
39   }
40 }
41 }  // namespace
42 
getStyle(const char * str)43 JSRelativeTimeFormat::Style JSRelativeTimeFormat::getStyle(const char* str) {
44   if (strcmp(str, "long") == 0) return JSRelativeTimeFormat::Style::LONG;
45   if (strcmp(str, "short") == 0) return JSRelativeTimeFormat::Style::SHORT;
46   if (strcmp(str, "narrow") == 0) return JSRelativeTimeFormat::Style::NARROW;
47   UNREACHABLE();
48 }
49 
getNumeric(const char * str)50 JSRelativeTimeFormat::Numeric JSRelativeTimeFormat::getNumeric(
51     const char* str) {
52   if (strcmp(str, "auto") == 0) return JSRelativeTimeFormat::Numeric::AUTO;
53   if (strcmp(str, "always") == 0) return JSRelativeTimeFormat::Numeric::ALWAYS;
54   UNREACHABLE();
55 }
56 
57 MaybeHandle<JSRelativeTimeFormat>
InitializeRelativeTimeFormat(Isolate * isolate,Handle<JSRelativeTimeFormat> relative_time_format_holder,Handle<Object> input_locales,Handle<Object> input_options)58 JSRelativeTimeFormat::InitializeRelativeTimeFormat(
59     Isolate* isolate, Handle<JSRelativeTimeFormat> relative_time_format_holder,
60     Handle<Object> input_locales, Handle<Object> input_options) {
61   Factory* factory = isolate->factory();
62   relative_time_format_holder->set_flags(0);
63   // 4. If options is undefined, then
64   Handle<JSReceiver> options;
65   if (input_options->IsUndefined(isolate)) {
66     // a. Let options be ObjectCreate(null).
67     options = isolate->factory()->NewJSObjectWithNullProto();
68     // 5. Else
69   } else {
70     // a. Let options be ? ToObject(options).
71     ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
72                                Object::ToObject(isolate, input_options),
73                                JSRelativeTimeFormat);
74   }
75 
76   // 10. Let r be ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]],
77   //                            requestedLocales, opt,
78   //                            %RelativeTimeFormat%.[[RelevantExtensionKeys]],
79   //                            localeData).
80   Handle<JSObject> r;
81   ASSIGN_RETURN_ON_EXCEPTION(isolate, r,
82                              Intl::ResolveLocale(isolate, "relativetimeformat",
83                                                  input_locales, options),
84                              JSRelativeTimeFormat);
85   Handle<Object> locale_obj =
86       JSObject::GetDataProperty(r, factory->locale_string());
87   Handle<String> locale;
88   ASSIGN_RETURN_ON_EXCEPTION(isolate, locale,
89                              Object::ToString(isolate, locale_obj),
90                              JSRelativeTimeFormat);
91 
92   // 11. Let locale be r.[[Locale]].
93   // 12. Set relativeTimeFormat.[[Locale]] to locale.
94   relative_time_format_holder->set_locale(*locale);
95 
96   // 14. Let s be ? GetOption(options, "style", "string",
97   //                          «"long", "short", "narrow"», "long").
98   std::unique_ptr<char[]> style_str = nullptr;
99   std::vector<const char*> style_values = {"long", "short", "narrow"};
100   Maybe<bool> maybe_found_style =
101       Intl::GetStringOption(isolate, options, "style", style_values,
102                             "Intl.RelativeTimeFormat", &style_str);
103   Style style_enum = Style::LONG;
104   MAYBE_RETURN(maybe_found_style, MaybeHandle<JSRelativeTimeFormat>());
105   if (maybe_found_style.FromJust()) {
106     DCHECK_NOT_NULL(style_str.get());
107     style_enum = getStyle(style_str.get());
108   }
109 
110   // 15. Set relativeTimeFormat.[[Style]] to s.
111   relative_time_format_holder->set_style(style_enum);
112 
113   // 16. Let numeric be ? GetOption(options, "numeric", "string",
114   //                                «"always", "auto"», "always").
115   std::unique_ptr<char[]> numeric_str = nullptr;
116   std::vector<const char*> numeric_values = {"always", "auto"};
117   Maybe<bool> maybe_found_numeric =
118       Intl::GetStringOption(isolate, options, "numeric", numeric_values,
119                             "Intl.RelativeTimeFormat", &numeric_str);
120   Numeric numeric_enum = Numeric::ALWAYS;
121   MAYBE_RETURN(maybe_found_numeric, MaybeHandle<JSRelativeTimeFormat>());
122   if (maybe_found_numeric.FromJust()) {
123     DCHECK_NOT_NULL(numeric_str.get());
124     numeric_enum = getNumeric(numeric_str.get());
125   }
126 
127   // 17. Set relativeTimeFormat.[[Numeric]] to numeric.
128   relative_time_format_holder->set_numeric(numeric_enum);
129 
130   std::unique_ptr<char[]> locale_name = locale->ToCString();
131   icu::Locale icu_locale(locale_name.get());
132   UErrorCode status = U_ZERO_ERROR;
133 
134   // 25. Let relativeTimeFormat.[[NumberFormat]] be
135   //     ? Construct(%NumberFormat%, « nfLocale, nfOptions »).
136   icu::NumberFormat* number_format =
137       icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status);
138   if (U_FAILURE(status)) {
139     delete number_format;
140     FATAL("Failed to create ICU number format, are ICU data files missing?");
141   }
142   CHECK_NOT_NULL(number_format);
143 
144   // Change UDISPCTX_CAPITALIZATION_NONE to other values if
145   // ECMA402 later include option to change capitalization.
146   // Ref: https://github.com/tc39/proposal-intl-relative-time/issues/11
147   icu::RelativeDateTimeFormatter* icu_formatter =
148       new icu::RelativeDateTimeFormatter(icu_locale, number_format,
149                                          getIcuStyle(style_enum),
150                                          UDISPCTX_CAPITALIZATION_NONE, status);
151   if (U_FAILURE(status)) {
152     delete icu_formatter;
153     FATAL(
154         "Failed to create ICU relative date time formatter, are ICU data files "
155         "missing?");
156   }
157   CHECK_NOT_NULL(icu_formatter);
158 
159   Handle<Managed<icu::RelativeDateTimeFormatter>> managed_formatter =
160       Managed<icu::RelativeDateTimeFormatter>::FromRawPtr(isolate, 0,
161                                                           icu_formatter);
162 
163   // 30. Set relativeTimeFormat.[[InitializedRelativeTimeFormat]] to true.
164   relative_time_format_holder->set_formatter(*managed_formatter);
165   // 31. Return relativeTimeFormat.
166   return relative_time_format_holder;
167 }
168 
ResolvedOptions(Isolate * isolate,Handle<JSRelativeTimeFormat> format_holder)169 Handle<JSObject> JSRelativeTimeFormat::ResolvedOptions(
170     Isolate* isolate, Handle<JSRelativeTimeFormat> format_holder) {
171   Factory* factory = isolate->factory();
172   Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
173   Handle<String> locale(format_holder->locale(), isolate);
174   JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
175                         NONE);
176   JSObject::AddProperty(isolate, result, factory->style_string(),
177                         format_holder->StyleAsString(), NONE);
178   JSObject::AddProperty(isolate, result, factory->numeric_string(),
179                         format_holder->NumericAsString(), NONE);
180   return result;
181 }
182 
UnpackFormatter(Handle<JSRelativeTimeFormat> holder)183 icu::RelativeDateTimeFormatter* JSRelativeTimeFormat::UnpackFormatter(
184     Handle<JSRelativeTimeFormat> holder) {
185   return Managed<icu::RelativeDateTimeFormatter>::cast(holder->formatter())
186       ->raw();
187 }
188 
StyleAsString() const189 Handle<String> JSRelativeTimeFormat::StyleAsString() const {
190   switch (style()) {
191     case Style::LONG:
192       return GetReadOnlyRoots().long_string_handle();
193     case Style::SHORT:
194       return GetReadOnlyRoots().short_string_handle();
195     case Style::NARROW:
196       return GetReadOnlyRoots().narrow_string_handle();
197     case Style::COUNT:
198       UNREACHABLE();
199   }
200 }
201 
NumericAsString() const202 Handle<String> JSRelativeTimeFormat::NumericAsString() const {
203   switch (numeric()) {
204     case Numeric::ALWAYS:
205       return GetReadOnlyRoots().always_string_handle();
206     case Numeric::AUTO:
207       return GetReadOnlyRoots().auto_string_handle();
208     case Numeric::COUNT:
209       UNREACHABLE();
210   }
211 }
212 
213 }  // namespace internal
214 }  // namespace v8
215