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