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 "src/objects/js-display-names.h"
10 
11 #include <memory>
12 #include <vector>
13 
14 #include "src/execution/isolate.h"
15 #include "src/heap/factory.h"
16 #include "src/objects/intl-objects.h"
17 #include "src/objects/js-display-names-inl.h"
18 #include "src/objects/managed-inl.h"
19 #include "src/objects/objects-inl.h"
20 #include "src/objects/option-utils.h"
21 #include "unicode/dtfmtsym.h"
22 #include "unicode/dtptngen.h"
23 #include "unicode/localebuilder.h"
24 #include "unicode/locdspnm.h"
25 #include "unicode/measfmt.h"
26 #include "unicode/timezone.h"
27 #include "unicode/tznames.h"
28 #include "unicode/uloc.h"
29 #include "unicode/unistr.h"
30 #include "unicode/uscript.h"
31 
32 namespace v8 {
33 namespace internal {
34 
35 namespace {
36 // Type: identifying the types of the display names.
37 //
38 // ecma402/#sec-properties-of-intl-displaynames-instances
39 enum class Type {
40   kUndefined,
41   kLanguage,
42   kRegion,
43   kScript,
44   kCurrency,
45   kCalendar,
46   kDateTimeField
47 };
48 
IsUnicodeScriptSubtag(const std::string& value)49 bool IsUnicodeScriptSubtag(const std::string& value) {
50   UErrorCode status = U_ZERO_ERROR;
51   icu::LocaleBuilder builder;
52   builder.setScript(value).build(status);
53   return U_SUCCESS(status);
54 }
55 
IsUnicodeRegionSubtag(const std::string& value)56 bool IsUnicodeRegionSubtag(const std::string& value) {
57   UErrorCode status = U_ZERO_ERROR;
58   icu::LocaleBuilder builder;
59   builder.setRegion(value).build(status);
60   return U_SUCCESS(status);
61 }
62 
ToUDisplayContext(JSDisplayNames::Style style)63 UDisplayContext ToUDisplayContext(JSDisplayNames::Style style) {
64   switch (style) {
65     case JSDisplayNames::Style::kLong:
66       return UDISPCTX_LENGTH_FULL;
67     case JSDisplayNames::Style::kShort:
68     case JSDisplayNames::Style::kNarrow:
69       return UDISPCTX_LENGTH_SHORT;
70   }
71 }
72 
73 }  // anonymous namespace
74 
75 // Abstract class for all different types.
76 class DisplayNamesInternal {
77  public:
78   DisplayNamesInternal() = default;
79   virtual ~DisplayNamesInternal() = default;
80   virtual const char* type() const = 0;
81   virtual icu::Locale locale() const = 0;
82   virtual Maybe<icu::UnicodeString> of(Isolate* isolate,
83                                        const char* code) const = 0;
84 };
85 
86 namespace {
87 
88 class LocaleDisplayNamesCommon : public DisplayNamesInternal {
89  public:
LocaleDisplayNamesCommon(const icu::Locale& locale, JSDisplayNames::Style style, bool fallback, bool dialect)90   LocaleDisplayNamesCommon(const icu::Locale& locale,
91                            JSDisplayNames::Style style, bool fallback,
92                            bool dialect)
93       : style_(style) {
94     UDisplayContext sub =
95         fallback ? UDISPCTX_SUBSTITUTE : UDISPCTX_NO_SUBSTITUTE;
96     UDisplayContext dialect_context =
97         dialect ? UDISPCTX_DIALECT_NAMES : UDISPCTX_STANDARD_NAMES;
98     UDisplayContext display_context[] = {ToUDisplayContext(style_),
99                                          dialect_context,
100                                          UDISPCTX_CAPITALIZATION_NONE, sub};
101     ldn_.reset(
102         icu::LocaleDisplayNames::createInstance(locale, display_context, 4));
103   }
104 
105   ~LocaleDisplayNamesCommon() override = default;
106 
107   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, bool dialect)119   LanguageNames(const icu::Locale& locale, JSDisplayNames::Style style,
120                 bool fallback, bool dialect)
121       : LocaleDisplayNamesCommon(locale, style, fallback, dialect) {}
122 
123   ~LanguageNames() override = default;
124 
125   const char* type() const override { return "language"; }
126 
127   Maybe<icu::UnicodeString> of(Isolate* isolate,
128                                const char* code) const override {
129     UErrorCode status = U_ZERO_ERROR;
130     // 1.a If code does not match the unicode_language_id production, throw a
131     // RangeError exception.
132 
133     // 1.b If IsStructurallyValidLanguageTag(code) is false, throw a RangeError
134     // exception.
135     icu::Locale l =
136         icu::Locale(icu::Locale::forLanguageTag(code, status).getBaseName());
137     // 1.c Set code to CanonicalizeUnicodeLocaleId(code).
138     l.canonicalize(status);
139     std::string checked = l.toLanguageTag<std::string>(status);
140 
141     if (U_FAILURE(status)) {
142       THROW_NEW_ERROR_RETURN_VALUE(
143           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
144           Nothing<icu::UnicodeString>());
145     }
146 
147     icu::UnicodeString result;
148     locale_display_names()->localeDisplayName(checked.c_str(), result);
149 
150     return Just(result);
151   }
152 };
153 
154 class RegionNames : public LocaleDisplayNamesCommon {
155  public:
RegionNames(const icu::Locale& locale, JSDisplayNames::Style style, bool fallback, bool dialect)156   RegionNames(const icu::Locale& locale, JSDisplayNames::Style style,
157               bool fallback, bool dialect)
158       : LocaleDisplayNamesCommon(locale, style, fallback, dialect) {}
159 
160   ~RegionNames() override = default;
161 
162   const char* type() const override { return "region"; }
163 
164   Maybe<icu::UnicodeString> of(Isolate* isolate,
165                                const char* code) const override {
166     std::string code_str(code);
167     if (!IsUnicodeRegionSubtag(code_str)) {
168       THROW_NEW_ERROR_RETURN_VALUE(
169           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
170           Nothing<icu::UnicodeString>());
171     }
172 
173     icu::UnicodeString result;
174     locale_display_names()->regionDisplayName(code_str.c_str(), result);
175     return Just(result);
176   }
177 };
178 
179 class ScriptNames : public LocaleDisplayNamesCommon {
180  public:
ScriptNames(const icu::Locale& locale, JSDisplayNames::Style style, bool fallback, bool dialect)181   ScriptNames(const icu::Locale& locale, JSDisplayNames::Style style,
182               bool fallback, bool dialect)
183       : LocaleDisplayNamesCommon(locale, style, fallback, dialect) {}
184 
185   ~ScriptNames() override = default;
186 
187   const char* type() const override { return "script"; }
188 
189   Maybe<icu::UnicodeString> of(Isolate* isolate,
190                                const char* code) const override {
191     std::string code_str(code);
192     if (!IsUnicodeScriptSubtag(code_str)) {
193       THROW_NEW_ERROR_RETURN_VALUE(
194           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
195           Nothing<icu::UnicodeString>());
196     }
197 
198     icu::UnicodeString result;
199     locale_display_names()->scriptDisplayName(code_str.c_str(), result);
200     return Just(result);
201   }
202 };
203 
204 class KeyValueDisplayNames : public LocaleDisplayNamesCommon {
205  public:
KeyValueDisplayNames(const icu::Locale& locale, JSDisplayNames::Style style, bool fallback, bool dialect, const char* key, bool prevent_fallback)206   KeyValueDisplayNames(const icu::Locale& locale, JSDisplayNames::Style style,
207                        bool fallback, bool dialect, const char* key,
208                        bool prevent_fallback)
209       : LocaleDisplayNamesCommon(locale, style, fallback, dialect),
210         key_(key),
211         prevent_fallback_(prevent_fallback) {}
212 
213   ~KeyValueDisplayNames() override = default;
214 
215   const char* type() const override { return key_.c_str(); }
216 
217   Maybe<icu::UnicodeString> of(Isolate* isolate,
218                                const char* code) const override {
219     std::string code_str(code);
220     icu::UnicodeString result;
221     locale_display_names()->keyValueDisplayName(key_.c_str(), code_str.c_str(),
222                                                 result);
223     // Work around the issue that the keyValueDisplayNames ignore no
224     // substituion and always fallback.
225     if (prevent_fallback_ && (result.length() == 3) &&
226         (code_str.length() == 3) &&
227         (result == icu::UnicodeString(code_str.c_str(), -1, US_INV))) {
228       result.setToBogus();
229     }
230 
231     return Just(result);
232   }
233 
234  private:
235   std::string key_;
236   bool prevent_fallback_;
237 };
238 
239 class CurrencyNames : public KeyValueDisplayNames {
240  public:
CurrencyNames(const icu::Locale& locale, JSDisplayNames::Style style, bool fallback, bool dialect)241   CurrencyNames(const icu::Locale& locale, JSDisplayNames::Style style,
242                 bool fallback, bool dialect)
243       : KeyValueDisplayNames(locale, style, fallback, dialect, "currency",
244                              fallback == false) {}
245 
246   ~CurrencyNames() override = default;
247 
248   Maybe<icu::UnicodeString> of(Isolate* isolate,
249                                const char* code) const override {
250     std::string code_str(code);
251     if (!Intl::IsWellFormedCurrency(code_str)) {
252       THROW_NEW_ERROR_RETURN_VALUE(
253           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
254           Nothing<icu::UnicodeString>());
255     }
256     return KeyValueDisplayNames::of(isolate, code);
257   }
258 };
259 
260 class CalendarNames : public KeyValueDisplayNames {
261  public:
CalendarNames(const icu::Locale& locale, JSDisplayNames::Style style, bool fallback, bool dialect)262   CalendarNames(const icu::Locale& locale, JSDisplayNames::Style style,
263                 bool fallback, bool dialect)
264       : KeyValueDisplayNames(locale, style, fallback, dialect, "calendar",
265                              false) {}
266 
267   ~CalendarNames() override = default;
268 
269   Maybe<icu::UnicodeString> of(Isolate* isolate,
270                                const char* code) const override {
271     std::string code_str(code);
272     if (!Intl::IsWellFormedCalendar(code_str)) {
273       THROW_NEW_ERROR_RETURN_VALUE(
274           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
275           Nothing<icu::UnicodeString>());
276     }
277     return KeyValueDisplayNames::of(isolate, strcmp(code, "gregory") == 0
278                                                  ? "gregorian"
279                                                  : strcmp(code, "ethioaa") == 0
280                                                        ? "ethiopic-amete-alem"
281                                                        : code);
282   }
283 };
284 
StyleToUDateTimePGDisplayWidth( JSDisplayNames::Style style)285 UDateTimePGDisplayWidth StyleToUDateTimePGDisplayWidth(
286     JSDisplayNames::Style style) {
287   switch (style) {
288     case JSDisplayNames::Style::kLong:
289       return UDATPG_WIDE;
290     case JSDisplayNames::Style::kShort:
291       return UDATPG_ABBREVIATED;
292     case JSDisplayNames::Style::kNarrow:
293       return UDATPG_NARROW;
294   }
295 }
296 
StringToUDateTimePatternField(const char* code)297 UDateTimePatternField StringToUDateTimePatternField(const char* code) {
298   switch (code[0]) {
299     case 'd':
300       if (strcmp(code, "day") == 0) return UDATPG_DAY_FIELD;
301       if (strcmp(code, "dayPeriod") == 0) return UDATPG_DAYPERIOD_FIELD;
302       break;
303     case 'e':
304       if (strcmp(code, "era") == 0) return UDATPG_ERA_FIELD;
305       break;
306     case 'h':
307       if (strcmp(code, "hour") == 0) return UDATPG_HOUR_FIELD;
308       break;
309     case 'm':
310       if (strcmp(code, "minute") == 0) return UDATPG_MINUTE_FIELD;
311       if (strcmp(code, "month") == 0) return UDATPG_MONTH_FIELD;
312       break;
313     case 'q':
314       if (strcmp(code, "quarter") == 0) return UDATPG_QUARTER_FIELD;
315       break;
316     case 's':
317       if (strcmp(code, "second") == 0) return UDATPG_SECOND_FIELD;
318       break;
319     case 't':
320       if (strcmp(code, "timeZoneName") == 0) return UDATPG_ZONE_FIELD;
321       break;
322     case 'w':
323       if (strcmp(code, "weekOfYear") == 0) return UDATPG_WEEK_OF_YEAR_FIELD;
324       if (strcmp(code, "weekday") == 0) return UDATPG_WEEKDAY_FIELD;
325       break;
326     case 'y':
327       if (strcmp(code, "year") == 0) return UDATPG_YEAR_FIELD;
328       break;
329     default:
330       break;
331   }
332   return UDATPG_FIELD_COUNT;
333 }
334 
335 class DateTimeFieldNames : public DisplayNamesInternal {
336  public:
DateTimeFieldNames(const icu::Locale& locale, JSDisplayNames::Style style, bool fallback)337   DateTimeFieldNames(const icu::Locale& locale, JSDisplayNames::Style style,
338                      bool fallback)
339       : locale_(locale), width_(StyleToUDateTimePGDisplayWidth(style)) {
340     UErrorCode status = U_ZERO_ERROR;
341     generator_.reset(
342         icu::DateTimePatternGenerator::createInstance(locale_, status));
343     DCHECK(U_SUCCESS(status));
344   }
345 
346   ~DateTimeFieldNames() override = default;
347 
348   const char* type() const override { return "dateTimeField"; }
349 
350   icu::Locale locale() const override { return locale_; }
351 
352   Maybe<icu::UnicodeString> of(Isolate* isolate,
353                                const char* code) const override {
354     UDateTimePatternField field = StringToUDateTimePatternField(code);
355     if (field == UDATPG_FIELD_COUNT) {
356       THROW_NEW_ERROR_RETURN_VALUE(
357           isolate, NewRangeError(MessageTemplate::kInvalidArgument),
358           Nothing<icu::UnicodeString>());
359     }
360     return Just(generator_->getFieldDisplayName(field, width_));
361   }
362 
363  private:
364   icu::Locale locale_;
365   UDateTimePGDisplayWidth width_;
366   std::unique_ptr<icu::DateTimePatternGenerator> generator_;
367 };
368 
CreateInternal(const icu::Locale& locale, JSDisplayNames::Style style, Type type, bool fallback, bool dialect)369 DisplayNamesInternal* CreateInternal(const icu::Locale& locale,
370                                      JSDisplayNames::Style style, Type type,
371                                      bool fallback, bool dialect) {
372   switch (type) {
373     case Type::kLanguage:
374       return new LanguageNames(locale, style, fallback, dialect);
375     case Type::kRegion:
376       return new RegionNames(locale, style, fallback, false);
377     case Type::kScript:
378       return new ScriptNames(locale, style, fallback, false);
379     case Type::kCurrency:
380       return new CurrencyNames(locale, style, fallback, false);
381     case Type::kCalendar:
382       return new CalendarNames(locale, style, fallback, false);
383     case Type::kDateTimeField:
384       return new DateTimeFieldNames(locale, style, fallback);
385     default:
386       UNREACHABLE();
387   }
388 }
389 
390 }  // anonymous namespace
391 
392 // ecma402 #sec-Intl.DisplayNames
New(Isolate* isolate, Handle<Map> map, Handle<Object> locales, Handle<Object> input_options)393 MaybeHandle<JSDisplayNames> JSDisplayNames::New(Isolate* isolate,
394                                                 Handle<Map> map,
395                                                 Handle<Object> locales,
396                                                 Handle<Object> input_options) {
397   const char* service = "Intl.DisplayNames";
398   Factory* factory = isolate->factory();
399 
400   Handle<JSReceiver> options;
401   // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
402   Maybe<std::vector<std::string>> maybe_requested_locales =
403       Intl::CanonicalizeLocaleList(isolate, locales);
404   MAYBE_RETURN(maybe_requested_locales, Handle<JSDisplayNames>());
405   std::vector<std::string> requested_locales =
406       maybe_requested_locales.FromJust();
407 
408   // 4. Let options be ? GetOptionsObject(options).
409   ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
410                              GetOptionsObject(isolate, input_options, service),
411                              JSDisplayNames);
412 
413   // Note: No need to create a record. It's not observable.
414   // 5. Let opt be a new Record.
415 
416   // 6. Let localeData be %DisplayNames%.[[LocaleData]].
417 
418   // 7. Let matcher be ? GetOption(options, "localeMatcher", "string", «
419   // "lookup", "best fit" », "best fit").
420   Maybe<Intl::MatcherOption> maybe_locale_matcher =
421       Intl::GetLocaleMatcher(isolate, options, service);
422   MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSDisplayNames>());
423 
424   // 8. Set opt.[[localeMatcher]] to matcher.
425   Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
426 
427   // ecma402/#sec-Intl.DisplayNames-internal-slots
428   // The value of the [[RelevantExtensionKeys]] internal slot is
429   // «  ».
430   std::set<std::string> relevant_extension_keys = {};
431   // 9. Let r be ResolveLocale(%DisplayNames%.[[AvailableLocales]],
432   //     requestedLocales, opt, %DisplayNames%.[[RelevantExtensionKeys]]).
433   Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
434       Intl::ResolveLocale(isolate, JSDisplayNames::GetAvailableLocales(),
435                           requested_locales, matcher, relevant_extension_keys);
436   if (maybe_resolve_locale.IsNothing()) {
437     THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
438                     JSDisplayNames);
439   }
440   Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
441 
442   icu::Locale icu_locale = r.icu_locale;
443 
444   // 10. Let s be ? GetOption(options, "style", "string",
445   //                          «"long", "short", "narrow"», "long").
446   Maybe<Style> maybe_style = GetStringOption<Style>(
447       isolate, options, "style", service, {"long", "short", "narrow"},
448       {Style::kLong, Style::kShort, Style::kNarrow}, Style::kLong);
449   MAYBE_RETURN(maybe_style, MaybeHandle<JSDisplayNames>());
450   Style style_enum = maybe_style.FromJust();
451 
452   // 11. Set displayNames.[[Style]] to style.
453 
454   // 12. Let type be ? GetOption(options, "type", "string", « "language",
455   // "region", "script", "currency" , "calendar", "dateTimeField", "unit"»,
456   // undefined).
457   Maybe<Type> maybe_type = GetStringOption<Type>(
458       isolate, options, "type", service,
459       {"language", "region", "script", "currency", "calendar", "dateTimeField"},
460       {Type::kLanguage, Type::kRegion, Type::kScript, Type::kCurrency,
461        Type::kCalendar, Type::kDateTimeField},
462       Type::kUndefined);
463   MAYBE_RETURN(maybe_type, MaybeHandle<JSDisplayNames>());
464   Type type_enum = maybe_type.FromJust();
465 
466   // 13. If type is undefined, throw a TypeError exception.
467   if (type_enum == Type::kUndefined) {
468     THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kInvalidArgument),
469                     JSDisplayNames);
470   }
471 
472   // 14. Set displayNames.[[Type]] to type.
473 
474   // 15. Let fallback be ? GetOption(options, "fallback", "string",
475   //     « "code", "none" », "code").
476   Maybe<Fallback> maybe_fallback = GetStringOption<Fallback>(
477       isolate, options, "fallback", service, {"code", "none"},
478       {Fallback::kCode, Fallback::kNone}, Fallback::kCode);
479   MAYBE_RETURN(maybe_fallback, MaybeHandle<JSDisplayNames>());
480   Fallback fallback_enum = maybe_fallback.FromJust();
481 
482   // 16. Set displayNames.[[Fallback]] to fallback.
483 
484   LanguageDisplay language_display_enum = LanguageDisplay::kDialect;
485   // 24. Let languageDisplay be ? GetOption(options, "languageDisplay",
486   // "string", « "dialect", "standard" », "dialect").
487   Maybe<LanguageDisplay> maybe_language_display =
488       GetStringOption<LanguageDisplay>(
489           isolate, options, "languageDisplay", service, {"dialect", "standard"},
490           {LanguageDisplay::kDialect, LanguageDisplay::kStandard},
491           LanguageDisplay::kDialect);
492   MAYBE_RETURN(maybe_language_display, MaybeHandle<JSDisplayNames>());
493   // 25. If type is "language", then
494   if (type_enum == Type::kLanguage) {
495     // a. Set displayNames.[[LanguageDisplay]] to languageDisplay.
496     language_display_enum = maybe_language_display.FromJust();
497   }
498 
499   // Set displayNames.[[Fallback]] to fallback.
500 
501   // 17. Set displayNames.[[Locale]] to the value of r.[[Locale]].
502 
503   // Let dataLocale be r.[[dataLocale]].
504 
505   // Let dataLocaleData be localeData.[[<dataLocale>]].
506 
507   // Let types be dataLocaleData.[[types]].
508 
509   // Assert: types is a Record (see 1.3.3).
510 
511   // Let typeFields be types.[[<type>]].
512 
513   // Assert: typeFields is a Record (see 1.3.3).
514 
515   // Let styleFields be typeFields.[[<style>]].
516 
517   // Assert: styleFields is a Record (see 1.3.3).
518 
519   // Set displayNames.[[Fields]] to styleFields.
520 
521   DisplayNamesInternal* internal = CreateInternal(
522       icu_locale, style_enum, type_enum, fallback_enum == Fallback::kCode,
523       language_display_enum == LanguageDisplay::kDialect);
524   if (internal == nullptr) {
525     THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError),
526                     JSDisplayNames);
527   }
528 
529   Handle<Managed<DisplayNamesInternal>> managed_internal =
530       Managed<DisplayNamesInternal>::FromRawPtr(isolate, 0, internal);
531 
532   Handle<JSDisplayNames> display_names =
533       Handle<JSDisplayNames>::cast(factory->NewFastOrSlowJSObjectFromMap(map));
534   display_names->set_flags(0);
535   display_names->set_style(style_enum);
536   display_names->set_fallback(fallback_enum);
537   display_names->set_language_display(language_display_enum);
538 
539   DisallowGarbageCollection no_gc;
540   display_names->set_internal(*managed_internal);
541 
542   // Return displayNames.
543   return display_names;
544 }
545 
546 // ecma402 #sec-Intl.DisplayNames.prototype.resolvedOptions
ResolvedOptions( Isolate* isolate, Handle<JSDisplayNames> display_names)547 Handle<JSObject> JSDisplayNames::ResolvedOptions(
548     Isolate* isolate, Handle<JSDisplayNames> display_names) {
549   Factory* factory = isolate->factory();
550   // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
551   Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
552 
553   DisplayNamesInternal* internal = display_names->internal().raw();
554 
555   Maybe<std::string> maybe_locale = Intl::ToLanguageTag(internal->locale());
556   DCHECK(maybe_locale.IsJust());
557   Handle<String> locale = isolate->factory()->NewStringFromAsciiChecked(
558       maybe_locale.FromJust().c_str());
559   Handle<String> style = display_names->StyleAsString();
560   Handle<String> type = factory->NewStringFromAsciiChecked(internal->type());
561   Handle<String> fallback = display_names->FallbackAsString();
562   Handle<String> language_display = display_names->LanguageDisplayAsString();
563 
564   Maybe<bool> maybe_create_locale = JSReceiver::CreateDataProperty(
565       isolate, options, factory->locale_string(), locale, Just(kDontThrow));
566   DCHECK(maybe_create_locale.FromJust());
567   USE(maybe_create_locale);
568 
569   Maybe<bool> maybe_create_style = JSReceiver::CreateDataProperty(
570       isolate, options, factory->style_string(), style, Just(kDontThrow));
571   DCHECK(maybe_create_style.FromJust());
572   USE(maybe_create_style);
573 
574   Maybe<bool> maybe_create_type = JSReceiver::CreateDataProperty(
575       isolate, options, factory->type_string(), type, Just(kDontThrow));
576   DCHECK(maybe_create_type.FromJust());
577   USE(maybe_create_type);
578 
579   Maybe<bool> maybe_create_fallback = JSReceiver::CreateDataProperty(
580       isolate, options, factory->fallback_string(), fallback, Just(kDontThrow));
581   DCHECK(maybe_create_fallback.FromJust());
582   USE(maybe_create_fallback);
583 
584     if (std::strcmp("language", internal->type()) == 0) {
585       Maybe<bool> maybe_create_language_display =
586           JSReceiver::CreateDataProperty(isolate, options,
587                                          factory->languageDisplay_string(),
588                                          language_display, Just(kDontThrow));
589       DCHECK(maybe_create_language_display.FromJust());
590       USE(maybe_create_language_display);
591     }
592 
593   return options;
594 }
595 
596 // ecma402 #sec-Intl.DisplayNames.prototype.of
Of(Isolate* isolate, Handle<JSDisplayNames> display_names, Handle<Object> code_obj)597 MaybeHandle<Object> JSDisplayNames::Of(Isolate* isolate,
598                                        Handle<JSDisplayNames> display_names,
599                                        Handle<Object> code_obj) {
600   Handle<String> code;
601   ASSIGN_RETURN_ON_EXCEPTION(isolate, code, Object::ToString(isolate, code_obj),
602                              Object);
603   DisplayNamesInternal* internal = display_names->internal().raw();
604   Maybe<icu::UnicodeString> maybe_result =
605       internal->of(isolate, code->ToCString().get());
606   MAYBE_RETURN(maybe_result, Handle<Object>());
607   icu::UnicodeString result = maybe_result.FromJust();
608   if (result.isBogus()) {
609     return isolate->factory()->undefined_value();
610   }
611   return Intl::ToString(isolate, result).ToHandleChecked();
612 }
613 
614 namespace {
615 
616 struct CheckCalendar {
keyv8::internal::__anon14836::CheckCalendar617   static const char* key() { return "calendar"; }
pathv8::internal::__anon14836::CheckCalendar618   static const char* path() { return nullptr; }
619 };
620 
621 }  // namespace
622 
GetAvailableLocales()623 const std::set<std::string>& JSDisplayNames::GetAvailableLocales() {
624   static base::LazyInstance<Intl::AvailableLocales<CheckCalendar>>::type
625       available_locales = LAZY_INSTANCE_INITIALIZER;
626   return available_locales.Pointer()->Get();
627 }
628 
StyleAsString() const629 Handle<String> JSDisplayNames::StyleAsString() const {
630   switch (style()) {
631     case Style::kLong:
632       return GetReadOnlyRoots().long_string_handle();
633     case Style::kShort:
634       return GetReadOnlyRoots().short_string_handle();
635     case Style::kNarrow:
636       return GetReadOnlyRoots().narrow_string_handle();
637   }
638   UNREACHABLE();
639 }
640 
FallbackAsString() const641 Handle<String> JSDisplayNames::FallbackAsString() const {
642   switch (fallback()) {
643     case Fallback::kCode:
644       return GetReadOnlyRoots().code_string_handle();
645     case Fallback::kNone:
646       return GetReadOnlyRoots().none_string_handle();
647   }
648   UNREACHABLE();
649 }
650 
LanguageDisplayAsString() const651 Handle<String> JSDisplayNames::LanguageDisplayAsString() const {
652   switch (language_display()) {
653     case LanguageDisplay::kDialect:
654       return GetReadOnlyRoots().dialect_string_handle();
655     case LanguageDisplay::kStandard:
656       return GetReadOnlyRoots().standard_string_handle();
657   }
658   UNREACHABLE();
659 }
660 
661 }  // namespace internal
662 }  // namespace v8
663