LCOV - code coverage report
Current view: top level - js/src/builtin - Intl.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 77 1919 4.0 %
Date: 2017-07-14 16:53:18 Functions: 6 171 3.5 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13