1 // Copyright 2019 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 <memory>
10 #include <vector>
11 
12 #include "src/objects/js-display-names-inl.h"
13 #include "src/objects/js-display-names.h"
14 
15 #include "src/execution/isolate.h"
16 #include "src/heap/factory.h"
17 #include "src/objects/intl-objects.h"
18 #include "src/objects/managed.h"
19 #include "src/objects/objects-inl.h"
20 
21 #include "unicode/dtfmtsym.h"
22 #include "unicode/dtptngen.h"
23 #include "unicode/localebuilder.h"
24 #include "unicode/locdspnm.h"
25 #include "unicode/timezone.h"
26 #include "unicode/tznames.h"
27 #include "unicode/uloc.h"
28 #include "unicode/unistr.h"
29 #include "unicode/uscript.h"
30 
31 namespace v8 {
32 namespace internal {
33 
34 namespace {
35 // Type: identifying the types of the display names.
36 //
37 // ecma402/#sec-properties-of-intl-displaynames-instances
38 enum class Type {
39   kUndefined,
40   kLanguage,
41   kRegion,
42   kScript,
43   kCurrency,
44   kWeekday,
45   kMonth,
46   kQuarter,
47   kDayPeriod,
48   kDateTimeField
49 };
50 
IsUnicodeScriptSubtag(const std::string & value)51 bool IsUnicodeScriptSubtag(const std::string& value) {
52   UErrorCode status = U_ZERO_ERROR;
53   icu::LocaleBuilder builder;
54   builder.setScript(value).build(status);
55   return U_SUCCESS(status);
56 }
57 
IsUnicodeRegionSubtag(const std::string & value)58 bool IsUnicodeRegionSubtag(const std::string& value) {
59   UErrorCode status = U_ZERO_ERROR;
60   icu::LocaleBuilder builder;
61   builder.setRegion(value).build(status);
62   return U_SUCCESS(status);
63 }
64 
ToUDisplayContext(JSDisplayNames::Style style)65 UDisplayContext ToUDisplayContext(JSDisplayNames::Style style) {
66   switch (style) {
67     case JSDisplayNames::Style::kLong:
68       return UDISPCTX_LENGTH_FULL;
69     case JSDisplayNames::Style::kShort:
70     case JSDisplayNames::Style::kNarrow:
71       return UDISPCTX_LENGTH_SHORT;
72   }
73 }
74 
75 }  // anonymous namespace
76 
77 // Abstract class for all different types.
78 class DisplayNamesInternal {
79  public:
80   DisplayNamesInternal() = default;
81   virtual ~DisplayNamesInternal() = default;
82   virtual const char* type() const = 0;
83   virtual icu::Locale locale() const = 0;
84   virtual Maybe<icu::UnicodeString> of(Isolate* isolate,
85                                        const char* code) const = 0;
calendar() const86   virtual const char* calendar() const { return nullptr; }
87 };
88 
89 namespace {
90 
91 class LocaleDisplayNamesCommon : public DisplayNamesInternal {
92  public:
LocaleDisplayNamesCommon(const icu::Locale & locale,JSDisplayNames::Style style,bool fallback)93   LocaleDisplayNamesCommon(const icu::Locale& locale,
94                            JSDisplayNames::Style style, bool fallback)
95       : style_(style) {
96     UDisplayContext sub =
97         fallback ? UDISPCTX_SUBSTITUTE : UDISPCTX_NO_SUBSTITUTE;
98     UDisplayContext display_context[] = {ToUDisplayContext(style_),
99                                          UDISPCTX_DIALECT_NAMES,
100                                          UDISPCTX_CAPITALIZATION_NONE, sub};
101     ldn_.reset(
102         icu::LocaleDisplayNames::createInstance(locale, display_context, 4));
103   }
104 
105   ~LocaleDisplayNamesCommon() override = default;
106 
locale() const107   icu::Locale locale() const override { return ldn_->getLocale(); }
108 
109  protected:
locale_display_names() const110   icu::LocaleDisplayNames* locale_display_names() const { return ldn_.get(); }
111 
112  private:
113   std::unique_ptr<icu::LocaleDisplayNames> ldn_;
114   JSDisplayNames::Style style_;
115 };
116 
117 class LanguageNames : public LocaleDisplayNamesCommon {
118  public:
LanguageNames(const icu::Locale & locale,JSDisplayNames::Style style,bool fallback)119   LanguageNames(const icu::Locale& locale, JSDisplayNames::Style style,
120                 bool fallback)
121       : LocaleDisplayNamesCommon(locale, style, fallback) {}
122   ~LanguageNames() override = default;
type() const123   const char* type() const override { return "language"; }
of(Isolate * isolate,const char * code) const124   Maybe<icu::UnicodeString> of(Isolate* isolate,
125                                const char* code) const override {
126     UErrorCode status = U_ZERO_ERROR;
127     icu::Locale l =
128         icu::Locale(icu::Locale::forLanguageTag(code, status).getBaseName());
129     std::string checked = l.toLanguageTag<std::string>(status);
130 
131     if (U_FAILURE(status)) {
132       THROW_NEW_ERROR_RETURN_VALUE(
133           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
134           Nothing<icu::UnicodeString>());
135     }
136 
137     icu::UnicodeString result;
138     locale_display_names()->localeDisplayName(checked.c_str(), result);
139 
140     return Just(result);
141   }
142 };
143 
144 class RegionNames : public LocaleDisplayNamesCommon {
145  public:
RegionNames(const icu::Locale & locale,JSDisplayNames::Style style,bool fallback)146   RegionNames(const icu::Locale& locale, JSDisplayNames::Style style,
147               bool fallback)
148       : LocaleDisplayNamesCommon(locale, style, fallback) {}
149   ~RegionNames() override = default;
type() const150   const char* type() const override { return "region"; }
of(Isolate * isolate,const char * code) const151   Maybe<icu::UnicodeString> of(Isolate* isolate,
152                                const char* code) const override {
153     std::string code_str(code);
154     if (!IsUnicodeRegionSubtag(code_str)) {
155       THROW_NEW_ERROR_RETURN_VALUE(
156           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
157           Nothing<icu::UnicodeString>());
158     }
159 
160     icu::UnicodeString result;
161     locale_display_names()->regionDisplayName(code_str.c_str(), result);
162     return Just(result);
163   }
164 };
165 
166 class ScriptNames : public LocaleDisplayNamesCommon {
167  public:
ScriptNames(const icu::Locale & locale,JSDisplayNames::Style style,bool fallback)168   ScriptNames(const icu::Locale& locale, JSDisplayNames::Style style,
169               bool fallback)
170       : LocaleDisplayNamesCommon(locale, style, fallback) {}
171   ~ScriptNames() override = default;
type() const172   const char* type() const override { return "script"; }
of(Isolate * isolate,const char * code) const173   Maybe<icu::UnicodeString> of(Isolate* isolate,
174                                const char* code) const override {
175     std::string code_str(code);
176     if (!IsUnicodeScriptSubtag(code_str)) {
177       THROW_NEW_ERROR_RETURN_VALUE(
178           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
179           Nothing<icu::UnicodeString>());
180     }
181 
182     icu::UnicodeString result;
183     locale_display_names()->scriptDisplayName(code_str.c_str(), result);
184     return Just(result);
185   }
186 };
187 
188 class CurrencyNames : public LocaleDisplayNamesCommon {
189  public:
CurrencyNames(const icu::Locale & locale,JSDisplayNames::Style style,bool fallback)190   CurrencyNames(const icu::Locale& locale, JSDisplayNames::Style style,
191                 bool fallback)
192       : LocaleDisplayNamesCommon(locale, style, fallback) {}
193   ~CurrencyNames() override = default;
type() const194   const char* type() const override { return "currency"; }
of(Isolate * isolate,const char * code) const195   Maybe<icu::UnicodeString> of(Isolate* isolate,
196                                const char* code) const override {
197     std::string code_str(code);
198     if (!Intl::IsWellFormedCurrency(code_str)) {
199       THROW_NEW_ERROR_RETURN_VALUE(
200           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
201           Nothing<icu::UnicodeString>());
202     }
203 
204     icu::UnicodeString result;
205     locale_display_names()->keyValueDisplayName("currency", code_str.c_str(),
206                                                 result);
207 
208     return Just(result);
209   }
210 };
211 
StyleToUDateTimePGDisplayWidth(JSDisplayNames::Style style)212 UDateTimePGDisplayWidth StyleToUDateTimePGDisplayWidth(
213     JSDisplayNames::Style style) {
214   switch (style) {
215     case JSDisplayNames::Style::kLong:
216       return UDATPG_WIDE;
217     case JSDisplayNames::Style::kShort:
218       return UDATPG_ABBREVIATED;
219     case JSDisplayNames::Style::kNarrow:
220       return UDATPG_NARROW;
221   }
222 }
223 
StringToUDateTimePatternField(const char * code)224 UDateTimePatternField StringToUDateTimePatternField(const char* code) {
225   switch (code[0]) {
226     case 'd':
227       if (strcmp(code, "day") == 0) return UDATPG_DAY_FIELD;
228       if (strcmp(code, "dayPeriod") == 0) return UDATPG_DAYPERIOD_FIELD;
229       break;
230     case 'e':
231       if (strcmp(code, "era") == 0) return UDATPG_ERA_FIELD;
232       break;
233     case 'h':
234       if (strcmp(code, "hour") == 0) return UDATPG_HOUR_FIELD;
235       break;
236     case 'm':
237       if (strcmp(code, "minute") == 0) return UDATPG_MINUTE_FIELD;
238       if (strcmp(code, "month") == 0) return UDATPG_MONTH_FIELD;
239       break;
240     case 'q':
241       if (strcmp(code, "quarter") == 0) return UDATPG_QUARTER_FIELD;
242       break;
243     case 's':
244       if (strcmp(code, "second") == 0) return UDATPG_SECOND_FIELD;
245       break;
246     case 't':
247       if (strcmp(code, "timeZoneName") == 0) return UDATPG_ZONE_FIELD;
248       break;
249     case 'w':
250       if (strcmp(code, "weekOfYear") == 0) return UDATPG_WEEK_OF_YEAR_FIELD;
251       if (strcmp(code, "weekday") == 0) return UDATPG_WEEKDAY_FIELD;
252       break;
253     case 'y':
254       if (strcmp(code, "year") == 0) return UDATPG_YEAR_FIELD;
255       break;
256     default:
257       break;
258   }
259   UNREACHABLE();
260 }
261 
262 class DateTimeFieldNames : public DisplayNamesInternal {
263  public:
DateTimeFieldNames(const icu::Locale & locale,JSDisplayNames::Style style)264   DateTimeFieldNames(const icu::Locale& locale, JSDisplayNames::Style style)
265       : locale_(locale), width_(StyleToUDateTimePGDisplayWidth(style)) {
266     UErrorCode status = U_ZERO_ERROR;
267     generator_.reset(
268         icu::DateTimePatternGenerator::createInstance(locale_, status));
269     DCHECK(U_SUCCESS(status));
270   }
271   ~DateTimeFieldNames() override = default;
type() const272   const char* type() const override { return "dateTimeField"; }
locale() const273   icu::Locale locale() const override { return locale_; }
of(Isolate * isolate,const char * code) const274   Maybe<icu::UnicodeString> of(Isolate* isolate,
275                                const char* code) const override {
276     UDateTimePatternField field = StringToUDateTimePatternField(code);
277     if (field == UDATPG_FIELD_COUNT) {
278       THROW_NEW_ERROR_RETURN_VALUE(
279           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
280           Nothing<icu::UnicodeString>());
281     }
282     return Just(generator_->getFieldDisplayName(field, width_));
283   }
284 
285  private:
286   icu::Locale locale_;
287   UDateTimePGDisplayWidth width_;
288   std::unique_ptr<icu::DateTimePatternGenerator> generator_;
289 };
290 
StyleToDtWidthType(JSDisplayNames::Style style,Type type)291 icu::DateFormatSymbols::DtWidthType StyleToDtWidthType(
292     JSDisplayNames::Style style, Type type) {
293   switch (style) {
294     case JSDisplayNames::Style::kLong:
295       return icu::DateFormatSymbols::WIDE;
296     case JSDisplayNames::Style::kShort:
297       return icu::DateFormatSymbols::SHORT;
298     case JSDisplayNames::Style::kNarrow:
299       if (type == Type::kQuarter) {
300         return icu::DateFormatSymbols::ABBREVIATED;
301       } else {
302         return icu::DateFormatSymbols::NARROW;
303       }
304   }
305 }
306 
307 class DateFormatSymbolsNames : public DisplayNamesInternal {
308  public:
DateFormatSymbolsNames(const char * type,const icu::Locale & locale,const icu::UnicodeString * array,int32_t length,const char * calendar)309   DateFormatSymbolsNames(const char* type, const icu::Locale& locale,
310                          const icu::UnicodeString* array, int32_t length,
311                          const char* calendar)
312       : type_(type),
313         locale_(locale),
314         array_(array),
315         length_(length),
316         calendar_(calendar) {}
317 
318   ~DateFormatSymbolsNames() override = default;
319 
type() const320   const char* type() const override { return type_; }
321 
locale() const322   icu::Locale locale() const override { return locale_; }
323 
calendar() const324   const char* calendar() const override {
325     if (calendar_.empty()) {
326       return nullptr;
327     }
328     return calendar_.c_str();
329   }
330 
331   virtual int32_t ComputeIndex(const char* code) const = 0;
332 
of(Isolate * isolate,const char * code) const333   Maybe<icu::UnicodeString> of(Isolate* isolate,
334                                const char* code) const override {
335     int32_t index = ComputeIndex(code);
336     if (index < 0 || index >= length_) {
337       THROW_NEW_ERROR_RETURN_VALUE(
338           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
339           Nothing<icu::UnicodeString>());
340     }
341     return Just(array_[index]);
342   }
343 
344  private:
345   const char* type_;
346   icu::Locale locale_;
347   const icu::UnicodeString* array_;
348   int32_t length_;
349   std::string calendar_;
350 };
351 
352 class WeekdayNames : public DateFormatSymbolsNames {
353  public:
WeekdayNames(const char * type,const icu::Locale & locale,const icu::UnicodeString * array,int32_t length,const char * calendar)354   WeekdayNames(const char* type, const icu::Locale& locale,
355                const icu::UnicodeString* array, int32_t length,
356                const char* calendar)
357       : DateFormatSymbolsNames(type, locale, array, length, calendar) {}
358   ~WeekdayNames() override = default;
359 
ComputeIndex(const char * code) const360   int32_t ComputeIndex(const char* code) const override {
361     int32_t i = atoi(code);
362     if (i == 7) return 1;
363     if (i > 0 && i < 7) return i + 1;
364     return -1;
365   }
366 };
367 
368 class MonthNames : public DateFormatSymbolsNames {
369  public:
MonthNames(const char * type,const icu::Locale & locale,const icu::UnicodeString * array,int32_t length,const char * calendar)370   MonthNames(const char* type, const icu::Locale& locale,
371              const icu::UnicodeString* array, int32_t length,
372              const char* calendar)
373       : DateFormatSymbolsNames(type, locale, array, length, calendar) {}
374   ~MonthNames() override = default;
375 
ComputeIndex(const char * code) const376   int32_t ComputeIndex(const char* code) const override {
377     return atoi(code) - 1;
378   }
379 };
380 
381 class QuarterNames : public DateFormatSymbolsNames {
382  public:
QuarterNames(const char * type,const icu::Locale & locale,const icu::UnicodeString * array,int32_t length,const char * calendar)383   QuarterNames(const char* type, const icu::Locale& locale,
384                const icu::UnicodeString* array, int32_t length,
385                const char* calendar)
386       : DateFormatSymbolsNames(type, locale, array, length, calendar) {}
387   ~QuarterNames() override = default;
388 
ComputeIndex(const char * code) const389   int32_t ComputeIndex(const char* code) const override {
390     return atoi(code) - 1;
391   }
392 };
393 
394 class DayPeriodNames : public DateFormatSymbolsNames {
395  public:
DayPeriodNames(const char * type,const icu::Locale & locale,const icu::UnicodeString * array,int32_t length,const char * calendar)396   DayPeriodNames(const char* type, const icu::Locale& locale,
397                  const icu::UnicodeString* array, int32_t length,
398                  const char* calendar)
399       : DateFormatSymbolsNames(type, locale, array, length, calendar) {}
400   ~DayPeriodNames() override = default;
401 
ComputeIndex(const char * code) const402   int32_t ComputeIndex(const char* code) const override {
403     if (strcmp("am", code) == 0) {
404       return 0;
405     } else if (strcmp("pm", code) == 0) {
406       return 1;
407     } else {
408       return -1;
409     }
410   }
411 };
412 
413 const char* gWeekday = "weekday";
414 const char* gMonth = "month";
415 const char* gQuarter = "quarter";
416 const char* gDayPeriod = "dayPeriod";
417 
CreateDateFormatSymbolsNames(const icu::Locale & locale,JSDisplayNames::Style style,Type type)418 DateFormatSymbolsNames* CreateDateFormatSymbolsNames(
419     const icu::Locale& locale, JSDisplayNames::Style style, Type type) {
420   UErrorCode status = U_ZERO_ERROR;
421   std::unique_ptr<icu::DateFormatSymbols> symbols(
422       icu::DateFormatSymbols::createForLocale(locale, status));
423   if (U_FAILURE(status)) {
424     return nullptr;
425   }
426   icu::DateFormatSymbols::DtWidthType width_type =
427       StyleToDtWidthType(style, type);
428   int32_t count = 0;
429   std::string calendar =
430       locale.getUnicodeKeywordValue<std::string>("ca", status);
431 
432   switch (type) {
433     case Type::kMonth:
434       return new MonthNames(
435           gMonth, locale,
436           symbols->getMonths(count, icu::DateFormatSymbols::STANDALONE,
437                              width_type),
438           count, calendar.c_str());
439     case Type::kWeekday:
440       return new WeekdayNames(
441           gWeekday, locale,
442           symbols->getWeekdays(count, icu::DateFormatSymbols::STANDALONE,
443                                width_type),
444           count, calendar.c_str());
445     case Type::kQuarter:
446       return new QuarterNames(
447           gQuarter, locale,
448           symbols->getQuarters(count, icu::DateFormatSymbols::STANDALONE,
449                                width_type),
450           count, calendar.c_str());
451     case Type::kDayPeriod:
452       return new DayPeriodNames(gDayPeriod, locale,
453                                 symbols->getAmPmStrings(count), count,
454                                 calendar.c_str());
455     default:
456       UNREACHABLE();
457   }
458 }
459 
CreateInternal(const icu::Locale & locale,JSDisplayNames::Style style,Type type,bool fallback)460 DisplayNamesInternal* CreateInternal(const icu::Locale& locale,
461                                      JSDisplayNames::Style style, Type type,
462                                      bool fallback) {
463   switch (type) {
464     case Type::kLanguage:
465       return new LanguageNames(locale, style, fallback);
466     case Type::kRegion:
467       return new RegionNames(locale, style, fallback);
468     case Type::kScript:
469       return new ScriptNames(locale, style, fallback);
470     case Type::kCurrency:
471       return new CurrencyNames(locale, style, fallback);
472     case Type::kDateTimeField:
473       return new DateTimeFieldNames(locale, style);
474     case Type::kMonth:
475     case Type::kWeekday:
476     case Type::kQuarter:
477     case Type::kDayPeriod:
478       return CreateDateFormatSymbolsNames(locale, style, type);
479     default:
480       UNREACHABLE();
481   }
482 }
483 
484 }  // anonymous namespace
485 
486 // ecma402 #sec-Intl.DisplayNames
New(Isolate * isolate,Handle<Map> map,Handle<Object> locales,Handle<Object> input_options)487 MaybeHandle<JSDisplayNames> JSDisplayNames::New(Isolate* isolate,
488                                                 Handle<Map> map,
489                                                 Handle<Object> locales,
490                                                 Handle<Object> input_options) {
491   const char* service = "Intl.DisplayNames";
492   Factory* factory = isolate->factory();
493 
494   Handle<JSReceiver> options;
495   // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
496   Maybe<std::vector<std::string>> maybe_requested_locales =
497       Intl::CanonicalizeLocaleList(isolate, locales);
498   MAYBE_RETURN(maybe_requested_locales, Handle<JSDisplayNames>());
499   std::vector<std::string> requested_locales =
500       maybe_requested_locales.FromJust();
501 
502   // 4. Let options be ? ToObject(options).
503   ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
504                              Object::ToObject(isolate, input_options),
505                              JSDisplayNames);
506 
507   // Note: No need to create a record. It's not observable.
508   // 5. Let opt be a new Record.
509 
510   // 6. Let localeData be %DisplayNames%.[[LocaleData]].
511 
512   // 7. Let matcher be ? GetOption(options, "localeMatcher", "string", «
513   // "lookup", "best fit" », "best fit").
514   Maybe<Intl::MatcherOption> maybe_locale_matcher =
515       Intl::GetLocaleMatcher(isolate, options, "Intl.DisplayNames");
516   MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSDisplayNames>());
517 
518   // 8. Set opt.[[localeMatcher]] to matcher.
519   Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
520 
521   std::unique_ptr<char[]> calendar_str = nullptr;
522   if (FLAG_harmony_intl_displaynames_date_types) {
523     const std::vector<const char*> empty_values = {};
524     // Let calendar be ? GetOption(options, "calendar",
525     //    "string", undefined, undefined).
526     Maybe<bool> maybe_calendar = Intl::GetStringOption(
527         isolate, options, "calendar", empty_values, service, &calendar_str);
528     MAYBE_RETURN(maybe_calendar, MaybeHandle<JSDisplayNames>());
529     // If calendar is not undefined, then
530     if (maybe_calendar.FromJust() && calendar_str != nullptr) {
531       // a. If calendar does not match the (3*8alphanum) *("-" (3*8alphanum))
532       //    sequence, throw a RangeError exception.
533       if (!Intl::IsWellFormedCalendar(calendar_str.get())) {
534         THROW_NEW_ERROR(
535             isolate,
536             NewRangeError(
537                 MessageTemplate::kInvalid, factory->calendar_string(),
538                 factory->NewStringFromAsciiChecked(calendar_str.get())),
539             JSDisplayNames);
540       }
541     }
542   }
543 
544   // Set opt.[[ca]] to calendar.
545 
546   // ecma402/#sec-Intl.DisplayNames-internal-slots
547   // The value of the [[RelevantExtensionKeys]] internal slot is
548   // « "ca" ».
549   std::set<std::string> relevant_extension_keys_ca = {"ca"};
550   std::set<std::string> relevant_extension_keys = {};
551   // 9. Let r be ResolveLocale(%DisplayNames%.[[AvailableLocales]],
552   //     requestedLocales, opt, %DisplayNames%.[[RelevantExtensionKeys]]).
553   Maybe<Intl::ResolvedLocale> maybe_resolve_locale = Intl::ResolveLocale(
554       isolate, JSDisplayNames::GetAvailableLocales(), requested_locales,
555       matcher,
556       FLAG_harmony_intl_displaynames_date_types ? relevant_extension_keys_ca
557                                                 : relevant_extension_keys);
558   if (maybe_resolve_locale.IsNothing()) {
559     THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
560                     JSDisplayNames);
561   }
562   Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
563 
564   icu::Locale icu_locale = r.icu_locale;
565   UErrorCode status = U_ZERO_ERROR;
566   if (calendar_str != nullptr &&
567       Intl::IsValidCalendar(icu_locale, calendar_str.get())) {
568     icu_locale.setUnicodeKeywordValue("ca", calendar_str.get(), status);
569     DCHECK(U_SUCCESS(status));
570   }
571 
572   // 10. Let s be ? GetOption(options, "style", "string",
573   //                          «"long", "short", "narrow"», "long").
574   Maybe<Style> maybe_style = Intl::GetStringOption<Style>(
575       isolate, options, "style", "Intl.DisplayNames",
576       {"long", "short", "narrow"},
577       {Style::kLong, Style::kShort, Style::kNarrow}, Style::kLong);
578   MAYBE_RETURN(maybe_style, MaybeHandle<JSDisplayNames>());
579   Style style_enum = maybe_style.FromJust();
580 
581   // 11. Set displayNames.[[Style]] to style.
582 
583   // 12. Let type be ? GetOption(options, "type", "string", « "language",
584   //     "region", "script", "currency", "weekday", "month", "quarter",
585   //     "dayPeriod", "dateTimeField" », undefined).
586   Maybe<Type> maybe_type =
587       FLAG_harmony_intl_displaynames_date_types
588           ? Intl::GetStringOption<Type>(
589                 isolate, options, "type", "Intl.DisplayNames",
590                 {"language", "region", "script", "currency", "weekday", "month",
591                  "quarter", "dayPeriod", "dateTimeField"},
592                 {
593                     Type::kLanguage,
594                     Type::kRegion,
595                     Type::kScript,
596                     Type::kCurrency,
597                     Type::kWeekday,
598                     Type::kMonth,
599                     Type::kQuarter,
600                     Type::kDayPeriod,
601                     Type::kDateTimeField,
602                 },
603                 Type::kUndefined)
604           : Intl::GetStringOption<Type>(
605                 isolate, options, "type", "Intl.DisplayNames",
606                 {"language", "region", "script", "currency"},
607                 {
608                     Type::kLanguage,
609                     Type::kRegion,
610                     Type::kScript,
611                     Type::kCurrency,
612                 },
613                 Type::kUndefined);
614   MAYBE_RETURN(maybe_type, MaybeHandle<JSDisplayNames>());
615   Type type_enum = maybe_type.FromJust();
616 
617   // 13. If type is undefined, throw a TypeError exception.
618   if (type_enum == Type::kUndefined) {
619     THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kInvalidArgument),
620                     JSDisplayNames);
621   }
622 
623   // 14. Set displayNames.[[Type]] to type.
624 
625   // 15. Let fallback be ? GetOption(options, "fallback", "string",
626   //     « "code", "none" », "code").
627   Maybe<Fallback> maybe_fallback = Intl::GetStringOption<Fallback>(
628       isolate, options, "fallback", "Intl.DisplayNames", {"code", "none"},
629       {Fallback::kCode, Fallback::kNone}, Fallback::kCode);
630   MAYBE_RETURN(maybe_fallback, MaybeHandle<JSDisplayNames>());
631   Fallback fallback_enum = maybe_fallback.FromJust();
632 
633   // 16. Set displayNames.[[Fallback]] to fallback.
634 
635   // 17. Set displayNames.[[Locale]] to the value of r.[[Locale]].
636 
637   // Let calendar be r.[[ca]].
638 
639   // Set displayNames.[[Calendar]] to calendar.
640 
641   // Let dataLocale be r.[[dataLocale]].
642 
643   // Let dataLocaleData be localeData.[[<dataLocale>]].
644 
645   // Let types be dataLocaleData.[[types]].
646 
647   // Assert: types is a Record (see 1.3.3).
648 
649   // Let typeFields be types.[[<type>]].
650 
651   // Assert: typeFields is a Record (see 1.3.3).
652 
653   // Let styleFields be typeFields.[[<style>]].
654 
655   // Assert: styleFields is a Record (see 1.3.3).
656 
657   // Set displayNames.[[Fields]] to styleFields.
658 
659   DisplayNamesInternal* internal = CreateInternal(
660       icu_locale, style_enum, type_enum, fallback_enum == Fallback::kCode);
661   if (internal == nullptr) {
662     THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError),
663                     JSDisplayNames);
664   }
665 
666   Handle<Managed<DisplayNamesInternal>> managed_internal =
667       Managed<DisplayNamesInternal>::FromRawPtr(isolate, 0, internal);
668 
669   Handle<JSDisplayNames> display_names =
670       Handle<JSDisplayNames>::cast(factory->NewFastOrSlowJSObjectFromMap(map));
671   display_names->set_flags(0);
672   display_names->set_style(style_enum);
673   display_names->set_fallback(fallback_enum);
674 
675   DisallowHeapAllocation no_gc;
676   display_names->set_internal(*managed_internal);
677 
678   // Return displayNames.
679   return display_names;
680 }
681 
682 // ecma402 #sec-Intl.DisplayNames.prototype.resolvedOptions
ResolvedOptions(Isolate * isolate,Handle<JSDisplayNames> display_names)683 Handle<JSObject> JSDisplayNames::ResolvedOptions(
684     Isolate* isolate, Handle<JSDisplayNames> display_names) {
685   Factory* factory = isolate->factory();
686   // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
687   Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
688 
689   DisplayNamesInternal* internal = display_names->internal().raw();
690 
691   Maybe<std::string> maybe_locale = Intl::ToLanguageTag(internal->locale());
692   DCHECK(maybe_locale.IsJust());
693   Handle<String> locale = isolate->factory()->NewStringFromAsciiChecked(
694       maybe_locale.FromJust().c_str());
695   Handle<String> style = display_names->StyleAsString();
696   Handle<String> type = factory->NewStringFromAsciiChecked(internal->type());
697   Handle<String> fallback = display_names->FallbackAsString();
698 
699   Maybe<bool> maybe_create_locale = JSReceiver::CreateDataProperty(
700       isolate, options, factory->locale_string(), locale, Just(kDontThrow));
701   DCHECK(maybe_create_locale.FromJust());
702   USE(maybe_create_locale);
703   if (internal->calendar() != nullptr) {
704     Maybe<bool> maybe_create_calendar = JSReceiver::CreateDataProperty(
705         isolate, options, factory->calendar_string(),
706         factory->NewStringFromAsciiChecked(internal->calendar()),
707         Just(kDontThrow));
708     DCHECK(maybe_create_calendar.FromJust());
709     USE(maybe_create_calendar);
710   }
711   Maybe<bool> maybe_create_style = JSReceiver::CreateDataProperty(
712       isolate, options, factory->style_string(), style, Just(kDontThrow));
713   DCHECK(maybe_create_style.FromJust());
714   USE(maybe_create_style);
715 
716   Maybe<bool> maybe_create_type = JSReceiver::CreateDataProperty(
717       isolate, options, factory->type_string(), type, Just(kDontThrow));
718   DCHECK(maybe_create_type.FromJust());
719   USE(maybe_create_type);
720 
721   Maybe<bool> maybe_create_fallback = JSReceiver::CreateDataProperty(
722       isolate, options, factory->fallback_string(), fallback, Just(kDontThrow));
723   DCHECK(maybe_create_fallback.FromJust());
724   USE(maybe_create_fallback);
725 
726   return options;
727 }
728 
729 // ecma402 #sec-Intl.DisplayNames.prototype.of
Of(Isolate * isolate,Handle<JSDisplayNames> display_names,Handle<Object> code_obj)730 MaybeHandle<Object> JSDisplayNames::Of(Isolate* isolate,
731                                        Handle<JSDisplayNames> display_names,
732                                        Handle<Object> code_obj) {
733   Handle<String> code;
734   ASSIGN_RETURN_ON_EXCEPTION(isolate, code, Object::ToString(isolate, code_obj),
735                              Object);
736   DisplayNamesInternal* internal = display_names->internal().raw();
737   Maybe<icu::UnicodeString> maybe_result =
738       internal->of(isolate, code->ToCString().get());
739   MAYBE_RETURN(maybe_result, Handle<Object>());
740   icu::UnicodeString result = maybe_result.FromJust();
741   if (result.isBogus()) {
742     return isolate->factory()->undefined_value();
743   }
744   return Intl::ToString(isolate, result).ToHandleChecked();
745 }
746 
747 namespace {
748 
749 struct CheckCalendar {
keyv8::internal::__anondec567e70311::CheckCalendar750   static const char* key() { return "calendar"; }
pathv8::internal::__anondec567e70311::CheckCalendar751   static const char* path() { return nullptr; }
752 };
753 
754 }  // namespace
755 
GetAvailableLocales()756 const std::set<std::string>& JSDisplayNames::GetAvailableLocales() {
757   static base::LazyInstance<Intl::AvailableLocales<CheckCalendar>>::type
758       available_locales = LAZY_INSTANCE_INITIALIZER;
759   return available_locales.Pointer()->Get();
760 }
761 
StyleAsString() const762 Handle<String> JSDisplayNames::StyleAsString() const {
763   switch (style()) {
764     case Style::kLong:
765       return GetReadOnlyRoots().long_string_handle();
766     case Style::kShort:
767       return GetReadOnlyRoots().short_string_handle();
768     case Style::kNarrow:
769       return GetReadOnlyRoots().narrow_string_handle();
770   }
771   UNREACHABLE();
772 }
773 
FallbackAsString() const774 Handle<String> JSDisplayNames::FallbackAsString() const {
775   switch (fallback()) {
776     case Fallback::kCode:
777       return GetReadOnlyRoots().code_string_handle();
778     case Fallback::kNone:
779       return GetReadOnlyRoots().none_string_handle();
780   }
781   UNREACHABLE();
782 }
783 
784 }  // namespace internal
785 }  // namespace v8
786