Line data Source code
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-date-time-format.h"
10 :
11 : #include <memory>
12 : #include <string>
13 : #include <vector>
14 :
15 : #include "src/date.h"
16 : #include "src/heap/factory.h"
17 : #include "src/isolate.h"
18 : #include "src/objects/intl-objects.h"
19 : #include "src/objects/js-date-time-format-inl.h"
20 :
21 : #include "unicode/calendar.h"
22 : #include "unicode/dtptngen.h"
23 : #include "unicode/gregocal.h"
24 : #include "unicode/smpdtfmt.h"
25 : #include "unicode/unistr.h"
26 :
27 : namespace v8 {
28 : namespace internal {
29 :
30 : namespace {
31 :
32 92670 : class PatternMap {
33 : public:
34 10855 : PatternMap(std::string pattern, std::string value)
35 21710 : : pattern(std::move(pattern)), value(std::move(value)) {}
36 63140 : virtual ~PatternMap() = default;
37 : std::string pattern;
38 : std::string value;
39 : };
40 :
41 4950 : class PatternItem {
42 : public:
43 2475 : PatternItem(const std::string property, std::vector<PatternMap> pairs,
44 : std::vector<const char*> allowed_values)
45 : : property(std::move(property)),
46 : pairs(std::move(pairs)),
47 4950 : allowed_values(allowed_values) {}
48 7425 : virtual ~PatternItem() = default;
49 :
50 : const std::string property;
51 : // It is important for the pattern in the pairs from longer one to shorter one
52 : // if the longer one contains substring of an shorter one.
53 : std::vector<PatternMap> pairs;
54 : std::vector<const char*> allowed_values;
55 : };
56 :
57 275 : static const std::vector<PatternItem> BuildPatternItems() {
58 : const std::vector<const char*> kLongShort = {"long", "short"};
59 : const std::vector<const char*> kNarrowLongShort = {"narrow", "long", "short"};
60 : const std::vector<const char*> k2DigitNumeric = {"2-digit", "numeric"};
61 : const std::vector<const char*> kNarrowLongShort2DigitNumeric = {
62 : "narrow", "long", "short", "2-digit", "numeric"};
63 : const std::vector<PatternItem> kPatternItems = {
64 : PatternItem("weekday",
65 : {{"EEEEE", "narrow"},
66 : {"EEEE", "long"},
67 : {"EEE", "short"},
68 : {"ccccc", "narrow"},
69 : {"cccc", "long"},
70 : {"ccc", "short"}},
71 1650 : kNarrowLongShort),
72 : PatternItem("era",
73 : {{"GGGGG", "narrow"}, {"GGGG", "long"}, {"GGG", "short"}},
74 825 : kNarrowLongShort),
75 : PatternItem("year", {{"yy", "2-digit"}, {"y", "numeric"}},
76 550 : k2DigitNumeric),
77 : // Sometimes we get L instead of M for month - standalone name.
78 : PatternItem("month",
79 : {{"MMMMM", "narrow"},
80 : {"MMMM", "long"},
81 : {"MMM", "short"},
82 : {"MM", "2-digit"},
83 : {"M", "numeric"},
84 : {"LLLLL", "narrow"},
85 : {"LLLL", "long"},
86 : {"LLL", "short"},
87 : {"LL", "2-digit"},
88 : {"L", "numeric"}},
89 2750 : kNarrowLongShort2DigitNumeric),
90 550 : PatternItem("day", {{"dd", "2-digit"}, {"d", "numeric"}}, k2DigitNumeric),
91 : PatternItem("hour",
92 : {{"HH", "2-digit"},
93 : {"H", "numeric"},
94 : {"hh", "2-digit"},
95 : {"h", "numeric"},
96 : {"kk", "2-digit"},
97 : {"k", "numeric"},
98 : {"KK", "2-digit"},
99 : {"K", "numeric"}},
100 2200 : k2DigitNumeric),
101 : PatternItem("minute", {{"mm", "2-digit"}, {"m", "numeric"}},
102 550 : k2DigitNumeric),
103 : PatternItem("second", {{"ss", "2-digit"}, {"s", "numeric"}},
104 550 : k2DigitNumeric),
105 : PatternItem("timeZoneName", {{"zzzz", "long"}, {"z", "short"}},
106 32725 : kLongShort)};
107 275 : return kPatternItems;
108 : }
109 :
110 : class PatternItems {
111 : public:
112 275 : PatternItems() : data(BuildPatternItems()) {}
113 0 : virtual ~PatternItems() {}
114 : const std::vector<PatternItem>& Get() const { return data; }
115 :
116 : private:
117 : const std::vector<PatternItem> data;
118 : };
119 :
120 : static const std::vector<PatternItem>& GetPatternItems() {
121 : static base::LazyInstance<PatternItems>::type items =
122 : LAZY_INSTANCE_INITIALIZER;
123 : return items.Pointer()->Get();
124 : }
125 :
126 16320 : class PatternData {
127 : public:
128 3060 : PatternData(const std::string property, std::vector<PatternMap> pairs,
129 : std::vector<const char*> allowed_values)
130 6120 : : property(std::move(property)), allowed_values(allowed_values) {
131 13600 : for (const auto& pair : pairs) {
132 21080 : map.insert(std::make_pair(pair.value, pair.pattern));
133 : }
134 3060 : }
135 16320 : virtual ~PatternData() = default;
136 :
137 : const std::string property;
138 : std::map<const std::string, const std::string> map;
139 : std::vector<const char*> allowed_values;
140 : };
141 :
142 340 : const std::vector<PatternData> CreateCommonData(const PatternData& hour_data) {
143 : std::vector<PatternData> build;
144 3400 : for (const PatternItem& item : GetPatternItems()) {
145 6120 : if (item.property == "hour") {
146 340 : build.push_back(hour_data);
147 : } else {
148 13600 : build.push_back(
149 : PatternData(item.property, item.pairs, item.allowed_values));
150 : }
151 : }
152 340 : return build;
153 : }
154 :
155 340 : const std::vector<PatternData> CreateData(const char* digit2,
156 : const char* numeric) {
157 : return CreateCommonData(
158 : PatternData("hour", {{digit2, "2-digit"}, {numeric, "numeric"}},
159 2720 : {"2-digit", "numeric"}));
160 : }
161 :
162 : // According to "Date Field Symbol Table" in
163 : // http://userguide.icu-project.org/formatparse/datetime
164 : // Symbol | Meaning | Example(s)
165 : // h hour in am/pm (1~12) h 7
166 : // hh 07
167 : // H hour in day (0~23) H 0
168 : // HH 00
169 : // k hour in day (1~24) k 24
170 : // kk 24
171 : // K hour in am/pm (0~11) K 0
172 : // KK 00
173 :
174 : class Pattern {
175 : public:
176 340 : Pattern(const char* d1, const char* d2) : data(CreateData(d1, d2)) {}
177 0 : virtual ~Pattern() {}
178 0 : virtual const std::vector<PatternData>& Get() const { return data; }
179 :
180 : private:
181 : std::vector<PatternData> data;
182 : };
183 :
184 : #define DEFFINE_TRAIT(name, d1, d2) \
185 : struct name { \
186 : static void Construct(void* allocated_ptr) { \
187 : new (allocated_ptr) Pattern(d1, d2); \
188 : } \
189 : };
190 20 : DEFFINE_TRAIT(H11Trait, "KK", "K")
191 245 : DEFFINE_TRAIT(H12Trait, "hh", "h")
192 60 : DEFFINE_TRAIT(H23Trait, "HH", "H")
193 15 : DEFFINE_TRAIT(H24Trait, "kk", "k")
194 0 : DEFFINE_TRAIT(HDefaultTrait, "jj", "j")
195 : #undef DEFFINE_TRAIT
196 :
197 3099 : const std::vector<PatternData>& GetPatternData(Intl::HourCycle hour_cycle) {
198 3099 : switch (hour_cycle) {
199 : case Intl::HourCycle::kH11: {
200 : static base::LazyInstance<Pattern, H11Trait>::type h11 =
201 : LAZY_INSTANCE_INITIALIZER;
202 45 : return h11.Pointer()->Get();
203 : }
204 : case Intl::HourCycle::kH12: {
205 : static base::LazyInstance<Pattern, H12Trait>::type h12 =
206 : LAZY_INSTANCE_INITIALIZER;
207 2739 : return h12.Pointer()->Get();
208 : }
209 : case Intl::HourCycle::kH23: {
210 : static base::LazyInstance<Pattern, H23Trait>::type h23 =
211 : LAZY_INSTANCE_INITIALIZER;
212 279 : return h23.Pointer()->Get();
213 : }
214 : case Intl::HourCycle::kH24: {
215 : static base::LazyInstance<Pattern, H24Trait>::type h24 =
216 : LAZY_INSTANCE_INITIALIZER;
217 36 : return h24.Pointer()->Get();
218 : }
219 : case Intl::HourCycle::kUndefined: {
220 : static base::LazyInstance<Pattern, HDefaultTrait>::type hDefault =
221 : LAZY_INSTANCE_INITIALIZER;
222 0 : return hDefault.Pointer()->Get();
223 : }
224 : default:
225 0 : UNREACHABLE();
226 : }
227 : }
228 :
229 72 : std::string GetGMTTzID(Isolate* isolate, const std::string& input) {
230 72 : std::string ret = "Etc/GMT";
231 72 : switch (input.length()) {
232 : case 8:
233 9 : if (input[7] == '0') return ret + '0';
234 : break;
235 : case 9:
236 72 : if ((input[7] == '+' || input[7] == '-') &&
237 36 : IsInRange(input[8], '0', '9')) {
238 72 : return ret + input[7] + input[8];
239 : }
240 : break;
241 : case 10:
242 54 : if ((input[7] == '+' || input[7] == '-') && (input[8] == '1') &&
243 27 : IsInRange(input[9], '0', '4')) {
244 81 : return ret + input[7] + input[8] + input[9];
245 : }
246 : break;
247 : }
248 0 : return "";
249 : }
250 :
251 : // Locale independenty version of isalpha for ascii range. This will return
252 : // false if the ch is alpha but not in ascii range.
253 : bool IsAsciiAlpha(char ch) {
254 11943 : return IsInRange(ch, 'A', 'Z') || IsInRange(ch, 'a', 'z');
255 : }
256 :
257 : // Locale independent toupper for ascii range. This will not return İ (dotted I)
258 : // for i under Turkish locale while std::toupper may.
259 7623 : char LocaleIndependentAsciiToUpper(char ch) {
260 8640 : return (IsInRange(ch, 'a', 'z')) ? (ch - 'a' + 'A') : ch;
261 : }
262 :
263 : // Locale independent tolower for ascii range.
264 : char LocaleIndependentAsciiToLower(char ch) {
265 4743 : return (IsInRange(ch, 'A', 'Z')) ? (ch - 'A' + 'a') : ch;
266 : }
267 :
268 : // Returns titlecased location, bueNos_airES -> Buenos_Aires
269 : // or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only
270 : // deals with ASCII only characters.
271 : // 'of', 'au' and 'es' are special-cased and lowercased.
272 : // Also "Antarctica/DumontDUrville" is special case.
273 : // ICU's timezone parsing is case sensitive, but ECMAScript is case insensitive
274 405 : std::string ToTitleCaseTimezoneLocation(Isolate* isolate,
275 : const std::string& input) {
276 : std::string title_cased;
277 : int word_length = 0;
278 6804 : for (char ch : input) {
279 : // Convert first char to upper case, the rest to lower case
280 6453 : if (IsAsciiAlpha(ch)) {
281 11448 : title_cased += word_length == 0 ? LocaleIndependentAsciiToUpper(ch)
282 : : LocaleIndependentAsciiToLower(ch);
283 5724 : word_length++;
284 729 : } else if (ch == '_' || ch == '-' || ch == '/') {
285 : // Special case Au/Es/Of to be lower case.
286 675 : if (word_length == 2) {
287 45 : size_t pos = title_cased.length() - 2;
288 45 : std::string substr = title_cased.substr(pos, 2);
289 90 : if (substr == "Of" || substr == "Es" || substr == "Au") {
290 72 : title_cased[pos] = LocaleIndependentAsciiToLower(title_cased[pos]);
291 : }
292 : }
293 : title_cased += ch;
294 : word_length = 0;
295 : } else {
296 : // Invalid input
297 : return std::string();
298 : }
299 : }
300 : // Special case
301 351 : if (title_cased == "Antarctica/Dumontdurville") {
302 18 : return "Antarctica/DumontDUrville";
303 : }
304 : return title_cased;
305 : }
306 :
307 : } // namespace
308 :
309 567 : std::string JSDateTimeFormat::CanonicalizeTimeZoneID(Isolate* isolate,
310 : const std::string& input) {
311 : std::string upper = input;
312 : transform(upper.begin(), upper.end(), upper.begin(),
313 : LocaleIndependentAsciiToUpper);
314 2052 : if (upper == "UTC" || upper == "GMT" || upper == "ETC/UTC" ||
315 : upper == "ETC/GMT") {
316 90 : return "UTC";
317 : }
318 : // We expect only _, '-' and / beside ASCII letters.
319 : // All inputs should conform to Area/Location(/Location)*, or Etc/GMT* .
320 : // TODO(jshin): 1. Support 'GB-Eire", 'EST5EDT", "ROK', 'US/*', 'NZ' and many
321 : // other aliases/linked names when moving timezone validation code to C++.
322 : // See crbug.com/364374 and crbug.com/v8/8007 .
323 : // 2. Resolve the difference betwee CLDR/ICU and IANA time zone db.
324 : // See http://unicode.org/cldr/trac/ticket/9892 and crbug.com/645807 .
325 477 : if (strncmp(upper.c_str(), "ETC/GMT", 7) == 0) {
326 72 : return GetGMTTzID(isolate, input);
327 : }
328 405 : return ToTitleCaseTimezoneLocation(isolate, input);
329 : }
330 :
331 : namespace {
332 :
333 36 : Handle<String> DateTimeStyleAsString(Isolate* isolate,
334 : JSDateTimeFormat::DateTimeStyle style) {
335 36 : switch (style) {
336 : case JSDateTimeFormat::DateTimeStyle::kFull:
337 : return ReadOnlyRoots(isolate).full_string_handle();
338 : case JSDateTimeFormat::DateTimeStyle::kLong:
339 : return ReadOnlyRoots(isolate).long_string_handle();
340 : case JSDateTimeFormat::DateTimeStyle::kMedium:
341 : return ReadOnlyRoots(isolate).medium_string_handle();
342 : case JSDateTimeFormat::DateTimeStyle::kShort:
343 : return ReadOnlyRoots(isolate).short_string_handle();
344 : case JSDateTimeFormat::DateTimeStyle::kUndefined:
345 0 : UNREACHABLE();
346 : }
347 0 : }
348 :
349 : } // namespace
350 :
351 : // ecma402 #sec-intl.datetimeformat.prototype.resolvedoptions
352 2529 : MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
353 : Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) {
354 : Factory* factory = isolate->factory();
355 : // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
356 2529 : Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
357 :
358 : Handle<Object> resolved_obj;
359 :
360 2529 : CHECK(!date_time_format->icu_locale().is_null());
361 2529 : CHECK_NOT_NULL(date_time_format->icu_locale()->raw());
362 : icu::Locale* icu_locale = date_time_format->icu_locale()->raw();
363 2529 : Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(*icu_locale);
364 2529 : MAYBE_RETURN(maybe_locale_str, MaybeHandle<JSObject>());
365 : std::string locale_str = maybe_locale_str.FromJust();
366 : Handle<String> locale =
367 2529 : factory->NewStringFromAsciiChecked(locale_str.c_str());
368 :
369 : icu::SimpleDateFormat* icu_simple_date_format =
370 : date_time_format->icu_simple_date_format()->raw();
371 : // calendar
372 2529 : const icu::Calendar* calendar = icu_simple_date_format->getCalendar();
373 : // getType() returns legacy calendar type name instead of LDML/BCP47 calendar
374 : // key values. intl.js maps them to BCP47 values for key "ca".
375 : // TODO(jshin): Consider doing it here, instead.
376 2529 : std::string calendar_str = calendar->getType();
377 :
378 : // Maps ICU calendar names to LDML/BCP47 types for key 'ca'.
379 : // See typeMap section in third_party/icu/source/data/misc/keyTypeData.txt
380 : // and
381 : // http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
382 2529 : if (calendar_str == "gregorian") {
383 : calendar_str = "gregory";
384 612 : } else if (calendar_str == "ethiopic-amete-alem") {
385 : calendar_str = "ethioaa";
386 : }
387 :
388 2529 : const icu::TimeZone& tz = calendar->getTimeZone();
389 2529 : icu::UnicodeString time_zone;
390 : tz.getID(time_zone);
391 2529 : UErrorCode status = U_ZERO_ERROR;
392 2529 : icu::UnicodeString canonical_time_zone;
393 2529 : icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
394 : Handle<Object> timezone_value;
395 2529 : if (U_SUCCESS(status)) {
396 : // In CLDR (http://unicode.org/cldr/trac/ticket/9943), Etc/UTC is made
397 : // a separate timezone ID from Etc/GMT even though they're still the same
398 : // timezone. We have Etc/UTC because 'UTC', 'Etc/Universal',
399 : // 'Etc/Zulu' and others are turned to 'Etc/UTC' by ICU. Etc/GMT comes
400 : // from Etc/GMT0, Etc/GMT+0, Etc/GMT-0, Etc/Greenwich.
401 : // ecma402#sec-canonicalizetimezonename step 3
402 10026 : if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/UTC") ||
403 7407 : canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
404 126 : timezone_value = factory->UTC_string();
405 : } else {
406 4806 : ASSIGN_RETURN_ON_EXCEPTION(isolate, timezone_value,
407 : Intl::ToString(isolate, canonical_time_zone),
408 : JSObject);
409 : }
410 : } else {
411 : // Somehow on Windows we will reach here.
412 0 : timezone_value = factory->undefined_value();
413 : }
414 :
415 : // Ugly hack. ICU doesn't expose numbering system in any way, so we have
416 : // to assume that for given locale NumberingSystem constructor produces the
417 : // same digits as NumberFormat/Calendar would.
418 : // Tracked by https://unicode-org.atlassian.net/browse/ICU-13431
419 2529 : std::string numbering_system = Intl::GetNumberingSystem(*icu_locale);
420 :
421 2529 : icu::UnicodeString pattern_unicode;
422 2529 : icu_simple_date_format->toPattern(pattern_unicode);
423 : std::string pattern;
424 2529 : pattern_unicode.toUTF8String(pattern);
425 :
426 : // 5. For each row of Table 6, except the header row, in table order, do
427 : // Table 6: Resolved Options of DateTimeFormat Instances
428 : // Internal Slot Property
429 : // [[Locale]] "locale"
430 : // [[Calendar]] "calendar"
431 : // [[NumberingSystem]] "numberingSystem"
432 : // [[TimeZone]] "timeZone"
433 : // [[HourCycle]] "hourCycle"
434 : // "hour12"
435 : // [[Weekday]] "weekday"
436 : // [[Era]] "era"
437 : // [[Year]] "year"
438 : // [[Month]] "month"
439 : // [[Day]] "day"
440 : // [[Hour]] "hour"
441 : // [[Minute]] "minute"
442 : // [[Second]] "second"
443 : // [[TimeZoneName]] "timeZoneName"
444 5058 : CHECK(JSReceiver::CreateDataProperty(isolate, options,
445 : factory->locale_string(), locale,
446 : Just(kDontThrow))
447 : .FromJust());
448 7587 : CHECK(JSReceiver::CreateDataProperty(
449 : isolate, options, factory->calendar_string(),
450 : factory->NewStringFromAsciiChecked(calendar_str.c_str()),
451 : Just(kDontThrow))
452 : .FromJust());
453 2529 : if (!numbering_system.empty()) {
454 7587 : CHECK(JSReceiver::CreateDataProperty(
455 : isolate, options, factory->numberingSystem_string(),
456 : factory->NewStringFromAsciiChecked(numbering_system.c_str()),
457 : Just(kDontThrow))
458 : .FromJust());
459 : }
460 5058 : CHECK(JSReceiver::CreateDataProperty(isolate, options,
461 : factory->timeZone_string(),
462 : timezone_value, Just(kDontThrow))
463 : .FromJust());
464 :
465 : // 5.b.i. Let hc be dtf.[[HourCycle]].
466 : Intl::HourCycle hc = date_time_format->hour_cycle();
467 :
468 2529 : if (hc != Intl::HourCycle::kUndefined) {
469 675 : CHECK(JSReceiver::CreateDataProperty(
470 : isolate, options, factory->hourCycle_string(),
471 : date_time_format->HourCycleAsString(), Just(kDontThrow))
472 : .FromJust());
473 225 : switch (hc) {
474 : // ii. If hc is "h11" or "h12", let v be true.
475 : case Intl::HourCycle::kH11:
476 : case Intl::HourCycle::kH12:
477 324 : CHECK(JSReceiver::CreateDataProperty(
478 : isolate, options, factory->hour12_string(),
479 : factory->true_value(), Just(kDontThrow))
480 : .FromJust());
481 : break;
482 : // iii. Else if, hc is "h23" or "h24", let v be false.
483 : case Intl::HourCycle::kH23:
484 : case Intl::HourCycle::kH24:
485 126 : CHECK(JSReceiver::CreateDataProperty(
486 : isolate, options, factory->hour12_string(),
487 : factory->false_value(), Just(kDontThrow))
488 : .FromJust());
489 : break;
490 : // iv. Else, let v be undefined.
491 : case Intl::HourCycle::kUndefined:
492 : break;
493 : }
494 : }
495 :
496 25290 : for (const auto& item : GetPatternItems()) {
497 96822 : for (const auto& pair : item.pairs) {
498 81126 : if (pattern.find(pair.pattern) != std::string::npos) {
499 28260 : CHECK(JSReceiver::CreateDataProperty(
500 : isolate, options,
501 : factory->NewStringFromAsciiChecked(item.property.c_str()),
502 : factory->NewStringFromAsciiChecked(pair.value.c_str()),
503 : Just(kDontThrow))
504 : .FromJust());
505 : break;
506 : }
507 : }
508 : }
509 :
510 : // dateStyle
511 2529 : if (date_time_format->date_style() != DateTimeStyle::kUndefined) {
512 54 : CHECK(JSReceiver::CreateDataProperty(
513 : isolate, options, factory->dateStyle_string(),
514 : DateTimeStyleAsString(isolate, date_time_format->date_style()),
515 : Just(kDontThrow))
516 : .FromJust());
517 : }
518 :
519 : // timeStyle
520 2529 : if (date_time_format->time_style() != DateTimeStyle::kUndefined) {
521 54 : CHECK(JSReceiver::CreateDataProperty(
522 : isolate, options, factory->timeStyle_string(),
523 : DateTimeStyleAsString(isolate, date_time_format->time_style()),
524 : Just(kDontThrow))
525 : .FromJust());
526 : }
527 :
528 2529 : return options;
529 : }
530 :
531 : namespace {
532 :
533 : // ecma402/#sec-formatdatetime
534 : // FormatDateTime( dateTimeFormat, x )
535 765 : MaybeHandle<String> FormatDateTime(Isolate* isolate,
536 : const icu::SimpleDateFormat& date_format,
537 : double x) {
538 765 : double date_value = DateCache::TimeClip(x);
539 765 : if (std::isnan(date_value)) {
540 108 : THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),
541 : String);
542 : }
543 :
544 711 : icu::UnicodeString result;
545 711 : date_format.format(date_value, result);
546 :
547 711 : return Intl::ToString(isolate, result);
548 : }
549 :
550 : } // namespace
551 :
552 : // ecma402/#sec-datetime-format-functions
553 : // DateTime Format Functions
554 576 : MaybeHandle<String> JSDateTimeFormat::DateTimeFormat(
555 : Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
556 : Handle<Object> date) {
557 : // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]]
558 : // internal slot.
559 :
560 : // 3. If date is not provided or is undefined, then
561 : double x;
562 576 : if (date->IsUndefined()) {
563 : // 3.a Let x be Call(%Date_now%, undefined).
564 27 : x = JSDate::CurrentTimeValue(isolate);
565 : } else {
566 : // 4. Else,
567 : // a. Let x be ? ToNumber(date).
568 1098 : ASSIGN_RETURN_ON_EXCEPTION(isolate, date, Object::ToNumber(isolate, date),
569 : String);
570 549 : CHECK(date->IsNumber());
571 : x = date->Number();
572 : }
573 : // 5. Return FormatDateTime(dtf, x).
574 : icu::SimpleDateFormat* format =
575 1152 : date_time_format->icu_simple_date_format()->raw();
576 576 : return FormatDateTime(isolate, *format, x);
577 : }
578 :
579 : namespace {
580 : Isolate::ICUObjectCacheType ConvertToCacheType(
581 : JSDateTimeFormat::DefaultsOption type) {
582 198 : switch (type) {
583 : case JSDateTimeFormat::DefaultsOption::kDate:
584 : return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForDate;
585 : case JSDateTimeFormat::DefaultsOption::kTime:
586 : return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForTime;
587 : case JSDateTimeFormat::DefaultsOption::kAll:
588 : return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormat;
589 : }
590 : }
591 : } // namespace
592 :
593 198 : MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime(
594 : Isolate* isolate, Handle<Object> date, Handle<Object> locales,
595 : Handle<Object> options, RequiredOption required, DefaultsOption defaults) {
596 : Isolate::ICUObjectCacheType cache_type = ConvertToCacheType(defaults);
597 :
598 : Factory* factory = isolate->factory();
599 : // 1. Let x be ? thisTimeValue(this value);
600 198 : if (!date->IsJSDate()) {
601 0 : THROW_NEW_ERROR(isolate,
602 : NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
603 : factory->Date_string()),
604 : String);
605 : }
606 :
607 : double const x = Handle<JSDate>::cast(date)->value()->Number();
608 : // 2. If x is NaN, return "Invalid Date"
609 198 : if (std::isnan(x)) {
610 0 : return factory->Invalid_Date_string();
611 : }
612 :
613 : // We only cache the instance when both locales and options are undefined,
614 : // as that is the only case when the specified side-effects of examining
615 : // those arguments are unobservable.
616 : bool can_cache =
617 279 : locales->IsUndefined(isolate) && options->IsUndefined(isolate);
618 198 : if (can_cache) {
619 : // Both locales and options are undefined, check the cache.
620 : icu::SimpleDateFormat* cached_icu_simple_date_format =
621 : static_cast<icu::SimpleDateFormat*>(
622 54 : isolate->get_cached_icu_object(cache_type));
623 54 : if (cached_icu_simple_date_format != nullptr) {
624 24 : return FormatDateTime(isolate, *cached_icu_simple_date_format, x);
625 : }
626 : }
627 : // 3. Let options be ? ToDateTimeOptions(options, required, defaults).
628 : Handle<JSObject> internal_options;
629 348 : ASSIGN_RETURN_ON_EXCEPTION(
630 : isolate, internal_options,
631 : ToDateTimeOptions(isolate, options, required, defaults), String);
632 :
633 : // 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
634 : Handle<JSFunction> constructor = Handle<JSFunction>(
635 330 : JSFunction::cast(isolate->context()
636 330 : ->native_context()
637 330 : ->intl_date_time_format_function()),
638 : isolate);
639 : Handle<JSObject> obj;
640 330 : ASSIGN_RETURN_ON_EXCEPTION(
641 : isolate, obj,
642 : JSObject::New(constructor, constructor, Handle<AllocationSite>::null()),
643 : String);
644 : Handle<JSDateTimeFormat> date_time_format;
645 330 : ASSIGN_RETURN_ON_EXCEPTION(
646 : isolate, date_time_format,
647 : JSDateTimeFormat::Initialize(isolate, Handle<JSDateTimeFormat>::cast(obj),
648 : locales, internal_options),
649 : String);
650 :
651 165 : if (can_cache) {
652 : isolate->set_icu_object_in_cache(
653 30 : cache_type, std::static_pointer_cast<icu::UObject>(
654 60 : date_time_format->icu_simple_date_format()->get()));
655 : }
656 : // 5. Return FormatDateTime(dateFormat, x).
657 : icu::SimpleDateFormat* format =
658 : date_time_format->icu_simple_date_format()->raw();
659 165 : return FormatDateTime(isolate, *format, x);
660 : }
661 :
662 : namespace {
663 :
664 27017 : Maybe<bool> IsPropertyUndefined(Isolate* isolate, Handle<JSObject> options,
665 : const char* property) {
666 : Factory* factory = isolate->factory();
667 : // i. Let prop be the property name.
668 : // ii. Let value be ? Get(options, prop).
669 : Handle<Object> value;
670 81051 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
671 : isolate, value,
672 : Object::GetPropertyOrElement(
673 : isolate, options, factory->NewStringFromAsciiChecked(property)),
674 : Nothing<bool>());
675 : return Just(value->IsUndefined(isolate));
676 : }
677 :
678 7714 : Maybe<bool> NeedsDefault(Isolate* isolate, Handle<JSObject> options,
679 : const std::vector<std::string>& props) {
680 : bool needs_default = true;
681 34731 : for (const auto& prop : props) {
682 : // i. Let prop be the property name.
683 : // ii. Let value be ? Get(options, prop)
684 : Maybe<bool> maybe_undefined =
685 27017 : IsPropertyUndefined(isolate, options, prop.c_str());
686 27017 : MAYBE_RETURN(maybe_undefined, Nothing<bool>());
687 : // iii. If value is not undefined, let needDefaults be false.
688 27017 : if (!maybe_undefined.FromJust()) {
689 : needs_default = false;
690 : }
691 : }
692 : return Just(needs_default);
693 : }
694 :
695 3109 : Maybe<bool> CreateDefault(Isolate* isolate, Handle<JSObject> options,
696 : const std::vector<std::string>& props) {
697 : Factory* factory = isolate->factory();
698 : // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
699 12436 : for (const auto& prop : props) {
700 27981 : MAYBE_RETURN(
701 : JSReceiver::CreateDataProperty(
702 : isolate, options, factory->NewStringFromAsciiChecked(prop.c_str()),
703 : factory->numeric_string(), Just(kThrowOnError)),
704 : Nothing<bool>());
705 : }
706 : return Just(true);
707 : }
708 :
709 : } // namespace
710 :
711 : // ecma-402/#sec-todatetimeoptions
712 3921 : MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
713 : Isolate* isolate, Handle<Object> input_options, RequiredOption required,
714 : DefaultsOption defaults) {
715 : Factory* factory = isolate->factory();
716 : // 1. If options is undefined, let options be null; otherwise let options be ?
717 : // ToObject(options).
718 : Handle<JSObject> options;
719 3921 : if (input_options->IsUndefined(isolate)) {
720 1875 : options = factory->NewJSObjectWithNullProto();
721 : } else {
722 : Handle<JSReceiver> options_obj;
723 4092 : ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj,
724 : Object::ToObject(isolate, input_options),
725 : JSObject);
726 : // 2. Let options be ObjectCreate(options).
727 4074 : ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
728 : JSObject::ObjectCreate(isolate, options_obj),
729 : JSObject);
730 : }
731 :
732 : // 3. Let needDefaults be true.
733 : bool needs_default = true;
734 :
735 : // 4. If required is "date" or "any", then
736 3912 : if (required == RequiredOption::kAny || required == RequiredOption::kDate) {
737 : // a. For each of the property names "weekday", "year", "month", "day", do
738 11625 : const std::vector<std::string> list({"weekday", "year", "month", "day"});
739 3875 : Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
740 3875 : MAYBE_RETURN(maybe_needs_default, Handle<JSObject>());
741 : needs_default = maybe_needs_default.FromJust();
742 : }
743 :
744 : // 5. If required is "time" or "any", then
745 3912 : if (required == RequiredOption::kAny || required == RequiredOption::kTime) {
746 : // a. For each of the property names "hour", "minute", "second", do
747 11517 : const std::vector<std::string> list({"hour", "minute", "second"});
748 3839 : Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
749 3839 : MAYBE_RETURN(maybe_needs_default, Handle<JSObject>());
750 : needs_default &= maybe_needs_default.FromJust();
751 : }
752 :
753 : // 6. If needDefaults is true and defaults is either "date" or "all", then
754 3912 : if (needs_default) {
755 3063 : if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kDate) {
756 : // a. For each of the property names "year", "month", "day", do)
757 9105 : const std::vector<std::string> list({"year", "month", "day"});
758 6070 : MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>());
759 : }
760 : // 7. If needDefaults is true and defaults is either "time" or "all", then
761 3063 : if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kTime) {
762 : // a. For each of the property names "hour", "minute", "second", do
763 222 : const std::vector<std::string> list({"hour", "minute", "second"});
764 148 : MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>());
765 : }
766 : }
767 : // 8. Return options.
768 3912 : return options;
769 : }
770 :
771 3168 : MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::UnwrapDateTimeFormat(
772 : Isolate* isolate, Handle<JSReceiver> format_holder) {
773 : Handle<Context> native_context =
774 6336 : Handle<Context>(isolate->context()->native_context(), isolate);
775 : Handle<JSFunction> constructor = Handle<JSFunction>(
776 6336 : JSFunction::cast(native_context->intl_date_time_format_function()),
777 : isolate);
778 : Handle<Object> dtf;
779 6336 : ASSIGN_RETURN_ON_EXCEPTION(
780 : isolate, dtf,
781 : Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,
782 : format_holder->IsJSDateTimeFormat()),
783 : JSDateTimeFormat);
784 : // 2. If Type(dtf) is not Object or dtf does not have an
785 : // [[InitializedDateTimeFormat]] internal slot, then
786 3168 : if (!dtf->IsJSDateTimeFormat()) {
787 : // a. Throw a TypeError exception.
788 135 : THROW_NEW_ERROR(isolate,
789 : NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
790 : isolate->factory()->NewStringFromAsciiChecked(
791 : "UnwrapDateTimeFormat"),
792 : format_holder),
793 : JSDateTimeFormat);
794 : }
795 : // 3. Return dtf.
796 3123 : return Handle<JSDateTimeFormat>::cast(dtf);
797 : }
798 :
799 : namespace {
800 :
801 : // ecma-402/#sec-isvalidtimezonename
802 513 : bool IsValidTimeZoneName(const icu::TimeZone& tz) {
803 513 : UErrorCode status = U_ZERO_ERROR;
804 513 : icu::UnicodeString id;
805 : tz.getID(id);
806 513 : icu::UnicodeString canonical;
807 513 : icu::TimeZone::getCanonicalID(id, canonical, status);
808 1026 : return U_SUCCESS(status) &&
809 1539 : canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV);
810 : }
811 :
812 3729 : std::unique_ptr<icu::TimeZone> CreateTimeZone(Isolate* isolate,
813 : const char* timezone) {
814 : // Create time zone as specified by the user. We have to re-create time zone
815 : // since calendar takes ownership.
816 3729 : if (timezone == nullptr) {
817 : // 19.a. Else / Let timeZone be DefaultTimeZone().
818 3162 : return std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault());
819 : }
820 : std::string canonicalized =
821 1134 : JSDateTimeFormat::CanonicalizeTimeZoneID(isolate, timezone);
822 567 : if (canonicalized.empty()) return std::unique_ptr<icu::TimeZone>();
823 : std::unique_ptr<icu::TimeZone> tz(
824 513 : icu::TimeZone::createTimeZone(canonicalized.c_str()));
825 : // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
826 : // i. Throw a RangeError exception.
827 513 : if (!IsValidTimeZoneName(*tz)) return std::unique_ptr<icu::TimeZone>();
828 : return tz;
829 : }
830 :
831 290 : class CalendarCache {
832 : public:
833 3585 : icu::Calendar* CreateCalendar(const icu::Locale& locale, icu::TimeZone* tz) {
834 3585 : icu::UnicodeString tz_id;
835 : tz->getID(tz_id);
836 : std::string key;
837 3585 : tz_id.toUTF8String<std::string>(key);
838 : key += ":";
839 : key += locale.getName();
840 :
841 3585 : base::MutexGuard guard(&mutex_);
842 : auto it = map_.find(key);
843 3585 : if (it != map_.end()) {
844 2060 : delete tz;
845 2060 : return it->second->clone();
846 : }
847 : // Create a calendar using locale, and apply time zone to it.
848 1525 : UErrorCode status = U_ZERO_ERROR;
849 : std::unique_ptr<icu::Calendar> calendar(
850 1525 : icu::Calendar::createInstance(tz, locale, status));
851 1525 : CHECK(U_SUCCESS(status));
852 1525 : CHECK_NOT_NULL(calendar.get());
853 :
854 3050 : if (calendar->getDynamicClassID() ==
855 1525 : icu::GregorianCalendar::getStaticClassID()) {
856 : icu::GregorianCalendar* gc =
857 : static_cast<icu::GregorianCalendar*>(calendar.get());
858 1070 : UErrorCode status = U_ZERO_ERROR;
859 : // The beginning of ECMAScript time, namely -(2**53)
860 : const double start_of_time = -9007199254740992;
861 1070 : gc->setGregorianChange(start_of_time, status);
862 : DCHECK(U_SUCCESS(status));
863 : }
864 :
865 1525 : if (map_.size() > 8) { // Cache at most 8 calendars.
866 : map_.clear();
867 : }
868 1525 : map_[key].reset(calendar.release());
869 3050 : return map_[key]->clone();
870 : }
871 :
872 : private:
873 : std::map<std::string, std::unique_ptr<icu::Calendar>> map_;
874 : base::Mutex mutex_;
875 : };
876 :
877 3585 : icu::Calendar* CreateCalendar(Isolate* isolate, const icu::Locale& icu_locale,
878 : icu::TimeZone* tz) {
879 : static base::LazyInstance<CalendarCache>::type calendar_cache =
880 : LAZY_INSTANCE_INITIALIZER;
881 3585 : return calendar_cache.Pointer()->CreateCalendar(icu_locale, tz);
882 : }
883 :
884 1633 : std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat(
885 : const icu::Locale& icu_locale, const icu::UnicodeString& skeleton,
886 : icu::DateTimePatternGenerator& generator) {
887 : // See https://github.com/tc39/ecma402/issues/225 . The best pattern
888 : // generation needs to be done in the base locale according to the
889 : // current spec however odd it may be. See also crbug.com/826549 .
890 : // This is a temporary work-around to get v8's external behavior to match
891 : // the current spec, but does not follow the spec provisions mentioned
892 : // in the above Ecma 402 issue.
893 : // TODO(jshin): The spec may need to be revised because using the base
894 : // locale for the pattern match is not quite right. Moreover, what to
895 : // do with 'related year' part when 'chinese/dangi' calendar is specified
896 : // has to be discussed. Revisit once the spec is clarified/revised.
897 1633 : icu::UnicodeString pattern;
898 1633 : UErrorCode status = U_ZERO_ERROR;
899 3266 : pattern = generator.getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH,
900 : status);
901 1633 : CHECK(U_SUCCESS(status));
902 :
903 : // Make formatter from skeleton. Calendar and numbering system are added
904 : // to the locale as Unicode extension (if they were specified at all).
905 1633 : status = U_ZERO_ERROR;
906 : std::unique_ptr<icu::SimpleDateFormat> date_format(
907 1633 : new icu::SimpleDateFormat(pattern, icu_locale, status));
908 1633 : if (U_FAILURE(status)) return std::unique_ptr<icu::SimpleDateFormat>();
909 :
910 1624 : CHECK_NOT_NULL(date_format.get());
911 : return date_format;
912 : }
913 :
914 275 : class DateFormatCache {
915 : public:
916 3108 : icu::SimpleDateFormat* Create(const icu::Locale& icu_locale,
917 : const icu::UnicodeString& skeleton,
918 : icu::DateTimePatternGenerator& generator) {
919 : std::string key;
920 3108 : skeleton.toUTF8String<std::string>(key);
921 : key += ":";
922 : key += icu_locale.getName();
923 :
924 3108 : base::MutexGuard guard(&mutex_);
925 : auto it = map_.find(key);
926 3108 : if (it != map_.end()) {
927 1475 : return static_cast<icu::SimpleDateFormat*>(it->second->clone());
928 : }
929 :
930 1633 : if (map_.size() > 8) { // Cache at most 8 DateFormats.
931 : map_.clear();
932 : }
933 : std::unique_ptr<icu::SimpleDateFormat> instance(
934 1633 : CreateICUDateFormat(icu_locale, skeleton, generator));
935 1633 : if (instance.get() == nullptr) return nullptr;
936 1624 : map_[key] = std::move(instance);
937 3248 : return static_cast<icu::SimpleDateFormat*>(map_[key]->clone());
938 : }
939 :
940 : private:
941 : std::map<std::string, std::unique_ptr<icu::SimpleDateFormat>> map_;
942 : base::Mutex mutex_;
943 : };
944 :
945 3108 : std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormatFromCache(
946 : const icu::Locale& icu_locale, const icu::UnicodeString& skeleton,
947 : icu::DateTimePatternGenerator& generator) {
948 : static base::LazyInstance<DateFormatCache>::type cache =
949 : LAZY_INSTANCE_INITIALIZER;
950 : return std::unique_ptr<icu::SimpleDateFormat>(
951 6216 : cache.Pointer()->Create(icu_locale, skeleton, generator));
952 : }
953 :
954 3801 : Intl::HourCycle HourCycleFromPattern(const icu::UnicodeString pattern) {
955 : bool in_quote = false;
956 8679 : for (int32_t i = 0; i < pattern.length(); i++) {
957 : char16_t ch = pattern[i];
958 6240 : switch (ch) {
959 : case '\'':
960 180 : in_quote = !in_quote;
961 180 : break;
962 : case 'K':
963 0 : if (!in_quote) return Intl::HourCycle::kH11;
964 : break;
965 : case 'h':
966 3531 : if (!in_quote) return Intl::HourCycle::kH12;
967 : break;
968 : case 'H':
969 270 : if (!in_quote) return Intl::HourCycle::kH23;
970 : break;
971 : case 'k':
972 0 : if (!in_quote) return Intl::HourCycle::kH24;
973 : break;
974 : }
975 : }
976 : return Intl::HourCycle::kUndefined;
977 : }
978 :
979 432 : icu::DateFormat::EStyle DateTimeStyleToEStyle(
980 : JSDateTimeFormat::DateTimeStyle style) {
981 432 : switch (style) {
982 : case JSDateTimeFormat::DateTimeStyle::kFull:
983 : return icu::DateFormat::EStyle::kFull;
984 : case JSDateTimeFormat::DateTimeStyle::kLong:
985 90 : return icu::DateFormat::EStyle::kLong;
986 : case JSDateTimeFormat::DateTimeStyle::kMedium:
987 90 : return icu::DateFormat::EStyle::kMedium;
988 : case JSDateTimeFormat::DateTimeStyle::kShort:
989 90 : return icu::DateFormat::EStyle::kShort;
990 : case JSDateTimeFormat::DateTimeStyle::kUndefined:
991 0 : UNREACHABLE();
992 : }
993 0 : }
994 :
995 0 : icu::UnicodeString ReplaceSkeleton(const icu::UnicodeString input,
996 : Intl::HourCycle hc) {
997 : icu::UnicodeString result;
998 : char16_t to;
999 0 : switch (hc) {
1000 : case Intl::HourCycle::kH11:
1001 : to = 'K';
1002 0 : break;
1003 : case Intl::HourCycle::kH12:
1004 : to = 'h';
1005 0 : break;
1006 : case Intl::HourCycle::kH23:
1007 : to = 'H';
1008 0 : break;
1009 : case Intl::HourCycle::kH24:
1010 : to = 'k';
1011 0 : break;
1012 : case Intl::HourCycle::kUndefined:
1013 0 : UNREACHABLE();
1014 : }
1015 0 : for (int32_t i = 0; i < input.length(); i++) {
1016 : switch (input[i]) {
1017 : // We need to skip 'a', 'b', 'B' here due to
1018 : // https://unicode-org.atlassian.net/browse/ICU-20437
1019 : case 'a':
1020 : V8_FALLTHROUGH;
1021 : case 'b':
1022 : V8_FALLTHROUGH;
1023 : case 'B':
1024 : // ignore
1025 : break;
1026 : case 'h':
1027 : V8_FALLTHROUGH;
1028 : case 'H':
1029 : V8_FALLTHROUGH;
1030 : case 'K':
1031 : V8_FALLTHROUGH;
1032 : case 'k':
1033 : result += to;
1034 : break;
1035 : default:
1036 : result += input[i];
1037 : break;
1038 : }
1039 : }
1040 0 : return result;
1041 : }
1042 :
1043 270 : std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern(
1044 : JSDateTimeFormat::DateTimeStyle date_style,
1045 : JSDateTimeFormat::DateTimeStyle time_style, const icu::Locale& icu_locale,
1046 : Intl::HourCycle hc, icu::DateTimePatternGenerator& generator) {
1047 : std::unique_ptr<icu::SimpleDateFormat> result;
1048 270 : if (date_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
1049 216 : if (time_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
1050 : result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
1051 162 : icu::DateFormat::createDateTimeInstance(
1052 : DateTimeStyleToEStyle(date_style),
1053 : DateTimeStyleToEStyle(time_style), icu_locale)));
1054 : } else {
1055 : result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
1056 54 : icu::DateFormat::createDateInstance(DateTimeStyleToEStyle(date_style),
1057 : icu_locale)));
1058 : // For instance without time, we do not need to worry about the hour cycle
1059 : // impact so we can return directly.
1060 : return result;
1061 : }
1062 : } else {
1063 54 : if (time_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
1064 : result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
1065 54 : icu::DateFormat::createTimeInstance(DateTimeStyleToEStyle(time_style),
1066 : icu_locale)));
1067 : } else {
1068 0 : UNREACHABLE();
1069 : }
1070 : }
1071 216 : icu::UnicodeString pattern;
1072 216 : pattern = result->toPattern(pattern);
1073 :
1074 216 : UErrorCode status = U_ZERO_ERROR;
1075 : icu::UnicodeString skeleton =
1076 432 : icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
1077 216 : CHECK(U_SUCCESS(status));
1078 :
1079 : // If the skeleton match the HourCycle, we just return it.
1080 216 : if (hc == HourCycleFromPattern(pattern)) {
1081 : return result;
1082 : }
1083 :
1084 0 : return CreateICUDateFormatFromCache(icu_locale, ReplaceSkeleton(skeleton, hc),
1085 0 : generator);
1086 : }
1087 :
1088 290 : class DateTimePatternGeneratorCache {
1089 : public:
1090 : // Return a clone copy that the caller have to free.
1091 3585 : icu::DateTimePatternGenerator* CreateGenerator(const icu::Locale& locale) {
1092 3585 : std::string key(locale.getBaseName());
1093 3585 : base::MutexGuard guard(&mutex_);
1094 : auto it = map_.find(key);
1095 3585 : if (it != map_.end()) {
1096 3190 : return it->second->clone();
1097 : }
1098 395 : UErrorCode status = U_ZERO_ERROR;
1099 790 : map_[key].reset(icu::DateTimePatternGenerator::createInstance(
1100 : icu::Locale(key.c_str()), status));
1101 395 : CHECK(U_SUCCESS(status));
1102 790 : return map_[key]->clone();
1103 : }
1104 :
1105 : private:
1106 : std::map<std::string, std::unique_ptr<icu::DateTimePatternGenerator>> map_;
1107 : base::Mutex mutex_;
1108 : };
1109 :
1110 : } // namespace
1111 :
1112 : enum FormatMatcherOption { kBestFit, kBasic };
1113 :
1114 : // ecma402/#sec-initializedatetimeformat
1115 3756 : MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
1116 : Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
1117 : Handle<Object> locales, Handle<Object> input_options) {
1118 : date_time_format->set_flags(0);
1119 : // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
1120 : Maybe<std::vector<std::string>> maybe_requested_locales =
1121 3756 : Intl::CanonicalizeLocaleList(isolate, locales);
1122 3756 : MAYBE_RETURN(maybe_requested_locales, Handle<JSDateTimeFormat>());
1123 : std::vector<std::string> requested_locales =
1124 3747 : maybe_requested_locales.FromJust();
1125 : // 2. Let options be ? ToDateTimeOptions(options, "any", "date").
1126 : Handle<JSObject> options;
1127 7494 : ASSIGN_RETURN_ON_EXCEPTION(
1128 : isolate, options,
1129 : JSDateTimeFormat::ToDateTimeOptions(
1130 : isolate, input_options, RequiredOption::kAny, DefaultsOption::kDate),
1131 : JSDateTimeFormat);
1132 :
1133 : // 4. Let matcher be ? GetOption(options, "localeMatcher", "string",
1134 : // « "lookup", "best fit" », "best fit").
1135 : // 5. Set opt.[[localeMatcher]] to matcher.
1136 : Maybe<Intl::MatcherOption> maybe_locale_matcher =
1137 3747 : Intl::GetLocaleMatcher(isolate, options, "Intl.DateTimeFormat");
1138 3747 : MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSDateTimeFormat>());
1139 : Intl::MatcherOption locale_matcher = maybe_locale_matcher.FromJust();
1140 :
1141 : // 6. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined,
1142 : // undefined).
1143 : bool hour12;
1144 : Maybe<bool> maybe_get_hour12 = Intl::GetBoolOption(
1145 3747 : isolate, options, "hour12", "Intl.DateTimeFormat", &hour12);
1146 3747 : MAYBE_RETURN(maybe_get_hour12, Handle<JSDateTimeFormat>());
1147 :
1148 : // 7. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11",
1149 : // "h12", "h23", "h24" », undefined).
1150 : Maybe<Intl::HourCycle> maybe_hour_cycle =
1151 3747 : Intl::GetHourCycle(isolate, options, "Intl.DateTimeFormat");
1152 3747 : MAYBE_RETURN(maybe_hour_cycle, MaybeHandle<JSDateTimeFormat>());
1153 : Intl::HourCycle hour_cycle = maybe_hour_cycle.FromJust();
1154 :
1155 : // 8. If hour12 is not undefined, then
1156 3747 : if (maybe_get_hour12.FromJust()) {
1157 : // a. Let hourCycle be null.
1158 : hour_cycle = Intl::HourCycle::kUndefined;
1159 : }
1160 : // 9. Set opt.[[hc]] to hourCycle.
1161 :
1162 : // ecma402/#sec-intl.datetimeformat-internal-slots
1163 : // The value of the [[RelevantExtensionKeys]] internal slot is
1164 : // « "ca", "nu", "hc" ».
1165 7494 : std::set<std::string> relevant_extension_keys = {"nu", "ca", "hc"};
1166 :
1167 : // 10. Let localeData be %DateTimeFormat%.[[LocaleData]].
1168 : // 11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]],
1169 : // requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]],
1170 : // localeData).
1171 : //
1172 : Intl::ResolvedLocale r = Intl::ResolveLocale(
1173 : isolate, JSDateTimeFormat::GetAvailableLocales(), requested_locales,
1174 7494 : locale_matcher, relevant_extension_keys);
1175 :
1176 7494 : icu::Locale icu_locale = r.icu_locale;
1177 : DCHECK(!icu_locale.isBogus());
1178 :
1179 : // 17. Let timeZone be ? Get(options, "timeZone").
1180 : const std::vector<const char*> empty_values;
1181 : std::unique_ptr<char[]> timezone = nullptr;
1182 : Maybe<bool> maybe_timezone =
1183 : Intl::GetStringOption(isolate, options, "timeZone", empty_values,
1184 11241 : "Intl.DateTimeFormat", &timezone);
1185 3747 : MAYBE_RETURN(maybe_timezone, Handle<JSDateTimeFormat>());
1186 :
1187 3729 : std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone.get());
1188 3729 : if (tz.get() == nullptr) {
1189 432 : THROW_NEW_ERROR(isolate,
1190 : NewRangeError(MessageTemplate::kInvalidTimeZone,
1191 : isolate->factory()->NewStringFromAsciiChecked(
1192 : timezone.get())),
1193 : JSDateTimeFormat);
1194 : }
1195 :
1196 : std::unique_ptr<icu::Calendar> calendar(
1197 3585 : CreateCalendar(isolate, icu_locale, tz.release()));
1198 :
1199 : // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
1200 : // i. Throw a RangeError exception.
1201 3585 : if (calendar.get() == nullptr) {
1202 0 : THROW_NEW_ERROR(isolate,
1203 : NewRangeError(MessageTemplate::kInvalidTimeZone,
1204 : isolate->factory()->NewStringFromAsciiChecked(
1205 : timezone.get())),
1206 : JSDateTimeFormat);
1207 : }
1208 :
1209 : static base::LazyInstance<DateTimePatternGeneratorCache>::type
1210 : generator_cache = LAZY_INSTANCE_INITIALIZER;
1211 :
1212 : std::unique_ptr<icu::DateTimePatternGenerator> generator(
1213 3585 : generator_cache.Pointer()->CreateGenerator(icu_locale));
1214 :
1215 : // 15.Let hcDefault be dataLocaleData.[[hourCycle]].
1216 3585 : UErrorCode status = U_ZERO_ERROR;
1217 7170 : icu::UnicodeString hour_pattern = generator->getBestPattern("jjmm", status);
1218 3585 : CHECK(U_SUCCESS(status));
1219 3585 : Intl::HourCycle hc_default = HourCycleFromPattern(hour_pattern);
1220 :
1221 : // 16.Let hc be r.[[hc]].
1222 : Intl::HourCycle hc = Intl::HourCycle::kUndefined;
1223 3585 : if (hour_cycle == Intl::HourCycle::kUndefined) {
1224 7170 : auto hc_extension_it = r.extensions.find("hc");
1225 3585 : if (hc_extension_it != r.extensions.end()) {
1226 216 : hc = Intl::ToHourCycle(hc_extension_it->second.c_str());
1227 : }
1228 : } else {
1229 : hc = hour_cycle;
1230 : }
1231 : // 17. If hc is null, then
1232 3585 : if (hc == Intl::HourCycle::kUndefined) {
1233 : // a. Set hc to hcDefault.
1234 : hc = hc_default;
1235 : }
1236 :
1237 : // 18. If hour12 is not undefined, then
1238 3585 : if (maybe_get_hour12.FromJust()) {
1239 : // a. If hour12 is true, then
1240 36 : if (hour12) {
1241 : // i. If hcDefault is "h11" or "h23", then
1242 36 : if (hc_default == Intl::HourCycle::kH11 ||
1243 18 : hc_default == Intl::HourCycle::kH23) {
1244 : // 1. Set hc to "h11".
1245 : hc = Intl::HourCycle::kH11;
1246 : // ii. Else,
1247 : } else {
1248 : // 1. Set hc to "h12".
1249 : hc = Intl::HourCycle::kH12;
1250 : }
1251 : // b. Else,
1252 : } else {
1253 : // ii. If hcDefault is "h11" or "h23", then
1254 36 : if (hc_default == Intl::HourCycle::kH11 ||
1255 18 : hc_default == Intl::HourCycle::kH23) {
1256 : // 1. Set hc to "h23".
1257 : hc = Intl::HourCycle::kH23;
1258 : // iii. Else,
1259 : } else {
1260 : // 1. Set hc to "h24".
1261 : hc = Intl::HourCycle::kH24;
1262 : }
1263 : }
1264 : }
1265 3585 : date_time_format->set_hour_cycle(hc);
1266 :
1267 : DateTimeStyle date_style = DateTimeStyle::kUndefined;
1268 : DateTimeStyle time_style = DateTimeStyle::kUndefined;
1269 : std::unique_ptr<icu::SimpleDateFormat> icu_date_format;
1270 :
1271 3585 : if (FLAG_harmony_intl_datetime_style) {
1272 : // 28. Let dateStyle be ? GetOption(options, "dateStyle", "string", «
1273 : // "full", "long", "medium", "short" », undefined).
1274 : Maybe<DateTimeStyle> maybe_date_style =
1275 : Intl::GetStringOption<DateTimeStyle>(
1276 : isolate, options, "dateStyle", "Intl.DateTimeFormat",
1277 : {"full", "long", "medium", "short"},
1278 : {DateTimeStyle::kFull, DateTimeStyle::kLong, DateTimeStyle::kMedium,
1279 : DateTimeStyle::kShort},
1280 1593 : DateTimeStyle::kUndefined);
1281 531 : MAYBE_RETURN(maybe_date_style, MaybeHandle<JSDateTimeFormat>());
1282 : // 29. If dateStyle is not undefined, set dateTimeFormat.[[DateStyle]] to
1283 : // dateStyle.
1284 : date_style = maybe_date_style.FromJust();
1285 405 : if (date_style != DateTimeStyle::kUndefined) {
1286 288 : date_time_format->set_date_style(date_style);
1287 : }
1288 :
1289 : // 30. Let timeStyle be ? GetOption(options, "timeStyle", "string", «
1290 : // "full", "long", "medium", "short" »).
1291 : Maybe<DateTimeStyle> maybe_time_style =
1292 : Intl::GetStringOption<DateTimeStyle>(
1293 : isolate, options, "timeStyle", "Intl.DateTimeFormat",
1294 : {"full", "long", "medium", "short"},
1295 : {DateTimeStyle::kFull, DateTimeStyle::kLong, DateTimeStyle::kMedium,
1296 : DateTimeStyle::kShort},
1297 1215 : DateTimeStyle::kUndefined);
1298 405 : MAYBE_RETURN(maybe_time_style, MaybeHandle<JSDateTimeFormat>());
1299 :
1300 : // 31. If timeStyle is not undefined, set dateTimeFormat.[[TimeStyle]] to
1301 : // timeStyle.
1302 : time_style = maybe_time_style.FromJust();
1303 315 : if (time_style != DateTimeStyle::kUndefined) {
1304 216 : date_time_format->set_time_style(time_style);
1305 : }
1306 :
1307 : // 32. If dateStyle or timeStyle are not undefined, then
1308 315 : if (date_style != DateTimeStyle::kUndefined ||
1309 : time_style != DateTimeStyle::kUndefined) {
1310 540 : icu_date_format = DateTimeStylePattern(date_style, time_style, icu_locale,
1311 : hc, *generator);
1312 : }
1313 : }
1314 : // 33. Else,
1315 3369 : if (icu_date_format.get() == nullptr) {
1316 : bool has_hour_option = false;
1317 : // b. For each row of Table 5, except the header row, do
1318 : std::string skeleton;
1319 3099 : for (const PatternData& item : GetPatternData(hc)) {
1320 27891 : std::unique_ptr<char[]> input;
1321 : // i. Let prop be the name given in the Property column of the row.
1322 : // ii. Let value be ? GetOption(options, prop, "string", « the strings
1323 : // given in the Values column of the row », undefined).
1324 : Maybe<bool> maybe_get_option = Intl::GetStringOption(
1325 : isolate, options, item.property.c_str(), item.allowed_values,
1326 83673 : "Intl.DateTimeFormat", &input);
1327 27891 : MAYBE_RETURN(maybe_get_option, Handle<JSDateTimeFormat>());
1328 27891 : if (maybe_get_option.FromJust()) {
1329 17610 : if (item.property == "hour") {
1330 : has_hour_option = true;
1331 : }
1332 : DCHECK_NOT_NULL(input.get());
1333 : // iii. Set opt.[[<prop>]] to value.
1334 17610 : skeleton += item.map.find(input.get())->second;
1335 : }
1336 : }
1337 :
1338 : enum FormatMatcherOption { kBestFit, kBasic };
1339 : // We implement only best fit algorithm, but still need to check
1340 : // if the formatMatcher values are in range.
1341 : // c. Let matcher be ? GetOption(options, "formatMatcher", "string",
1342 : // « "basic", "best fit" », "best fit").
1343 : Maybe<FormatMatcherOption> maybe_format_matcher =
1344 : Intl::GetStringOption<FormatMatcherOption>(
1345 : isolate, options, "formatMatcher", "Intl.DateTimeFormat",
1346 : {"best fit", "basic"},
1347 : {FormatMatcherOption::kBestFit, FormatMatcherOption::kBasic},
1348 9297 : FormatMatcherOption::kBestFit);
1349 3099 : MAYBE_RETURN(maybe_format_matcher, MaybeHandle<JSDateTimeFormat>());
1350 : // TODO(ftang): uncomment the following line and handle format_matcher.
1351 : // FormatMatcherOption format_matcher = maybe_format_matcher.FromJust();
1352 :
1353 6198 : icu::UnicodeString skeleton_ustr(skeleton.c_str());
1354 : icu_date_format =
1355 6198 : CreateICUDateFormatFromCache(icu_locale, skeleton_ustr, *generator);
1356 3099 : if (icu_date_format.get() == nullptr) {
1357 : // Remove extensions and try again.
1358 9 : icu_locale = icu::Locale(icu_locale.getBaseName());
1359 : icu_date_format =
1360 18 : CreateICUDateFormatFromCache(icu_locale, skeleton_ustr, *generator);
1361 9 : if (icu_date_format.get() == nullptr) {
1362 0 : FATAL("Failed to create ICU date format, are ICU data files missing?");
1363 : }
1364 : }
1365 :
1366 : // g. If dateTimeFormat.[[Hour]] is not undefined, then
1367 3099 : if (!has_hour_option) {
1368 : // h. Else, i. Set dateTimeFormat.[[HourCycle]] to undefined.
1369 2683 : date_time_format->set_hour_cycle(Intl::HourCycle::kUndefined);
1370 : }
1371 : }
1372 :
1373 : // The creation of Calendar depends on timeZone so we have to put 13 after 17.
1374 : // Also icu_date_format is not created until here.
1375 : // 13. Set dateTimeFormat.[[Calendar]] to r.[[ca]].
1376 3369 : icu_date_format->adoptCalendar(calendar.release());
1377 :
1378 : // 12.1.1 InitializeDateTimeFormat ( dateTimeFormat, locales, options )
1379 : //
1380 : // Steps 8-9 set opt.[[hc]] to value *other than undefined*
1381 : // if "hour12" is set or "hourCycle" is set in the option.
1382 : //
1383 : // 9.2.6 ResolveLocale (... )
1384 : // Step 8.h / 8.i and 8.k
1385 : //
1386 : // An hour12 option always overrides an hourCycle option.
1387 : // Additionally hour12 and hourCycle both clear out any existing Unicode
1388 : // extension key in the input locale.
1389 : //
1390 : // See details in https://github.com/tc39/test262/pull/2035
1391 6702 : if (maybe_get_hour12.FromJust() ||
1392 : maybe_hour_cycle.FromJust() != Intl::HourCycle::kUndefined) {
1393 72 : auto hc_extension_it = r.extensions.find("hc");
1394 36 : if (hc_extension_it != r.extensions.end()) {
1395 0 : if (date_time_format->hour_cycle() !=
1396 0 : Intl::ToHourCycle(hc_extension_it->second.c_str())) {
1397 : // Remove -hc- if it does not agree with what we used.
1398 0 : UErrorCode status = U_ZERO_ERROR;
1399 0 : icu_locale.setKeywordValue(uloc_toLegacyKey("hc"), nullptr, status);
1400 0 : CHECK(U_SUCCESS(status));
1401 : }
1402 : }
1403 : }
1404 :
1405 : Handle<Managed<icu::Locale>> managed_locale =
1406 3369 : Managed<icu::Locale>::FromRawPtr(isolate, 0, icu_locale.clone());
1407 :
1408 3369 : date_time_format->set_icu_locale(*managed_locale);
1409 : Handle<Managed<icu::SimpleDateFormat>> managed_format =
1410 : Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0,
1411 6738 : std::move(icu_date_format));
1412 3369 : date_time_format->set_icu_simple_date_format(*managed_format);
1413 :
1414 3369 : return date_time_format;
1415 : }
1416 :
1417 : namespace {
1418 :
1419 : // The list comes from third_party/icu/source/i18n/unicode/udat.h.
1420 : // They're mapped to DateTimeFormat components listed at
1421 : // https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
1422 387 : Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
1423 387 : switch (field_id) {
1424 : case -1:
1425 : return isolate->factory()->literal_string();
1426 : case UDAT_YEAR_FIELD:
1427 : case UDAT_EXTENDED_YEAR_FIELD:
1428 : case UDAT_YEAR_NAME_FIELD:
1429 : return isolate->factory()->year_string();
1430 : case UDAT_MONTH_FIELD:
1431 : case UDAT_STANDALONE_MONTH_FIELD:
1432 : return isolate->factory()->month_string();
1433 : case UDAT_DATE_FIELD:
1434 : return isolate->factory()->day_string();
1435 : case UDAT_HOUR_OF_DAY1_FIELD:
1436 : case UDAT_HOUR_OF_DAY0_FIELD:
1437 : case UDAT_HOUR1_FIELD:
1438 : case UDAT_HOUR0_FIELD:
1439 : return isolate->factory()->hour_string();
1440 : case UDAT_MINUTE_FIELD:
1441 : return isolate->factory()->minute_string();
1442 : case UDAT_SECOND_FIELD:
1443 : return isolate->factory()->second_string();
1444 : case UDAT_DAY_OF_WEEK_FIELD:
1445 : case UDAT_DOW_LOCAL_FIELD:
1446 : case UDAT_STANDALONE_DAY_FIELD:
1447 : return isolate->factory()->weekday_string();
1448 : case UDAT_AM_PM_FIELD:
1449 : return isolate->factory()->dayPeriod_string();
1450 : case UDAT_TIMEZONE_FIELD:
1451 : case UDAT_TIMEZONE_RFC_FIELD:
1452 : case UDAT_TIMEZONE_GENERIC_FIELD:
1453 : case UDAT_TIMEZONE_SPECIAL_FIELD:
1454 : case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
1455 : case UDAT_TIMEZONE_ISO_FIELD:
1456 : case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
1457 : return isolate->factory()->timeZoneName_string();
1458 : case UDAT_ERA_FIELD:
1459 : return isolate->factory()->era_string();
1460 : default:
1461 : // Other UDAT_*_FIELD's cannot show up because there is no way to specify
1462 : // them via options of Intl.DateTimeFormat.
1463 0 : UNREACHABLE();
1464 : // To prevent MSVC from issuing C4715 warning.
1465 : return Handle<String>();
1466 : }
1467 : }
1468 :
1469 : } // namespace
1470 :
1471 153 : MaybeHandle<Object> JSDateTimeFormat::FormatToParts(
1472 : Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
1473 : double date_value) {
1474 : Factory* factory = isolate->factory();
1475 : icu::SimpleDateFormat* format =
1476 : date_time_format->icu_simple_date_format()->raw();
1477 153 : CHECK_NOT_NULL(format);
1478 :
1479 153 : icu::UnicodeString formatted;
1480 306 : icu::FieldPositionIterator fp_iter;
1481 153 : icu::FieldPosition fp;
1482 153 : UErrorCode status = U_ZERO_ERROR;
1483 153 : format->format(date_value, formatted, &fp_iter, status);
1484 153 : if (U_FAILURE(status)) {
1485 0 : THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
1486 : }
1487 :
1488 : Handle<JSArray> result = factory->NewJSArray(0);
1489 : int32_t length = formatted.length();
1490 153 : if (length == 0) return result;
1491 :
1492 : int index = 0;
1493 : int32_t previous_end_pos = 0;
1494 : Handle<String> substring;
1495 927 : while (fp_iter.next(fp)) {
1496 : int32_t begin_pos = fp.getBeginIndex();
1497 : int32_t end_pos = fp.getEndIndex();
1498 :
1499 387 : if (previous_end_pos < begin_pos) {
1500 432 : ASSIGN_RETURN_ON_EXCEPTION(
1501 : isolate, substring,
1502 : Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
1503 : Object);
1504 : Intl::AddElement(isolate, result, index,
1505 216 : IcuDateFieldIdToDateType(-1, isolate), substring);
1506 216 : ++index;
1507 : }
1508 774 : ASSIGN_RETURN_ON_EXCEPTION(
1509 : isolate, substring,
1510 : Intl::ToString(isolate, formatted, begin_pos, end_pos), Object);
1511 387 : Intl::AddElement(isolate, result, index,
1512 : IcuDateFieldIdToDateType(fp.getField(), isolate),
1513 387 : substring);
1514 : previous_end_pos = end_pos;
1515 387 : ++index;
1516 : }
1517 153 : if (previous_end_pos < length) {
1518 0 : ASSIGN_RETURN_ON_EXCEPTION(
1519 : isolate, substring,
1520 : Intl::ToString(isolate, formatted, previous_end_pos, length), Object);
1521 : Intl::AddElement(isolate, result, index,
1522 0 : IcuDateFieldIdToDateType(-1, isolate), substring);
1523 : }
1524 153 : JSObject::ValidateElements(*result);
1525 153 : return result;
1526 : }
1527 :
1528 131 : const std::set<std::string>& JSDateTimeFormat::GetAvailableLocales() {
1529 3878 : return Intl::GetAvailableLocalesForDateFormat();
1530 : }
1531 :
1532 225 : Handle<String> JSDateTimeFormat::HourCycleAsString() const {
1533 225 : switch (hour_cycle()) {
1534 : case Intl::HourCycle::kUndefined:
1535 : return GetReadOnlyRoots().undefined_string_handle();
1536 : case Intl::HourCycle::kH11:
1537 : return GetReadOnlyRoots().h11_string_handle();
1538 : case Intl::HourCycle::kH12:
1539 : return GetReadOnlyRoots().h12_string_handle();
1540 : case Intl::HourCycle::kH23:
1541 : return GetReadOnlyRoots().h23_string_handle();
1542 : case Intl::HourCycle::kH24:
1543 : return GetReadOnlyRoots().h24_string_handle();
1544 : default:
1545 0 : UNREACHABLE();
1546 : }
1547 : }
1548 :
1549 : } // namespace internal
1550 120216 : } // namespace v8
|