Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * The Intl module specified by standard ECMA-402,
9 : * ECMAScript Internationalization API Specification.
10 : */
11 :
12 : #include "builtin/Intl.h"
13 :
14 : #include "mozilla/Casting.h"
15 : #include "mozilla/HashFunctions.h"
16 : #include "mozilla/PodOperations.h"
17 : #include "mozilla/Range.h"
18 : #include "mozilla/TypeTraits.h"
19 :
20 : #include <string.h>
21 :
22 : #include "jsapi.h"
23 : #include "jsatom.h"
24 : #include "jscntxt.h"
25 : #include "jsfriendapi.h"
26 : #include "jsobj.h"
27 : #include "jsstr.h"
28 : #include "jsutil.h"
29 :
30 : #include "builtin/IntlTimeZoneData.h"
31 : #include "ds/Sort.h"
32 : #if ENABLE_INTL_API
33 : #include "unicode/ucal.h"
34 : #include "unicode/ucol.h"
35 : #include "unicode/udat.h"
36 : #include "unicode/udatpg.h"
37 : #include "unicode/uenum.h"
38 : #include "unicode/uloc.h"
39 : #include "unicode/unum.h"
40 : #include "unicode/unumsys.h"
41 : #include "unicode/upluralrules.h"
42 : #include "unicode/ustring.h"
43 : #endif
44 : #include "vm/DateTime.h"
45 : #include "vm/GlobalObject.h"
46 : #include "vm/Interpreter.h"
47 : #include "vm/SelfHosting.h"
48 : #include "vm/Stack.h"
49 : #include "vm/String.h"
50 : #include "vm/StringBuffer.h"
51 : #include "vm/Unicode.h"
52 :
53 : #include "jsobjinlines.h"
54 :
55 : #include "vm/NativeObject-inl.h"
56 :
57 : using namespace js;
58 :
59 : using mozilla::AssertedCast;
60 : using mozilla::IsFinite;
61 : using mozilla::IsNaN;
62 : using mozilla::IsNegativeZero;
63 : using mozilla::PodCopy;
64 : using mozilla::Range;
65 : using mozilla::RangedPtr;
66 :
67 : /*
68 : * Pervasive note: ICU functions taking a UErrorCode in/out parameter always
69 : * test that parameter before doing anything, and will return immediately if
70 : * the value indicates that a failure occurred in a prior ICU call,
71 : * without doing anything else. See
72 : * http://userguide.icu-project.org/design#TOC-Error-Handling
73 : */
74 :
75 :
76 : /******************** ICU stubs ********************/
77 :
78 : #if !ENABLE_INTL_API
79 :
80 : /*
81 : * When the Internationalization API isn't enabled, we also shouldn't link
82 : * against ICU. However, we still want to compile this code in order to prevent
83 : * bit rot. The following stub implementations for ICU functions make this
84 : * possible. The functions using them should never be called, so they assert
85 : * and return error codes. Signatures adapted from ICU header files locid.h,
86 : * numsys.h, ucal.h, ucol.h, udat.h, udatpg.h, uenum.h, unum.h, uloc.h;
87 : * see the ICU directory for license.
88 : */
89 :
90 : namespace {
91 :
92 : enum UErrorCode {
93 : U_ZERO_ERROR,
94 : U_BUFFER_OVERFLOW_ERROR,
95 : };
96 :
97 : typedef bool UBool;
98 : typedef char16_t UChar;
99 : typedef double UDate;
100 :
101 : inline UBool
102 : U_FAILURE(UErrorCode code)
103 : {
104 : MOZ_CRASH("U_FAILURE: Intl API disabled");
105 : }
106 :
107 : const char*
108 : uloc_getAvailable(int32_t n)
109 : {
110 : MOZ_CRASH("uloc_getAvailable: Intl API disabled");
111 : }
112 :
113 : int32_t
114 : uloc_countAvailable()
115 : {
116 : MOZ_CRASH("uloc_countAvailable: Intl API disabled");
117 : }
118 :
119 : UBool
120 : uloc_isRightToLeft(const char* locale)
121 : {
122 : MOZ_CRASH("uloc_isRightToLeft: Intl API disabled");
123 : }
124 :
125 : struct UEnumeration;
126 :
127 : int32_t
128 : uenum_count(UEnumeration* en, UErrorCode* status)
129 : {
130 : MOZ_CRASH("uenum_count: Intl API disabled");
131 : }
132 :
133 : const char*
134 : uenum_next(UEnumeration* en, int32_t* resultLength, UErrorCode* status)
135 : {
136 : MOZ_CRASH("uenum_next: Intl API disabled");
137 : }
138 :
139 : void
140 : uenum_close(UEnumeration* en)
141 : {
142 : MOZ_CRASH("uenum_close: Intl API disabled");
143 : }
144 :
145 : struct UCollator;
146 :
147 : enum UColAttribute {
148 : UCOL_ALTERNATE_HANDLING,
149 : UCOL_CASE_FIRST,
150 : UCOL_CASE_LEVEL,
151 : UCOL_NORMALIZATION_MODE,
152 : UCOL_STRENGTH,
153 : UCOL_NUMERIC_COLLATION,
154 : };
155 :
156 : enum UColAttributeValue {
157 : UCOL_DEFAULT = -1,
158 : UCOL_PRIMARY = 0,
159 : UCOL_SECONDARY = 1,
160 : UCOL_TERTIARY = 2,
161 : UCOL_OFF = 16,
162 : UCOL_ON = 17,
163 : UCOL_SHIFTED = 20,
164 : UCOL_LOWER_FIRST = 24,
165 : UCOL_UPPER_FIRST = 25,
166 : };
167 :
168 : enum UCollationResult {
169 : UCOL_EQUAL = 0,
170 : UCOL_GREATER = 1,
171 : UCOL_LESS = -1
172 : };
173 :
174 : int32_t
175 : ucol_countAvailable()
176 : {
177 : MOZ_CRASH("ucol_countAvailable: Intl API disabled");
178 : }
179 :
180 : const char*
181 : ucol_getAvailable(int32_t localeIndex)
182 : {
183 : MOZ_CRASH("ucol_getAvailable: Intl API disabled");
184 : }
185 :
186 : UEnumeration*
187 : ucol_openAvailableLocales(UErrorCode* status)
188 : {
189 : MOZ_CRASH("ucol_openAvailableLocales: Intl API disabled");
190 : }
191 :
192 : UCollator*
193 : ucol_open(const char* loc, UErrorCode* status)
194 : {
195 : MOZ_CRASH("ucol_open: Intl API disabled");
196 : }
197 :
198 : UColAttributeValue
199 : ucol_getAttribute(const UCollator* coll, UColAttribute attr, UErrorCode* status)
200 : {
201 : MOZ_CRASH("ucol_getAttribute: Intl API disabled");
202 : }
203 :
204 : void
205 : ucol_setAttribute(UCollator* coll, UColAttribute attr, UColAttributeValue value, UErrorCode* status)
206 : {
207 : MOZ_CRASH("ucol_setAttribute: Intl API disabled");
208 : }
209 :
210 : UCollationResult
211 : ucol_strcoll(const UCollator* coll, const UChar* source, int32_t sourceLength,
212 : const UChar* target, int32_t targetLength)
213 : {
214 : MOZ_CRASH("ucol_strcoll: Intl API disabled");
215 : }
216 :
217 : void
218 : ucol_close(UCollator* coll)
219 : {
220 : MOZ_CRASH("ucol_close: Intl API disabled");
221 : }
222 :
223 : UEnumeration*
224 : ucol_getKeywordValuesForLocale(const char* key, const char* locale, UBool commonlyUsed,
225 : UErrorCode* status)
226 : {
227 : MOZ_CRASH("ucol_getKeywordValuesForLocale: Intl API disabled");
228 : }
229 :
230 : struct UParseError;
231 : struct UFieldPosition;
232 : struct UFieldPositionIterator;
233 : typedef void* UNumberFormat;
234 :
235 : enum UNumberFormatStyle {
236 : UNUM_DECIMAL = 1,
237 : UNUM_CURRENCY,
238 : UNUM_PERCENT,
239 : UNUM_CURRENCY_ISO,
240 : UNUM_CURRENCY_PLURAL,
241 : };
242 :
243 : enum UNumberFormatRoundingMode {
244 : UNUM_ROUND_HALFUP,
245 : };
246 :
247 : enum UNumberFormatAttribute {
248 : UNUM_GROUPING_USED,
249 : UNUM_MIN_INTEGER_DIGITS,
250 : UNUM_MAX_FRACTION_DIGITS,
251 : UNUM_MIN_FRACTION_DIGITS,
252 : UNUM_ROUNDING_MODE,
253 : UNUM_SIGNIFICANT_DIGITS_USED,
254 : UNUM_MIN_SIGNIFICANT_DIGITS,
255 : UNUM_MAX_SIGNIFICANT_DIGITS,
256 : };
257 :
258 : enum UNumberFormatTextAttribute {
259 : UNUM_CURRENCY_CODE,
260 : };
261 :
262 : int32_t
263 : unum_countAvailable()
264 : {
265 : MOZ_CRASH("unum_countAvailable: Intl API disabled");
266 : }
267 :
268 : const char*
269 : unum_getAvailable(int32_t localeIndex)
270 : {
271 : MOZ_CRASH("unum_getAvailable: Intl API disabled");
272 : }
273 :
274 : UNumberFormat*
275 : unum_open(UNumberFormatStyle style, const UChar* pattern, int32_t patternLength,
276 : const char* locale, UParseError* parseErr, UErrorCode* status)
277 : {
278 : MOZ_CRASH("unum_open: Intl API disabled");
279 : }
280 :
281 : void
282 : unum_setAttribute(UNumberFormat* fmt, UNumberFormatAttribute attr, int32_t newValue)
283 : {
284 : MOZ_CRASH("unum_setAttribute: Intl API disabled");
285 : }
286 :
287 : int32_t
288 : unum_formatDoubleForFields(const UNumberFormat* fmt, double number, UChar* result,
289 : int32_t resultLength, UFieldPositionIterator* fpositer,
290 : UErrorCode* status)
291 : {
292 : MOZ_CRASH("unum_formatDoubleForFields: Intl API disabled");
293 : }
294 :
295 : enum UNumberFormatFields {
296 : UNUM_INTEGER_FIELD,
297 : UNUM_GROUPING_SEPARATOR_FIELD,
298 : UNUM_DECIMAL_SEPARATOR_FIELD,
299 : UNUM_FRACTION_FIELD,
300 : UNUM_SIGN_FIELD,
301 : UNUM_PERCENT_FIELD,
302 : UNUM_CURRENCY_FIELD,
303 : UNUM_PERMILL_FIELD,
304 : UNUM_EXPONENT_SYMBOL_FIELD,
305 : UNUM_EXPONENT_SIGN_FIELD,
306 : UNUM_EXPONENT_FIELD,
307 : UNUM_FIELD_COUNT,
308 : };
309 :
310 : void
311 : unum_close(UNumberFormat* fmt)
312 : {
313 : MOZ_CRASH("unum_close: Intl API disabled");
314 : }
315 :
316 : void
317 : unum_setTextAttribute(UNumberFormat* fmt, UNumberFormatTextAttribute tag, const UChar* newValue,
318 : int32_t newValueLength, UErrorCode* status)
319 : {
320 : MOZ_CRASH("unum_setTextAttribute: Intl API disabled");
321 : }
322 :
323 : typedef void* UNumberingSystem;
324 :
325 : UNumberingSystem*
326 : unumsys_open(const char* locale, UErrorCode* status)
327 : {
328 : MOZ_CRASH("unumsys_open: Intl API disabled");
329 : }
330 :
331 : const char*
332 : unumsys_getName(const UNumberingSystem* unumsys)
333 : {
334 : MOZ_CRASH("unumsys_getName: Intl API disabled");
335 : }
336 :
337 : void
338 : unumsys_close(UNumberingSystem* unumsys)
339 : {
340 : MOZ_CRASH("unumsys_close: Intl API disabled");
341 : }
342 :
343 : typedef void* UCalendar;
344 :
345 : enum UCalendarType {
346 : UCAL_TRADITIONAL,
347 : UCAL_DEFAULT = UCAL_TRADITIONAL,
348 : UCAL_GREGORIAN
349 : };
350 :
351 : enum UCalendarAttribute {
352 : UCAL_FIRST_DAY_OF_WEEK,
353 : UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
354 : };
355 :
356 : enum UCalendarDaysOfWeek {
357 : UCAL_SUNDAY,
358 : UCAL_MONDAY,
359 : UCAL_TUESDAY,
360 : UCAL_WEDNESDAY,
361 : UCAL_THURSDAY,
362 : UCAL_FRIDAY,
363 : UCAL_SATURDAY
364 : };
365 :
366 : enum UCalendarWeekdayType {
367 : UCAL_WEEKDAY,
368 : UCAL_WEEKEND,
369 : UCAL_WEEKEND_ONSET,
370 : UCAL_WEEKEND_CEASE
371 : };
372 :
373 : enum UCalendarDateFields {
374 : UCAL_ERA,
375 : UCAL_YEAR,
376 : UCAL_MONTH,
377 : UCAL_WEEK_OF_YEAR,
378 : UCAL_WEEK_OF_MONTH,
379 : UCAL_DATE,
380 : UCAL_DAY_OF_YEAR,
381 : UCAL_DAY_OF_WEEK,
382 : UCAL_DAY_OF_WEEK_IN_MONTH,
383 : UCAL_AM_PM,
384 : UCAL_HOUR,
385 : UCAL_HOUR_OF_DAY,
386 : UCAL_MINUTE,
387 : UCAL_SECOND,
388 : UCAL_MILLISECOND,
389 : UCAL_ZONE_OFFSET,
390 : UCAL_DST_OFFSET,
391 : UCAL_YEAR_WOY,
392 : UCAL_DOW_LOCAL,
393 : UCAL_EXTENDED_YEAR,
394 : UCAL_JULIAN_DAY,
395 : UCAL_MILLISECONDS_IN_DAY,
396 : UCAL_IS_LEAP_MONTH,
397 : UCAL_FIELD_COUNT,
398 : UCAL_DAY_OF_MONTH = UCAL_DATE
399 : };
400 :
401 : enum UCalendarMonths {
402 : UCAL_JANUARY,
403 : UCAL_FEBRUARY,
404 : UCAL_MARCH,
405 : UCAL_APRIL,
406 : UCAL_MAY,
407 : UCAL_JUNE,
408 : UCAL_JULY,
409 : UCAL_AUGUST,
410 : UCAL_SEPTEMBER,
411 : UCAL_OCTOBER,
412 : UCAL_NOVEMBER,
413 : UCAL_DECEMBER,
414 : UCAL_UNDECIMBER
415 : };
416 :
417 : enum UCalendarAMPMs {
418 : UCAL_AM,
419 : UCAL_PM
420 : };
421 :
422 : UCalendar*
423 : ucal_open(const UChar* zoneID, int32_t len, const char* locale,
424 : UCalendarType type, UErrorCode* status)
425 : {
426 : MOZ_CRASH("ucal_open: Intl API disabled");
427 : }
428 :
429 : const char*
430 : ucal_getType(const UCalendar* cal, UErrorCode* status)
431 : {
432 : MOZ_CRASH("ucal_getType: Intl API disabled");
433 : }
434 :
435 : UEnumeration*
436 : ucal_getKeywordValuesForLocale(const char* key, const char* locale,
437 : UBool commonlyUsed, UErrorCode* status)
438 : {
439 : MOZ_CRASH("ucal_getKeywordValuesForLocale: Intl API disabled");
440 : }
441 :
442 : void
443 : ucal_close(UCalendar* cal)
444 : {
445 : MOZ_CRASH("ucal_close: Intl API disabled");
446 : }
447 :
448 : UCalendarWeekdayType
449 : ucal_getDayOfWeekType(const UCalendar *cal, UCalendarDaysOfWeek dayOfWeek, UErrorCode* status)
450 : {
451 : MOZ_CRASH("ucal_getDayOfWeekType: Intl API disabled");
452 : }
453 :
454 : int32_t
455 : ucal_getAttribute(const UCalendar* cal,
456 : UCalendarAttribute attr)
457 : {
458 : MOZ_CRASH("ucal_getAttribute: Intl API disabled");
459 : }
460 :
461 : int32_t
462 : ucal_get(const UCalendar *cal, UCalendarDateFields field, UErrorCode *status)
463 : {
464 : MOZ_CRASH("ucal_get: Intl API disabled");
465 : }
466 :
467 : UEnumeration*
468 : ucal_openTimeZones(UErrorCode* status)
469 : {
470 : MOZ_CRASH("ucal_openTimeZones: Intl API disabled");
471 : }
472 :
473 : int32_t
474 : ucal_getCanonicalTimeZoneID(const UChar* id, int32_t len, UChar* result, int32_t resultCapacity,
475 : UBool* isSystemID, UErrorCode* status)
476 : {
477 : MOZ_CRASH("ucal_getCanonicalTimeZoneID: Intl API disabled");
478 : }
479 :
480 : int32_t
481 : ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* status)
482 : {
483 : MOZ_CRASH("ucal_getDefaultTimeZone: Intl API disabled");
484 : }
485 :
486 : enum UDateTimePatternField {
487 : UDATPG_YEAR_FIELD,
488 : UDATPG_MONTH_FIELD,
489 : UDATPG_WEEK_OF_YEAR_FIELD,
490 : UDATPG_DAY_FIELD,
491 : };
492 :
493 : typedef void* UDateTimePatternGenerator;
494 :
495 : UDateTimePatternGenerator*
496 : udatpg_open(const char* locale, UErrorCode* pErrorCode)
497 : {
498 : MOZ_CRASH("udatpg_open: Intl API disabled");
499 : }
500 :
501 : int32_t
502 : udatpg_getBestPattern(UDateTimePatternGenerator* dtpg, const UChar* skeleton,
503 : int32_t length, UChar* bestPattern, int32_t capacity,
504 : UErrorCode* pErrorCode)
505 : {
506 : MOZ_CRASH("udatpg_getBestPattern: Intl API disabled");
507 : }
508 :
509 : static const UChar *
510 : udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg,
511 : UDateTimePatternField field,
512 : int32_t *pLength)
513 : {
514 : MOZ_CRASH("udatpg_getAppendItemName: Intl API disabled");
515 : }
516 :
517 : void
518 : udatpg_close(UDateTimePatternGenerator* dtpg)
519 : {
520 : MOZ_CRASH("udatpg_close: Intl API disabled");
521 : }
522 :
523 : typedef void* UCalendar;
524 : typedef void* UDateFormat;
525 :
526 : enum UDateFormatField {
527 : UDAT_ERA_FIELD = 0,
528 : UDAT_YEAR_FIELD = 1,
529 : UDAT_MONTH_FIELD = 2,
530 : UDAT_DATE_FIELD = 3,
531 : UDAT_HOUR_OF_DAY1_FIELD = 4,
532 : UDAT_HOUR_OF_DAY0_FIELD = 5,
533 : UDAT_MINUTE_FIELD = 6,
534 : UDAT_SECOND_FIELD = 7,
535 : UDAT_FRACTIONAL_SECOND_FIELD = 8,
536 : UDAT_DAY_OF_WEEK_FIELD = 9,
537 : UDAT_DAY_OF_YEAR_FIELD = 10,
538 : UDAT_DAY_OF_WEEK_IN_MONTH_FIELD = 11,
539 : UDAT_WEEK_OF_YEAR_FIELD = 12,
540 : UDAT_WEEK_OF_MONTH_FIELD = 13,
541 : UDAT_AM_PM_FIELD = 14,
542 : UDAT_HOUR1_FIELD = 15,
543 : UDAT_HOUR0_FIELD = 16,
544 : UDAT_TIMEZONE_FIELD = 17,
545 : UDAT_YEAR_WOY_FIELD = 18,
546 : UDAT_DOW_LOCAL_FIELD = 19,
547 : UDAT_EXTENDED_YEAR_FIELD = 20,
548 : UDAT_JULIAN_DAY_FIELD = 21,
549 : UDAT_MILLISECONDS_IN_DAY_FIELD = 22,
550 : UDAT_TIMEZONE_RFC_FIELD = 23,
551 : UDAT_TIMEZONE_GENERIC_FIELD = 24,
552 : UDAT_STANDALONE_DAY_FIELD = 25,
553 : UDAT_STANDALONE_MONTH_FIELD = 26,
554 : UDAT_QUARTER_FIELD = 27,
555 : UDAT_STANDALONE_QUARTER_FIELD = 28,
556 : UDAT_TIMEZONE_SPECIAL_FIELD = 29,
557 : UDAT_YEAR_NAME_FIELD = 30,
558 : UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD = 31,
559 : UDAT_TIMEZONE_ISO_FIELD = 32,
560 : UDAT_TIMEZONE_ISO_LOCAL_FIELD = 33,
561 : UDAT_RELATED_YEAR_FIELD = 34,
562 : UDAT_AM_PM_MIDNIGHT_NOON_FIELD = 35,
563 : UDAT_FLEXIBLE_DAY_PERIOD_FIELD = 36,
564 : UDAT_TIME_SEPARATOR_FIELD = 37,
565 : UDAT_FIELD_COUNT = 38
566 : };
567 :
568 : enum UDateFormatStyle {
569 : UDAT_FULL,
570 : UDAT_LONG,
571 : UDAT_MEDIUM,
572 : UDAT_SHORT,
573 : UDAT_DEFAULT = UDAT_MEDIUM,
574 : UDAT_NONE = -1,
575 : UDAT_PATTERN = -2,
576 : UDAT_IGNORE = UDAT_PATTERN
577 : };
578 :
579 : enum UDateFormatSymbolType {
580 : UDAT_ERAS,
581 : UDAT_MONTHS,
582 : UDAT_SHORT_MONTHS,
583 : UDAT_WEEKDAYS,
584 : UDAT_SHORT_WEEKDAYS,
585 : UDAT_AM_PMS,
586 : UDAT_LOCALIZED_CHARS,
587 : UDAT_ERA_NAMES,
588 : UDAT_NARROW_MONTHS,
589 : UDAT_NARROW_WEEKDAYS,
590 : UDAT_STANDALONE_MONTHS,
591 : UDAT_STANDALONE_SHORT_MONTHS,
592 : UDAT_STANDALONE_NARROW_MONTHS,
593 : UDAT_STANDALONE_WEEKDAYS,
594 : UDAT_STANDALONE_SHORT_WEEKDAYS,
595 : UDAT_STANDALONE_NARROW_WEEKDAYS,
596 : UDAT_QUARTERS,
597 : UDAT_SHORT_QUARTERS,
598 : UDAT_STANDALONE_QUARTERS,
599 : UDAT_STANDALONE_SHORT_QUARTERS,
600 : UDAT_SHORTER_WEEKDAYS,
601 : UDAT_STANDALONE_SHORTER_WEEKDAYS,
602 : UDAT_CYCLIC_YEARS_WIDE,
603 : UDAT_CYCLIC_YEARS_ABBREVIATED,
604 : UDAT_CYCLIC_YEARS_NARROW,
605 : UDAT_ZODIAC_NAMES_WIDE,
606 : UDAT_ZODIAC_NAMES_ABBREVIATED,
607 : UDAT_ZODIAC_NAMES_NARROW
608 : };
609 :
610 : int32_t
611 : udat_countAvailable()
612 : {
613 : MOZ_CRASH("udat_countAvailable: Intl API disabled");
614 : }
615 :
616 : int32_t
617 : udat_toPattern(const UDateFormat* fmt, UBool localized, UChar* result,
618 : int32_t resultLength, UErrorCode* status)
619 : {
620 : MOZ_CRASH("udat_toPattern: Intl API disabled");
621 : }
622 :
623 : const char*
624 : udat_getAvailable(int32_t localeIndex)
625 : {
626 : MOZ_CRASH("udat_getAvailable: Intl API disabled");
627 : }
628 :
629 : UDateFormat*
630 : udat_open(UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const char* locale,
631 : const UChar* tzID, int32_t tzIDLength, const UChar* pattern,
632 : int32_t patternLength, UErrorCode* status)
633 : {
634 : MOZ_CRASH("udat_open: Intl API disabled");
635 : }
636 :
637 : const UCalendar*
638 : udat_getCalendar(const UDateFormat* fmt)
639 : {
640 : MOZ_CRASH("udat_getCalendar: Intl API disabled");
641 : }
642 :
643 : void
644 : ucal_setGregorianChange(UCalendar* cal, UDate date, UErrorCode* pErrorCode)
645 : {
646 : MOZ_CRASH("ucal_setGregorianChange: Intl API disabled");
647 : }
648 :
649 : int32_t
650 : udat_format(const UDateFormat* format, UDate dateToFormat, UChar* result,
651 : int32_t resultLength, UFieldPosition* position, UErrorCode* status)
652 : {
653 : MOZ_CRASH("udat_format: Intl API disabled");
654 : }
655 :
656 : int32_t
657 : udat_formatForFields(const UDateFormat* format, UDate dateToFormat,
658 : UChar* result, int32_t resultLength, UFieldPositionIterator* fpositer,
659 : UErrorCode* status)
660 : {
661 : MOZ_CRASH("udat_formatForFields: Intl API disabled");
662 : }
663 :
664 : UFieldPositionIterator*
665 : ufieldpositer_open(UErrorCode* status)
666 : {
667 : MOZ_CRASH("ufieldpositer_open: Intl API disabled");
668 : }
669 :
670 : void
671 : ufieldpositer_close(UFieldPositionIterator* fpositer)
672 : {
673 : MOZ_CRASH("ufieldpositer_close: Intl API disabled");
674 : }
675 :
676 : int32_t
677 : ufieldpositer_next(UFieldPositionIterator* fpositer, int32_t* beginIndex, int32_t* endIndex)
678 : {
679 : MOZ_CRASH("ufieldpositer_next: Intl API disabled");
680 : }
681 :
682 : void
683 : udat_close(UDateFormat* format)
684 : {
685 : MOZ_CRASH("udat_close: Intl API disabled");
686 : }
687 :
688 : int32_t
689 : udat_getSymbols(const UDateFormat *fmt, UDateFormatSymbolType type, int32_t symbolIndex,
690 : UChar *result, int32_t resultLength, UErrorCode *status)
691 : {
692 : MOZ_CRASH("udat_getSymbols: Intl API disabled");
693 : }
694 :
695 : typedef void* UPluralRules;
696 :
697 : enum UPluralType {
698 : UPLURAL_TYPE_CARDINAL,
699 : UPLURAL_TYPE_ORDINAL
700 : };
701 :
702 : void
703 : uplrules_close(UPluralRules *uplrules)
704 : {
705 : MOZ_CRASH("uplrules_close: Intl API disabled");
706 : }
707 :
708 : UPluralRules*
709 : uplrules_openForType(const char *locale, UPluralType type, UErrorCode *status)
710 : {
711 : MOZ_CRASH("uplrules_openForType: Intl API disabled");
712 : }
713 :
714 : int32_t
715 : uplrules_selectWithFormat(const UPluralRules* uplrules, double number, const UNumberFormat* fmt,
716 : UChar* keyword, int32_t capacity, UErrorCode* status)
717 : {
718 : MOZ_CRASH("uplrules_selectWithFormat: Intl API disabled");
719 : }
720 :
721 : UEnumeration*
722 : uplrules_getKeywords(const UPluralRules* uplrules, UErrorCode* status)
723 : {
724 : MOZ_CRASH("uplrules_getKeywords: Intl API disabled");
725 : }
726 :
727 : int32_t
728 : u_strToLower(UChar* dest, int32_t destCapacity, const UChar* src, int32_t srcLength,
729 : const char* locale, UErrorCode* pErrorCode)
730 : {
731 : MOZ_CRASH("u_strToLower: Intl API disabled");
732 : }
733 :
734 : int32_t
735 : u_strToUpper(UChar* dest, int32_t destCapacity, const UChar* src, int32_t srcLength,
736 : const char* locale, UErrorCode* pErrorCode)
737 : {
738 : MOZ_CRASH("u_strToUpper: Intl API disabled");
739 : }
740 :
741 : const char*
742 : uloc_toUnicodeLocaleType(const char* keyword, const char* value)
743 : {
744 : MOZ_CRASH("uloc_toUnicodeLocaleType: Intl API disabled");
745 : }
746 :
747 : } // anonymous namespace
748 :
749 : #endif
750 :
751 :
752 : /******************** Common to Intl constructors ********************/
753 :
754 : static bool
755 0 : IntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
756 : HandleValue locales, HandleValue options)
757 : {
758 0 : FixedInvokeArgs<3> args(cx);
759 :
760 0 : args[0].setObject(*obj);
761 0 : args[1].set(locales);
762 0 : args[2].set(options);
763 :
764 0 : RootedValue thisv(cx, NullValue());
765 0 : RootedValue ignored(cx);
766 0 : if (!js::CallSelfHostedFunction(cx, initializer, thisv, args, &ignored))
767 0 : return false;
768 :
769 0 : MOZ_ASSERT(ignored.isUndefined(),
770 : "Unexpected return value from non-legacy Intl object initializer");
771 0 : return true;
772 : }
773 :
774 : enum class DateTimeFormatOptions
775 : {
776 : Standard,
777 : EnableMozExtensions,
778 : };
779 :
780 : static bool
781 0 : LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
782 : HandleValue thisValue, HandleValue locales, HandleValue options,
783 : DateTimeFormatOptions dtfOptions, MutableHandleValue result)
784 : {
785 0 : FixedInvokeArgs<5> args(cx);
786 :
787 0 : args[0].setObject(*obj);
788 0 : args[1].set(thisValue);
789 0 : args[2].set(locales);
790 0 : args[3].set(options);
791 0 : args[4].setBoolean(dtfOptions == DateTimeFormatOptions::EnableMozExtensions);
792 :
793 0 : RootedValue thisv(cx, NullValue());
794 0 : if (!js::CallSelfHostedFunction(cx, initializer, thisv, args, result))
795 0 : return false;
796 :
797 0 : MOZ_ASSERT(result.isObject(), "Legacy Intl object initializer must return an object");
798 0 : return true;
799 : }
800 :
801 : // CountAvailable and GetAvailable describe the signatures used for ICU API
802 : // to determine available locales for various functionality.
803 : using CountAvailable = int32_t (*)();
804 : using GetAvailable = const char* (*)(int32_t localeIndex);
805 :
806 : static bool
807 0 : intl_availableLocales(JSContext* cx, CountAvailable countAvailable,
808 : GetAvailable getAvailable, MutableHandleValue result)
809 : {
810 0 : RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
811 0 : if (!locales)
812 0 : return false;
813 :
814 : #if ENABLE_INTL_API
815 0 : RootedAtom a(cx);
816 0 : uint32_t count = countAvailable();
817 0 : for (uint32_t i = 0; i < count; i++) {
818 0 : const char* locale = getAvailable(i);
819 0 : auto lang = DuplicateString(cx, locale);
820 0 : if (!lang)
821 0 : return false;
822 : char* p;
823 0 : while ((p = strchr(lang.get(), '_')))
824 0 : *p = '-';
825 0 : a = Atomize(cx, lang.get(), strlen(lang.get()));
826 0 : if (!a)
827 0 : return false;
828 0 : if (!DefineProperty(cx, locales, a->asPropertyName(), TrueHandleValue, nullptr, nullptr,
829 : JSPROP_ENUMERATE))
830 : {
831 0 : return false;
832 : }
833 : }
834 : #endif
835 0 : result.setObject(*locales);
836 0 : return true;
837 : }
838 :
839 : /**
840 : * Returns the object holding the internal properties for obj.
841 : */
842 : static JSObject*
843 0 : GetInternals(JSContext* cx, HandleObject obj)
844 : {
845 0 : FixedInvokeArgs<1> args(cx);
846 :
847 0 : args[0].setObject(*obj);
848 :
849 0 : RootedValue v(cx, NullValue());
850 0 : if (!js::CallSelfHostedFunction(cx, cx->names().getInternals, v, args, &v))
851 0 : return nullptr;
852 :
853 0 : return &v.toObject();
854 : }
855 :
856 : static bool
857 0 : equal(const char* s1, const char* s2)
858 : {
859 0 : return !strcmp(s1, s2);
860 : }
861 :
862 : static const char*
863 0 : icuLocale(const char* locale)
864 : {
865 0 : if (equal(locale, "und"))
866 0 : return ""; // ICU root locale
867 0 : return locale;
868 : }
869 :
870 : // Simple RAII for ICU objects. Unfortunately, ICU's C++ API is uniformly
871 : // unstable, so we can't use its smart pointers for this.
872 : template <typename T, void (Delete)(T*)>
873 : class ScopedICUObject
874 : {
875 : T* ptr_;
876 :
877 : public:
878 0 : explicit ScopedICUObject(T* ptr)
879 0 : : ptr_(ptr)
880 0 : {}
881 :
882 0 : ~ScopedICUObject() {
883 0 : if (ptr_)
884 0 : Delete(ptr_);
885 0 : }
886 :
887 : // In cases where an object should be deleted on abnormal exits,
888 : // but returned to the caller if everything goes well, call forget()
889 : // to transfer the object just before returning.
890 0 : T* forget() {
891 0 : T* tmp = ptr_;
892 0 : ptr_ = nullptr;
893 0 : return tmp;
894 : }
895 : };
896 :
897 : // Starting with ICU 59, UChar defaults to char16_t.
898 : static_assert(mozilla::IsSame<UChar, char16_t>::value,
899 : "We don't support redefining UChar to a different type");
900 :
901 : // The inline capacity we use for the char16_t Vectors.
902 : static const size_t INITIAL_CHAR_BUFFER_SIZE = 32;
903 :
904 : template <typename ICUStringFunction>
905 : static JSString*
906 0 : Call(JSContext* cx, const ICUStringFunction& strFn)
907 : {
908 0 : Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
909 0 : MOZ_ALWAYS_TRUE(chars.resize(INITIAL_CHAR_BUFFER_SIZE));
910 :
911 0 : UErrorCode status = U_ZERO_ERROR;
912 0 : int32_t size = strFn(chars.begin(), INITIAL_CHAR_BUFFER_SIZE, &status);
913 0 : if (status == U_BUFFER_OVERFLOW_ERROR) {
914 0 : MOZ_ASSERT(size >= 0);
915 0 : if (!chars.resize(size_t(size)))
916 0 : return nullptr;
917 0 : status = U_ZERO_ERROR;
918 0 : strFn(chars.begin(), size, &status);
919 : }
920 0 : if (U_FAILURE(status)) {
921 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
922 0 : return nullptr;
923 : }
924 :
925 0 : MOZ_ASSERT(size >= 0);
926 0 : return NewStringCopyN<CanGC>(cx, chars.begin(), size_t(size));
927 : }
928 :
929 :
930 : /******************** Collator ********************/
931 :
932 : const ClassOps CollatorObject::classOps_ = {
933 : nullptr, /* addProperty */
934 : nullptr, /* delProperty */
935 : nullptr, /* getProperty */
936 : nullptr, /* setProperty */
937 : nullptr, /* enumerate */
938 : nullptr, /* newEnumerate */
939 : nullptr, /* resolve */
940 : nullptr, /* mayResolve */
941 : CollatorObject::finalize
942 : };
943 :
944 : const Class CollatorObject::class_ = {
945 : js_Object_str,
946 : JSCLASS_HAS_RESERVED_SLOTS(CollatorObject::SLOT_COUNT) |
947 : JSCLASS_FOREGROUND_FINALIZE,
948 : &CollatorObject::classOps_
949 : };
950 :
951 : #if JS_HAS_TOSOURCE
952 : static bool
953 0 : collator_toSource(JSContext* cx, unsigned argc, Value* vp)
954 : {
955 0 : CallArgs args = CallArgsFromVp(argc, vp);
956 0 : args.rval().setString(cx->names().Collator);
957 0 : return true;
958 : }
959 : #endif
960 :
961 : static const JSFunctionSpec collator_static_methods[] = {
962 : JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_Collator_supportedLocalesOf", 1, 0),
963 : JS_FS_END
964 : };
965 :
966 : static const JSFunctionSpec collator_methods[] = {
967 : JS_SELF_HOSTED_FN("resolvedOptions", "Intl_Collator_resolvedOptions", 0, 0),
968 : #if JS_HAS_TOSOURCE
969 : JS_FN(js_toSource_str, collator_toSource, 0, 0),
970 : #endif
971 : JS_FS_END
972 : };
973 :
974 : static const JSPropertySpec collator_properties[] = {
975 : JS_SELF_HOSTED_GET("compare", "Intl_Collator_compare_get", 0),
976 : JS_STRING_SYM_PS(toStringTag, "Object", JSPROP_READONLY),
977 : JS_PS_END
978 : };
979 :
980 : /**
981 : * 10.1.2 Intl.Collator([ locales [, options]])
982 : *
983 : * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
984 : */
985 : static bool
986 0 : Collator(JSContext* cx, const CallArgs& args)
987 : {
988 : // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
989 :
990 : // Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
991 0 : RootedObject proto(cx);
992 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
993 0 : return false;
994 :
995 0 : if (!proto) {
996 0 : proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
997 0 : if (!proto)
998 0 : return false;
999 : }
1000 :
1001 0 : Rooted<CollatorObject*> collator(cx, NewObjectWithGivenProto<CollatorObject>(cx, proto));
1002 0 : if (!collator)
1003 0 : return false;
1004 :
1005 0 : collator->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
1006 0 : collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
1007 :
1008 0 : RootedValue locales(cx, args.get(0));
1009 0 : RootedValue options(cx, args.get(1));
1010 :
1011 : // Step 6.
1012 0 : if (!IntlInitialize(cx, collator, cx->names().InitializeCollator, locales, options))
1013 0 : return false;
1014 :
1015 0 : args.rval().setObject(*collator);
1016 0 : return true;
1017 : }
1018 :
1019 : static bool
1020 0 : Collator(JSContext* cx, unsigned argc, Value* vp)
1021 : {
1022 0 : CallArgs args = CallArgsFromVp(argc, vp);
1023 0 : return Collator(cx, args);
1024 : }
1025 :
1026 : bool
1027 0 : js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
1028 : {
1029 0 : CallArgs args = CallArgsFromVp(argc, vp);
1030 0 : MOZ_ASSERT(args.length() == 2);
1031 0 : MOZ_ASSERT(!args.isConstructing());
1032 :
1033 0 : return Collator(cx, args);
1034 : }
1035 :
1036 : void
1037 0 : CollatorObject::finalize(FreeOp* fop, JSObject* obj)
1038 : {
1039 0 : MOZ_ASSERT(fop->onActiveCooperatingThread());
1040 :
1041 0 : const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
1042 0 : if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
1043 0 : ucol_close(coll);
1044 0 : }
1045 :
1046 : static JSObject*
1047 6 : CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
1048 : {
1049 18 : RootedFunction ctor(cx, GlobalObject::createConstructor(cx, &Collator, cx->names().Collator,
1050 18 : 0));
1051 6 : if (!ctor)
1052 0 : return nullptr;
1053 :
1054 12 : RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
1055 6 : if (!proto)
1056 0 : return nullptr;
1057 :
1058 6 : if (!LinkConstructorAndPrototype(cx, ctor, proto))
1059 0 : return nullptr;
1060 :
1061 : // 10.2.2
1062 6 : if (!JS_DefineFunctions(cx, ctor, collator_static_methods))
1063 0 : return nullptr;
1064 :
1065 : // 10.3.5
1066 6 : if (!JS_DefineFunctions(cx, proto, collator_methods))
1067 0 : return nullptr;
1068 :
1069 : // 10.3.2 and 10.3.3
1070 6 : if (!JS_DefineProperties(cx, proto, collator_properties))
1071 0 : return nullptr;
1072 :
1073 : // 8.1
1074 12 : RootedValue ctorValue(cx, ObjectValue(*ctor));
1075 6 : if (!DefineProperty(cx, Intl, cx->names().Collator, ctorValue, nullptr, nullptr, 0))
1076 0 : return nullptr;
1077 :
1078 6 : return proto;
1079 : }
1080 :
1081 : bool
1082 0 : js::intl_Collator_availableLocales(JSContext* cx, unsigned argc, Value* vp)
1083 : {
1084 0 : CallArgs args = CallArgsFromVp(argc, vp);
1085 0 : MOZ_ASSERT(args.length() == 0);
1086 :
1087 0 : RootedValue result(cx);
1088 0 : if (!intl_availableLocales(cx, ucol_countAvailable, ucol_getAvailable, &result))
1089 0 : return false;
1090 0 : args.rval().set(result);
1091 0 : return true;
1092 : }
1093 :
1094 : bool
1095 0 : js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
1096 : {
1097 0 : CallArgs args = CallArgsFromVp(argc, vp);
1098 0 : MOZ_ASSERT(args.length() == 1);
1099 0 : MOZ_ASSERT(args[0].isString());
1100 :
1101 0 : JSAutoByteString locale(cx, args[0].toString());
1102 0 : if (!locale)
1103 0 : return false;
1104 0 : UErrorCode status = U_ZERO_ERROR;
1105 0 : UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
1106 0 : if (U_FAILURE(status)) {
1107 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1108 0 : return false;
1109 : }
1110 0 : ScopedICUObject<UEnumeration, uenum_close> toClose(values);
1111 :
1112 0 : uint32_t count = uenum_count(values, &status);
1113 0 : if (U_FAILURE(status)) {
1114 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1115 0 : return false;
1116 : }
1117 :
1118 0 : RootedObject collations(cx, NewDenseEmptyArray(cx));
1119 0 : if (!collations)
1120 0 : return false;
1121 :
1122 0 : uint32_t index = 0;
1123 :
1124 : // The first element of the collations array must be |null| per
1125 : // ES2017 Intl, 10.2.3 Internal Slots.
1126 0 : if (!DefineElement(cx, collations, index++, NullHandleValue))
1127 0 : return false;
1128 :
1129 0 : RootedValue element(cx);
1130 0 : for (uint32_t i = 0; i < count; i++) {
1131 0 : const char* collation = uenum_next(values, nullptr, &status);
1132 0 : if (U_FAILURE(status)) {
1133 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1134 0 : return false;
1135 : }
1136 :
1137 : // Per ECMA-402, 10.2.3, we don't include standard and search:
1138 : // "The values 'standard' and 'search' must not be used as elements in
1139 : // any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co
1140 : // array."
1141 0 : if (equal(collation, "standard") || equal(collation, "search"))
1142 0 : continue;
1143 :
1144 : // ICU returns old-style keyword values; map them to BCP 47 equivalents.
1145 0 : JSString* jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
1146 0 : if (!jscollation)
1147 0 : return false;
1148 0 : element = StringValue(jscollation);
1149 0 : if (!DefineElement(cx, collations, index++, element))
1150 0 : return false;
1151 : }
1152 :
1153 0 : args.rval().setObject(*collations);
1154 0 : return true;
1155 : }
1156 :
1157 : /**
1158 : * Returns a new UCollator with the locale and collation options
1159 : * of the given Collator.
1160 : */
1161 : static UCollator*
1162 0 : NewUCollator(JSContext* cx, Handle<CollatorObject*> collator)
1163 : {
1164 0 : RootedValue value(cx);
1165 :
1166 0 : RootedObject internals(cx, GetInternals(cx, collator));
1167 0 : if (!internals)
1168 0 : return nullptr;
1169 :
1170 0 : if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
1171 0 : return nullptr;
1172 0 : JSAutoByteString locale(cx, value.toString());
1173 0 : if (!locale)
1174 0 : return nullptr;
1175 :
1176 : // UCollator options with default values.
1177 0 : UColAttributeValue uStrength = UCOL_DEFAULT;
1178 0 : UColAttributeValue uCaseLevel = UCOL_OFF;
1179 0 : UColAttributeValue uAlternate = UCOL_DEFAULT;
1180 0 : UColAttributeValue uNumeric = UCOL_OFF;
1181 : // Normalization is always on to meet the canonical equivalence requirement.
1182 0 : UColAttributeValue uNormalization = UCOL_ON;
1183 0 : UColAttributeValue uCaseFirst = UCOL_DEFAULT;
1184 :
1185 0 : if (!GetProperty(cx, internals, internals, cx->names().usage, &value))
1186 0 : return nullptr;
1187 0 : JSLinearString* usage = value.toString()->ensureLinear(cx);
1188 0 : if (!usage)
1189 0 : return nullptr;
1190 0 : if (StringEqualsAscii(usage, "search")) {
1191 : // ICU expects search as a Unicode locale extension on locale.
1192 : // Unicode locale extensions must occur before private use extensions.
1193 0 : const char* oldLocale = locale.ptr();
1194 : const char* p;
1195 : size_t index;
1196 0 : size_t localeLen = strlen(oldLocale);
1197 0 : if ((p = strstr(oldLocale, "-x-")))
1198 0 : index = p - oldLocale;
1199 : else
1200 0 : index = localeLen;
1201 :
1202 : const char* insert;
1203 0 : if ((p = strstr(oldLocale, "-u-")) && static_cast<size_t>(p - oldLocale) < index) {
1204 0 : index = p - oldLocale + 2;
1205 0 : insert = "-co-search";
1206 : } else {
1207 0 : insert = "-u-co-search";
1208 : }
1209 0 : size_t insertLen = strlen(insert);
1210 0 : char* newLocale = cx->pod_malloc<char>(localeLen + insertLen + 1);
1211 0 : if (!newLocale)
1212 0 : return nullptr;
1213 0 : memcpy(newLocale, oldLocale, index);
1214 0 : memcpy(newLocale + index, insert, insertLen);
1215 0 : memcpy(newLocale + index + insertLen, oldLocale + index, localeLen - index + 1); // '\0'
1216 0 : locale.clear();
1217 0 : locale.initBytes(JS::UniqueChars(newLocale));
1218 : } else {
1219 0 : MOZ_ASSERT(StringEqualsAscii(usage, "sort"));
1220 : }
1221 :
1222 : // We don't need to look at the collation property - it can only be set
1223 : // via the Unicode locale extension and is therefore already set on
1224 : // locale.
1225 :
1226 0 : if (!GetProperty(cx, internals, internals, cx->names().sensitivity, &value))
1227 0 : return nullptr;
1228 0 : JSLinearString* sensitivity = value.toString()->ensureLinear(cx);
1229 0 : if (!sensitivity)
1230 0 : return nullptr;
1231 0 : if (StringEqualsAscii(sensitivity, "base")) {
1232 0 : uStrength = UCOL_PRIMARY;
1233 0 : } else if (StringEqualsAscii(sensitivity, "accent")) {
1234 0 : uStrength = UCOL_SECONDARY;
1235 0 : } else if (StringEqualsAscii(sensitivity, "case")) {
1236 0 : uStrength = UCOL_PRIMARY;
1237 0 : uCaseLevel = UCOL_ON;
1238 : } else {
1239 0 : MOZ_ASSERT(StringEqualsAscii(sensitivity, "variant"));
1240 0 : uStrength = UCOL_TERTIARY;
1241 : }
1242 :
1243 0 : if (!GetProperty(cx, internals, internals, cx->names().ignorePunctuation, &value))
1244 0 : return nullptr;
1245 : // According to the ICU team, UCOL_SHIFTED causes punctuation to be
1246 : // ignored. Looking at Unicode Technical Report 35, Unicode Locale Data
1247 : // Markup Language, "shifted" causes whitespace and punctuation to be
1248 : // ignored - that's a bit more than asked for, but there's no way to get
1249 : // less.
1250 0 : if (value.toBoolean())
1251 0 : uAlternate = UCOL_SHIFTED;
1252 :
1253 0 : if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
1254 0 : return nullptr;
1255 0 : if (!value.isUndefined() && value.toBoolean())
1256 0 : uNumeric = UCOL_ON;
1257 :
1258 0 : if (!GetProperty(cx, internals, internals, cx->names().caseFirst, &value))
1259 0 : return nullptr;
1260 0 : if (!value.isUndefined()) {
1261 0 : JSLinearString* caseFirst = value.toString()->ensureLinear(cx);
1262 0 : if (!caseFirst)
1263 0 : return nullptr;
1264 0 : if (StringEqualsAscii(caseFirst, "upper")) {
1265 0 : uCaseFirst = UCOL_UPPER_FIRST;
1266 0 : } else if (StringEqualsAscii(caseFirst, "lower")) {
1267 0 : uCaseFirst = UCOL_LOWER_FIRST;
1268 : } else {
1269 0 : MOZ_ASSERT(StringEqualsAscii(caseFirst, "false"));
1270 0 : uCaseFirst = UCOL_OFF;
1271 : }
1272 : }
1273 :
1274 0 : UErrorCode status = U_ZERO_ERROR;
1275 0 : UCollator* coll = ucol_open(icuLocale(locale.ptr()), &status);
1276 0 : if (U_FAILURE(status)) {
1277 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1278 0 : return nullptr;
1279 : }
1280 :
1281 0 : ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
1282 0 : ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
1283 0 : ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
1284 0 : ucol_setAttribute(coll, UCOL_NUMERIC_COLLATION, uNumeric, &status);
1285 0 : ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, uNormalization, &status);
1286 0 : ucol_setAttribute(coll, UCOL_CASE_FIRST, uCaseFirst, &status);
1287 0 : if (U_FAILURE(status)) {
1288 0 : ucol_close(coll);
1289 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1290 0 : return nullptr;
1291 : }
1292 :
1293 0 : return coll;
1294 : }
1295 :
1296 : static bool
1297 0 : intl_CompareStrings(JSContext* cx, UCollator* coll, HandleString str1, HandleString str2,
1298 : MutableHandleValue result)
1299 : {
1300 0 : MOZ_ASSERT(str1);
1301 0 : MOZ_ASSERT(str2);
1302 :
1303 0 : if (str1 == str2) {
1304 0 : result.setInt32(0);
1305 0 : return true;
1306 : }
1307 :
1308 0 : AutoStableStringChars stableChars1(cx);
1309 0 : if (!stableChars1.initTwoByte(cx, str1))
1310 0 : return false;
1311 :
1312 0 : AutoStableStringChars stableChars2(cx);
1313 0 : if (!stableChars2.initTwoByte(cx, str2))
1314 0 : return false;
1315 :
1316 0 : mozilla::Range<const char16_t> chars1 = stableChars1.twoByteRange();
1317 0 : mozilla::Range<const char16_t> chars2 = stableChars2.twoByteRange();
1318 :
1319 0 : UCollationResult uresult = ucol_strcoll(coll,
1320 0 : chars1.begin().get(), chars1.length(),
1321 0 : chars2.begin().get(), chars2.length());
1322 : int32_t res;
1323 0 : switch (uresult) {
1324 0 : case UCOL_LESS: res = -1; break;
1325 0 : case UCOL_EQUAL: res = 0; break;
1326 0 : case UCOL_GREATER: res = 1; break;
1327 0 : default: MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
1328 : }
1329 0 : result.setInt32(res);
1330 0 : return true;
1331 : }
1332 :
1333 : bool
1334 0 : js::intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp)
1335 : {
1336 0 : CallArgs args = CallArgsFromVp(argc, vp);
1337 0 : MOZ_ASSERT(args.length() == 3);
1338 0 : MOZ_ASSERT(args[0].isObject());
1339 0 : MOZ_ASSERT(args[1].isString());
1340 0 : MOZ_ASSERT(args[2].isString());
1341 :
1342 0 : Rooted<CollatorObject*> collator(cx, &args[0].toObject().as<CollatorObject>());
1343 :
1344 : // Obtain a cached UCollator object.
1345 : // XXX Does this handle Collator instances from other globals correctly?
1346 0 : void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
1347 0 : UCollator* coll = static_cast<UCollator*>(priv);
1348 0 : if (!coll) {
1349 0 : coll = NewUCollator(cx, collator);
1350 0 : if (!coll)
1351 0 : return false;
1352 0 : collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
1353 : }
1354 :
1355 : // Use the UCollator to actually compare the strings.
1356 0 : RootedString str1(cx, args[1].toString());
1357 0 : RootedString str2(cx, args[2].toString());
1358 0 : return intl_CompareStrings(cx, coll, str1, str2, args.rval());
1359 : }
1360 :
1361 0 : js::SharedIntlData::LocaleHasher::Lookup::Lookup(JSLinearString* locale)
1362 0 : : js::SharedIntlData::LinearStringLookup(locale)
1363 : {
1364 0 : if (isLatin1)
1365 0 : hash = mozilla::HashString(latin1Chars, length);
1366 : else
1367 0 : hash = mozilla::HashString(twoByteChars, length);
1368 0 : }
1369 :
1370 : bool
1371 0 : js::SharedIntlData::LocaleHasher::match(Locale key, const Lookup& lookup)
1372 : {
1373 0 : if (key->length() != lookup.length)
1374 0 : return false;
1375 :
1376 0 : if (key->hasLatin1Chars()) {
1377 0 : const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
1378 0 : if (lookup.isLatin1)
1379 0 : return EqualChars(keyChars, lookup.latin1Chars, lookup.length);
1380 0 : return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
1381 : }
1382 :
1383 0 : const char16_t* keyChars = key->twoByteChars(lookup.nogc);
1384 0 : if (lookup.isLatin1)
1385 0 : return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
1386 0 : return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
1387 : }
1388 :
1389 : bool
1390 0 : js::SharedIntlData::ensureUpperCaseFirstLocales(JSContext* cx)
1391 : {
1392 0 : if (upperCaseFirstInitialized)
1393 0 : return true;
1394 :
1395 : // If ensureUpperCaseFirstLocales() was called previously, but didn't
1396 : // complete due to OOM, clear all data and start from scratch.
1397 0 : if (upperCaseFirstLocales.initialized())
1398 0 : upperCaseFirstLocales.finish();
1399 0 : if (!upperCaseFirstLocales.init()) {
1400 0 : ReportOutOfMemory(cx);
1401 0 : return false;
1402 : }
1403 :
1404 0 : UErrorCode status = U_ZERO_ERROR;
1405 0 : UEnumeration* available = ucol_openAvailableLocales(&status);
1406 0 : if (U_FAILURE(status)) {
1407 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1408 0 : return false;
1409 : }
1410 0 : ScopedICUObject<UEnumeration, uenum_close> toClose(available);
1411 :
1412 0 : RootedAtom locale(cx);
1413 : while (true) {
1414 : int32_t size;
1415 0 : const char* rawLocale = uenum_next(available, &size, &status);
1416 0 : if (U_FAILURE(status)) {
1417 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1418 0 : return false;
1419 : }
1420 :
1421 0 : if (rawLocale == nullptr)
1422 0 : break;
1423 :
1424 0 : UCollator* collator = ucol_open(rawLocale, &status);
1425 0 : if (U_FAILURE(status)) {
1426 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1427 0 : return false;
1428 : }
1429 0 : ScopedICUObject<UCollator, ucol_close> toCloseCollator(collator);
1430 :
1431 0 : UColAttributeValue caseFirst = ucol_getAttribute(collator, UCOL_CASE_FIRST, &status);
1432 0 : if (U_FAILURE(status)) {
1433 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1434 0 : return false;
1435 : }
1436 :
1437 0 : if (caseFirst != UCOL_UPPER_FIRST)
1438 0 : continue;
1439 :
1440 0 : MOZ_ASSERT(size >= 0);
1441 0 : locale = Atomize(cx, rawLocale, size_t(size));
1442 0 : if (!locale)
1443 0 : return false;
1444 :
1445 0 : LocaleHasher::Lookup lookup(locale);
1446 0 : LocaleSet::AddPtr p = upperCaseFirstLocales.lookupForAdd(lookup);
1447 :
1448 : // ICU shouldn't report any duplicate locales, but if it does, just
1449 : // ignore the duplicated locale.
1450 0 : if (!p && !upperCaseFirstLocales.add(p, locale)) {
1451 0 : ReportOutOfMemory(cx);
1452 0 : return false;
1453 : }
1454 0 : }
1455 :
1456 0 : MOZ_ASSERT(!upperCaseFirstInitialized,
1457 : "ensureUpperCaseFirstLocales is neither reentrant nor thread-safe");
1458 0 : upperCaseFirstInitialized = true;
1459 :
1460 0 : return true;
1461 : }
1462 :
1463 : bool
1464 0 : js::SharedIntlData::isUpperCaseFirst(JSContext* cx, HandleString locale, bool* isUpperFirst)
1465 : {
1466 0 : if (!ensureUpperCaseFirstLocales(cx))
1467 0 : return false;
1468 :
1469 0 : RootedLinearString localeLinear(cx, locale->ensureLinear(cx));
1470 0 : if (!localeLinear)
1471 0 : return false;
1472 :
1473 0 : LocaleHasher::Lookup lookup(localeLinear);
1474 0 : *isUpperFirst = upperCaseFirstLocales.has(lookup);
1475 :
1476 0 : return true;
1477 : }
1478 :
1479 : bool
1480 0 : js::intl_isUpperCaseFirst(JSContext* cx, unsigned argc, Value* vp)
1481 : {
1482 0 : CallArgs args = CallArgsFromVp(argc, vp);
1483 0 : MOZ_ASSERT(args.length() == 1);
1484 0 : MOZ_ASSERT(args[0].isString());
1485 :
1486 0 : SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
1487 :
1488 0 : RootedString locale(cx, args[0].toString());
1489 : bool isUpperFirst;
1490 0 : if (!sharedIntlData.isUpperCaseFirst(cx, locale, &isUpperFirst))
1491 0 : return false;
1492 :
1493 0 : args.rval().setBoolean(isUpperFirst);
1494 0 : return true;
1495 : }
1496 :
1497 :
1498 : /******************** NumberFormat ********************/
1499 :
1500 : const ClassOps NumberFormatObject::classOps_ = {
1501 : nullptr, /* addProperty */
1502 : nullptr, /* delProperty */
1503 : nullptr, /* getProperty */
1504 : nullptr, /* setProperty */
1505 : nullptr, /* enumerate */
1506 : nullptr, /* newEnumerate */
1507 : nullptr, /* resolve */
1508 : nullptr, /* mayResolve */
1509 : NumberFormatObject::finalize
1510 : };
1511 :
1512 : const Class NumberFormatObject::class_ = {
1513 : js_Object_str,
1514 : JSCLASS_HAS_RESERVED_SLOTS(NumberFormatObject::SLOT_COUNT) |
1515 : JSCLASS_FOREGROUND_FINALIZE,
1516 : &NumberFormatObject::classOps_
1517 : };
1518 :
1519 : #if JS_HAS_TOSOURCE
1520 : static bool
1521 0 : numberFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
1522 : {
1523 0 : CallArgs args = CallArgsFromVp(argc, vp);
1524 0 : args.rval().setString(cx->names().NumberFormat);
1525 0 : return true;
1526 : }
1527 : #endif
1528 :
1529 : static const JSFunctionSpec numberFormat_static_methods[] = {
1530 : JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_NumberFormat_supportedLocalesOf", 1, 0),
1531 : JS_FS_END
1532 : };
1533 :
1534 : static const JSFunctionSpec numberFormat_methods[] = {
1535 : JS_SELF_HOSTED_FN("resolvedOptions", "Intl_NumberFormat_resolvedOptions", 0, 0),
1536 : #if JS_HAS_TOSOURCE
1537 : JS_FN(js_toSource_str, numberFormat_toSource, 0, 0),
1538 : #endif
1539 : JS_FS_END
1540 : };
1541 :
1542 : static const JSPropertySpec numberFormat_properties[] = {
1543 : JS_SELF_HOSTED_GET("format", "Intl_NumberFormat_format_get", 0),
1544 : JS_STRING_SYM_PS(toStringTag, "Object", JSPROP_READONLY),
1545 : JS_PS_END
1546 : };
1547 :
1548 : /**
1549 : * 11.2.1 Intl.NumberFormat([ locales [, options]])
1550 : *
1551 : * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
1552 : */
1553 : static bool
1554 0 : NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
1555 : {
1556 : // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
1557 :
1558 : // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
1559 0 : RootedObject proto(cx);
1560 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
1561 0 : return false;
1562 :
1563 0 : if (!proto) {
1564 0 : proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
1565 0 : if (!proto)
1566 0 : return false;
1567 : }
1568 :
1569 0 : Rooted<NumberFormatObject*> numberFormat(cx);
1570 0 : numberFormat = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
1571 0 : if (!numberFormat)
1572 0 : return false;
1573 :
1574 0 : numberFormat->setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
1575 0 : numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
1576 :
1577 0 : RootedValue thisValue(cx, construct ? ObjectValue(*numberFormat) : args.thisv());
1578 0 : RootedValue locales(cx, args.get(0));
1579 0 : RootedValue options(cx, args.get(1));
1580 :
1581 : // Step 3.
1582 0 : return LegacyIntlInitialize(cx, numberFormat, cx->names().InitializeNumberFormat, thisValue,
1583 0 : locales, options, DateTimeFormatOptions::Standard, args.rval());
1584 : }
1585 :
1586 : static bool
1587 0 : NumberFormat(JSContext* cx, unsigned argc, Value* vp)
1588 : {
1589 0 : CallArgs args = CallArgsFromVp(argc, vp);
1590 0 : return NumberFormat(cx, args, args.isConstructing());
1591 : }
1592 :
1593 : bool
1594 0 : js::intl_NumberFormat(JSContext* cx, unsigned argc, Value* vp)
1595 : {
1596 0 : CallArgs args = CallArgsFromVp(argc, vp);
1597 0 : MOZ_ASSERT(args.length() == 2);
1598 0 : MOZ_ASSERT(!args.isConstructing());
1599 : // intl_NumberFormat is an intrinsic for self-hosted JavaScript, so it
1600 : // cannot be used with "new", but it still has to be treated as a
1601 : // constructor.
1602 0 : return NumberFormat(cx, args, true);
1603 : }
1604 :
1605 : void
1606 0 : NumberFormatObject::finalize(FreeOp* fop, JSObject* obj)
1607 : {
1608 0 : MOZ_ASSERT(fop->onActiveCooperatingThread());
1609 :
1610 : const Value& slot =
1611 0 : obj->as<NumberFormatObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT);
1612 0 : if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
1613 0 : unum_close(nf);
1614 0 : }
1615 :
1616 : static JSObject*
1617 6 : CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
1618 : MutableHandleObject constructor)
1619 : {
1620 12 : RootedFunction ctor(cx);
1621 6 : ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
1622 6 : if (!ctor)
1623 0 : return nullptr;
1624 :
1625 12 : RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
1626 6 : if (!proto)
1627 0 : return nullptr;
1628 :
1629 6 : if (!LinkConstructorAndPrototype(cx, ctor, proto))
1630 0 : return nullptr;
1631 :
1632 : // 11.3.2
1633 6 : if (!JS_DefineFunctions(cx, ctor, numberFormat_static_methods))
1634 0 : return nullptr;
1635 :
1636 : // 11.4.4
1637 6 : if (!JS_DefineFunctions(cx, proto, numberFormat_methods))
1638 0 : return nullptr;
1639 :
1640 : // 11.4.2 and 11.4.3
1641 6 : if (!JS_DefineProperties(cx, proto, numberFormat_properties))
1642 0 : return nullptr;
1643 :
1644 : // If the still-experimental NumberFormat.prototype.formatToParts method is
1645 : // enabled, also add it.
1646 6 : if (cx->compartment()->creationOptions().experimentalNumberFormatFormatToPartsEnabled()) {
1647 0 : RootedValue ftp(cx);
1648 0 : HandlePropertyName name = cx->names().formatToParts;
1649 0 : if (!GlobalObject::getSelfHostedFunction(cx, cx->global(),
1650 0 : cx->names().NumberFormatFormatToParts,
1651 : name, 1, &ftp))
1652 : {
1653 0 : return nullptr;
1654 : }
1655 :
1656 0 : if (!DefineProperty(cx, proto, cx->names().formatToParts, ftp, nullptr, nullptr, 0))
1657 0 : return nullptr;
1658 : }
1659 :
1660 : // 8.1
1661 12 : RootedValue ctorValue(cx, ObjectValue(*ctor));
1662 6 : if (!DefineProperty(cx, Intl, cx->names().NumberFormat, ctorValue, nullptr, nullptr, 0))
1663 0 : return nullptr;
1664 :
1665 6 : constructor.set(ctor);
1666 6 : return proto;
1667 : }
1668 :
1669 : bool
1670 0 : js::intl_NumberFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
1671 : {
1672 0 : CallArgs args = CallArgsFromVp(argc, vp);
1673 0 : MOZ_ASSERT(args.length() == 0);
1674 :
1675 0 : RootedValue result(cx);
1676 0 : if (!intl_availableLocales(cx, unum_countAvailable, unum_getAvailable, &result))
1677 0 : return false;
1678 0 : args.rval().set(result);
1679 0 : return true;
1680 : }
1681 :
1682 : bool
1683 0 : js::intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp)
1684 : {
1685 0 : CallArgs args = CallArgsFromVp(argc, vp);
1686 0 : MOZ_ASSERT(args.length() == 1);
1687 0 : MOZ_ASSERT(args[0].isString());
1688 :
1689 0 : JSAutoByteString locale(cx, args[0].toString());
1690 0 : if (!locale)
1691 0 : return false;
1692 :
1693 0 : UErrorCode status = U_ZERO_ERROR;
1694 0 : UNumberingSystem* numbers = unumsys_open(icuLocale(locale.ptr()), &status);
1695 0 : if (U_FAILURE(status)) {
1696 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1697 0 : return false;
1698 : }
1699 :
1700 0 : ScopedICUObject<UNumberingSystem, unumsys_close> toClose(numbers);
1701 :
1702 0 : const char* name = unumsys_getName(numbers);
1703 0 : RootedString jsname(cx, JS_NewStringCopyZ(cx, name));
1704 0 : if (!jsname)
1705 0 : return false;
1706 :
1707 0 : args.rval().setString(jsname);
1708 0 : return true;
1709 : }
1710 :
1711 :
1712 : /**
1713 : * This creates new UNumberFormat with calculated digit formatting
1714 : * properties for PluralRules.
1715 : *
1716 : * This is similar to NewUNumberFormat but doesn't allow for currency or
1717 : * percent types.
1718 : */
1719 : static UNumberFormat*
1720 0 : NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralRules)
1721 : {
1722 0 : RootedObject internals(cx, GetInternals(cx, pluralRules));
1723 0 : if (!internals)
1724 0 : return nullptr;
1725 :
1726 0 : RootedValue value(cx);
1727 :
1728 0 : if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
1729 0 : return nullptr;
1730 0 : JSAutoByteString locale(cx, value.toString());
1731 0 : if (!locale)
1732 0 : return nullptr;
1733 :
1734 0 : uint32_t uMinimumIntegerDigits = 1;
1735 0 : uint32_t uMinimumFractionDigits = 0;
1736 0 : uint32_t uMaximumFractionDigits = 3;
1737 0 : int32_t uMinimumSignificantDigits = -1;
1738 0 : int32_t uMaximumSignificantDigits = -1;
1739 :
1740 : bool hasP;
1741 0 : if (!HasProperty(cx, internals, cx->names().minimumSignificantDigits, &hasP))
1742 0 : return nullptr;
1743 :
1744 0 : if (hasP) {
1745 0 : if (!GetProperty(cx, internals, internals, cx->names().minimumSignificantDigits, &value))
1746 0 : return nullptr;
1747 0 : uMinimumSignificantDigits = value.toInt32();
1748 :
1749 0 : if (!GetProperty(cx, internals, internals, cx->names().maximumSignificantDigits, &value))
1750 0 : return nullptr;
1751 0 : uMaximumSignificantDigits = value.toInt32();
1752 : } else {
1753 0 : if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits, &value))
1754 0 : return nullptr;
1755 0 : uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
1756 :
1757 0 : if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits, &value))
1758 0 : return nullptr;
1759 0 : uMinimumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
1760 :
1761 0 : if (!GetProperty(cx, internals, internals, cx->names().maximumFractionDigits, &value))
1762 0 : return nullptr;
1763 0 : uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
1764 : }
1765 :
1766 0 : UErrorCode status = U_ZERO_ERROR;
1767 : UNumberFormat* nf =
1768 0 : unum_open(UNUM_DECIMAL, nullptr, 0, icuLocale(locale.ptr()), nullptr, &status);
1769 0 : if (U_FAILURE(status)) {
1770 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1771 0 : return nullptr;
1772 : }
1773 0 : ScopedICUObject<UNumberFormat, unum_close> toClose(nf);
1774 :
1775 0 : if (uMinimumSignificantDigits != -1) {
1776 0 : unum_setAttribute(nf, UNUM_SIGNIFICANT_DIGITS_USED, true);
1777 0 : unum_setAttribute(nf, UNUM_MIN_SIGNIFICANT_DIGITS, uMinimumSignificantDigits);
1778 0 : unum_setAttribute(nf, UNUM_MAX_SIGNIFICANT_DIGITS, uMaximumSignificantDigits);
1779 : } else {
1780 0 : unum_setAttribute(nf, UNUM_MIN_INTEGER_DIGITS, uMinimumIntegerDigits);
1781 0 : unum_setAttribute(nf, UNUM_MIN_FRACTION_DIGITS, uMinimumFractionDigits);
1782 0 : unum_setAttribute(nf, UNUM_MAX_FRACTION_DIGITS, uMaximumFractionDigits);
1783 : }
1784 :
1785 0 : return toClose.forget();
1786 : }
1787 :
1788 :
1789 : /**
1790 : * Returns a new UNumberFormat with the locale and number formatting options
1791 : * of the given NumberFormat.
1792 : */
1793 : static UNumberFormat*
1794 0 : NewUNumberFormat(JSContext* cx, Handle<NumberFormatObject*> numberFormat)
1795 : {
1796 0 : RootedValue value(cx);
1797 :
1798 0 : RootedObject internals(cx, GetInternals(cx, numberFormat));
1799 0 : if (!internals)
1800 0 : return nullptr;
1801 :
1802 0 : if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
1803 0 : return nullptr;
1804 0 : JSAutoByteString locale(cx, value.toString());
1805 0 : if (!locale)
1806 0 : return nullptr;
1807 :
1808 : // UNumberFormat options with default values
1809 0 : UNumberFormatStyle uStyle = UNUM_DECIMAL;
1810 0 : const UChar* uCurrency = nullptr;
1811 0 : uint32_t uMinimumIntegerDigits = 1;
1812 0 : uint32_t uMinimumFractionDigits = 0;
1813 0 : uint32_t uMaximumFractionDigits = 3;
1814 0 : int32_t uMinimumSignificantDigits = -1;
1815 0 : int32_t uMaximumSignificantDigits = -1;
1816 0 : bool uUseGrouping = true;
1817 :
1818 : // Sprinkle appropriate rooting flavor over things the GC might care about.
1819 0 : RootedString currency(cx);
1820 0 : AutoStableStringChars stableChars(cx);
1821 :
1822 : // We don't need to look at numberingSystem - it can only be set via
1823 : // the Unicode locale extension and is therefore already set on locale.
1824 :
1825 0 : if (!GetProperty(cx, internals, internals, cx->names().style, &value))
1826 0 : return nullptr;
1827 0 : JSLinearString* style = value.toString()->ensureLinear(cx);
1828 0 : if (!style)
1829 0 : return nullptr;
1830 :
1831 0 : if (StringEqualsAscii(style, "currency")) {
1832 0 : if (!GetProperty(cx, internals, internals, cx->names().currency, &value))
1833 0 : return nullptr;
1834 0 : currency = value.toString();
1835 0 : MOZ_ASSERT(currency->length() == 3,
1836 : "IsWellFormedCurrencyCode permits only length-3 strings");
1837 0 : if (!stableChars.initTwoByte(cx, currency))
1838 0 : return nullptr;
1839 : // uCurrency remains owned by stableChars.
1840 0 : uCurrency = stableChars.twoByteRange().begin().get();
1841 :
1842 0 : if (!GetProperty(cx, internals, internals, cx->names().currencyDisplay, &value))
1843 0 : return nullptr;
1844 0 : JSLinearString* currencyDisplay = value.toString()->ensureLinear(cx);
1845 0 : if (!currencyDisplay)
1846 0 : return nullptr;
1847 0 : if (StringEqualsAscii(currencyDisplay, "code")) {
1848 0 : uStyle = UNUM_CURRENCY_ISO;
1849 0 : } else if (StringEqualsAscii(currencyDisplay, "symbol")) {
1850 0 : uStyle = UNUM_CURRENCY;
1851 : } else {
1852 0 : MOZ_ASSERT(StringEqualsAscii(currencyDisplay, "name"));
1853 0 : uStyle = UNUM_CURRENCY_PLURAL;
1854 : }
1855 0 : } else if (StringEqualsAscii(style, "percent")) {
1856 0 : uStyle = UNUM_PERCENT;
1857 : } else {
1858 0 : MOZ_ASSERT(StringEqualsAscii(style, "decimal"));
1859 0 : uStyle = UNUM_DECIMAL;
1860 : }
1861 :
1862 : bool hasP;
1863 0 : if (!HasProperty(cx, internals, cx->names().minimumSignificantDigits, &hasP))
1864 0 : return nullptr;
1865 :
1866 0 : if (hasP) {
1867 0 : if (!GetProperty(cx, internals, internals, cx->names().minimumSignificantDigits, &value))
1868 0 : return nullptr;
1869 0 : uMinimumSignificantDigits = value.toInt32();
1870 :
1871 0 : if (!GetProperty(cx, internals, internals, cx->names().maximumSignificantDigits, &value))
1872 0 : return nullptr;
1873 0 : uMaximumSignificantDigits = value.toInt32();
1874 : } else {
1875 0 : if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits, &value))
1876 0 : return nullptr;
1877 0 : uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
1878 :
1879 0 : if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits, &value))
1880 0 : return nullptr;
1881 0 : uMinimumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
1882 :
1883 0 : if (!GetProperty(cx, internals, internals, cx->names().maximumFractionDigits, &value))
1884 0 : return nullptr;
1885 0 : uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
1886 : }
1887 :
1888 0 : if (!GetProperty(cx, internals, internals, cx->names().useGrouping, &value))
1889 0 : return nullptr;
1890 0 : uUseGrouping = value.toBoolean();
1891 :
1892 0 : UErrorCode status = U_ZERO_ERROR;
1893 0 : UNumberFormat* nf = unum_open(uStyle, nullptr, 0, icuLocale(locale.ptr()), nullptr, &status);
1894 0 : if (U_FAILURE(status)) {
1895 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1896 0 : return nullptr;
1897 : }
1898 0 : ScopedICUObject<UNumberFormat, unum_close> toClose(nf);
1899 :
1900 0 : if (uCurrency) {
1901 0 : unum_setTextAttribute(nf, UNUM_CURRENCY_CODE, uCurrency, 3, &status);
1902 0 : if (U_FAILURE(status)) {
1903 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
1904 0 : return nullptr;
1905 : }
1906 : }
1907 0 : if (uMinimumSignificantDigits != -1) {
1908 0 : unum_setAttribute(nf, UNUM_SIGNIFICANT_DIGITS_USED, true);
1909 0 : unum_setAttribute(nf, UNUM_MIN_SIGNIFICANT_DIGITS, uMinimumSignificantDigits);
1910 0 : unum_setAttribute(nf, UNUM_MAX_SIGNIFICANT_DIGITS, uMaximumSignificantDigits);
1911 : } else {
1912 0 : unum_setAttribute(nf, UNUM_MIN_INTEGER_DIGITS, uMinimumIntegerDigits);
1913 0 : unum_setAttribute(nf, UNUM_MIN_FRACTION_DIGITS, uMinimumFractionDigits);
1914 0 : unum_setAttribute(nf, UNUM_MAX_FRACTION_DIGITS, uMaximumFractionDigits);
1915 : }
1916 0 : unum_setAttribute(nf, UNUM_GROUPING_USED, uUseGrouping);
1917 0 : unum_setAttribute(nf, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
1918 :
1919 0 : return toClose.forget();
1920 : }
1921 :
1922 : static JSString*
1923 0 : PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double* x,
1924 : UFieldPositionIterator* fpositer)
1925 : {
1926 : // PartitionNumberPattern doesn't consider -0.0 to be negative.
1927 0 : if (IsNegativeZero(*x))
1928 0 : *x = 0.0;
1929 :
1930 0 : return Call(cx, [nf, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
1931 0 : return unum_formatDoubleForFields(nf, *x, chars, size, fpositer, status);
1932 0 : });
1933 : }
1934 :
1935 : static bool
1936 0 : intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result)
1937 : {
1938 : // Passing null for |fpositer| will just not compute partition information,
1939 : // letting us common up all ICU number-formatting code.
1940 0 : JSString* str = PartitionNumberPattern(cx, nf, &x, nullptr);
1941 0 : if (!str)
1942 0 : return false;
1943 :
1944 0 : result.setString(str);
1945 0 : return true;
1946 : }
1947 :
1948 : using FieldType = ImmutablePropertyNamePtr JSAtomState::*;
1949 :
1950 : static FieldType
1951 0 : GetFieldTypeForNumberField(UNumberFormatFields fieldName, double d)
1952 : {
1953 : // See intl/icu/source/i18n/unicode/unum.h for a detailed field list. This
1954 : // list is deliberately exhaustive: cases might have to be added/removed if
1955 : // this code is compiled with a different ICU with more UNumberFormatFields
1956 : // enum initializers. Please guard such cases with appropriate ICU
1957 : // version-testing #ifdefs, should cross-version divergence occur.
1958 0 : switch (fieldName) {
1959 : case UNUM_INTEGER_FIELD:
1960 0 : if (IsNaN(d))
1961 0 : return &JSAtomState::nan;
1962 0 : if (!IsFinite(d))
1963 0 : return &JSAtomState::infinity;
1964 0 : return &JSAtomState::integer;
1965 :
1966 : case UNUM_GROUPING_SEPARATOR_FIELD:
1967 0 : return &JSAtomState::group;
1968 :
1969 : case UNUM_DECIMAL_SEPARATOR_FIELD:
1970 0 : return &JSAtomState::decimal;
1971 :
1972 : case UNUM_FRACTION_FIELD:
1973 0 : return &JSAtomState::fraction;
1974 :
1975 : case UNUM_SIGN_FIELD: {
1976 0 : MOZ_ASSERT(!IsNegativeZero(d),
1977 : "-0 should have been excluded by PartitionNumberPattern");
1978 :
1979 : // Manual trawling through the ICU call graph appears to indicate that
1980 : // the basic formatting we request will never include a positive sign.
1981 : // But this analysis may be mistaken, so don't absolutely trust it.
1982 0 : return d < 0 ? &JSAtomState::minusSign : &JSAtomState::plusSign;
1983 : }
1984 :
1985 : case UNUM_PERCENT_FIELD:
1986 0 : return &JSAtomState::percentSign;
1987 :
1988 : case UNUM_CURRENCY_FIELD:
1989 0 : return &JSAtomState::currency;
1990 :
1991 : case UNUM_PERMILL_FIELD:
1992 0 : MOZ_ASSERT_UNREACHABLE("unexpected permill field found, even though "
1993 : "we don't use any user-defined patterns that "
1994 : "would require a permill field");
1995 : break;
1996 :
1997 : case UNUM_EXPONENT_SYMBOL_FIELD:
1998 : case UNUM_EXPONENT_SIGN_FIELD:
1999 : case UNUM_EXPONENT_FIELD:
2000 0 : MOZ_ASSERT_UNREACHABLE("exponent field unexpectedly found in "
2001 : "formatted number, even though UNUM_SCIENTIFIC "
2002 : "and scientific notation were never requested");
2003 : break;
2004 :
2005 : #ifndef U_HIDE_DEPRECATED_API
2006 : case UNUM_FIELD_COUNT:
2007 0 : MOZ_ASSERT_UNREACHABLE("format field sentinel value returned by "
2008 : "iterator!");
2009 : break;
2010 : #endif
2011 : }
2012 :
2013 0 : MOZ_ASSERT_UNREACHABLE("unenumerated, undocumented format field returned "
2014 : "by iterator");
2015 : return nullptr;
2016 : }
2017 :
2018 : static bool
2019 0 : intl_FormatNumberToParts(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result)
2020 : {
2021 0 : UErrorCode status = U_ZERO_ERROR;
2022 :
2023 0 : UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
2024 0 : if (U_FAILURE(status)) {
2025 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
2026 0 : return false;
2027 : }
2028 :
2029 0 : MOZ_ASSERT(fpositer);
2030 0 : ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer);
2031 :
2032 0 : RootedString overallResult(cx, PartitionNumberPattern(cx, nf, &x, fpositer));
2033 0 : if (!overallResult)
2034 0 : return false;
2035 :
2036 0 : RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
2037 0 : if (!partsArray)
2038 0 : return false;
2039 :
2040 : // First, vacuum up fields in the overall formatted string.
2041 :
2042 : struct Field
2043 : {
2044 : uint32_t begin;
2045 : uint32_t end;
2046 : FieldType type;
2047 :
2048 : // Needed for vector-resizing scratch space.
2049 : Field() = default;
2050 :
2051 0 : Field(uint32_t begin, uint32_t end, FieldType type)
2052 0 : : begin(begin), end(end), type(type)
2053 0 : {}
2054 : };
2055 :
2056 : using FieldsVector = Vector<Field, 16>;
2057 0 : FieldsVector fields(cx);
2058 :
2059 : int32_t fieldInt, beginIndexInt, endIndexInt;
2060 0 : while ((fieldInt = ufieldpositer_next(fpositer, &beginIndexInt, &endIndexInt)) >= 0) {
2061 0 : MOZ_ASSERT(beginIndexInt >= 0);
2062 0 : MOZ_ASSERT(endIndexInt >= 0);
2063 0 : MOZ_ASSERT(beginIndexInt < endIndexInt,
2064 : "erm, aren't fields always non-empty?");
2065 :
2066 0 : FieldType type = GetFieldTypeForNumberField(UNumberFormatFields(fieldInt), x);
2067 0 : if (!fields.emplaceBack(uint32_t(beginIndexInt), uint32_t(endIndexInt), type))
2068 0 : return false;
2069 : }
2070 :
2071 : // Second, merge sort the fields vector. Expand the vector to have scratch
2072 : // space for performing the sort.
2073 0 : size_t fieldsLen = fields.length();
2074 0 : if (!fields.resizeUninitialized(fieldsLen * 2))
2075 0 : return false;
2076 :
2077 0 : MOZ_ALWAYS_TRUE(MergeSort(fields.begin(), fieldsLen, fields.begin() + fieldsLen,
2078 : [](const Field& left, const Field& right,
2079 : bool* lessOrEqual)
2080 : {
2081 : // Sort first by begin index, then to place
2082 : // enclosing fields before nested fields.
2083 : *lessOrEqual = left.begin < right.begin ||
2084 : (left.begin == right.begin &&
2085 : left.end > right.end);
2086 : return true;
2087 : }));
2088 :
2089 : // Deallocate the scratch space.
2090 0 : if (!fields.resize(fieldsLen))
2091 0 : return false;
2092 :
2093 : // Third, iterate over the sorted field list to generate a sequence of
2094 : // parts (what ECMA-402 actually exposes). A part is a maximal character
2095 : // sequence entirely within no field or a single most-nested field.
2096 : //
2097 : // Diagrams may be helpful to illustrate how fields map to parts. Consider
2098 : // formatting -19,766,580,028,249.41, the US national surplus (negative
2099 : // because it's actually a debt) on October 18, 2016.
2100 : //
2101 : // var options =
2102 : // { style: "currency", currency: "USD", currencyDisplay: "name" };
2103 : // var usdFormatter = new Intl.NumberFormat("en-US", options);
2104 : // usdFormatter.format(-19766580028249.41);
2105 : //
2106 : // The formatted result is "-19,766,580,028,249.41 US dollars". ICU
2107 : // identifies these fields in the string:
2108 : //
2109 : // UNUM_GROUPING_SEPARATOR_FIELD
2110 : // |
2111 : // UNUM_SIGN_FIELD | UNUM_DECIMAL_SEPARATOR_FIELD
2112 : // | __________/| |
2113 : // | / | | | |
2114 : // "-19,766,580,028,249.41 US dollars"
2115 : // \________________/ |/ \_______/
2116 : // | | |
2117 : // UNUM_INTEGER_FIELD | UNUM_CURRENCY_FIELD
2118 : // |
2119 : // UNUM_FRACTION_FIELD
2120 : //
2121 : // These fields map to parts as follows:
2122 : //
2123 : // integer decimal
2124 : // _____|________ |
2125 : // / /| |\ |\ |\ | literal
2126 : // /| / | | \ | \ | \| |
2127 : // "-19,766,580,028,249.41 US dollars"
2128 : // | \___|___|___/ |/ \________/
2129 : // | | | |
2130 : // | group | currency
2131 : // | |
2132 : // minusSign fraction
2133 : //
2134 : // The sign is a part. Each comma is a part, splitting the integer field
2135 : // into parts for trillions/billions/&c. digits. The decimal point is a
2136 : // part. Cents are a part. The space between cents and currency is a part
2137 : // (outside any field). Last, the currency field is a part.
2138 : //
2139 : // Because parts fully partition the formatted string, we only track the
2140 : // end of each part -- the beginning is implicitly the last part's end.
2141 : struct Part
2142 : {
2143 : uint32_t end;
2144 : FieldType type;
2145 : };
2146 :
2147 0 : class PartGenerator
2148 : {
2149 : // The fields in order from start to end, then least to most nested.
2150 : const FieldsVector& fields;
2151 :
2152 : // Index of the current field, in |fields|, being considered to
2153 : // determine part boundaries. |lastEnd <= fields[index].begin| is an
2154 : // invariant.
2155 : size_t index;
2156 :
2157 : // The end index of the last part produced, always less than or equal
2158 : // to |limit|, strictly increasing.
2159 : uint32_t lastEnd;
2160 :
2161 : // The length of the overall formatted string.
2162 : const uint32_t limit;
2163 :
2164 : Vector<size_t, 4> enclosingFields;
2165 :
2166 0 : void popEnclosingFieldsEndingAt(uint32_t end) {
2167 0 : MOZ_ASSERT_IF(enclosingFields.length() > 0,
2168 : fields[enclosingFields.back()].end >= end);
2169 :
2170 0 : while (enclosingFields.length() > 0 && fields[enclosingFields.back()].end == end)
2171 0 : enclosingFields.popBack();
2172 0 : }
2173 :
2174 0 : bool nextPartInternal(Part* part) {
2175 0 : size_t len = fields.length();
2176 0 : MOZ_ASSERT(index <= len);
2177 :
2178 : // If we're out of fields, all that remains are part(s) consisting
2179 : // of trailing portions of enclosing fields, and maybe a final
2180 : // literal part.
2181 0 : if (index == len) {
2182 0 : if (enclosingFields.length() > 0) {
2183 0 : const auto& enclosing = fields[enclosingFields.popCopy()];
2184 0 : part->end = enclosing.end;
2185 0 : part->type = enclosing.type;
2186 :
2187 : // If additional enclosing fields end where this part ends,
2188 : // pop them as well.
2189 0 : popEnclosingFieldsEndingAt(part->end);
2190 : } else {
2191 0 : part->end = limit;
2192 0 : part->type = &JSAtomState::literal;
2193 : }
2194 :
2195 0 : return true;
2196 : }
2197 :
2198 : // Otherwise we still have a field to process.
2199 0 : const Field* current = &fields[index];
2200 0 : MOZ_ASSERT(lastEnd <= current->begin);
2201 0 : MOZ_ASSERT(current->begin < current->end);
2202 :
2203 : // But first, deal with inter-field space.
2204 0 : if (lastEnd < current->begin) {
2205 0 : if (enclosingFields.length() > 0) {
2206 : // Space between fields, within an enclosing field, is part
2207 : // of that enclosing field, until the start of the current
2208 : // field or the end of the enclosing field, whichever is
2209 : // earlier.
2210 0 : const auto& enclosing = fields[enclosingFields.back()];
2211 0 : part->end = std::min(enclosing.end, current->begin);
2212 0 : part->type = enclosing.type;
2213 0 : popEnclosingFieldsEndingAt(part->end);
2214 : } else {
2215 : // If there's no enclosing field, the space is a literal.
2216 0 : part->end = current->begin;
2217 0 : part->type = &JSAtomState::literal;
2218 : }
2219 :
2220 0 : return true;
2221 : }
2222 :
2223 : // Otherwise, the part spans a prefix of the current field. Find
2224 : // the most-nested field containing that prefix.
2225 : const Field* next;
2226 0 : do {
2227 0 : current = &fields[index];
2228 :
2229 : // If the current field is last, the part extends to its end.
2230 0 : if (++index == len) {
2231 0 : part->end = current->end;
2232 0 : part->type = current->type;
2233 0 : return true;
2234 : }
2235 :
2236 0 : next = &fields[index];
2237 0 : MOZ_ASSERT(current->begin <= next->begin);
2238 0 : MOZ_ASSERT(current->begin < next->end);
2239 :
2240 : // If the next field nests within the current field, push an
2241 : // enclosing field. (If there are no nested fields, don't
2242 : // bother pushing a field that'd be immediately popped.)
2243 0 : if (current->end > next->begin) {
2244 0 : if (!enclosingFields.append(index - 1))
2245 0 : return false;
2246 : }
2247 :
2248 : // Do so until the next field begins after this one.
2249 0 : } while (current->begin == next->begin);
2250 :
2251 0 : part->type = current->type;
2252 :
2253 0 : if (current->end <= next->begin) {
2254 : // The next field begins after the current field ends. Therefore
2255 : // the current part ends at the end of the current field.
2256 0 : part->end = current->end;
2257 0 : popEnclosingFieldsEndingAt(part->end);
2258 : } else {
2259 : // The current field encloses the next one. The current part
2260 : // ends where the next field/part will start.
2261 0 : part->end = next->begin;
2262 : }
2263 :
2264 0 : return true;
2265 : }
2266 :
2267 : public:
2268 0 : PartGenerator(JSContext* cx, const FieldsVector& vec, uint32_t limit)
2269 0 : : fields(vec), index(0), lastEnd(0), limit(limit), enclosingFields(cx)
2270 0 : {}
2271 :
2272 0 : bool nextPart(bool* hasPart, Part* part) {
2273 : // There are no parts left if we've partitioned the entire string.
2274 0 : if (lastEnd == limit) {
2275 0 : MOZ_ASSERT(enclosingFields.length() == 0);
2276 0 : *hasPart = false;
2277 0 : return true;
2278 : }
2279 :
2280 0 : if (!nextPartInternal(part))
2281 0 : return false;
2282 :
2283 0 : *hasPart = true;
2284 0 : lastEnd = part->end;
2285 0 : return true;
2286 : }
2287 : };
2288 :
2289 : // Finally, generate the result array.
2290 0 : size_t lastEndIndex = 0;
2291 0 : uint32_t partIndex = 0;
2292 0 : RootedObject singlePart(cx);
2293 0 : RootedValue propVal(cx);
2294 :
2295 0 : PartGenerator gen(cx, fields, overallResult->length());
2296 : do {
2297 : bool hasPart;
2298 : Part part;
2299 0 : if (!gen.nextPart(&hasPart, &part))
2300 0 : return false;
2301 :
2302 0 : if (!hasPart)
2303 0 : break;
2304 :
2305 0 : FieldType type = part.type;
2306 0 : size_t endIndex = part.end;
2307 :
2308 0 : MOZ_ASSERT(lastEndIndex < endIndex);
2309 :
2310 0 : singlePart = NewBuiltinClassInstance<PlainObject>(cx);
2311 0 : if (!singlePart)
2312 0 : return false;
2313 :
2314 0 : propVal.setString(cx->names().*type);
2315 0 : if (!DefineProperty(cx, singlePart, cx->names().type, propVal))
2316 0 : return false;
2317 :
2318 : JSLinearString* partSubstr =
2319 0 : NewDependentString(cx, overallResult, lastEndIndex, endIndex - lastEndIndex);
2320 0 : if (!partSubstr)
2321 0 : return false;
2322 :
2323 0 : propVal.setString(partSubstr);
2324 0 : if (!DefineProperty(cx, singlePart, cx->names().value, propVal))
2325 0 : return false;
2326 :
2327 0 : propVal.setObject(*singlePart);
2328 0 : if (!DefineElement(cx, partsArray, partIndex, propVal))
2329 0 : return false;
2330 :
2331 0 : lastEndIndex = endIndex;
2332 0 : partIndex++;
2333 : } while (true);
2334 :
2335 0 : MOZ_ASSERT(lastEndIndex == overallResult->length(),
2336 : "result array must partition the entire string");
2337 :
2338 0 : result.setObject(*partsArray);
2339 0 : return true;
2340 : }
2341 :
2342 : bool
2343 0 : js::intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp)
2344 : {
2345 0 : CallArgs args = CallArgsFromVp(argc, vp);
2346 0 : MOZ_ASSERT(args.length() == 3);
2347 0 : MOZ_ASSERT(args[0].isObject());
2348 0 : MOZ_ASSERT(args[1].isNumber());
2349 0 : MOZ_ASSERT(args[2].isBoolean());
2350 :
2351 0 : Rooted<NumberFormatObject*> numberFormat(cx, &args[0].toObject().as<NumberFormatObject>());
2352 :
2353 : // Obtain a cached UNumberFormat object.
2354 : void* priv =
2355 0 : numberFormat->getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT).toPrivate();
2356 0 : UNumberFormat* nf = static_cast<UNumberFormat*>(priv);
2357 0 : if (!nf) {
2358 0 : nf = NewUNumberFormat(cx, numberFormat);
2359 0 : if (!nf)
2360 0 : return false;
2361 0 : numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nf));
2362 : }
2363 :
2364 : // Use the UNumberFormat to actually format the number.
2365 0 : if (args[2].toBoolean()) {
2366 0 : return intl_FormatNumberToParts(cx, nf, args[1].toNumber(), args.rval());
2367 : }
2368 0 : return intl_FormatNumber(cx, nf, args[1].toNumber(), args.rval());
2369 : }
2370 :
2371 :
2372 : /******************** DateTimeFormat ********************/
2373 :
2374 : const ClassOps DateTimeFormatObject::classOps_ = {
2375 : nullptr, /* addProperty */
2376 : nullptr, /* delProperty */
2377 : nullptr, /* getProperty */
2378 : nullptr, /* setProperty */
2379 : nullptr, /* enumerate */
2380 : nullptr, /* newEnumerate */
2381 : nullptr, /* resolve */
2382 : nullptr, /* mayResolve */
2383 : DateTimeFormatObject::finalize
2384 : };
2385 :
2386 : const Class DateTimeFormatObject::class_ = {
2387 : js_Object_str,
2388 : JSCLASS_HAS_RESERVED_SLOTS(DateTimeFormatObject::SLOT_COUNT) |
2389 : JSCLASS_FOREGROUND_FINALIZE,
2390 : &DateTimeFormatObject::classOps_
2391 : };
2392 :
2393 : #if JS_HAS_TOSOURCE
2394 : static bool
2395 0 : dateTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
2396 : {
2397 0 : CallArgs args = CallArgsFromVp(argc, vp);
2398 0 : args.rval().setString(cx->names().DateTimeFormat);
2399 0 : return true;
2400 : }
2401 : #endif
2402 :
2403 : static const JSFunctionSpec dateTimeFormat_static_methods[] = {
2404 : JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_DateTimeFormat_supportedLocalesOf", 1, 0),
2405 : JS_FS_END
2406 : };
2407 :
2408 : static const JSFunctionSpec dateTimeFormat_methods[] = {
2409 : JS_SELF_HOSTED_FN("resolvedOptions", "Intl_DateTimeFormat_resolvedOptions", 0, 0),
2410 : JS_SELF_HOSTED_FN("formatToParts", "Intl_DateTimeFormat_formatToParts", 1, 0),
2411 : #if JS_HAS_TOSOURCE
2412 : JS_FN(js_toSource_str, dateTimeFormat_toSource, 0, 0),
2413 : #endif
2414 : JS_FS_END
2415 : };
2416 :
2417 : static const JSPropertySpec dateTimeFormat_properties[] = {
2418 : JS_SELF_HOSTED_GET("format", "Intl_DateTimeFormat_format_get", 0),
2419 : JS_STRING_SYM_PS(toStringTag, "Object", JSPROP_READONLY),
2420 : JS_PS_END
2421 : };
2422 :
2423 : /**
2424 : * 12.2.1 Intl.DateTimeFormat([ locales [, options]])
2425 : *
2426 : * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
2427 : */
2428 : static bool
2429 0 : DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct, DateTimeFormatOptions dtfOptions)
2430 : {
2431 : // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
2432 :
2433 : // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
2434 0 : RootedObject proto(cx);
2435 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
2436 0 : return false;
2437 :
2438 0 : if (!proto) {
2439 0 : proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
2440 0 : if (!proto)
2441 0 : return false;
2442 : }
2443 :
2444 0 : Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
2445 0 : dateTimeFormat = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
2446 0 : if (!dateTimeFormat)
2447 0 : return false;
2448 :
2449 0 : dateTimeFormat->setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
2450 0 : dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
2451 0 : PrivateValue(nullptr));
2452 :
2453 0 : RootedValue thisValue(cx, construct ? ObjectValue(*dateTimeFormat) : args.thisv());
2454 0 : RootedValue locales(cx, args.get(0));
2455 0 : RootedValue options(cx, args.get(1));
2456 :
2457 : // Step 3.
2458 0 : return LegacyIntlInitialize(cx, dateTimeFormat, cx->names().InitializeDateTimeFormat,
2459 0 : thisValue, locales, options, dtfOptions, args.rval());
2460 : }
2461 :
2462 : static bool
2463 0 : DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
2464 : {
2465 0 : CallArgs args = CallArgsFromVp(argc, vp);
2466 0 : return DateTimeFormat(cx, args, args.isConstructing(), DateTimeFormatOptions::Standard);
2467 : }
2468 :
2469 : static bool
2470 0 : MozDateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
2471 : {
2472 0 : CallArgs args = CallArgsFromVp(argc, vp);
2473 :
2474 : // Don't allow to call mozIntl.DateTimeFormat as a function. That way we
2475 : // don't need to worry how to handle the legacy initialization semantics
2476 : // when applied on mozIntl.DateTimeFormat.
2477 0 : if (!ThrowIfNotConstructing(cx, args, "mozIntl.DateTimeFormat"))
2478 0 : return false;
2479 :
2480 0 : return DateTimeFormat(cx, args, true, DateTimeFormatOptions::EnableMozExtensions);
2481 : }
2482 :
2483 : bool
2484 0 : js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
2485 : {
2486 0 : CallArgs args = CallArgsFromVp(argc, vp);
2487 0 : MOZ_ASSERT(args.length() == 2);
2488 0 : MOZ_ASSERT(!args.isConstructing());
2489 : // intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it
2490 : // cannot be used with "new", but it still has to be treated as a
2491 : // constructor.
2492 0 : return DateTimeFormat(cx, args, true, DateTimeFormatOptions::Standard);
2493 : }
2494 :
2495 : void
2496 0 : DateTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
2497 : {
2498 0 : MOZ_ASSERT(fop->onActiveCooperatingThread());
2499 :
2500 : const Value& slot =
2501 0 : obj->as<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT);
2502 0 : if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
2503 0 : udat_close(df);
2504 0 : }
2505 :
2506 : static JSObject*
2507 6 : CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
2508 : MutableHandleObject constructor, DateTimeFormatOptions dtfOptions)
2509 : {
2510 12 : RootedFunction ctor(cx);
2511 : ctor = dtfOptions == DateTimeFormatOptions::EnableMozExtensions
2512 18 : ? GlobalObject::createConstructor(cx, MozDateTimeFormat, cx->names().DateTimeFormat, 0)
2513 12 : : GlobalObject::createConstructor(cx, DateTimeFormat, cx->names().DateTimeFormat, 0);
2514 6 : if (!ctor)
2515 0 : return nullptr;
2516 :
2517 12 : RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
2518 6 : if (!proto)
2519 0 : return nullptr;
2520 :
2521 6 : if (!LinkConstructorAndPrototype(cx, ctor, proto))
2522 0 : return nullptr;
2523 :
2524 : // 12.3.2
2525 6 : if (!JS_DefineFunctions(cx, ctor, dateTimeFormat_static_methods))
2526 0 : return nullptr;
2527 :
2528 : // 12.4.4 and 12.4.5
2529 6 : if (!JS_DefineFunctions(cx, proto, dateTimeFormat_methods))
2530 0 : return nullptr;
2531 :
2532 : // 12.4.2 and 12.4.3
2533 6 : if (!JS_DefineProperties(cx, proto, dateTimeFormat_properties))
2534 0 : return nullptr;
2535 :
2536 : // 8.1
2537 12 : RootedValue ctorValue(cx, ObjectValue(*ctor));
2538 6 : if (!DefineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, nullptr, nullptr, 0))
2539 0 : return nullptr;
2540 :
2541 6 : constructor.set(ctor);
2542 6 : return proto;
2543 : }
2544 :
2545 : bool
2546 0 : js::AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
2547 : {
2548 0 : Handle<GlobalObject*> global = cx->global();
2549 :
2550 0 : RootedObject mozDateTimeFormat(cx);
2551 : JSObject* mozDateTimeFormatProto =
2552 0 : CreateDateTimeFormatPrototype(cx, intl, global, &mozDateTimeFormat, DateTimeFormatOptions::EnableMozExtensions);
2553 0 : return mozDateTimeFormatProto != nullptr;
2554 : }
2555 :
2556 : bool
2557 0 : js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
2558 : {
2559 0 : CallArgs args = CallArgsFromVp(argc, vp);
2560 0 : MOZ_ASSERT(args.length() == 0);
2561 :
2562 0 : RootedValue result(cx);
2563 0 : if (!intl_availableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
2564 0 : return false;
2565 0 : args.rval().set(result);
2566 0 : return true;
2567 : }
2568 :
2569 : static bool
2570 0 : DefaultCalendar(JSContext* cx, const JSAutoByteString& locale, MutableHandleValue rval)
2571 : {
2572 0 : UErrorCode status = U_ZERO_ERROR;
2573 0 : UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
2574 :
2575 : // This correctly handles nullptr |cal| when opening failed.
2576 0 : ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
2577 :
2578 0 : const char* calendar = ucal_getType(cal, &status);
2579 0 : if (U_FAILURE(status)) {
2580 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
2581 0 : return false;
2582 : }
2583 :
2584 : // ICU returns old-style keyword values; map them to BCP 47 equivalents
2585 0 : JSString* str = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
2586 0 : if (!str)
2587 0 : return false;
2588 :
2589 0 : rval.setString(str);
2590 0 : return true;
2591 : }
2592 :
2593 : struct CalendarAlias
2594 : {
2595 : const char* const calendar;
2596 : const char* const alias;
2597 : };
2598 :
2599 : const CalendarAlias calendarAliases[] = {
2600 : { "islamic-civil", "islamicc" },
2601 : { "ethioaa", "ethiopic-amete-alem" }
2602 : };
2603 :
2604 : bool
2605 0 : js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
2606 : {
2607 0 : CallArgs args = CallArgsFromVp(argc, vp);
2608 0 : MOZ_ASSERT(args.length() == 1);
2609 0 : MOZ_ASSERT(args[0].isString());
2610 :
2611 0 : JSAutoByteString locale(cx, args[0].toString());
2612 0 : if (!locale)
2613 0 : return false;
2614 :
2615 0 : RootedObject calendars(cx, NewDenseEmptyArray(cx));
2616 0 : if (!calendars)
2617 0 : return false;
2618 0 : uint32_t index = 0;
2619 :
2620 : // We need the default calendar for the locale as the first result.
2621 0 : RootedValue element(cx);
2622 0 : if (!DefaultCalendar(cx, locale, &element))
2623 0 : return false;
2624 :
2625 0 : if (!DefineElement(cx, calendars, index++, element))
2626 0 : return false;
2627 :
2628 : // Now get the calendars that "would make a difference", i.e., not the default.
2629 0 : UErrorCode status = U_ZERO_ERROR;
2630 0 : UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
2631 0 : if (U_FAILURE(status)) {
2632 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
2633 0 : return false;
2634 : }
2635 0 : ScopedICUObject<UEnumeration, uenum_close> toClose(values);
2636 :
2637 0 : uint32_t count = uenum_count(values, &status);
2638 0 : if (U_FAILURE(status)) {
2639 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
2640 0 : return false;
2641 : }
2642 :
2643 0 : for (; count > 0; count--) {
2644 0 : const char* calendar = uenum_next(values, nullptr, &status);
2645 0 : if (U_FAILURE(status)) {
2646 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
2647 0 : return false;
2648 : }
2649 :
2650 : // ICU returns old-style keyword values; map them to BCP 47 equivalents
2651 0 : calendar = uloc_toUnicodeLocaleType("ca", calendar);
2652 :
2653 0 : JSString* jscalendar = JS_NewStringCopyZ(cx, calendar);
2654 0 : if (!jscalendar)
2655 0 : return false;
2656 0 : element = StringValue(jscalendar);
2657 0 : if (!DefineElement(cx, calendars, index++, element))
2658 0 : return false;
2659 :
2660 : // ICU doesn't return calendar aliases, append them here.
2661 0 : for (const auto& calendarAlias : calendarAliases) {
2662 0 : if (equal(calendar, calendarAlias.calendar)) {
2663 0 : JSString* jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
2664 0 : if (!jscalendar)
2665 0 : return false;
2666 0 : element = StringValue(jscalendar);
2667 0 : if (!DefineElement(cx, calendars, index++, element))
2668 0 : return false;
2669 : }
2670 : }
2671 : }
2672 :
2673 0 : args.rval().setObject(*calendars);
2674 0 : return true;
2675 : }
2676 :
2677 : bool
2678 0 : js::intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp)
2679 : {
2680 0 : CallArgs args = CallArgsFromVp(argc, vp);
2681 0 : MOZ_ASSERT(args.length() == 1);
2682 0 : MOZ_ASSERT(args[0].isString());
2683 :
2684 0 : JSAutoByteString locale(cx, args[0].toString());
2685 0 : if (!locale)
2686 0 : return false;
2687 :
2688 0 : return DefaultCalendar(cx, locale, args.rval());
2689 : }
2690 :
2691 : template<typename Char>
2692 : static constexpr Char
2693 0 : ToUpperASCII(Char c)
2694 : {
2695 0 : return ('a' <= c && c <= 'z')
2696 : ? (c & ~0x20)
2697 0 : : c;
2698 : }
2699 :
2700 : static_assert(ToUpperASCII('a') == 'A', "verifying 'a' uppercases correctly");
2701 : static_assert(ToUpperASCII('m') == 'M', "verifying 'm' uppercases correctly");
2702 : static_assert(ToUpperASCII('z') == 'Z', "verifying 'z' uppercases correctly");
2703 : static_assert(ToUpperASCII(u'a') == u'A', "verifying u'a' uppercases correctly");
2704 : static_assert(ToUpperASCII(u'k') == u'K', "verifying u'k' uppercases correctly");
2705 : static_assert(ToUpperASCII(u'z') == u'Z', "verifying u'z' uppercases correctly");
2706 :
2707 : template<typename Char1, typename Char2>
2708 : static bool
2709 0 : EqualCharsIgnoreCaseASCII(const Char1* s1, const Char2* s2, size_t len)
2710 : {
2711 0 : for (const Char1* s1end = s1 + len; s1 < s1end; s1++, s2++) {
2712 0 : if (ToUpperASCII(*s1) != ToUpperASCII(*s2))
2713 0 : return false;
2714 : }
2715 0 : return true;
2716 : }
2717 :
2718 : template<typename Char>
2719 : static js::HashNumber
2720 0 : HashStringIgnoreCaseASCII(const Char* s, size_t length)
2721 : {
2722 0 : uint32_t hash = 0;
2723 0 : for (size_t i = 0; i < length; i++)
2724 0 : hash = mozilla::AddToHash(hash, ToUpperASCII(s[i]));
2725 0 : return hash;
2726 : }
2727 :
2728 0 : js::SharedIntlData::TimeZoneHasher::Lookup::Lookup(JSLinearString* timeZone)
2729 0 : : js::SharedIntlData::LinearStringLookup(timeZone)
2730 : {
2731 0 : if (isLatin1)
2732 0 : hash = HashStringIgnoreCaseASCII(latin1Chars, length);
2733 : else
2734 0 : hash = HashStringIgnoreCaseASCII(twoByteChars, length);
2735 0 : }
2736 :
2737 : bool
2738 0 : js::SharedIntlData::TimeZoneHasher::match(TimeZoneName key, const Lookup& lookup)
2739 : {
2740 0 : if (key->length() != lookup.length)
2741 0 : return false;
2742 :
2743 : // Compare time zone names ignoring ASCII case differences.
2744 0 : if (key->hasLatin1Chars()) {
2745 0 : const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
2746 0 : if (lookup.isLatin1)
2747 0 : return EqualCharsIgnoreCaseASCII(keyChars, lookup.latin1Chars, lookup.length);
2748 0 : return EqualCharsIgnoreCaseASCII(keyChars, lookup.twoByteChars, lookup.length);
2749 : }
2750 :
2751 0 : const char16_t* keyChars = key->twoByteChars(lookup.nogc);
2752 0 : if (lookup.isLatin1)
2753 0 : return EqualCharsIgnoreCaseASCII(lookup.latin1Chars, keyChars, lookup.length);
2754 0 : return EqualCharsIgnoreCaseASCII(keyChars, lookup.twoByteChars, lookup.length);
2755 : }
2756 :
2757 : static bool
2758 0 : IsLegacyICUTimeZone(const char* timeZone)
2759 : {
2760 0 : for (const auto& legacyTimeZone : js::timezone::legacyICUTimeZones) {
2761 0 : if (equal(timeZone, legacyTimeZone))
2762 0 : return true;
2763 : }
2764 0 : return false;
2765 : }
2766 :
2767 : bool
2768 0 : js::SharedIntlData::ensureTimeZones(JSContext* cx)
2769 : {
2770 0 : if (timeZoneDataInitialized)
2771 0 : return true;
2772 :
2773 : // If ensureTimeZones() was called previously, but didn't complete due to
2774 : // OOM, clear all sets/maps and start from scratch.
2775 0 : if (availableTimeZones.initialized())
2776 0 : availableTimeZones.finish();
2777 0 : if (!availableTimeZones.init()) {
2778 0 : ReportOutOfMemory(cx);
2779 0 : return false;
2780 : }
2781 :
2782 0 : UErrorCode status = U_ZERO_ERROR;
2783 0 : UEnumeration* values = ucal_openTimeZones(&status);
2784 0 : if (U_FAILURE(status)) {
2785 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
2786 0 : return false;
2787 : }
2788 0 : ScopedICUObject<UEnumeration, uenum_close> toClose(values);
2789 :
2790 0 : RootedAtom timeZone(cx);
2791 : while (true) {
2792 : int32_t size;
2793 0 : const char* rawTimeZone = uenum_next(values, &size, &status);
2794 0 : if (U_FAILURE(status)) {
2795 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
2796 0 : return false;
2797 : }
2798 :
2799 0 : if (rawTimeZone == nullptr)
2800 0 : break;
2801 :
2802 : // Skip legacy ICU time zone names.
2803 0 : if (IsLegacyICUTimeZone(rawTimeZone))
2804 0 : continue;
2805 :
2806 0 : MOZ_ASSERT(size >= 0);
2807 0 : timeZone = Atomize(cx, rawTimeZone, size_t(size));
2808 0 : if (!timeZone)
2809 0 : return false;
2810 :
2811 0 : TimeZoneHasher::Lookup lookup(timeZone);
2812 0 : TimeZoneSet::AddPtr p = availableTimeZones.lookupForAdd(lookup);
2813 :
2814 : // ICU shouldn't report any duplicate time zone names, but if it does,
2815 : // just ignore the duplicate name.
2816 0 : if (!p && !availableTimeZones.add(p, timeZone)) {
2817 0 : ReportOutOfMemory(cx);
2818 0 : return false;
2819 : }
2820 0 : }
2821 :
2822 0 : if (ianaZonesTreatedAsLinksByICU.initialized())
2823 0 : ianaZonesTreatedAsLinksByICU.finish();
2824 0 : if (!ianaZonesTreatedAsLinksByICU.init()) {
2825 0 : ReportOutOfMemory(cx);
2826 0 : return false;
2827 : }
2828 :
2829 0 : for (const char* rawTimeZone : timezone::ianaZonesTreatedAsLinksByICU) {
2830 0 : MOZ_ASSERT(rawTimeZone != nullptr);
2831 0 : timeZone = Atomize(cx, rawTimeZone, strlen(rawTimeZone));
2832 0 : if (!timeZone)
2833 0 : return false;
2834 :
2835 0 : TimeZoneHasher::Lookup lookup(timeZone);
2836 0 : TimeZoneSet::AddPtr p = ianaZonesTreatedAsLinksByICU.lookupForAdd(lookup);
2837 0 : MOZ_ASSERT(!p, "Duplicate entry in timezone::ianaZonesTreatedAsLinksByICU");
2838 :
2839 0 : if (!ianaZonesTreatedAsLinksByICU.add(p, timeZone)) {
2840 0 : ReportOutOfMemory(cx);
2841 0 : return false;
2842 : }
2843 : }
2844 :
2845 0 : if (ianaLinksCanonicalizedDifferentlyByICU.initialized())
2846 0 : ianaLinksCanonicalizedDifferentlyByICU.finish();
2847 0 : if (!ianaLinksCanonicalizedDifferentlyByICU.init()) {
2848 0 : ReportOutOfMemory(cx);
2849 0 : return false;
2850 : }
2851 :
2852 0 : RootedAtom linkName(cx);
2853 0 : RootedAtom& target = timeZone;
2854 0 : for (const auto& linkAndTarget : timezone::ianaLinksCanonicalizedDifferentlyByICU) {
2855 0 : const char* rawLinkName = linkAndTarget.link;
2856 0 : const char* rawTarget = linkAndTarget.target;
2857 :
2858 0 : MOZ_ASSERT(rawLinkName != nullptr);
2859 0 : linkName = Atomize(cx, rawLinkName, strlen(rawLinkName));
2860 0 : if (!linkName)
2861 0 : return false;
2862 :
2863 0 : MOZ_ASSERT(rawTarget != nullptr);
2864 0 : target = Atomize(cx, rawTarget, strlen(rawTarget));
2865 0 : if (!target)
2866 0 : return false;
2867 :
2868 0 : TimeZoneHasher::Lookup lookup(linkName);
2869 0 : TimeZoneMap::AddPtr p = ianaLinksCanonicalizedDifferentlyByICU.lookupForAdd(lookup);
2870 0 : MOZ_ASSERT(!p, "Duplicate entry in timezone::ianaLinksCanonicalizedDifferentlyByICU");
2871 :
2872 0 : if (!ianaLinksCanonicalizedDifferentlyByICU.add(p, linkName, target)) {
2873 0 : ReportOutOfMemory(cx);
2874 0 : return false;
2875 : }
2876 : }
2877 :
2878 0 : MOZ_ASSERT(!timeZoneDataInitialized, "ensureTimeZones is neither reentrant nor thread-safe");
2879 0 : timeZoneDataInitialized = true;
2880 :
2881 0 : return true;
2882 : }
2883 :
2884 : bool
2885 0 : js::SharedIntlData::validateTimeZoneName(JSContext* cx, HandleString timeZone,
2886 : MutableHandleAtom result)
2887 : {
2888 0 : if (!ensureTimeZones(cx))
2889 0 : return false;
2890 :
2891 0 : RootedLinearString timeZoneLinear(cx, timeZone->ensureLinear(cx));
2892 0 : if (!timeZoneLinear)
2893 0 : return false;
2894 :
2895 0 : TimeZoneHasher::Lookup lookup(timeZoneLinear);
2896 0 : if (TimeZoneSet::Ptr p = availableTimeZones.lookup(lookup))
2897 0 : result.set(*p);
2898 :
2899 0 : return true;
2900 : }
2901 :
2902 : bool
2903 0 : js::SharedIntlData::tryCanonicalizeTimeZoneConsistentWithIANA(JSContext* cx, HandleString timeZone,
2904 : MutableHandleAtom result)
2905 : {
2906 0 : if (!ensureTimeZones(cx))
2907 0 : return false;
2908 :
2909 0 : RootedLinearString timeZoneLinear(cx, timeZone->ensureLinear(cx));
2910 0 : if (!timeZoneLinear)
2911 0 : return false;
2912 :
2913 0 : TimeZoneHasher::Lookup lookup(timeZoneLinear);
2914 0 : MOZ_ASSERT(availableTimeZones.has(lookup), "Invalid time zone name");
2915 :
2916 0 : if (TimeZoneMap::Ptr p = ianaLinksCanonicalizedDifferentlyByICU.lookup(lookup)) {
2917 : // The effectively supported time zones aren't known at compile time,
2918 : // when
2919 : // 1. SpiderMonkey was compiled with "--with-system-icu".
2920 : // 2. ICU's dynamic time zone data loading feature was used.
2921 : // (ICU supports loading time zone files at runtime through the
2922 : // ICU_TIMEZONE_FILES_DIR environment variable.)
2923 : // Ensure ICU supports the new target zone before applying the update.
2924 0 : TimeZoneName targetTimeZone = p->value();
2925 0 : TimeZoneHasher::Lookup targetLookup(targetTimeZone);
2926 0 : if (availableTimeZones.has(targetLookup))
2927 0 : result.set(targetTimeZone);
2928 0 : } else if (TimeZoneSet::Ptr p = ianaZonesTreatedAsLinksByICU.lookup(lookup)) {
2929 0 : result.set(*p);
2930 : }
2931 :
2932 0 : return true;
2933 : }
2934 :
2935 : void
2936 0 : js::SharedIntlData::destroyInstance()
2937 : {
2938 0 : availableTimeZones.finish();
2939 0 : ianaZonesTreatedAsLinksByICU.finish();
2940 0 : ianaLinksCanonicalizedDifferentlyByICU.finish();
2941 0 : upperCaseFirstLocales.finish();
2942 0 : }
2943 :
2944 : void
2945 22 : js::SharedIntlData::trace(JSTracer* trc)
2946 : {
2947 : // Atoms are always tenured.
2948 22 : if (!JS::CurrentThreadIsHeapMinorCollecting()) {
2949 1 : availableTimeZones.trace(trc);
2950 1 : ianaZonesTreatedAsLinksByICU.trace(trc);
2951 1 : ianaLinksCanonicalizedDifferentlyByICU.trace(trc);
2952 1 : upperCaseFirstLocales.trace(trc);
2953 : }
2954 22 : }
2955 :
2956 : size_t
2957 0 : js::SharedIntlData::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
2958 : {
2959 0 : return availableTimeZones.sizeOfExcludingThis(mallocSizeOf) +
2960 0 : ianaZonesTreatedAsLinksByICU.sizeOfExcludingThis(mallocSizeOf) +
2961 0 : ianaLinksCanonicalizedDifferentlyByICU.sizeOfExcludingThis(mallocSizeOf) +
2962 0 : upperCaseFirstLocales.sizeOfExcludingThis(mallocSizeOf);
2963 : }
2964 :
2965 : bool
2966 0 : js::intl_IsValidTimeZoneName(JSContext* cx, unsigned argc, Value* vp)
2967 : {
2968 0 : CallArgs args = CallArgsFromVp(argc, vp);
2969 0 : MOZ_ASSERT(args.length() == 1);
2970 0 : MOZ_ASSERT(args[0].isString());
2971 :
2972 0 : SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
2973 :
2974 0 : RootedString timeZone(cx, args[0].toString());
2975 0 : RootedAtom validatedTimeZone(cx);
2976 0 : if (!sharedIntlData.validateTimeZoneName(cx, timeZone, &validatedTimeZone))
2977 0 : return false;
2978 :
2979 0 : if (validatedTimeZone) {
2980 0 : cx->markAtom(validatedTimeZone);
2981 0 : args.rval().setString(validatedTimeZone);
2982 : } else {
2983 0 : args.rval().setNull();
2984 : }
2985 :
2986 0 : return true;
2987 : }
2988 :
2989 : bool
2990 0 : js::intl_canonicalizeTimeZone(JSContext* cx, unsigned argc, Value* vp)
2991 : {
2992 0 : CallArgs args = CallArgsFromVp(argc, vp);
2993 0 : MOZ_ASSERT(args.length() == 1);
2994 0 : MOZ_ASSERT(args[0].isString());
2995 :
2996 0 : SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
2997 :
2998 : // Some time zone names are canonicalized differently by ICU -- handle
2999 : // those first:
3000 0 : RootedString timeZone(cx, args[0].toString());
3001 0 : RootedAtom ianaTimeZone(cx);
3002 0 : if (!sharedIntlData.tryCanonicalizeTimeZoneConsistentWithIANA(cx, timeZone, &ianaTimeZone))
3003 0 : return false;
3004 :
3005 0 : if (ianaTimeZone) {
3006 0 : cx->markAtom(ianaTimeZone);
3007 0 : args.rval().setString(ianaTimeZone);
3008 0 : return true;
3009 : }
3010 :
3011 0 : AutoStableStringChars stableChars(cx);
3012 0 : if (!stableChars.initTwoByte(cx, timeZone))
3013 0 : return false;
3014 :
3015 0 : mozilla::Range<const char16_t> tzchars = stableChars.twoByteRange();
3016 :
3017 0 : JSString* str = Call(cx, [&tzchars](UChar* chars, uint32_t size, UErrorCode* status) {
3018 0 : return ucal_getCanonicalTimeZoneID(tzchars.begin().get(), tzchars.length(),
3019 : chars, size, nullptr, status);
3020 0 : });
3021 0 : if (!str)
3022 0 : return false;
3023 :
3024 0 : args.rval().setString(str);
3025 0 : return true;
3026 : }
3027 :
3028 : bool
3029 0 : js::intl_defaultTimeZone(JSContext* cx, unsigned argc, Value* vp)
3030 : {
3031 0 : CallArgs args = CallArgsFromVp(argc, vp);
3032 0 : MOZ_ASSERT(args.length() == 0);
3033 :
3034 : // The current default might be stale, because JS::ResetTimeZone() doesn't
3035 : // immediately update ICU's default time zone. So perform an update if
3036 : // needed.
3037 0 : js::ResyncICUDefaultTimeZone();
3038 :
3039 0 : JSString* str = Call(cx, ucal_getDefaultTimeZone);
3040 0 : if (!str)
3041 0 : return false;
3042 :
3043 0 : args.rval().setString(str);
3044 0 : return true;
3045 : }
3046 :
3047 : bool
3048 0 : js::intl_defaultTimeZoneOffset(JSContext* cx, unsigned argc, Value* vp) {
3049 0 : CallArgs args = CallArgsFromVp(argc, vp);
3050 0 : MOZ_ASSERT(args.length() == 0);
3051 :
3052 0 : UErrorCode status = U_ZERO_ERROR;
3053 0 : const UChar* uTimeZone = nullptr;
3054 0 : int32_t uTimeZoneLength = 0;
3055 0 : const char* rootLocale = "";
3056 0 : UCalendar* cal = ucal_open(uTimeZone, uTimeZoneLength, rootLocale, UCAL_DEFAULT, &status);
3057 0 : if (U_FAILURE(status)) {
3058 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3059 0 : return false;
3060 : }
3061 0 : ScopedICUObject<UCalendar, ucal_close> toClose(cal);
3062 :
3063 0 : int32_t offset = ucal_get(cal, UCAL_ZONE_OFFSET, &status);
3064 0 : if (U_FAILURE(status)) {
3065 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3066 0 : return false;
3067 : }
3068 :
3069 0 : args.rval().setInt32(offset);
3070 0 : return true;
3071 : }
3072 :
3073 : bool
3074 0 : js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
3075 : {
3076 0 : CallArgs args = CallArgsFromVp(argc, vp);
3077 0 : MOZ_ASSERT(args.length() == 2);
3078 0 : MOZ_ASSERT(args[0].isString());
3079 0 : MOZ_ASSERT(args[1].isString());
3080 :
3081 0 : JSAutoByteString locale(cx, args[0].toString());
3082 0 : if (!locale)
3083 0 : return false;
3084 :
3085 0 : AutoStableStringChars skeleton(cx);
3086 0 : if (!skeleton.initTwoByte(cx, args[1].toString()))
3087 0 : return false;
3088 :
3089 0 : mozilla::Range<const char16_t> skelChars = skeleton.twoByteRange();
3090 :
3091 0 : UErrorCode status = U_ZERO_ERROR;
3092 0 : UDateTimePatternGenerator* gen = udatpg_open(icuLocale(locale.ptr()), &status);
3093 0 : if (U_FAILURE(status)) {
3094 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3095 0 : return false;
3096 : }
3097 0 : ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
3098 :
3099 0 : JSString* str = Call(cx, [gen, &skelChars](UChar* chars, uint32_t size, UErrorCode* status) {
3100 0 : return udatpg_getBestPattern(gen, skelChars.begin().get(), skelChars.length(),
3101 : chars, size, status);
3102 0 : });
3103 0 : if (!str)
3104 0 : return false;
3105 :
3106 0 : args.rval().setString(str);
3107 0 : return true;
3108 : }
3109 :
3110 : bool
3111 0 : js::intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp)
3112 : {
3113 0 : CallArgs args = CallArgsFromVp(argc, vp);
3114 0 : MOZ_ASSERT(args.length() == 4);
3115 0 : MOZ_ASSERT(args[0].isString());
3116 :
3117 0 : JSAutoByteString locale(cx, args[0].toString());
3118 0 : if (!locale)
3119 0 : return false;
3120 :
3121 0 : UDateFormatStyle dateStyle = UDAT_NONE;
3122 0 : UDateFormatStyle timeStyle = UDAT_NONE;
3123 :
3124 0 : if (args[1].isString()) {
3125 0 : JSLinearString* dateStyleStr = args[1].toString()->ensureLinear(cx);
3126 0 : if (!dateStyleStr)
3127 0 : return false;
3128 :
3129 0 : if (StringEqualsAscii(dateStyleStr, "full"))
3130 0 : dateStyle = UDAT_FULL;
3131 0 : else if (StringEqualsAscii(dateStyleStr, "long"))
3132 0 : dateStyle = UDAT_LONG;
3133 0 : else if (StringEqualsAscii(dateStyleStr, "medium"))
3134 0 : dateStyle = UDAT_MEDIUM;
3135 0 : else if (StringEqualsAscii(dateStyleStr, "short"))
3136 0 : dateStyle = UDAT_SHORT;
3137 : else
3138 0 : MOZ_ASSERT_UNREACHABLE("unexpected dateStyle");
3139 : }
3140 :
3141 0 : if (args[2].isString()) {
3142 0 : JSLinearString* timeStyleStr = args[2].toString()->ensureLinear(cx);
3143 0 : if (!timeStyleStr)
3144 0 : return false;
3145 :
3146 0 : if (StringEqualsAscii(timeStyleStr, "full"))
3147 0 : timeStyle = UDAT_FULL;
3148 0 : else if (StringEqualsAscii(timeStyleStr, "long"))
3149 0 : timeStyle = UDAT_LONG;
3150 0 : else if (StringEqualsAscii(timeStyleStr, "medium"))
3151 0 : timeStyle = UDAT_MEDIUM;
3152 0 : else if (StringEqualsAscii(timeStyleStr, "short"))
3153 0 : timeStyle = UDAT_SHORT;
3154 : else
3155 0 : MOZ_ASSERT_UNREACHABLE("unexpected timeStyle");
3156 : }
3157 :
3158 0 : AutoStableStringChars timeZone(cx);
3159 0 : if (!timeZone.initTwoByte(cx, args[3].toString()))
3160 0 : return false;
3161 :
3162 0 : mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
3163 :
3164 0 : UErrorCode status = U_ZERO_ERROR;
3165 0 : UDateFormat* df = udat_open(timeStyle, dateStyle, icuLocale(locale.ptr()),
3166 0 : timeZoneChars.begin().get(), timeZoneChars.length(),
3167 0 : nullptr, -1, &status);
3168 0 : if (U_FAILURE(status)) {
3169 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3170 0 : return false;
3171 : }
3172 0 : ScopedICUObject<UDateFormat, udat_close> toClose(df);
3173 :
3174 0 : JSString* str = Call(cx, [df](UChar* chars, uint32_t size, UErrorCode* status) {
3175 0 : return udat_toPattern(df, false, chars, size, status);
3176 0 : });
3177 0 : if (!str)
3178 0 : return false;
3179 0 : args.rval().setString(str);
3180 0 : return true;
3181 : }
3182 :
3183 : /**
3184 : * Returns a new UDateFormat with the locale and date-time formatting options
3185 : * of the given DateTimeFormat.
3186 : */
3187 : static UDateFormat*
3188 0 : NewUDateFormat(JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat)
3189 : {
3190 0 : RootedValue value(cx);
3191 :
3192 0 : RootedObject internals(cx, GetInternals(cx, dateTimeFormat));
3193 0 : if (!internals)
3194 0 : return nullptr;
3195 :
3196 0 : if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
3197 0 : return nullptr;
3198 0 : JSAutoByteString locale(cx, value.toString());
3199 0 : if (!locale)
3200 0 : return nullptr;
3201 :
3202 : // We don't need to look at calendar and numberingSystem - they can only be
3203 : // set via the Unicode locale extension and are therefore already set on
3204 : // locale.
3205 :
3206 0 : if (!GetProperty(cx, internals, internals, cx->names().timeZone, &value))
3207 0 : return nullptr;
3208 :
3209 0 : AutoStableStringChars timeZone(cx);
3210 0 : if (!timeZone.initTwoByte(cx, value.toString()))
3211 0 : return nullptr;
3212 :
3213 0 : mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
3214 :
3215 0 : if (!GetProperty(cx, internals, internals, cx->names().pattern, &value))
3216 0 : return nullptr;
3217 :
3218 0 : AutoStableStringChars pattern(cx);
3219 0 : if (!pattern.initTwoByte(cx, value.toString()))
3220 0 : return nullptr;
3221 :
3222 0 : mozilla::Range<const char16_t> patternChars = pattern.twoByteRange();
3223 :
3224 0 : UErrorCode status = U_ZERO_ERROR;
3225 : UDateFormat* df =
3226 0 : udat_open(UDAT_PATTERN, UDAT_PATTERN, icuLocale(locale.ptr()),
3227 0 : timeZoneChars.begin().get(), timeZoneChars.length(),
3228 0 : patternChars.begin().get(), patternChars.length(), &status);
3229 0 : if (U_FAILURE(status)) {
3230 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3231 0 : return nullptr;
3232 : }
3233 :
3234 : // ECMAScript requires the Gregorian calendar to be used from the beginning
3235 : // of ECMAScript time.
3236 0 : UCalendar* cal = const_cast<UCalendar*>(udat_getCalendar(df));
3237 0 : ucal_setGregorianChange(cal, StartOfTime, &status);
3238 :
3239 : // An error here means the calendar is not Gregorian, so we don't care.
3240 :
3241 0 : return df;
3242 : }
3243 :
3244 : static bool
3245 0 : intl_FormatDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue result)
3246 : {
3247 0 : if (!IsFinite(x)) {
3248 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
3249 0 : return false;
3250 : }
3251 :
3252 0 : JSString* str = Call(cx, [df, x](UChar* chars, int32_t size, UErrorCode* status) {
3253 : return udat_format(df, x, chars, size, nullptr, status);
3254 0 : });
3255 0 : if (!str)
3256 0 : return false;
3257 :
3258 0 : result.setString(str);
3259 0 : return true;
3260 : }
3261 :
3262 : static FieldType
3263 0 : GetFieldTypeForFormatField(UDateFormatField fieldName)
3264 : {
3265 : // See intl/icu/source/i18n/unicode/udat.h for a detailed field list. This
3266 : // switch is deliberately exhaustive: cases might have to be added/removed
3267 : // if this code is compiled with a different ICU with more
3268 : // UDateFormatField enum initializers. Please guard such cases with
3269 : // appropriate ICU version-testing #ifdefs, should cross-version divergence
3270 : // occur.
3271 0 : switch (fieldName) {
3272 : case UDAT_ERA_FIELD:
3273 0 : return &JSAtomState::era;
3274 : case UDAT_YEAR_FIELD:
3275 : case UDAT_YEAR_WOY_FIELD:
3276 : case UDAT_EXTENDED_YEAR_FIELD:
3277 : case UDAT_YEAR_NAME_FIELD:
3278 0 : return &JSAtomState::year;
3279 :
3280 : case UDAT_MONTH_FIELD:
3281 : case UDAT_STANDALONE_MONTH_FIELD:
3282 0 : return &JSAtomState::month;
3283 :
3284 : case UDAT_DATE_FIELD:
3285 : case UDAT_JULIAN_DAY_FIELD:
3286 0 : return &JSAtomState::day;
3287 :
3288 : case UDAT_HOUR_OF_DAY1_FIELD:
3289 : case UDAT_HOUR_OF_DAY0_FIELD:
3290 : case UDAT_HOUR1_FIELD:
3291 : case UDAT_HOUR0_FIELD:
3292 0 : return &JSAtomState::hour;
3293 :
3294 : case UDAT_MINUTE_FIELD:
3295 0 : return &JSAtomState::minute;
3296 :
3297 : case UDAT_SECOND_FIELD:
3298 0 : return &JSAtomState::second;
3299 :
3300 : case UDAT_DAY_OF_WEEK_FIELD:
3301 : case UDAT_STANDALONE_DAY_FIELD:
3302 : case UDAT_DOW_LOCAL_FIELD:
3303 : case UDAT_DAY_OF_WEEK_IN_MONTH_FIELD:
3304 0 : return &JSAtomState::weekday;
3305 :
3306 : case UDAT_AM_PM_FIELD:
3307 0 : return &JSAtomState::dayPeriod;
3308 :
3309 : case UDAT_TIMEZONE_FIELD:
3310 0 : return &JSAtomState::timeZoneName;
3311 :
3312 : case UDAT_FRACTIONAL_SECOND_FIELD:
3313 : case UDAT_DAY_OF_YEAR_FIELD:
3314 : case UDAT_WEEK_OF_YEAR_FIELD:
3315 : case UDAT_WEEK_OF_MONTH_FIELD:
3316 : case UDAT_MILLISECONDS_IN_DAY_FIELD:
3317 : case UDAT_TIMEZONE_RFC_FIELD:
3318 : case UDAT_TIMEZONE_GENERIC_FIELD:
3319 : case UDAT_QUARTER_FIELD:
3320 : case UDAT_STANDALONE_QUARTER_FIELD:
3321 : case UDAT_TIMEZONE_SPECIAL_FIELD:
3322 : case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
3323 : case UDAT_TIMEZONE_ISO_FIELD:
3324 : case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
3325 : #ifndef U_HIDE_INTERNAL_API
3326 : case UDAT_RELATED_YEAR_FIELD:
3327 : #endif
3328 : case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
3329 : case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
3330 : #ifndef U_HIDE_INTERNAL_API
3331 : case UDAT_TIME_SEPARATOR_FIELD:
3332 : #endif
3333 : // These fields are all unsupported.
3334 0 : return nullptr;
3335 :
3336 : #ifndef U_HIDE_DEPRECATED_API
3337 : case UDAT_FIELD_COUNT:
3338 0 : MOZ_ASSERT_UNREACHABLE("format field sentinel value returned by "
3339 : "iterator!");
3340 : #endif
3341 : }
3342 :
3343 0 : MOZ_ASSERT_UNREACHABLE("unenumerated, undocumented format field returned "
3344 : "by iterator");
3345 : return nullptr;
3346 : }
3347 :
3348 : static bool
3349 0 : intl_FormatToPartsDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue result)
3350 : {
3351 0 : if (!IsFinite(x)) {
3352 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
3353 0 : return false;
3354 : }
3355 :
3356 0 : UErrorCode status = U_ZERO_ERROR;
3357 0 : UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
3358 0 : if (U_FAILURE(status)) {
3359 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3360 0 : return false;
3361 : }
3362 0 : ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer);
3363 :
3364 0 : RootedString overallResult(cx);
3365 0 : overallResult = Call(cx, [df, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
3366 : return udat_formatForFields(df, x, chars, size, fpositer, status);
3367 0 : });
3368 0 : if (!overallResult)
3369 0 : return false;
3370 :
3371 0 : RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
3372 0 : if (!partsArray)
3373 0 : return false;
3374 :
3375 0 : if (overallResult->length() == 0) {
3376 : // An empty string contains no parts, so avoid extra work below.
3377 0 : result.setObject(*partsArray);
3378 0 : return true;
3379 : }
3380 :
3381 0 : size_t lastEndIndex = 0;
3382 :
3383 0 : uint32_t partIndex = 0;
3384 0 : RootedObject singlePart(cx);
3385 0 : RootedValue partType(cx);
3386 0 : RootedValue val(cx);
3387 :
3388 0 : auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex) {
3389 0 : singlePart = NewBuiltinClassInstance<PlainObject>(cx);
3390 0 : if (!singlePart)
3391 0 : return false;
3392 :
3393 0 : partType = StringValue(cx->names().*type);
3394 0 : if (!DefineProperty(cx, singlePart, cx->names().type, partType))
3395 0 : return false;
3396 :
3397 : JSLinearString* partSubstr =
3398 0 : NewDependentString(cx, overallResult, beginIndex, endIndex - beginIndex);
3399 0 : if (!partSubstr)
3400 0 : return false;
3401 :
3402 0 : val = StringValue(partSubstr);
3403 0 : if (!DefineProperty(cx, singlePart, cx->names().value, val))
3404 0 : return false;
3405 :
3406 0 : val = ObjectValue(*singlePart);
3407 0 : if (!DefineElement(cx, partsArray, partIndex, val))
3408 0 : return false;
3409 :
3410 0 : lastEndIndex = endIndex;
3411 0 : partIndex++;
3412 0 : return true;
3413 0 : };
3414 :
3415 : int32_t fieldInt, beginIndexInt, endIndexInt;
3416 0 : while ((fieldInt = ufieldpositer_next(fpositer, &beginIndexInt, &endIndexInt)) >= 0) {
3417 0 : MOZ_ASSERT(beginIndexInt >= 0);
3418 0 : MOZ_ASSERT(endIndexInt >= 0);
3419 0 : MOZ_ASSERT(beginIndexInt <= endIndexInt,
3420 : "field iterator returning invalid range");
3421 :
3422 0 : size_t beginIndex(beginIndexInt);
3423 0 : size_t endIndex(endIndexInt);
3424 :
3425 : // Technically this isn't guaranteed. But it appears true in pratice,
3426 : // and http://bugs.icu-project.org/trac/ticket/12024 is expected to
3427 : // correct the documentation lapse.
3428 0 : MOZ_ASSERT(lastEndIndex <= beginIndex,
3429 : "field iteration didn't return fields in order start to "
3430 : "finish as expected");
3431 :
3432 0 : if (FieldType type = GetFieldTypeForFormatField(static_cast<UDateFormatField>(fieldInt))) {
3433 0 : if (lastEndIndex < beginIndex) {
3434 0 : if (!AppendPart(&JSAtomState::literal, lastEndIndex, beginIndex))
3435 0 : return false;
3436 : }
3437 :
3438 0 : if (!AppendPart(type, beginIndex, endIndex))
3439 0 : return false;
3440 : }
3441 : }
3442 :
3443 : // Append any final literal.
3444 0 : if (lastEndIndex < overallResult->length()) {
3445 0 : if (!AppendPart(&JSAtomState::literal, lastEndIndex, overallResult->length()))
3446 0 : return false;
3447 : }
3448 :
3449 0 : result.setObject(*partsArray);
3450 0 : return true;
3451 : }
3452 :
3453 : bool
3454 0 : js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp)
3455 : {
3456 0 : CallArgs args = CallArgsFromVp(argc, vp);
3457 0 : MOZ_ASSERT(args.length() == 3);
3458 0 : MOZ_ASSERT(args[0].isObject());
3459 0 : MOZ_ASSERT(args[1].isNumber());
3460 0 : MOZ_ASSERT(args[2].isBoolean());
3461 :
3462 0 : Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
3463 0 : dateTimeFormat = &args[0].toObject().as<DateTimeFormatObject>();
3464 :
3465 : // Obtain a cached UDateFormat object.
3466 : void* priv =
3467 0 : dateTimeFormat->getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT).toPrivate();
3468 0 : UDateFormat* df = static_cast<UDateFormat*>(priv);
3469 0 : if (!df) {
3470 0 : df = NewUDateFormat(cx, dateTimeFormat);
3471 0 : if (!df)
3472 0 : return false;
3473 0 : dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(df));
3474 : }
3475 :
3476 : // Use the UDateFormat to actually format the time stamp.
3477 0 : return args[2].toBoolean()
3478 0 : ? intl_FormatToPartsDateTime(cx, df, args[1].toNumber(), args.rval())
3479 0 : : intl_FormatDateTime(cx, df, args[1].toNumber(), args.rval());
3480 : }
3481 :
3482 :
3483 : /**************** PluralRules *****************/
3484 :
3485 : const ClassOps PluralRulesObject::classOps_ = {
3486 : nullptr, /* addProperty */
3487 : nullptr, /* delProperty */
3488 : nullptr, /* getProperty */
3489 : nullptr, /* setProperty */
3490 : nullptr, /* enumerate */
3491 : nullptr, /* newEnumerate */
3492 : nullptr, /* resolve */
3493 : nullptr, /* mayResolve */
3494 : PluralRulesObject::finalize
3495 : };
3496 :
3497 : const Class PluralRulesObject::class_ = {
3498 : js_Object_str,
3499 : JSCLASS_HAS_RESERVED_SLOTS(PluralRulesObject::SLOT_COUNT) |
3500 : JSCLASS_FOREGROUND_FINALIZE,
3501 : &PluralRulesObject::classOps_
3502 : };
3503 :
3504 : #if JS_HAS_TOSOURCE
3505 : static bool
3506 0 : pluralRules_toSource(JSContext* cx, unsigned argc, Value* vp)
3507 : {
3508 0 : CallArgs args = CallArgsFromVp(argc, vp);
3509 0 : args.rval().setString(cx->names().PluralRules);
3510 0 : return true;
3511 : }
3512 : #endif
3513 :
3514 : static const JSFunctionSpec pluralRules_static_methods[] = {
3515 : JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_PluralRules_supportedLocalesOf", 1, 0),
3516 : JS_FS_END
3517 : };
3518 :
3519 : static const JSFunctionSpec pluralRules_methods[] = {
3520 : JS_SELF_HOSTED_FN("resolvedOptions", "Intl_PluralRules_resolvedOptions", 0, 0),
3521 : JS_SELF_HOSTED_FN("select", "Intl_PluralRules_select", 1, 0),
3522 : #if JS_HAS_TOSOURCE
3523 : JS_FN(js_toSource_str, pluralRules_toSource, 0, 0),
3524 : #endif
3525 : JS_FS_END
3526 : };
3527 :
3528 : /**
3529 : * PluralRules constructor.
3530 : * Spec: ECMAScript 402 API, PluralRules, 1.1
3531 : */
3532 : static bool
3533 0 : PluralRules(JSContext* cx, unsigned argc, Value* vp)
3534 : {
3535 0 : CallArgs args = CallArgsFromVp(argc, vp);
3536 :
3537 : // Step 1.
3538 0 : if (!ThrowIfNotConstructing(cx, args, "Intl.PluralRules"))
3539 0 : return false;
3540 :
3541 : // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
3542 0 : RootedObject proto(cx);
3543 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
3544 0 : return false;
3545 :
3546 0 : if (!proto) {
3547 0 : proto = GlobalObject::getOrCreatePluralRulesPrototype(cx, cx->global());
3548 0 : if (!proto)
3549 0 : return false;
3550 : }
3551 :
3552 0 : Rooted<PluralRulesObject*> pluralRules(cx);
3553 0 : pluralRules = NewObjectWithGivenProto<PluralRulesObject>(cx, proto);
3554 0 : if (!pluralRules)
3555 0 : return false;
3556 :
3557 0 : pluralRules->setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
3558 0 : pluralRules->setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
3559 :
3560 0 : RootedValue locales(cx, args.get(0));
3561 0 : RootedValue options(cx, args.get(1));
3562 :
3563 : // Step 3.
3564 0 : if (!IntlInitialize(cx, pluralRules, cx->names().InitializePluralRules, locales, options))
3565 0 : return false;
3566 :
3567 0 : args.rval().setObject(*pluralRules);
3568 0 : return true;
3569 : }
3570 :
3571 : void
3572 0 : PluralRulesObject::finalize(FreeOp* fop, JSObject* obj)
3573 : {
3574 0 : MOZ_ASSERT(fop->onActiveCooperatingThread());
3575 :
3576 : const Value& slot =
3577 0 : obj->as<PluralRulesObject>().getReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT);
3578 0 : if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
3579 0 : uplrules_close(pr);
3580 0 : }
3581 :
3582 : static JSObject*
3583 0 : CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
3584 : {
3585 0 : RootedFunction ctor(cx);
3586 0 : ctor = global->createConstructor(cx, &PluralRules, cx->names().PluralRules, 0);
3587 0 : if (!ctor)
3588 0 : return nullptr;
3589 :
3590 0 : RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
3591 0 : if (!proto)
3592 0 : return nullptr;
3593 :
3594 0 : if (!LinkConstructorAndPrototype(cx, ctor, proto))
3595 0 : return nullptr;
3596 :
3597 0 : if (!JS_DefineFunctions(cx, ctor, pluralRules_static_methods))
3598 0 : return nullptr;
3599 :
3600 0 : if (!JS_DefineFunctions(cx, proto, pluralRules_methods))
3601 0 : return nullptr;
3602 :
3603 0 : RootedValue ctorValue(cx, ObjectValue(*ctor));
3604 0 : if (!DefineProperty(cx, Intl, cx->names().PluralRules, ctorValue, nullptr, nullptr, 0))
3605 0 : return nullptr;
3606 :
3607 0 : return proto;
3608 : }
3609 :
3610 : /* static */ bool
3611 0 : js::GlobalObject::addPluralRulesConstructor(JSContext* cx, HandleObject intl)
3612 : {
3613 0 : Handle<GlobalObject*> global = cx->global();
3614 :
3615 : {
3616 0 : const HeapSlot& slot = global->getReservedSlotRef(PLURAL_RULES_PROTO);
3617 0 : if (!slot.isUndefined()) {
3618 0 : MOZ_ASSERT(slot.isObject());
3619 : JS_ReportErrorASCII(cx,
3620 : "the PluralRules constructor can't be added "
3621 0 : "multiple times in the same global");
3622 0 : return false;
3623 : }
3624 : }
3625 :
3626 0 : JSObject* pluralRulesProto = CreatePluralRulesPrototype(cx, intl, global);
3627 0 : if (!pluralRulesProto)
3628 0 : return false;
3629 :
3630 0 : global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
3631 0 : return true;
3632 : }
3633 :
3634 : bool
3635 0 : js::AddPluralRulesConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
3636 : {
3637 0 : return GlobalObject::addPluralRulesConstructor(cx, intl);
3638 : }
3639 :
3640 : bool
3641 0 : js::intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp)
3642 : {
3643 0 : CallArgs args = CallArgsFromVp(argc, vp);
3644 0 : MOZ_ASSERT(args.length() == 0);
3645 :
3646 0 : RootedValue result(cx);
3647 : // We're going to use ULocale availableLocales as per ICU recommendation:
3648 : // https://ssl.icu-project.org/trac/ticket/12756
3649 0 : if (!intl_availableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
3650 0 : return false;
3651 0 : args.rval().set(result);
3652 0 : return true;
3653 : }
3654 :
3655 : bool
3656 0 : js::intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp)
3657 : {
3658 0 : CallArgs args = CallArgsFromVp(argc, vp);
3659 0 : MOZ_ASSERT(args.length() == 2);
3660 :
3661 0 : Rooted<PluralRulesObject*> pluralRules(cx, &args[0].toObject().as<PluralRulesObject>());
3662 :
3663 0 : UNumberFormat* nf = NewUNumberFormatForPluralRules(cx, pluralRules);
3664 0 : if (!nf)
3665 0 : return false;
3666 :
3667 0 : ScopedICUObject<UNumberFormat, unum_close> closeNumberFormat(nf);
3668 :
3669 0 : RootedObject internals(cx, GetInternals(cx, pluralRules));
3670 0 : if (!internals)
3671 0 : return false;
3672 :
3673 0 : RootedValue value(cx);
3674 :
3675 0 : if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
3676 0 : return false;
3677 0 : JSAutoByteString locale(cx, value.toString());
3678 0 : if (!locale)
3679 0 : return false;
3680 :
3681 0 : if (!GetProperty(cx, internals, internals, cx->names().type, &value))
3682 0 : return false;
3683 0 : RootedLinearString type(cx, value.toString()->ensureLinear(cx));
3684 0 : if (!type)
3685 0 : return false;
3686 :
3687 0 : double x = args[1].toNumber();
3688 :
3689 : UPluralType category;
3690 0 : if (StringEqualsAscii(type, "cardinal")) {
3691 0 : category = UPLURAL_TYPE_CARDINAL;
3692 : } else {
3693 0 : MOZ_ASSERT(StringEqualsAscii(type, "ordinal"));
3694 0 : category = UPLURAL_TYPE_ORDINAL;
3695 : }
3696 :
3697 : // TODO: Cache UPluralRules in PluralRulesObject::UPluralRulesSlot.
3698 0 : UErrorCode status = U_ZERO_ERROR;
3699 0 : UPluralRules* pr = uplrules_openForType(icuLocale(locale.ptr()), category, &status);
3700 0 : if (U_FAILURE(status)) {
3701 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3702 0 : return false;
3703 : }
3704 0 : ScopedICUObject<UPluralRules, uplrules_close> closePluralRules(pr);
3705 :
3706 0 : JSString* str = Call(cx, [pr, x, nf](UChar* chars, int32_t size, UErrorCode* status) {
3707 : return uplrules_selectWithFormat(pr, x, nf, chars, size, status);
3708 0 : });
3709 0 : if (!str)
3710 0 : return false;
3711 :
3712 0 : args.rval().setString(str);
3713 0 : return true;
3714 : }
3715 :
3716 : bool
3717 0 : js::intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp)
3718 : {
3719 0 : CallArgs args = CallArgsFromVp(argc, vp);
3720 0 : MOZ_ASSERT(args.length() == 2);
3721 :
3722 0 : JSAutoByteString locale(cx, args[0].toString());
3723 0 : if (!locale)
3724 0 : return false;
3725 :
3726 0 : JSLinearString* type = args[1].toString()->ensureLinear(cx);
3727 0 : if (!type)
3728 0 : return false;
3729 :
3730 : UPluralType category;
3731 0 : if (StringEqualsAscii(type, "cardinal")) {
3732 0 : category = UPLURAL_TYPE_CARDINAL;
3733 : } else {
3734 0 : MOZ_ASSERT(StringEqualsAscii(type, "ordinal"));
3735 0 : category = UPLURAL_TYPE_ORDINAL;
3736 : }
3737 :
3738 0 : UErrorCode status = U_ZERO_ERROR;
3739 0 : UPluralRules* pr = uplrules_openForType(icuLocale(locale.ptr()), category, &status);
3740 0 : if (U_FAILURE(status)) {
3741 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3742 0 : return false;
3743 : }
3744 0 : ScopedICUObject<UPluralRules, uplrules_close> closePluralRules(pr);
3745 :
3746 0 : UEnumeration* ue = uplrules_getKeywords(pr, &status);
3747 0 : if (U_FAILURE(status)) {
3748 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3749 0 : return false;
3750 : }
3751 0 : ScopedICUObject<UEnumeration, uenum_close> closeEnum(ue);
3752 :
3753 0 : RootedObject res(cx, NewDenseEmptyArray(cx));
3754 0 : if (!res)
3755 0 : return false;
3756 :
3757 0 : RootedValue element(cx);
3758 0 : uint32_t i = 0;
3759 :
3760 : do {
3761 : int32_t catSize;
3762 0 : const char* cat = uenum_next(ue, &catSize, &status);
3763 0 : if (U_FAILURE(status)) {
3764 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3765 0 : return false;
3766 : }
3767 :
3768 0 : if (!cat)
3769 0 : break;
3770 :
3771 0 : MOZ_ASSERT(catSize >= 0);
3772 0 : JSString* str = NewStringCopyN<CanGC>(cx, cat, catSize);
3773 0 : if (!str)
3774 0 : return false;
3775 :
3776 0 : element.setString(str);
3777 0 : if (!DefineElement(cx, res, i++, element))
3778 0 : return false;
3779 : } while (true);
3780 :
3781 0 : args.rval().setObject(*res);
3782 0 : return true;
3783 : }
3784 :
3785 :
3786 : /******************** String ********************/
3787 :
3788 : static const char*
3789 0 : CaseMappingLocale(JSLinearString* locale)
3790 : {
3791 0 : MOZ_ASSERT(locale->length() >= 2, "locale is a valid language tag");
3792 :
3793 : // Lithuanian, Turkish, and Azeri have language dependent case mappings.
3794 : static const char languagesWithSpecialCasing[][3] = { "lt", "tr", "az" };
3795 :
3796 : // All strings in |languagesWithSpecialCasing| are of length two, so we
3797 : // only need to compare the first two characters to find a matching locale.
3798 : // ES2017 Intl, §9.2.2 BestAvailableLocale
3799 0 : if (locale->length() == 2 || locale->latin1OrTwoByteChar(2) == '-') {
3800 0 : for (const auto& language : languagesWithSpecialCasing) {
3801 0 : if (locale->latin1OrTwoByteChar(0) == language[0] &&
3802 0 : locale->latin1OrTwoByteChar(1) == language[1])
3803 : {
3804 0 : return language;
3805 : }
3806 : }
3807 : }
3808 :
3809 0 : return ""; // ICU root locale
3810 : }
3811 :
3812 : static bool
3813 0 : HasLanguageDependentCasing(JSLinearString* locale)
3814 : {
3815 0 : return !equal(CaseMappingLocale(locale), "");
3816 : }
3817 :
3818 : bool
3819 0 : js::intl_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
3820 : {
3821 0 : CallArgs args = CallArgsFromVp(argc, vp);
3822 0 : MOZ_ASSERT(args.length() == 2);
3823 0 : MOZ_ASSERT(args[0].isString());
3824 0 : MOZ_ASSERT(args[1].isString());
3825 :
3826 0 : RootedLinearString linear(cx, args[0].toString()->ensureLinear(cx));
3827 0 : if (!linear)
3828 0 : return false;
3829 :
3830 0 : RootedLinearString locale(cx, args[1].toString()->ensureLinear(cx));
3831 0 : if (!locale)
3832 0 : return false;
3833 :
3834 : // Call String.prototype.toLowerCase() for language independent casing.
3835 0 : if (!HasLanguageDependentCasing(locale)) {
3836 0 : JSString* str = js::StringToLowerCase(cx, linear);
3837 0 : if (!str)
3838 0 : return false;
3839 :
3840 0 : args.rval().setString(str);
3841 0 : return true;
3842 : }
3843 :
3844 0 : AutoStableStringChars inputChars(cx);
3845 0 : if (!inputChars.initTwoByte(cx, linear))
3846 0 : return false;
3847 0 : mozilla::Range<const char16_t> input = inputChars.twoByteRange();
3848 :
3849 : // Maximum case mapping length is three characters.
3850 : static_assert(JSString::MAX_LENGTH < INT32_MAX / 3,
3851 : "Case conversion doesn't overflow int32_t indices");
3852 :
3853 0 : JSString* str = Call(cx, [&input, &locale](UChar* chars, int32_t size, UErrorCode* status) {
3854 0 : return u_strToLower(chars, size, input.begin().get(), input.length(),
3855 0 : CaseMappingLocale(locale), status);
3856 0 : });
3857 0 : if (!str)
3858 0 : return false;
3859 :
3860 0 : args.rval().setString(str);
3861 0 : return true;
3862 : }
3863 :
3864 : bool
3865 0 : js::intl_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
3866 : {
3867 0 : CallArgs args = CallArgsFromVp(argc, vp);
3868 0 : MOZ_ASSERT(args.length() == 2);
3869 0 : MOZ_ASSERT(args[0].isString());
3870 0 : MOZ_ASSERT(args[1].isString());
3871 :
3872 0 : RootedLinearString linear(cx, args[0].toString()->ensureLinear(cx));
3873 0 : if (!linear)
3874 0 : return false;
3875 :
3876 0 : RootedLinearString locale(cx, args[1].toString()->ensureLinear(cx));
3877 0 : if (!locale)
3878 0 : return false;
3879 :
3880 : // Call String.prototype.toUpperCase() for language independent casing.
3881 0 : if (!HasLanguageDependentCasing(locale)) {
3882 0 : JSString* str = js::StringToUpperCase(cx, linear);
3883 0 : if (!str)
3884 0 : return false;
3885 :
3886 0 : args.rval().setString(str);
3887 0 : return true;
3888 : }
3889 :
3890 0 : AutoStableStringChars inputChars(cx);
3891 0 : if (!inputChars.initTwoByte(cx, linear))
3892 0 : return false;
3893 0 : mozilla::Range<const char16_t> input = inputChars.twoByteRange();
3894 :
3895 : // Maximum case mapping length is three characters.
3896 : static_assert(JSString::MAX_LENGTH < INT32_MAX / 3,
3897 : "Case conversion doesn't overflow int32_t indices");
3898 :
3899 0 : JSString* str = Call(cx, [&input, &locale](UChar* chars, int32_t size, UErrorCode* status) {
3900 0 : return u_strToUpper(chars, size, input.begin().get(), input.length(),
3901 0 : CaseMappingLocale(locale), status);
3902 0 : });
3903 0 : if (!str)
3904 0 : return false;
3905 :
3906 0 : args.rval().setString(str);
3907 0 : return true;
3908 : }
3909 :
3910 :
3911 : /******************** Intl ********************/
3912 :
3913 : bool
3914 0 : js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
3915 : {
3916 0 : CallArgs args = CallArgsFromVp(argc, vp);
3917 0 : MOZ_ASSERT(args.length() == 1);
3918 :
3919 0 : JSAutoByteString locale(cx, args[0].toString());
3920 0 : if (!locale)
3921 0 : return false;
3922 :
3923 0 : UErrorCode status = U_ZERO_ERROR;
3924 0 : const UChar* uTimeZone = nullptr;
3925 0 : int32_t uTimeZoneLength = 0;
3926 0 : UCalendar* cal = ucal_open(uTimeZone, uTimeZoneLength, locale.ptr(), UCAL_DEFAULT, &status);
3927 0 : if (U_FAILURE(status)) {
3928 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3929 0 : return false;
3930 : }
3931 0 : ScopedICUObject<UCalendar, ucal_close> toClose(cal);
3932 :
3933 0 : RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
3934 0 : if (!info)
3935 0 : return false;
3936 :
3937 0 : RootedValue v(cx);
3938 0 : int32_t firstDayOfWeek = ucal_getAttribute(cal, UCAL_FIRST_DAY_OF_WEEK);
3939 0 : v.setInt32(firstDayOfWeek);
3940 :
3941 0 : if (!DefineProperty(cx, info, cx->names().firstDayOfWeek, v))
3942 0 : return false;
3943 :
3944 0 : int32_t minDays = ucal_getAttribute(cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK);
3945 0 : v.setInt32(minDays);
3946 0 : if (!DefineProperty(cx, info, cx->names().minDays, v))
3947 0 : return false;
3948 :
3949 0 : UCalendarWeekdayType prevDayType = ucal_getDayOfWeekType(cal, UCAL_SATURDAY, &status);
3950 0 : if (U_FAILURE(status)) {
3951 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3952 0 : return false;
3953 : }
3954 :
3955 0 : RootedValue weekendStart(cx), weekendEnd(cx);
3956 :
3957 0 : for (int i = UCAL_SUNDAY; i <= UCAL_SATURDAY; i++) {
3958 0 : UCalendarDaysOfWeek dayOfWeek = static_cast<UCalendarDaysOfWeek>(i);
3959 0 : UCalendarWeekdayType type = ucal_getDayOfWeekType(cal, dayOfWeek, &status);
3960 0 : if (U_FAILURE(status)) {
3961 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3962 0 : return false;
3963 : }
3964 :
3965 0 : if (prevDayType != type) {
3966 0 : switch (type) {
3967 : case UCAL_WEEKDAY:
3968 : // If the first Weekday after Weekend is Sunday (1),
3969 : // then the last Weekend day is Saturday (7).
3970 : // Otherwise we'll just take the previous days number.
3971 0 : weekendEnd.setInt32(i == 1 ? 7 : i - 1);
3972 0 : break;
3973 : case UCAL_WEEKEND:
3974 0 : weekendStart.setInt32(i);
3975 0 : break;
3976 : case UCAL_WEEKEND_ONSET:
3977 : case UCAL_WEEKEND_CEASE:
3978 : // At the time this code was added, ICU apparently never behaves this way,
3979 : // so just throw, so that users will report a bug and we can decide what to
3980 : // do.
3981 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
3982 0 : return false;
3983 : default:
3984 0 : break;
3985 : }
3986 : }
3987 :
3988 0 : prevDayType = type;
3989 : }
3990 :
3991 0 : MOZ_ASSERT(weekendStart.isInt32());
3992 0 : MOZ_ASSERT(weekendEnd.isInt32());
3993 :
3994 0 : if (!DefineProperty(cx, info, cx->names().weekendStart, weekendStart))
3995 0 : return false;
3996 :
3997 0 : if (!DefineProperty(cx, info, cx->names().weekendEnd, weekendEnd))
3998 0 : return false;
3999 :
4000 0 : args.rval().setObject(*info);
4001 0 : return true;
4002 : }
4003 :
4004 : static void
4005 0 : ReportBadKey(JSContext* cx, const Range<const JS::Latin1Char>& range)
4006 : {
4007 0 : JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY,
4008 0 : range.begin().get());
4009 0 : }
4010 :
4011 : static void
4012 0 : ReportBadKey(JSContext* cx, const Range<const char16_t>& range)
4013 : {
4014 0 : JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY,
4015 0 : range.begin().get());
4016 0 : }
4017 :
4018 : template<typename ConstChar>
4019 : static bool
4020 0 : MatchPart(RangedPtr<ConstChar> iter, const RangedPtr<ConstChar> end,
4021 : const char* part, size_t partlen)
4022 : {
4023 0 : for (size_t i = 0; i < partlen; iter++, i++) {
4024 0 : if (iter == end || *iter != part[i])
4025 0 : return false;
4026 : }
4027 :
4028 0 : return true;
4029 : }
4030 :
4031 : template<typename ConstChar, size_t N>
4032 : inline bool
4033 0 : MatchPart(RangedPtr<ConstChar>* iter, const RangedPtr<ConstChar> end, const char (&part)[N])
4034 : {
4035 0 : if (!MatchPart(*iter, end, part, N - 1))
4036 0 : return false;
4037 :
4038 0 : *iter += N - 1;
4039 0 : return true;
4040 : }
4041 :
4042 : enum class DisplayNameStyle
4043 : {
4044 : Narrow,
4045 : Short,
4046 : Long,
4047 : };
4048 :
4049 : template<typename ConstChar>
4050 : static JSString*
4051 0 : ComputeSingleDisplayName(JSContext* cx, UDateFormat* fmt, UDateTimePatternGenerator* dtpg,
4052 : DisplayNameStyle style,
4053 : Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE>& chars,
4054 : const Range<ConstChar>& pattern)
4055 : {
4056 0 : RangedPtr<ConstChar> iter = pattern.begin();
4057 0 : const RangedPtr<ConstChar> end = pattern.end();
4058 :
4059 0 : auto MatchSlash = [cx, pattern, &iter, end]() {
4060 0 : if (MOZ_LIKELY(iter != end && *iter == '/')) {
4061 0 : iter++;
4062 0 : return true;
4063 : }
4064 :
4065 0 : ReportBadKey(cx, pattern);
4066 0 : return false;
4067 0 : };
4068 :
4069 0 : if (!MatchPart(&iter, end, "dates")) {
4070 0 : ReportBadKey(cx, pattern);
4071 0 : return nullptr;
4072 : }
4073 :
4074 0 : if (!MatchSlash())
4075 0 : return nullptr;
4076 :
4077 0 : if (MatchPart(&iter, end, "fields")) {
4078 0 : if (!MatchSlash())
4079 0 : return nullptr;
4080 :
4081 : UDateTimePatternField fieldType;
4082 :
4083 0 : if (MatchPart(&iter, end, "year")) {
4084 0 : fieldType = UDATPG_YEAR_FIELD;
4085 0 : } else if (MatchPart(&iter, end, "month")) {
4086 0 : fieldType = UDATPG_MONTH_FIELD;
4087 0 : } else if (MatchPart(&iter, end, "week")) {
4088 0 : fieldType = UDATPG_WEEK_OF_YEAR_FIELD;
4089 0 : } else if (MatchPart(&iter, end, "day")) {
4090 0 : fieldType = UDATPG_DAY_FIELD;
4091 : } else {
4092 0 : ReportBadKey(cx, pattern);
4093 0 : return nullptr;
4094 : }
4095 :
4096 : // This part must be the final part with no trailing data.
4097 0 : if (iter != end) {
4098 0 : ReportBadKey(cx, pattern);
4099 0 : return nullptr;
4100 : }
4101 :
4102 : int32_t resultSize;
4103 0 : const UChar* value = udatpg_getAppendItemName(dtpg, fieldType, &resultSize);
4104 0 : MOZ_ASSERT(resultSize >= 0);
4105 :
4106 0 : return NewStringCopyN<CanGC>(cx, value, size_t(resultSize));
4107 : }
4108 :
4109 0 : if (MatchPart(&iter, end, "gregorian")) {
4110 0 : if (!MatchSlash())
4111 0 : return nullptr;
4112 :
4113 : UDateFormatSymbolType symbolType;
4114 : int32_t index;
4115 :
4116 0 : if (MatchPart(&iter, end, "months")) {
4117 0 : if (!MatchSlash())
4118 0 : return nullptr;
4119 :
4120 0 : switch (style) {
4121 : case DisplayNameStyle::Narrow:
4122 0 : symbolType = UDAT_STANDALONE_NARROW_MONTHS;
4123 0 : break;
4124 :
4125 : case DisplayNameStyle::Short:
4126 0 : symbolType = UDAT_STANDALONE_SHORT_MONTHS;
4127 0 : break;
4128 :
4129 : case DisplayNameStyle::Long:
4130 0 : symbolType = UDAT_STANDALONE_MONTHS;
4131 0 : break;
4132 : }
4133 :
4134 0 : if (MatchPart(&iter, end, "january")) {
4135 0 : index = UCAL_JANUARY;
4136 0 : } else if (MatchPart(&iter, end, "february")) {
4137 0 : index = UCAL_FEBRUARY;
4138 0 : } else if (MatchPart(&iter, end, "march")) {
4139 0 : index = UCAL_MARCH;
4140 0 : } else if (MatchPart(&iter, end, "april")) {
4141 0 : index = UCAL_APRIL;
4142 0 : } else if (MatchPart(&iter, end, "may")) {
4143 0 : index = UCAL_MAY;
4144 0 : } else if (MatchPart(&iter, end, "june")) {
4145 0 : index = UCAL_JUNE;
4146 0 : } else if (MatchPart(&iter, end, "july")) {
4147 0 : index = UCAL_JULY;
4148 0 : } else if (MatchPart(&iter, end, "august")) {
4149 0 : index = UCAL_AUGUST;
4150 0 : } else if (MatchPart(&iter, end, "september")) {
4151 0 : index = UCAL_SEPTEMBER;
4152 0 : } else if (MatchPart(&iter, end, "october")) {
4153 0 : index = UCAL_OCTOBER;
4154 0 : } else if (MatchPart(&iter, end, "november")) {
4155 0 : index = UCAL_NOVEMBER;
4156 0 : } else if (MatchPart(&iter, end, "december")) {
4157 0 : index = UCAL_DECEMBER;
4158 : } else {
4159 0 : ReportBadKey(cx, pattern);
4160 0 : return nullptr;
4161 : }
4162 0 : } else if (MatchPart(&iter, end, "weekdays")) {
4163 0 : if (!MatchSlash())
4164 0 : return nullptr;
4165 :
4166 0 : switch (style) {
4167 : case DisplayNameStyle::Narrow:
4168 0 : symbolType = UDAT_STANDALONE_NARROW_WEEKDAYS;
4169 0 : break;
4170 :
4171 : case DisplayNameStyle::Short:
4172 0 : symbolType = UDAT_STANDALONE_SHORT_WEEKDAYS;
4173 0 : break;
4174 :
4175 : case DisplayNameStyle::Long:
4176 0 : symbolType = UDAT_STANDALONE_WEEKDAYS;
4177 0 : break;
4178 : }
4179 :
4180 0 : if (MatchPart(&iter, end, "monday")) {
4181 0 : index = UCAL_MONDAY;
4182 0 : } else if (MatchPart(&iter, end, "tuesday")) {
4183 0 : index = UCAL_TUESDAY;
4184 0 : } else if (MatchPart(&iter, end, "wednesday")) {
4185 0 : index = UCAL_WEDNESDAY;
4186 0 : } else if (MatchPart(&iter, end, "thursday")) {
4187 0 : index = UCAL_THURSDAY;
4188 0 : } else if (MatchPart(&iter, end, "friday")) {
4189 0 : index = UCAL_FRIDAY;
4190 0 : } else if (MatchPart(&iter, end, "saturday")) {
4191 0 : index = UCAL_SATURDAY;
4192 0 : } else if (MatchPart(&iter, end, "sunday")) {
4193 0 : index = UCAL_SUNDAY;
4194 : } else {
4195 0 : ReportBadKey(cx, pattern);
4196 0 : return nullptr;
4197 : }
4198 0 : } else if (MatchPart(&iter, end, "dayperiods")) {
4199 0 : if (!MatchSlash())
4200 0 : return nullptr;
4201 :
4202 0 : symbolType = UDAT_AM_PMS;
4203 :
4204 0 : if (MatchPart(&iter, end, "am")) {
4205 0 : index = UCAL_AM;
4206 0 : } else if (MatchPart(&iter, end, "pm")) {
4207 0 : index = UCAL_PM;
4208 : } else {
4209 0 : ReportBadKey(cx, pattern);
4210 0 : return nullptr;
4211 : }
4212 : } else {
4213 0 : ReportBadKey(cx, pattern);
4214 0 : return nullptr;
4215 : }
4216 :
4217 : // This part must be the final part with no trailing data.
4218 0 : if (iter != end) {
4219 0 : ReportBadKey(cx, pattern);
4220 0 : return nullptr;
4221 : }
4222 :
4223 0 : return Call(cx, [fmt, symbolType, index](UChar* chars, int32_t size, UErrorCode* status) {
4224 : return udat_getSymbols(fmt, symbolType, index, chars, size, status);
4225 0 : });
4226 : }
4227 :
4228 0 : ReportBadKey(cx, pattern);
4229 0 : return nullptr;
4230 : }
4231 :
4232 : bool
4233 0 : js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
4234 : {
4235 0 : CallArgs args = CallArgsFromVp(argc, vp);
4236 0 : MOZ_ASSERT(args.length() == 3);
4237 :
4238 0 : RootedString str(cx);
4239 :
4240 : // 1. Assert: locale is a string.
4241 0 : str = args[0].toString();
4242 0 : JSAutoByteString locale;
4243 0 : if (!locale.encodeUtf8(cx, str))
4244 0 : return false;
4245 :
4246 : // 2. Assert: style is a string.
4247 0 : JSLinearString* style = args[1].toString()->ensureLinear(cx);
4248 0 : if (!style)
4249 0 : return false;
4250 :
4251 : DisplayNameStyle dnStyle;
4252 0 : if (StringEqualsAscii(style, "narrow")) {
4253 0 : dnStyle = DisplayNameStyle::Narrow;
4254 0 : } else if (StringEqualsAscii(style, "short")) {
4255 0 : dnStyle = DisplayNameStyle::Short;
4256 : } else {
4257 0 : MOZ_ASSERT(StringEqualsAscii(style, "long"));
4258 0 : dnStyle = DisplayNameStyle::Long;
4259 : }
4260 :
4261 : // 3. Assert: keys is an Array.
4262 0 : RootedArrayObject keys(cx, &args[2].toObject().as<ArrayObject>());
4263 0 : if (!keys)
4264 0 : return false;
4265 :
4266 : // 4. Let result be ArrayCreate(0).
4267 0 : RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length()));
4268 0 : if (!result)
4269 0 : return false;
4270 :
4271 0 : UErrorCode status = U_ZERO_ERROR;
4272 :
4273 : UDateFormat* fmt =
4274 0 : udat_open(UDAT_DEFAULT, UDAT_DEFAULT, icuLocale(locale.ptr()),
4275 0 : nullptr, 0, nullptr, 0, &status);
4276 0 : if (U_FAILURE(status)) {
4277 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
4278 0 : return false;
4279 : }
4280 0 : ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
4281 :
4282 : // UDateTimePatternGenerator will be needed for translations of date and
4283 : // time fields like "month", "week", "day" etc.
4284 0 : UDateTimePatternGenerator* dtpg = udatpg_open(icuLocale(locale.ptr()), &status);
4285 0 : if (U_FAILURE(status)) {
4286 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
4287 0 : return false;
4288 : }
4289 0 : ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg);
4290 :
4291 0 : Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
4292 0 : if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
4293 0 : return false;
4294 :
4295 : // 5. For each element of keys,
4296 0 : RootedString keyValStr(cx);
4297 0 : RootedValue v(cx);
4298 0 : for (uint32_t i = 0; i < keys->length(); i++) {
4299 0 : if (!GetElement(cx, keys, keys, i, &v))
4300 0 : return false;
4301 :
4302 0 : keyValStr = v.toString();
4303 :
4304 0 : AutoStableStringChars stablePatternChars(cx);
4305 0 : if (!stablePatternChars.init(cx, keyValStr))
4306 0 : return false;
4307 :
4308 : // 5.a. Perform an implementation dependent algorithm to map a key to a
4309 : // corresponding display name.
4310 : JSString* displayName =
4311 0 : stablePatternChars.isLatin1()
4312 0 : ? ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle, chars,
4313 0 : stablePatternChars.latin1Range())
4314 0 : : ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle, chars,
4315 0 : stablePatternChars.twoByteRange());
4316 0 : if (!displayName)
4317 0 : return false;
4318 :
4319 : // 5.b. Append the result string to result.
4320 0 : v.setString(displayName);
4321 0 : if (!DefineElement(cx, result, i, v))
4322 0 : return false;
4323 : }
4324 :
4325 : // 6. Return result.
4326 0 : args.rval().setObject(*result);
4327 0 : return true;
4328 : }
4329 :
4330 : bool
4331 0 : js::intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp)
4332 : {
4333 0 : CallArgs args = CallArgsFromVp(argc, vp);
4334 0 : MOZ_ASSERT(args.length() == 1);
4335 :
4336 0 : JSAutoByteString locale(cx, args[0].toString());
4337 0 : if (!locale)
4338 0 : return false;
4339 :
4340 0 : RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
4341 0 : if (!info)
4342 0 : return false;
4343 :
4344 0 : if (!DefineProperty(cx, info, cx->names().locale, args[0]))
4345 0 : return false;
4346 :
4347 0 : bool rtl = uloc_isRightToLeft(icuLocale(locale.ptr()));
4348 :
4349 0 : RootedValue dir(cx, StringValue(rtl ? cx->names().rtl : cx->names().ltr));
4350 :
4351 0 : if (!DefineProperty(cx, info, cx->names().direction, dir))
4352 0 : return false;
4353 :
4354 0 : args.rval().setObject(*info);
4355 0 : return true;
4356 : }
4357 :
4358 : const Class js::IntlClass = {
4359 : js_Object_str,
4360 : JSCLASS_HAS_CACHED_PROTO(JSProto_Intl)
4361 : };
4362 :
4363 : #if JS_HAS_TOSOURCE
4364 : static bool
4365 0 : intl_toSource(JSContext* cx, unsigned argc, Value* vp)
4366 : {
4367 0 : CallArgs args = CallArgsFromVp(argc, vp);
4368 0 : args.rval().setString(cx->names().Intl);
4369 0 : return true;
4370 : }
4371 : #endif
4372 :
4373 : static const JSFunctionSpec intl_static_methods[] = {
4374 : #if JS_HAS_TOSOURCE
4375 : JS_FN(js_toSource_str, intl_toSource, 0, 0),
4376 : #endif
4377 : JS_SELF_HOSTED_FN("getCanonicalLocales", "Intl_getCanonicalLocales", 1, 0),
4378 : JS_FS_END
4379 : };
4380 :
4381 : /**
4382 : * Initializes the Intl Object and its standard built-in properties.
4383 : * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
4384 : */
4385 : /* static */ bool
4386 6 : GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
4387 : {
4388 12 : RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
4389 6 : if (!proto)
4390 0 : return false;
4391 :
4392 : // The |Intl| object is just a plain object with some "static" function
4393 : // properties and some constructor properties.
4394 12 : RootedObject intl(cx, NewObjectWithGivenProto(cx, &IntlClass, proto, SingletonObject));
4395 6 : if (!intl)
4396 0 : return false;
4397 :
4398 : // Add the static functions.
4399 6 : if (!JS_DefineFunctions(cx, intl, intl_static_methods))
4400 0 : return false;
4401 :
4402 : // Add the constructor properties, computing and returning the relevant
4403 : // prototype objects needed below.
4404 12 : RootedObject collatorProto(cx, CreateCollatorPrototype(cx, intl, global));
4405 6 : if (!collatorProto)
4406 0 : return false;
4407 12 : RootedObject dateTimeFormatProto(cx), dateTimeFormat(cx);
4408 6 : dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat, DateTimeFormatOptions::Standard);
4409 6 : if (!dateTimeFormatProto)
4410 0 : return false;
4411 12 : RootedObject numberFormatProto(cx), numberFormat(cx);
4412 6 : numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
4413 6 : if (!numberFormatProto)
4414 0 : return false;
4415 :
4416 : // The |Intl| object is fully set up now, so define the global property.
4417 12 : RootedValue intlValue(cx, ObjectValue(*intl));
4418 6 : if (!DefineProperty(cx, global, cx->names().Intl, intlValue, nullptr, nullptr,
4419 : JSPROP_RESOLVING))
4420 : {
4421 0 : return false;
4422 : }
4423 :
4424 : // Now that the |Intl| object is successfully added, we can OOM-safely fill
4425 : // in all relevant reserved global slots.
4426 :
4427 : // Cache the various prototypes, for use in creating instances of these
4428 : // objects with the proper [[Prototype]] as "the original value of
4429 : // |Intl.Collator.prototype|" and similar. For builtin classes like
4430 : // |String.prototype| we have |JSProto_*| that enables
4431 : // |getPrototype(JSProto_*)|, but that has global-object-property-related
4432 : // baggage we don't need or want, so we use one-off reserved slots.
4433 6 : global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
4434 6 : global->setReservedSlot(DATE_TIME_FORMAT, ObjectValue(*dateTimeFormat));
4435 6 : global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
4436 6 : global->setReservedSlot(NUMBER_FORMAT, ObjectValue(*numberFormat));
4437 6 : global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
4438 :
4439 : // Also cache |Intl| to implement spec language that conditions behavior
4440 : // based on values being equal to "the standard built-in |Intl| object".
4441 : // Use |setConstructor| to correspond with |JSProto_Intl|.
4442 : //
4443 : // XXX We should possibly do a one-off reserved slot like above.
4444 6 : global->setConstructor(JSProto_Intl, ObjectValue(*intl));
4445 6 : return true;
4446 : }
4447 :
4448 : JSObject*
4449 6 : js::InitIntlClass(JSContext* cx, HandleObject obj)
4450 : {
4451 6 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
4452 6 : if (!GlobalObject::initIntlObject(cx, global))
4453 0 : return nullptr;
4454 :
4455 6 : return &global->getConstructor(JSProto_Intl).toObject();
4456 : }
|