js/src/builtin/Intl.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     7 /*
     8  * The Intl module specified by standard ECMA-402,
     9  * ECMAScript Internationalization API Specification.
    10  */
    12 #include "builtin/Intl.h"
    14 #include <string.h>
    16 #include "jsapi.h"
    17 #include "jsatom.h"
    18 #include "jscntxt.h"
    19 #include "jsobj.h"
    21 #if ENABLE_INTL_API
    22 #include "unicode/locid.h"
    23 #include "unicode/numsys.h"
    24 #include "unicode/ucal.h"
    25 #include "unicode/ucol.h"
    26 #include "unicode/udat.h"
    27 #include "unicode/udatpg.h"
    28 #include "unicode/uenum.h"
    29 #include "unicode/unum.h"
    30 #include "unicode/ustring.h"
    31 #endif
    32 #include "vm/DateTime.h"
    33 #include "vm/GlobalObject.h"
    34 #include "vm/Interpreter.h"
    35 #include "vm/Stack.h"
    36 #include "vm/StringBuffer.h"
    38 #include "jsobjinlines.h"
    40 #include "vm/ObjectImpl-inl.h"
    42 using namespace js;
    44 using mozilla::IsFinite;
    45 using mozilla::IsNegativeZero;
    47 #if ENABLE_INTL_API
    48 using icu::Locale;
    49 using icu::NumberingSystem;
    50 #endif
    53 /*
    54  * Pervasive note: ICU functions taking a UErrorCode in/out parameter always
    55  * test that parameter before doing anything, and will return immediately if
    56  * the value indicates that a failure occurred in a prior ICU call,
    57  * without doing anything else. See
    58  * http://userguide.icu-project.org/design#TOC-Error-Handling
    59  */
    62 /******************** ICU stubs ********************/
    64 #if !ENABLE_INTL_API
    66 /*
    67  * When the Internationalization API isn't enabled, we also shouldn't link
    68  * against ICU. However, we still want to compile this code in order to prevent
    69  * bit rot. The following stub implementations for ICU functions make this
    70  * possible. The functions using them should never be called, so they assert
    71  * and return error codes. Signatures adapted from ICU header files locid.h,
    72  * numsys.h, ucal.h, ucol.h, udat.h, udatpg.h, uenum.h, unum.h; see the ICU
    73  * directory for license.
    74  */
    76 static int32_t
    77 u_strlen(const UChar *s)
    78 {
    79     MOZ_ASSUME_UNREACHABLE("u_strlen: Intl API disabled");
    80 }
    82 struct UEnumeration;
    84 static int32_t
    85 uenum_count(UEnumeration *en, UErrorCode *status)
    86 {
    87     MOZ_ASSUME_UNREACHABLE("uenum_count: Intl API disabled");
    88 }
    90 static const char *
    91 uenum_next(UEnumeration *en, int32_t *resultLength, UErrorCode *status)
    92 {
    93     MOZ_ASSUME_UNREACHABLE("uenum_next: Intl API disabled");
    94 }
    96 static void
    97 uenum_close(UEnumeration *en)
    98 {
    99     MOZ_ASSUME_UNREACHABLE("uenum_close: Intl API disabled");
   100 }
   102 struct UCollator;
   104 enum UColAttribute {
   105      UCOL_ALTERNATE_HANDLING,
   106      UCOL_CASE_FIRST,
   107      UCOL_CASE_LEVEL,
   108      UCOL_NORMALIZATION_MODE,
   109      UCOL_STRENGTH,
   110      UCOL_NUMERIC_COLLATION,
   111 };
   113 enum UColAttributeValue {
   114   UCOL_DEFAULT = -1,
   115   UCOL_PRIMARY = 0,
   116   UCOL_SECONDARY = 1,
   117   UCOL_TERTIARY = 2,
   118   UCOL_OFF = 16,
   119   UCOL_ON = 17,
   120   UCOL_SHIFTED = 20,
   121   UCOL_LOWER_FIRST = 24,
   122   UCOL_UPPER_FIRST = 25,
   123 };
   125 enum UCollationResult {
   126   UCOL_EQUAL = 0,
   127   UCOL_GREATER = 1,
   128   UCOL_LESS = -1
   129 };
   131 static int32_t
   132 ucol_countAvailable(void)
   133 {
   134     MOZ_ASSUME_UNREACHABLE("ucol_countAvailable: Intl API disabled");
   135 }
   137 static const char *
   138 ucol_getAvailable(int32_t localeIndex)
   139 {
   140     MOZ_ASSUME_UNREACHABLE("ucol_getAvailable: Intl API disabled");
   141 }
   143 static UCollator *
   144 ucol_open(const char *loc, UErrorCode *status)
   145 {
   146     MOZ_ASSUME_UNREACHABLE("ucol_open: Intl API disabled");
   147 }
   149 static void
   150 ucol_setAttribute(UCollator *coll, UColAttribute attr, UColAttributeValue value, UErrorCode *status)
   151 {
   152     MOZ_ASSUME_UNREACHABLE("ucol_setAttribute: Intl API disabled");
   153 }
   155 static UCollationResult
   156 ucol_strcoll(const UCollator *coll, const UChar *source, int32_t sourceLength,
   157              const UChar *target, int32_t targetLength)
   158 {
   159     MOZ_ASSUME_UNREACHABLE("ucol_strcoll: Intl API disabled");
   160 }
   162 static void
   163 ucol_close(UCollator *coll)
   164 {
   165     MOZ_ASSUME_UNREACHABLE("ucol_close: Intl API disabled");
   166 }
   168 static UEnumeration *
   169 ucol_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed,
   170                                UErrorCode *status)
   171 {
   172     MOZ_ASSUME_UNREACHABLE("ucol_getKeywordValuesForLocale: Intl API disabled");
   173 }
   175 struct UParseError;
   176 struct UFieldPosition;
   177 typedef void *UNumberFormat;
   179 enum UNumberFormatStyle {
   180     UNUM_DECIMAL = 1,
   181     UNUM_CURRENCY,
   182     UNUM_PERCENT,
   183     UNUM_CURRENCY_ISO,
   184     UNUM_CURRENCY_PLURAL,
   185 };
   187 enum UNumberFormatRoundingMode {
   188     UNUM_ROUND_HALFUP,
   189 };
   191 enum UNumberFormatAttribute {
   192   UNUM_GROUPING_USED,
   193   UNUM_MIN_INTEGER_DIGITS,
   194   UNUM_MAX_FRACTION_DIGITS,
   195   UNUM_MIN_FRACTION_DIGITS,
   196   UNUM_ROUNDING_MODE,
   197   UNUM_SIGNIFICANT_DIGITS_USED,
   198   UNUM_MIN_SIGNIFICANT_DIGITS,
   199   UNUM_MAX_SIGNIFICANT_DIGITS,
   200 };
   202 enum UNumberFormatTextAttribute {
   203   UNUM_CURRENCY_CODE,
   204 };
   206 static int32_t
   207 unum_countAvailable(void)
   208 {
   209     MOZ_ASSUME_UNREACHABLE("unum_countAvailable: Intl API disabled");
   210 }
   212 static const char *
   213 unum_getAvailable(int32_t localeIndex)
   214 {
   215     MOZ_ASSUME_UNREACHABLE("unum_getAvailable: Intl API disabled");
   216 }
   218 static UNumberFormat *
   219 unum_open(UNumberFormatStyle style, const UChar *pattern, int32_t patternLength,
   220           const char *locale, UParseError *parseErr, UErrorCode *status)
   221 {
   222     MOZ_ASSUME_UNREACHABLE("unum_open: Intl API disabled");
   223 }
   225 static void
   226 unum_setAttribute(UNumberFormat *fmt, UNumberFormatAttribute  attr, int32_t newValue)
   227 {
   228     MOZ_ASSUME_UNREACHABLE("unum_setAttribute: Intl API disabled");
   229 }
   231 static int32_t
   232 unum_formatDouble(const UNumberFormat *fmt, double number, UChar *result,
   233                   int32_t resultLength, UFieldPosition *pos, UErrorCode *status)
   234 {
   235     MOZ_ASSUME_UNREACHABLE("unum_formatDouble: Intl API disabled");
   236 }
   238 static void
   239 unum_close(UNumberFormat *fmt)
   240 {
   241     MOZ_ASSUME_UNREACHABLE("unum_close: Intl API disabled");
   242 }
   244 static void
   245 unum_setTextAttribute(UNumberFormat *fmt, UNumberFormatTextAttribute tag, const UChar *newValue,
   246                       int32_t newValueLength, UErrorCode *status)
   247 {
   248     MOZ_ASSUME_UNREACHABLE("unum_setTextAttribute: Intl API disabled");
   249 }
   251 class Locale {
   252   public:
   253     Locale(const char *language, const char *country = 0, const char *variant = 0,
   254            const char *keywordsAndValues = 0);
   255 };
   257 Locale::Locale(const char *language, const char *country, const char *variant,
   258                const char *keywordsAndValues)
   259 {
   260     MOZ_ASSUME_UNREACHABLE("Locale::Locale: Intl API disabled");
   261 }
   263 class NumberingSystem {
   264   public:
   265     static NumberingSystem *createInstance(const Locale &inLocale, UErrorCode &status);
   266     const char *getName();
   267 };
   269 NumberingSystem *
   270 NumberingSystem::createInstance(const Locale &inLocale, UErrorCode &status)
   271 {
   272     MOZ_ASSUME_UNREACHABLE("NumberingSystem::createInstance: Intl API disabled");
   273 }
   275 const char *
   276 NumberingSystem::getName()
   277 {
   278     MOZ_ASSUME_UNREACHABLE("NumberingSystem::getName: Intl API disabled");
   279 }
   281 typedef void *UCalendar;
   283 enum UCalendarType {
   284   UCAL_TRADITIONAL,
   285   UCAL_DEFAULT = UCAL_TRADITIONAL,
   286   UCAL_GREGORIAN
   287 };
   289 static UCalendar *
   290 ucal_open(const UChar *zoneID, int32_t len, const char *locale,
   291           UCalendarType type, UErrorCode *status)
   292 {
   293     MOZ_ASSUME_UNREACHABLE("ucal_open: Intl API disabled");
   294 }
   296 static const char *
   297 ucal_getType(const UCalendar *cal, UErrorCode *status)
   298 {
   299     MOZ_ASSUME_UNREACHABLE("ucal_getType: Intl API disabled");
   300 }
   302 static UEnumeration *
   303 ucal_getKeywordValuesForLocale(const char *key, const char *locale,
   304                                UBool commonlyUsed, UErrorCode *status)
   305 {
   306     MOZ_ASSUME_UNREACHABLE("ucal_getKeywordValuesForLocale: Intl API disabled");
   307 }
   309 static void
   310 ucal_close(UCalendar *cal)
   311 {
   312     MOZ_ASSUME_UNREACHABLE("ucal_close: Intl API disabled");
   313 }
   315 typedef void *UDateTimePatternGenerator;
   317 static UDateTimePatternGenerator *
   318 udatpg_open(const char *locale, UErrorCode *pErrorCode)
   319 {
   320     MOZ_ASSUME_UNREACHABLE("udatpg_open: Intl API disabled");
   321 }
   323 static int32_t
   324 udatpg_getBestPattern(UDateTimePatternGenerator *dtpg, const UChar *skeleton,
   325                       int32_t length, UChar *bestPattern, int32_t capacity,
   326                       UErrorCode *pErrorCode)
   327 {
   328     MOZ_ASSUME_UNREACHABLE("udatpg_getBestPattern: Intl API disabled");
   329 }
   331 static void
   332 udatpg_close(UDateTimePatternGenerator *dtpg)
   333 {
   334     MOZ_ASSUME_UNREACHABLE("udatpg_close: Intl API disabled");
   335 }
   337 typedef void *UCalendar;
   338 typedef void *UDateFormat;
   340 enum UDateFormatStyle {
   341     UDAT_PATTERN = -2,
   342     UDAT_IGNORE = UDAT_PATTERN
   343 };
   345 static int32_t
   346 udat_countAvailable(void)
   347 {
   348     MOZ_ASSUME_UNREACHABLE("udat_countAvailable: Intl API disabled");
   349 }
   351 static const char *
   352 udat_getAvailable(int32_t localeIndex)
   353 {
   354     MOZ_ASSUME_UNREACHABLE("udat_getAvailable: Intl API disabled");
   355 }
   357 static UDateFormat *
   358 udat_open(UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const char *locale,
   359           const UChar *tzID, int32_t tzIDLength, const UChar *pattern,
   360           int32_t patternLength, UErrorCode *status)
   361 {
   362     MOZ_ASSUME_UNREACHABLE("udat_open: Intl API disabled");
   363 }
   365 static const UCalendar *
   366 udat_getCalendar(const UDateFormat *fmt)
   367 {
   368     MOZ_ASSUME_UNREACHABLE("udat_getCalendar: Intl API disabled");
   369 }
   371 static void
   372 ucal_setGregorianChange(UCalendar *cal, UDate date, UErrorCode *pErrorCode)
   373 {
   374     MOZ_ASSUME_UNREACHABLE("ucal_setGregorianChange: Intl API disabled");
   375 }
   377 static int32_t
   378 udat_format(const UDateFormat *format, UDate dateToFormat, UChar *result,
   379             int32_t resultLength, UFieldPosition *position, UErrorCode *status)
   380 {
   381     MOZ_ASSUME_UNREACHABLE("udat_format: Intl API disabled");
   382 }
   384 static void
   385 udat_close(UDateFormat *format)
   386 {
   387     MOZ_ASSUME_UNREACHABLE("udat_close: Intl API disabled");
   388 }
   390 #endif
   393 /******************** Common to Intl constructors ********************/
   395 static bool
   396 IntlInitialize(JSContext *cx, HandleObject obj, Handle<PropertyName*> initializer,
   397                HandleValue locales, HandleValue options)
   398 {
   399     RootedValue initializerValue(cx);
   400     if (!GlobalObject::getIntrinsicValue(cx, cx->global(), initializer, &initializerValue))
   401         return false;
   402     JS_ASSERT(initializerValue.isObject());
   403     JS_ASSERT(initializerValue.toObject().is<JSFunction>());
   405     InvokeArgs args(cx);
   406     if (!args.init(3))
   407         return false;
   409     args.setCallee(initializerValue);
   410     args.setThis(NullValue());
   411     args[0].setObject(*obj);
   412     args[1].set(locales);
   413     args[2].set(options);
   415     return Invoke(cx, args);
   416 }
   418 // CountAvailable and GetAvailable describe the signatures used for ICU API
   419 // to determine available locales for various functionality.
   420 typedef int32_t
   421 (* CountAvailable)(void);
   423 typedef const char *
   424 (* GetAvailable)(int32_t localeIndex);
   426 static bool
   427 intl_availableLocales(JSContext *cx, CountAvailable countAvailable,
   428                       GetAvailable getAvailable, MutableHandleValue result)
   429 {
   430     RootedObject locales(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, nullptr));
   431     if (!locales)
   432         return false;
   434 #if ENABLE_INTL_API
   435     uint32_t count = countAvailable();
   436     RootedValue t(cx, BooleanValue(true));
   437     for (uint32_t i = 0; i < count; i++) {
   438         const char *locale = getAvailable(i);
   439         ScopedJSFreePtr<char> lang(JS_strdup(cx, locale));
   440         if (!lang)
   441             return false;
   442         char *p;
   443         while ((p = strchr(lang, '_')))
   444             *p = '-';
   445         RootedAtom a(cx, Atomize(cx, lang, strlen(lang)));
   446         if (!a)
   447             return false;
   448         if (!JSObject::defineProperty(cx, locales, a->asPropertyName(), t,
   449                                       JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
   450         {
   451             return false;
   452         }
   453     }
   454 #endif
   455     result.setObject(*locales);
   456     return true;
   457 }
   459 /**
   460  * Returns the object holding the internal properties for obj.
   461  */
   462 static bool
   463 GetInternals(JSContext *cx, HandleObject obj, MutableHandleObject internals)
   464 {
   465     RootedValue getInternalsValue(cx);
   466     if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals, &getInternalsValue))
   467         return false;
   468     JS_ASSERT(getInternalsValue.isObject());
   469     JS_ASSERT(getInternalsValue.toObject().is<JSFunction>());
   471     InvokeArgs args(cx);
   472     if (!args.init(1))
   473         return false;
   475     args.setCallee(getInternalsValue);
   476     args.setThis(NullValue());
   477     args[0].setObject(*obj);
   479     if (!Invoke(cx, args))
   480         return false;
   481     internals.set(&args.rval().toObject());
   482     return true;
   483 }
   485 static bool
   486 equal(const char *s1, const char *s2)
   487 {
   488     return !strcmp(s1, s2);
   489 }
   491 static bool
   492 equal(JSAutoByteString &s1, const char *s2)
   493 {
   494     return !strcmp(s1.ptr(), s2);
   495 }
   497 static const char *
   498 icuLocale(const char *locale)
   499 {
   500     if (equal(locale, "und"))
   501         return ""; // ICU root locale
   502     return locale;
   503 }
   505 // Simple RAII for ICU objects. MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE
   506 // unfortunately doesn't work because of namespace incompatibilities
   507 // (TypeSpecificDelete cannot be in icu and mozilla at the same time)
   508 // and because ICU declares both UNumberFormat and UDateTimePatternGenerator
   509 // as void*.
   510 template <typename T>
   511 class ScopedICUObject
   512 {
   513     T *ptr_;
   514     void (* deleter_)(T*);
   516   public:
   517     ScopedICUObject(T *ptr, void (*deleter)(T*))
   518       : ptr_(ptr),
   519         deleter_(deleter)
   520     {}
   522     ~ScopedICUObject() {
   523         if (ptr_)
   524             deleter_(ptr_);
   525     }
   527     // In cases where an object should be deleted on abnormal exits,
   528     // but returned to the caller if everything goes well, call forget()
   529     // to transfer the object just before returning.
   530     T *forget() {
   531         T *tmp = ptr_;
   532         ptr_ = nullptr;
   533         return tmp;
   534     }
   535 };
   537 // As a small optimization (not important for correctness), this is the inline
   538 // capacity of a StringBuffer.
   539 static const size_t INITIAL_STRING_BUFFER_SIZE = 32;
   542 /******************** Collator ********************/
   544 static void collator_finalize(FreeOp *fop, JSObject *obj);
   546 static const uint32_t UCOLLATOR_SLOT = 0;
   547 static const uint32_t COLLATOR_SLOTS_COUNT = 1;
   549 static const Class CollatorClass = {
   550     js_Object_str,
   551     JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
   552     JS_PropertyStub,         /* addProperty */
   553     JS_DeletePropertyStub,   /* delProperty */
   554     JS_PropertyStub,         /* getProperty */
   555     JS_StrictPropertyStub,   /* setProperty */
   556     JS_EnumerateStub,
   557     JS_ResolveStub,
   558     JS_ConvertStub,
   559     collator_finalize
   560 };
   562 #if JS_HAS_TOSOURCE
   563 static bool
   564 collator_toSource(JSContext *cx, unsigned argc, Value *vp)
   565 {
   566     CallArgs args = CallArgsFromVp(argc, vp);
   567     args.rval().setString(cx->names().Collator);
   568     return true;
   569 }
   570 #endif
   572 static const JSFunctionSpec collator_static_methods[] = {
   573     JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_Collator_supportedLocalesOf", 1, 0),
   574     JS_FS_END
   575 };
   577 static const JSFunctionSpec collator_methods[] = {
   578     JS_SELF_HOSTED_FN("resolvedOptions", "Intl_Collator_resolvedOptions", 0, 0),
   579 #if JS_HAS_TOSOURCE
   580     JS_FN(js_toSource_str, collator_toSource, 0, 0),
   581 #endif
   582     JS_FS_END
   583 };
   585 /**
   586  * Collator constructor.
   587  * Spec: ECMAScript Internationalization API Specification, 10.1
   588  */
   589 static bool
   590 Collator(JSContext *cx, CallArgs args, bool construct)
   591 {
   592     RootedObject obj(cx);
   594     if (!construct) {
   595         // 10.1.2.1 step 3
   596         JSObject *intl = cx->global()->getOrCreateIntlObject(cx);
   597         if (!intl)
   598             return false;
   599         RootedValue self(cx, args.thisv());
   600         if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
   601             // 10.1.2.1 step 4
   602             obj = ToObject(cx, self);
   603             if (!obj)
   604                 return false;
   606             // 10.1.2.1 step 5
   607             bool extensible;
   608             if (!JSObject::isExtensible(cx, obj, &extensible))
   609                 return false;
   610             if (!extensible)
   611                 return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
   612         } else {
   613             // 10.1.2.1 step 3.a
   614             construct = true;
   615         }
   616     }
   617     if (construct) {
   618         // 10.1.3.1 paragraph 2
   619         RootedObject proto(cx, cx->global()->getOrCreateCollatorPrototype(cx));
   620         if (!proto)
   621             return false;
   622         obj = NewObjectWithGivenProto(cx, &CollatorClass, proto, cx->global());
   623         if (!obj)
   624             return false;
   626         obj->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
   627     }
   629     // 10.1.2.1 steps 1 and 2; 10.1.3.1 steps 1 and 2
   630     RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
   631     RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
   633     // 10.1.2.1 step 6; 10.1.3.1 step 3
   634     if (!IntlInitialize(cx, obj, cx->names().InitializeCollator, locales, options))
   635         return false;
   637     // 10.1.2.1 steps 3.a and 7
   638     args.rval().setObject(*obj);
   639     return true;
   640 }
   642 static bool
   643 Collator(JSContext *cx, unsigned argc, Value *vp)
   644 {
   645     CallArgs args = CallArgsFromVp(argc, vp);
   646     return Collator(cx, args, args.isConstructing());
   647 }
   649 bool
   650 js::intl_Collator(JSContext *cx, unsigned argc, Value *vp)
   651 {
   652     CallArgs args = CallArgsFromVp(argc, vp);
   653     JS_ASSERT(args.length() == 2);
   654     // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
   655     // be used with "new", but it still has to be treated as a constructor.
   656     return Collator(cx, args, true);
   657 }
   659 static void
   660 collator_finalize(FreeOp *fop, JSObject *obj)
   661 {
   662     UCollator *coll = static_cast<UCollator*>(obj->getReservedSlot(UCOLLATOR_SLOT).toPrivate());
   663     if (coll)
   664         ucol_close(coll);
   665 }
   667 static JSObject *
   668 InitCollatorClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
   669 {
   670     RootedFunction ctor(cx, global->createConstructor(cx, &Collator, cx->names().Collator, 0));
   671     if (!ctor)
   672         return nullptr;
   674     RootedObject proto(cx, global->as<GlobalObject>().getOrCreateCollatorPrototype(cx));
   675     if (!proto)
   676         return nullptr;
   677     if (!LinkConstructorAndPrototype(cx, ctor, proto))
   678         return nullptr;
   680     // 10.2.2
   681     if (!JS_DefineFunctions(cx, ctor, collator_static_methods))
   682         return nullptr;
   684     // 10.3.2 and 10.3.3
   685     if (!JS_DefineFunctions(cx, proto, collator_methods))
   686         return nullptr;
   688     /*
   689      * Install the getter for Collator.prototype.compare, which returns a bound
   690      * comparison function for the specified Collator object (suitable for
   691      * passing to methods like Array.prototype.sort).
   692      */
   693     RootedValue getter(cx);
   694     if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CollatorCompareGet, &getter))
   695         return nullptr;
   696     if (!JSObject::defineProperty(cx, proto, cx->names().compare, UndefinedHandleValue,
   697                                   JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
   698                                   nullptr, JSPROP_GETTER | JSPROP_SHARED))
   699     {
   700         return nullptr;
   701     }
   703     // 10.2.1 and 10.3
   704     if (!IntlInitialize(cx, proto, cx->names().InitializeCollator, UndefinedHandleValue,
   705                         UndefinedHandleValue))
   706     {
   707         return nullptr;
   708     }
   710     // 8.1
   711     RootedValue ctorValue(cx, ObjectValue(*ctor));
   712     if (!JSObject::defineProperty(cx, Intl, cx->names().Collator, ctorValue,
   713                                   JS_PropertyStub, JS_StrictPropertyStub, 0))
   714     {
   715         return nullptr;
   716     }
   718     return ctor;
   719 }
   721 bool
   722 GlobalObject::initCollatorProto(JSContext *cx, Handle<GlobalObject*> global)
   723 {
   724     RootedObject proto(cx, global->createBlankPrototype(cx, &CollatorClass));
   725     if (!proto)
   726         return false;
   727     proto->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
   728     global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*proto));
   729     return true;
   730 }
   732 bool
   733 js::intl_Collator_availableLocales(JSContext *cx, unsigned argc, Value *vp)
   734 {
   735     CallArgs args = CallArgsFromVp(argc, vp);
   736     JS_ASSERT(args.length() == 0);
   738     RootedValue result(cx);
   739     if (!intl_availableLocales(cx, ucol_countAvailable, ucol_getAvailable, &result))
   740         return false;
   741     args.rval().set(result);
   742     return true;
   743 }
   745 bool
   746 js::intl_availableCollations(JSContext *cx, unsigned argc, Value *vp)
   747 {
   748     CallArgs args = CallArgsFromVp(argc, vp);
   749     JS_ASSERT(args.length() == 1);
   750     JS_ASSERT(args[0].isString());
   752     JSAutoByteString locale(cx, args[0].toString());
   753     if (!locale)
   754         return false;
   755     UErrorCode status = U_ZERO_ERROR;
   756     UEnumeration *values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
   757     if (U_FAILURE(status)) {
   758         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   759         return false;
   760     }
   761     ScopedICUObject<UEnumeration> toClose(values, uenum_close);
   763     uint32_t count = uenum_count(values, &status);
   764     if (U_FAILURE(status)) {
   765         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   766         return false;
   767     }
   769     RootedObject collations(cx, NewDenseEmptyArray(cx));
   770     if (!collations)
   771         return false;
   773     uint32_t index = 0;
   774     for (uint32_t i = 0; i < count; i++) {
   775         const char *collation = uenum_next(values, nullptr, &status);
   776         if (U_FAILURE(status)) {
   777             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   778             return false;
   779         }
   781         // Per ECMA-402, 10.2.3, we don't include standard and search:
   782         // "The values 'standard' and 'search' must not be used as elements in
   783         // any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co
   784         // array."
   785         if (equal(collation, "standard") || equal(collation, "search"))
   786             continue;
   788         // ICU returns old-style keyword values; map them to BCP 47 equivalents
   789         // (see http://bugs.icu-project.org/trac/ticket/9620).
   790         if (equal(collation, "dictionary"))
   791             collation = "dict";
   792         else if (equal(collation, "gb2312han"))
   793             collation = "gb2312";
   794         else if (equal(collation, "phonebook"))
   795             collation = "phonebk";
   796         else if (equal(collation, "traditional"))
   797             collation = "trad";
   799         RootedString jscollation(cx, JS_NewStringCopyZ(cx, collation));
   800         if (!jscollation)
   801             return false;
   802         RootedValue element(cx, StringValue(jscollation));
   803         if (!JSObject::defineElement(cx, collations, index++, element))
   804             return false;
   805     }
   807     args.rval().setObject(*collations);
   808     return true;
   809 }
   811 /**
   812  * Returns a new UCollator with the locale and collation options
   813  * of the given Collator.
   814  */
   815 static UCollator *
   816 NewUCollator(JSContext *cx, HandleObject collator)
   817 {
   818     RootedValue value(cx);
   820     RootedObject internals(cx);
   821     if (!GetInternals(cx, collator, &internals))
   822         return nullptr;
   824     if (!JSObject::getProperty(cx, internals, internals, cx->names().locale, &value))
   825         return nullptr;
   826     JSAutoByteString locale(cx, value.toString());
   827     if (!locale)
   828         return nullptr;
   830     // UCollator options with default values.
   831     UColAttributeValue uStrength = UCOL_DEFAULT;
   832     UColAttributeValue uCaseLevel = UCOL_OFF;
   833     UColAttributeValue uAlternate = UCOL_DEFAULT;
   834     UColAttributeValue uNumeric = UCOL_OFF;
   835     // Normalization is always on to meet the canonical equivalence requirement.
   836     UColAttributeValue uNormalization = UCOL_ON;
   837     UColAttributeValue uCaseFirst = UCOL_DEFAULT;
   839     if (!JSObject::getProperty(cx, internals, internals, cx->names().usage, &value))
   840         return nullptr;
   841     JSAutoByteString usage(cx, value.toString());
   842     if (!usage)
   843         return nullptr;
   844     if (equal(usage, "search")) {
   845         // ICU expects search as a Unicode locale extension on locale.
   846         // Unicode locale extensions must occur before private use extensions.
   847         const char *oldLocale = locale.ptr();
   848         const char *p;
   849         size_t index;
   850         size_t localeLen = strlen(oldLocale);
   851         if ((p = strstr(oldLocale, "-x-")))
   852             index = p - oldLocale;
   853         else
   854             index = localeLen;
   856         const char *insert;
   857         if ((p = strstr(oldLocale, "-u-")) && static_cast<size_t>(p - oldLocale) < index) {
   858             index = p - oldLocale + 2;
   859             insert = "-co-search";
   860         } else {
   861             insert = "-u-co-search";
   862         }
   863         size_t insertLen = strlen(insert);
   864         char *newLocale = cx->pod_malloc<char>(localeLen + insertLen + 1);
   865         if (!newLocale)
   866             return nullptr;
   867         memcpy(newLocale, oldLocale, index);
   868         memcpy(newLocale + index, insert, insertLen);
   869         memcpy(newLocale + index + insertLen, oldLocale + index, localeLen - index + 1); // '\0'
   870         locale.clear();
   871         locale.initBytes(newLocale);
   872     }
   874     // We don't need to look at the collation property - it can only be set
   875     // via the Unicode locale extension and is therefore already set on
   876     // locale.
   878     if (!JSObject::getProperty(cx, internals, internals, cx->names().sensitivity, &value))
   879         return nullptr;
   880     JSAutoByteString sensitivity(cx, value.toString());
   881     if (!sensitivity)
   882         return nullptr;
   883     if (equal(sensitivity, "base")) {
   884         uStrength = UCOL_PRIMARY;
   885     } else if (equal(sensitivity, "accent")) {
   886         uStrength = UCOL_SECONDARY;
   887     } else if (equal(sensitivity, "case")) {
   888         uStrength = UCOL_PRIMARY;
   889         uCaseLevel = UCOL_ON;
   890     } else {
   891         JS_ASSERT(equal(sensitivity, "variant"));
   892         uStrength = UCOL_TERTIARY;
   893     }
   895     if (!JSObject::getProperty(cx, internals, internals, cx->names().ignorePunctuation, &value))
   896         return nullptr;
   897     // According to the ICU team, UCOL_SHIFTED causes punctuation to be
   898     // ignored. Looking at Unicode Technical Report 35, Unicode Locale Data
   899     // Markup Language, "shifted" causes whitespace and punctuation to be
   900     // ignored - that's a bit more than asked for, but there's no way to get
   901     // less.
   902     if (value.toBoolean())
   903         uAlternate = UCOL_SHIFTED;
   905     if (!JSObject::getProperty(cx, internals, internals, cx->names().numeric, &value))
   906         return nullptr;
   907     if (!value.isUndefined() && value.toBoolean())
   908         uNumeric = UCOL_ON;
   910     if (!JSObject::getProperty(cx, internals, internals, cx->names().caseFirst, &value))
   911         return nullptr;
   912     if (!value.isUndefined()) {
   913         JSAutoByteString caseFirst(cx, value.toString());
   914         if (!caseFirst)
   915             return nullptr;
   916         if (equal(caseFirst, "upper"))
   917             uCaseFirst = UCOL_UPPER_FIRST;
   918         else if (equal(caseFirst, "lower"))
   919             uCaseFirst = UCOL_LOWER_FIRST;
   920         else
   921             JS_ASSERT(equal(caseFirst, "false"));
   922     }
   924     UErrorCode status = U_ZERO_ERROR;
   925     UCollator *coll = ucol_open(icuLocale(locale.ptr()), &status);
   926     if (U_FAILURE(status)) {
   927         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   928         return nullptr;
   929     }
   931     ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
   932     ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
   933     ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
   934     ucol_setAttribute(coll, UCOL_NUMERIC_COLLATION, uNumeric, &status);
   935     ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, uNormalization, &status);
   936     ucol_setAttribute(coll, UCOL_CASE_FIRST, uCaseFirst, &status);
   937     if (U_FAILURE(status)) {
   938         ucol_close(coll);
   939         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   940         return nullptr;
   941     }
   943     return coll;
   944 }
   946 static bool
   947 intl_CompareStrings(JSContext *cx, UCollator *coll, HandleString str1, HandleString str2, MutableHandleValue result)
   948 {
   949     JS_ASSERT(str1);
   950     JS_ASSERT(str2);
   952     if (str1 == str2) {
   953         result.setInt32(0);
   954         return true;
   955     }
   957     size_t length1 = str1->length();
   958     const jschar *chars1 = str1->getChars(cx);
   959     if (!chars1)
   960         return false;
   961     size_t length2 = str2->length();
   962     const jschar *chars2 = str2->getChars(cx);
   963     if (!chars2)
   964         return false;
   966     UCollationResult uresult = ucol_strcoll(coll, JSCharToUChar(chars1),
   967                                             length1, JSCharToUChar(chars2),
   968                                             length2);
   970     int32_t res;
   971     switch (uresult) {
   972         case UCOL_LESS: res = -1; break;
   973         case UCOL_EQUAL: res = 0; break;
   974         case UCOL_GREATER: res = 1; break;
   975         default: MOZ_ASSUME_UNREACHABLE("ucol_strcoll returned bad UCollationResult");
   976     }
   977     result.setInt32(res);
   978     return true;
   979 }
   981 bool
   982 js::intl_CompareStrings(JSContext *cx, unsigned argc, Value *vp)
   983 {
   984     CallArgs args = CallArgsFromVp(argc, vp);
   985     JS_ASSERT(args.length() == 3);
   986     JS_ASSERT(args[0].isObject());
   987     JS_ASSERT(args[1].isString());
   988     JS_ASSERT(args[2].isString());
   990     RootedObject collator(cx, &args[0].toObject());
   992     // Obtain a UCollator object, cached if possible.
   993     // XXX Does this handle Collator instances from other globals correctly?
   994     bool isCollatorInstance = collator->getClass() == &CollatorClass;
   995     UCollator *coll;
   996     if (isCollatorInstance) {
   997         coll = static_cast<UCollator *>(collator->getReservedSlot(UCOLLATOR_SLOT).toPrivate());
   998         if (!coll) {
   999             coll = NewUCollator(cx, collator);
  1000             if (!coll)
  1001                 return false;
  1002             collator->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(coll));
  1004     } else {
  1005         // There's no good place to cache the ICU collator for an object
  1006         // that has been initialized as a Collator but is not a Collator
  1007         // instance. One possibility might be to add a Collator instance as an
  1008         // internal property to each such object.
  1009         coll = NewUCollator(cx, collator);
  1010         if (!coll)
  1011             return false;
  1014     // Use the UCollator to actually compare the strings.
  1015     RootedString str1(cx, args[1].toString());
  1016     RootedString str2(cx, args[2].toString());
  1017     RootedValue result(cx);
  1018     bool success = intl_CompareStrings(cx, coll, str1, str2, &result);
  1020     if (!isCollatorInstance)
  1021         ucol_close(coll);
  1022     if (!success)
  1023         return false;
  1024     args.rval().set(result);
  1025     return true;
  1029 /******************** NumberFormat ********************/
  1031 static void numberFormat_finalize(FreeOp *fop, JSObject *obj);
  1033 static const uint32_t UNUMBER_FORMAT_SLOT = 0;
  1034 static const uint32_t NUMBER_FORMAT_SLOTS_COUNT = 1;
  1036 static const Class NumberFormatClass = {
  1037     js_Object_str,
  1038     JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
  1039     JS_PropertyStub,         /* addProperty */
  1040     JS_DeletePropertyStub,   /* delProperty */
  1041     JS_PropertyStub,         /* getProperty */
  1042     JS_StrictPropertyStub,   /* setProperty */
  1043     JS_EnumerateStub,
  1044     JS_ResolveStub,
  1045     JS_ConvertStub,
  1046     numberFormat_finalize
  1047 };
  1049 #if JS_HAS_TOSOURCE
  1050 static bool
  1051 numberFormat_toSource(JSContext *cx, unsigned argc, Value *vp)
  1053     CallArgs args = CallArgsFromVp(argc, vp);
  1054     args.rval().setString(cx->names().NumberFormat);
  1055     return true;
  1057 #endif
  1059 static const JSFunctionSpec numberFormat_static_methods[] = {
  1060     JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_NumberFormat_supportedLocalesOf", 1, 0),
  1061     JS_FS_END
  1062 };
  1064 static const JSFunctionSpec numberFormat_methods[] = {
  1065     JS_SELF_HOSTED_FN("resolvedOptions", "Intl_NumberFormat_resolvedOptions", 0, 0),
  1066 #if JS_HAS_TOSOURCE
  1067     JS_FN(js_toSource_str, numberFormat_toSource, 0, 0),
  1068 #endif
  1069     JS_FS_END
  1070 };
  1072 /**
  1073  * NumberFormat constructor.
  1074  * Spec: ECMAScript Internationalization API Specification, 11.1
  1075  */
  1076 static bool
  1077 NumberFormat(JSContext *cx, CallArgs args, bool construct)
  1079     RootedObject obj(cx);
  1081     if (!construct) {
  1082         // 11.1.2.1 step 3
  1083         JSObject *intl = cx->global()->getOrCreateIntlObject(cx);
  1084         if (!intl)
  1085             return false;
  1086         RootedValue self(cx, args.thisv());
  1087         if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
  1088             // 11.1.2.1 step 4
  1089             obj = ToObject(cx, self);
  1090             if (!obj)
  1091                 return false;
  1093             // 11.1.2.1 step 5
  1094             bool extensible;
  1095             if (!JSObject::isExtensible(cx, obj, &extensible))
  1096                 return false;
  1097             if (!extensible)
  1098                 return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
  1099         } else {
  1100             // 11.1.2.1 step 3.a
  1101             construct = true;
  1104     if (construct) {
  1105         // 11.1.3.1 paragraph 2
  1106         RootedObject proto(cx, cx->global()->getOrCreateNumberFormatPrototype(cx));
  1107         if (!proto)
  1108             return false;
  1109         obj = NewObjectWithGivenProto(cx, &NumberFormatClass, proto, cx->global());
  1110         if (!obj)
  1111             return false;
  1113         obj->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
  1116     // 11.1.2.1 steps 1 and 2; 11.1.3.1 steps 1 and 2
  1117     RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
  1118     RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
  1120     // 11.1.2.1 step 6; 11.1.3.1 step 3
  1121     if (!IntlInitialize(cx, obj, cx->names().InitializeNumberFormat, locales, options))
  1122         return false;
  1124     // 11.1.2.1 steps 3.a and 7
  1125     args.rval().setObject(*obj);
  1126     return true;
  1129 static bool
  1130 NumberFormat(JSContext *cx, unsigned argc, Value *vp)
  1132     CallArgs args = CallArgsFromVp(argc, vp);
  1133     return NumberFormat(cx, args, args.isConstructing());
  1136 bool
  1137 js::intl_NumberFormat(JSContext *cx, unsigned argc, Value *vp)
  1139     CallArgs args = CallArgsFromVp(argc, vp);
  1140     JS_ASSERT(args.length() == 2);
  1141     // intl_NumberFormat is an intrinsic for self-hosted JavaScript, so it
  1142     // cannot be used with "new", but it still has to be treated as a
  1143     // constructor.
  1144     return NumberFormat(cx, args, true);
  1147 static void
  1148 numberFormat_finalize(FreeOp *fop, JSObject *obj)
  1150     UNumberFormat *nf =
  1151         static_cast<UNumberFormat*>(obj->getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
  1152     if (nf)
  1153         unum_close(nf);
  1156 static JSObject *
  1157 InitNumberFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
  1159     RootedFunction ctor(cx, global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0));
  1160     if (!ctor)
  1161         return nullptr;
  1163     RootedObject proto(cx, global->as<GlobalObject>().getOrCreateNumberFormatPrototype(cx));
  1164     if (!proto)
  1165         return nullptr;
  1166     if (!LinkConstructorAndPrototype(cx, ctor, proto))
  1167         return nullptr;
  1169     // 11.2.2
  1170     if (!JS_DefineFunctions(cx, ctor, numberFormat_static_methods))
  1171         return nullptr;
  1173     // 11.3.2 and 11.3.3
  1174     if (!JS_DefineFunctions(cx, proto, numberFormat_methods))
  1175         return nullptr;
  1177     /*
  1178      * Install the getter for NumberFormat.prototype.format, which returns a
  1179      * bound formatting function for the specified NumberFormat object (suitable
  1180      * for passing to methods like Array.prototype.map).
  1181      */
  1182     RootedValue getter(cx);
  1183     if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().NumberFormatFormatGet, &getter))
  1184         return nullptr;
  1185     if (!JSObject::defineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
  1186                                   JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
  1187                                   nullptr, JSPROP_GETTER | JSPROP_SHARED))
  1189         return nullptr;
  1192     // 11.2.1 and 11.3
  1193     if (!IntlInitialize(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue,
  1194                         UndefinedHandleValue))
  1196         return nullptr;
  1199     // 8.1
  1200     RootedValue ctorValue(cx, ObjectValue(*ctor));
  1201     if (!JSObject::defineProperty(cx, Intl, cx->names().NumberFormat, ctorValue,
  1202                                   JS_PropertyStub, JS_StrictPropertyStub, 0))
  1204         return nullptr;
  1207     return ctor;
  1210 bool
  1211 GlobalObject::initNumberFormatProto(JSContext *cx, Handle<GlobalObject*> global)
  1213     RootedObject proto(cx, global->createBlankPrototype(cx, &NumberFormatClass));
  1214     if (!proto)
  1215         return false;
  1216     proto->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
  1217     global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*proto));
  1218     return true;
  1221 bool
  1222 js::intl_NumberFormat_availableLocales(JSContext *cx, unsigned argc, Value *vp)
  1224     CallArgs args = CallArgsFromVp(argc, vp);
  1225     JS_ASSERT(args.length() == 0);
  1227     RootedValue result(cx);
  1228     if (!intl_availableLocales(cx, unum_countAvailable, unum_getAvailable, &result))
  1229         return false;
  1230     args.rval().set(result);
  1231     return true;
  1234 bool
  1235 js::intl_numberingSystem(JSContext *cx, unsigned argc, Value *vp)
  1237     CallArgs args = CallArgsFromVp(argc, vp);
  1238     JS_ASSERT(args.length() == 1);
  1239     JS_ASSERT(args[0].isString());
  1241     JSAutoByteString locale(cx, args[0].toString());
  1242     if (!locale)
  1243         return false;
  1245     // There's no C API for numbering system, so use the C++ API and hope it
  1246     // won't break. http://bugs.icu-project.org/trac/ticket/10039
  1247     Locale ulocale(locale.ptr());
  1248     UErrorCode status = U_ZERO_ERROR;
  1249     NumberingSystem *numbers = NumberingSystem::createInstance(ulocale, status);
  1250     if (U_FAILURE(status)) {
  1251         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1252         return false;
  1254     const char *name = numbers->getName();
  1255     RootedString jsname(cx, JS_NewStringCopyZ(cx, name));
  1256     delete numbers;
  1257     if (!jsname)
  1258         return false;
  1259     args.rval().setString(jsname);
  1260     return true;
  1263 /**
  1264  * Returns a new UNumberFormat with the locale and number formatting options
  1265  * of the given NumberFormat.
  1266  */
  1267 static UNumberFormat *
  1268 NewUNumberFormat(JSContext *cx, HandleObject numberFormat)
  1270     RootedValue value(cx);
  1272     RootedObject internals(cx);
  1273     if (!GetInternals(cx, numberFormat, &internals))
  1274        return nullptr;
  1276     if (!JSObject::getProperty(cx, internals, internals, cx->names().locale, &value))
  1277         return nullptr;
  1278     JSAutoByteString locale(cx, value.toString());
  1279     if (!locale)
  1280         return nullptr;
  1282     // UNumberFormat options with default values
  1283     UNumberFormatStyle uStyle = UNUM_DECIMAL;
  1284     const UChar *uCurrency = nullptr;
  1285     uint32_t uMinimumIntegerDigits = 1;
  1286     uint32_t uMinimumFractionDigits = 0;
  1287     uint32_t uMaximumFractionDigits = 3;
  1288     int32_t uMinimumSignificantDigits = -1;
  1289     int32_t uMaximumSignificantDigits = -1;
  1290     bool uUseGrouping = true;
  1292     // Sprinkle appropriate rooting flavor over things the GC might care about.
  1293     RootedString currency(cx);
  1295     // We don't need to look at numberingSystem - it can only be set via
  1296     // the Unicode locale extension and is therefore already set on locale.
  1298     if (!JSObject::getProperty(cx, internals, internals, cx->names().style, &value))
  1299         return nullptr;
  1300     JSAutoByteString style(cx, value.toString());
  1301     if (!style)
  1302         return nullptr;
  1304     if (equal(style, "currency")) {
  1305         if (!JSObject::getProperty(cx, internals, internals, cx->names().currency, &value))
  1306             return nullptr;
  1307         currency = value.toString();
  1308         MOZ_ASSERT(currency->length() == 3, "IsWellFormedCurrencyCode permits only length-3 strings");
  1309         // uCurrency remains owned by currency.
  1310         uCurrency = JSCharToUChar(JS_GetStringCharsZ(cx, currency));
  1311         if (!uCurrency)
  1312             return nullptr;
  1314         if (!JSObject::getProperty(cx, internals, internals, cx->names().currencyDisplay, &value))
  1315             return nullptr;
  1316         JSAutoByteString currencyDisplay(cx, value.toString());
  1317         if (!currencyDisplay)
  1318             return nullptr;
  1319         if (equal(currencyDisplay, "code")) {
  1320             uStyle = UNUM_CURRENCY_ISO;
  1321         } else if (equal(currencyDisplay, "symbol")) {
  1322             uStyle = UNUM_CURRENCY;
  1323         } else {
  1324             JS_ASSERT(equal(currencyDisplay, "name"));
  1325             uStyle = UNUM_CURRENCY_PLURAL;
  1327     } else if (equal(style, "percent")) {
  1328         uStyle = UNUM_PERCENT;
  1329     } else {
  1330         JS_ASSERT(equal(style, "decimal"));
  1331         uStyle = UNUM_DECIMAL;
  1334     RootedId id(cx, NameToId(cx->names().minimumSignificantDigits));
  1335     bool hasP;
  1336     if (!JSObject::hasProperty(cx, internals, id, &hasP))
  1337         return nullptr;
  1338     if (hasP) {
  1339         if (!JSObject::getProperty(cx, internals, internals, cx->names().minimumSignificantDigits,
  1340                                    &value))
  1342             return nullptr;
  1344         uMinimumSignificantDigits = int32_t(value.toNumber());
  1345         if (!JSObject::getProperty(cx, internals, internals, cx->names().maximumSignificantDigits,
  1346                                    &value))
  1348             return nullptr;
  1350         uMaximumSignificantDigits = int32_t(value.toNumber());
  1351     } else {
  1352         if (!JSObject::getProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
  1353                                    &value))
  1355             return nullptr;
  1357         uMinimumIntegerDigits = int32_t(value.toNumber());
  1358         if (!JSObject::getProperty(cx, internals, internals, cx->names().minimumFractionDigits,
  1359                                    &value))
  1361             return nullptr;
  1363         uMinimumFractionDigits = int32_t(value.toNumber());
  1364         if (!JSObject::getProperty(cx, internals, internals, cx->names().maximumFractionDigits,
  1365                                    &value))
  1367             return nullptr;
  1369         uMaximumFractionDigits = int32_t(value.toNumber());
  1372     if (!JSObject::getProperty(cx, internals, internals, cx->names().useGrouping, &value))
  1373         return nullptr;
  1374     uUseGrouping = value.toBoolean();
  1376     UErrorCode status = U_ZERO_ERROR;
  1377     UNumberFormat *nf = unum_open(uStyle, nullptr, 0, icuLocale(locale.ptr()), nullptr, &status);
  1378     if (U_FAILURE(status)) {
  1379         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1380         return nullptr;
  1382     ScopedICUObject<UNumberFormat> toClose(nf, unum_close);
  1384     if (uCurrency) {
  1385         unum_setTextAttribute(nf, UNUM_CURRENCY_CODE, uCurrency, 3, &status);
  1386         if (U_FAILURE(status)) {
  1387             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1388             return nullptr;
  1391     if (uMinimumSignificantDigits != -1) {
  1392         unum_setAttribute(nf, UNUM_SIGNIFICANT_DIGITS_USED, true);
  1393         unum_setAttribute(nf, UNUM_MIN_SIGNIFICANT_DIGITS, uMinimumSignificantDigits);
  1394         unum_setAttribute(nf, UNUM_MAX_SIGNIFICANT_DIGITS, uMaximumSignificantDigits);
  1395     } else {
  1396         unum_setAttribute(nf, UNUM_MIN_INTEGER_DIGITS, uMinimumIntegerDigits);
  1397         unum_setAttribute(nf, UNUM_MIN_FRACTION_DIGITS, uMinimumFractionDigits);
  1398         unum_setAttribute(nf, UNUM_MAX_FRACTION_DIGITS, uMaximumFractionDigits);
  1400     unum_setAttribute(nf, UNUM_GROUPING_USED, uUseGrouping);
  1401     unum_setAttribute(nf, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
  1403     return toClose.forget();
  1406 static bool
  1407 intl_FormatNumber(JSContext *cx, UNumberFormat *nf, double x, MutableHandleValue result)
  1409     // FormatNumber doesn't consider -0.0 to be negative.
  1410     if (IsNegativeZero(x))
  1411         x = 0.0;
  1413     StringBuffer chars(cx);
  1414     if (!chars.resize(INITIAL_STRING_BUFFER_SIZE))
  1415         return false;
  1416     UErrorCode status = U_ZERO_ERROR;
  1417     int size = unum_formatDouble(nf, x, JSCharToUChar(chars.begin()),
  1418                                  INITIAL_STRING_BUFFER_SIZE, nullptr, &status);
  1419     if (status == U_BUFFER_OVERFLOW_ERROR) {
  1420         if (!chars.resize(size))
  1421             return false;
  1422         status = U_ZERO_ERROR;
  1423         unum_formatDouble(nf, x, JSCharToUChar(chars.begin()),
  1424                           size, nullptr, &status);
  1426     if (U_FAILURE(status)) {
  1427         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1428         return false;
  1431     // Trim any unused characters.
  1432     if (!chars.resize(size))
  1433         return false;
  1435     RootedString str(cx, chars.finishString());
  1436     if (!str)
  1437         return false;
  1439     result.setString(str);
  1440     return true;
  1443 bool
  1444 js::intl_FormatNumber(JSContext *cx, unsigned argc, Value *vp)
  1446     CallArgs args = CallArgsFromVp(argc, vp);
  1447     JS_ASSERT(args.length() == 2);
  1448     JS_ASSERT(args[0].isObject());
  1449     JS_ASSERT(args[1].isNumber());
  1451     RootedObject numberFormat(cx, &args[0].toObject());
  1453     // Obtain a UNumberFormat object, cached if possible.
  1454     bool isNumberFormatInstance = numberFormat->getClass() == &NumberFormatClass;
  1455     UNumberFormat *nf;
  1456     if (isNumberFormatInstance) {
  1457         nf = static_cast<UNumberFormat*>(numberFormat->getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
  1458         if (!nf) {
  1459             nf = NewUNumberFormat(cx, numberFormat);
  1460             if (!nf)
  1461                 return false;
  1462             numberFormat->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nf));
  1464     } else {
  1465         // There's no good place to cache the ICU number format for an object
  1466         // that has been initialized as a NumberFormat but is not a
  1467         // NumberFormat instance. One possibility might be to add a
  1468         // NumberFormat instance as an internal property to each such object.
  1469         nf = NewUNumberFormat(cx, numberFormat);
  1470         if (!nf)
  1471             return false;
  1474     // Use the UNumberFormat to actually format the number.
  1475     RootedValue result(cx);
  1476     bool success = intl_FormatNumber(cx, nf, args[1].toNumber(), &result);
  1478     if (!isNumberFormatInstance)
  1479         unum_close(nf);
  1480     if (!success)
  1481         return false;
  1482     args.rval().set(result);
  1483     return true;
  1487 /******************** DateTimeFormat ********************/
  1489 static void dateTimeFormat_finalize(FreeOp *fop, JSObject *obj);
  1491 static const uint32_t UDATE_FORMAT_SLOT = 0;
  1492 static const uint32_t DATE_TIME_FORMAT_SLOTS_COUNT = 1;
  1494 static const Class DateTimeFormatClass = {
  1495     js_Object_str,
  1496     JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
  1497     JS_PropertyStub,         /* addProperty */
  1498     JS_DeletePropertyStub,   /* delProperty */
  1499     JS_PropertyStub,         /* getProperty */
  1500     JS_StrictPropertyStub,   /* setProperty */
  1501     JS_EnumerateStub,
  1502     JS_ResolveStub,
  1503     JS_ConvertStub,
  1504     dateTimeFormat_finalize
  1505 };
  1507 #if JS_HAS_TOSOURCE
  1508 static bool
  1509 dateTimeFormat_toSource(JSContext *cx, unsigned argc, Value *vp)
  1511     CallArgs args = CallArgsFromVp(argc, vp);
  1512     args.rval().setString(cx->names().DateTimeFormat);
  1513     return true;
  1515 #endif
  1517 static const JSFunctionSpec dateTimeFormat_static_methods[] = {
  1518     JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_DateTimeFormat_supportedLocalesOf", 1, 0),
  1519     JS_FS_END
  1520 };
  1522 static const JSFunctionSpec dateTimeFormat_methods[] = {
  1523     JS_SELF_HOSTED_FN("resolvedOptions", "Intl_DateTimeFormat_resolvedOptions", 0, 0),
  1524 #if JS_HAS_TOSOURCE
  1525     JS_FN(js_toSource_str, dateTimeFormat_toSource, 0, 0),
  1526 #endif
  1527     JS_FS_END
  1528 };
  1530 /**
  1531  * DateTimeFormat constructor.
  1532  * Spec: ECMAScript Internationalization API Specification, 12.1
  1533  */
  1534 static bool
  1535 DateTimeFormat(JSContext *cx, CallArgs args, bool construct)
  1537     RootedObject obj(cx);
  1539     if (!construct) {
  1540         // 12.1.2.1 step 3
  1541         JSObject *intl = cx->global()->getOrCreateIntlObject(cx);
  1542         if (!intl)
  1543             return false;
  1544         RootedValue self(cx, args.thisv());
  1545         if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
  1546             // 12.1.2.1 step 4
  1547             obj = ToObject(cx, self);
  1548             if (!obj)
  1549                 return false;
  1551             // 12.1.2.1 step 5
  1552             bool extensible;
  1553             if (!JSObject::isExtensible(cx, obj, &extensible))
  1554                 return false;
  1555             if (!extensible)
  1556                 return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
  1557         } else {
  1558             // 12.1.2.1 step 3.a
  1559             construct = true;
  1562     if (construct) {
  1563         // 12.1.3.1 paragraph 2
  1564         RootedObject proto(cx, cx->global()->getOrCreateDateTimeFormatPrototype(cx));
  1565         if (!proto)
  1566             return false;
  1567         obj = NewObjectWithGivenProto(cx, &DateTimeFormatClass, proto, cx->global());
  1568         if (!obj)
  1569             return false;
  1571         obj->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
  1574     // 12.1.2.1 steps 1 and 2; 12.1.3.1 steps 1 and 2
  1575     RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
  1576     RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
  1578     // 12.1.2.1 step 6; 12.1.3.1 step 3
  1579     if (!IntlInitialize(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
  1580         return false;
  1582     // 12.1.2.1 steps 3.a and 7
  1583     args.rval().setObject(*obj);
  1584     return true;
  1587 static bool
  1588 DateTimeFormat(JSContext *cx, unsigned argc, Value *vp)
  1590     CallArgs args = CallArgsFromVp(argc, vp);
  1591     return DateTimeFormat(cx, args, args.isConstructing());
  1594 bool
  1595 js::intl_DateTimeFormat(JSContext *cx, unsigned argc, Value *vp)
  1597     CallArgs args = CallArgsFromVp(argc, vp);
  1598     JS_ASSERT(args.length() == 2);
  1599     // intl_DateTimeFormat 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     return DateTimeFormat(cx, args, true);
  1605 static void
  1606 dateTimeFormat_finalize(FreeOp *fop, JSObject *obj)
  1608     UDateFormat *df = static_cast<UDateFormat*>(obj->getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
  1609     if (df)
  1610         udat_close(df);
  1613 static JSObject *
  1614 InitDateTimeFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
  1616     RootedFunction ctor(cx, global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0));
  1617     if (!ctor)
  1618         return nullptr;
  1620     RootedObject proto(cx, global->as<GlobalObject>().getOrCreateDateTimeFormatPrototype(cx));
  1621     if (!proto)
  1622         return nullptr;
  1623     if (!LinkConstructorAndPrototype(cx, ctor, proto))
  1624         return nullptr;
  1626     // 12.2.2
  1627     if (!JS_DefineFunctions(cx, ctor, dateTimeFormat_static_methods))
  1628         return nullptr;
  1630     // 12.3.2 and 12.3.3
  1631     if (!JS_DefineFunctions(cx, proto, dateTimeFormat_methods))
  1632         return nullptr;
  1634     /*
  1635      * Install the getter for DateTimeFormat.prototype.format, which returns a
  1636      * bound formatting function for the specified DateTimeFormat object
  1637      * (suitable for passing to methods like Array.prototype.map).
  1638      */
  1639     RootedValue getter(cx);
  1640     if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().DateTimeFormatFormatGet, &getter))
  1641         return nullptr;
  1642     if (!JSObject::defineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
  1643                                   JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
  1644                                   nullptr, JSPROP_GETTER | JSPROP_SHARED))
  1646         return nullptr;
  1649     // 12.2.1 and 12.3
  1650     if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue,
  1651                         UndefinedHandleValue))
  1653         return nullptr;
  1656     // 8.1
  1657     RootedValue ctorValue(cx, ObjectValue(*ctor));
  1658     if (!JSObject::defineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue,
  1659                                   JS_PropertyStub, JS_StrictPropertyStub, 0))
  1661         return nullptr;
  1664     return ctor;
  1667 bool
  1668 GlobalObject::initDateTimeFormatProto(JSContext *cx, Handle<GlobalObject*> global)
  1670     RootedObject proto(cx, global->createBlankPrototype(cx, &DateTimeFormatClass));
  1671     if (!proto)
  1672         return false;
  1673     proto->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
  1674     global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*proto));
  1675     return true;
  1678 bool
  1679 js::intl_DateTimeFormat_availableLocales(JSContext *cx, unsigned argc, Value *vp)
  1681     CallArgs args = CallArgsFromVp(argc, vp);
  1682     JS_ASSERT(args.length() == 0);
  1684     RootedValue result(cx);
  1685     if (!intl_availableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
  1686         return false;
  1687     args.rval().set(result);
  1688     return true;
  1691 // ICU returns old-style keyword values; map them to BCP 47 equivalents
  1692 // (see http://bugs.icu-project.org/trac/ticket/9620).
  1693 static const char *
  1694 bcp47CalendarName(const char *icuName)
  1696     if (equal(icuName, "ethiopic-amete-alem"))
  1697         return "ethioaa";
  1698     if (equal(icuName, "gregorian"))
  1699         return "gregory";
  1700     if (equal(icuName, "islamic-civil"))
  1701         return "islamicc";
  1702     return icuName;
  1705 bool
  1706 js::intl_availableCalendars(JSContext *cx, unsigned argc, Value *vp)
  1708     CallArgs args = CallArgsFromVp(argc, vp);
  1709     JS_ASSERT(args.length() == 1);
  1710     JS_ASSERT(args[0].isString());
  1712     JSAutoByteString locale(cx, args[0].toString());
  1713     if (!locale)
  1714         return false;
  1716     RootedObject calendars(cx, NewDenseEmptyArray(cx));
  1717     if (!calendars)
  1718         return false;
  1719     uint32_t index = 0;
  1721     // We need the default calendar for the locale as the first result.
  1722     UErrorCode status = U_ZERO_ERROR;
  1723     UCalendar *cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
  1724     const char *calendar = ucal_getType(cal, &status);
  1725     if (U_FAILURE(status)) {
  1726         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1727         return false;
  1729     ucal_close(cal);
  1730     RootedString jscalendar(cx, JS_NewStringCopyZ(cx, bcp47CalendarName(calendar)));
  1731     if (!jscalendar)
  1732         return false;
  1733     RootedValue element(cx, StringValue(jscalendar));
  1734     if (!JSObject::defineElement(cx, calendars, index++, element))
  1735         return false;
  1737     // Now get the calendars that "would make a difference", i.e., not the default.
  1738     UEnumeration *values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
  1739     if (U_FAILURE(status)) {
  1740         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1741         return false;
  1743     ScopedICUObject<UEnumeration> toClose(values, uenum_close);
  1745     uint32_t count = uenum_count(values, &status);
  1746     if (U_FAILURE(status)) {
  1747         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1748         return false;
  1751     for (; count > 0; count--) {
  1752         calendar = uenum_next(values, nullptr, &status);
  1753         if (U_FAILURE(status)) {
  1754             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1755             return false;
  1758         jscalendar = JS_NewStringCopyZ(cx, bcp47CalendarName(calendar));
  1759         if (!jscalendar)
  1760             return false;
  1761         element = StringValue(jscalendar);
  1762         if (!JSObject::defineElement(cx, calendars, index++, element))
  1763             return false;
  1766     args.rval().setObject(*calendars);
  1767     return true;
  1770 bool
  1771 js::intl_patternForSkeleton(JSContext *cx, unsigned argc, Value *vp)
  1773     CallArgs args = CallArgsFromVp(argc, vp);
  1774     JS_ASSERT(args.length() == 2);
  1775     JS_ASSERT(args[0].isString());
  1776     JS_ASSERT(args[1].isString());
  1778     JSAutoByteString locale(cx, args[0].toString());
  1779     if (!locale)
  1780         return false;
  1781     RootedString jsskeleton(cx, args[1].toString());
  1782     const jschar *skeleton = JS_GetStringCharsZ(cx, jsskeleton);
  1783     if (!skeleton)
  1784         return false;
  1785     uint32_t skeletonLen = u_strlen(JSCharToUChar(skeleton));
  1787     UErrorCode status = U_ZERO_ERROR;
  1788     UDateTimePatternGenerator *gen = udatpg_open(icuLocale(locale.ptr()), &status);
  1789     if (U_FAILURE(status)) {
  1790         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1791         return false;
  1793     ScopedICUObject<UDateTimePatternGenerator> toClose(gen, udatpg_close);
  1795     int32_t size = udatpg_getBestPattern(gen, JSCharToUChar(skeleton),
  1796                                          skeletonLen, nullptr, 0, &status);
  1797     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
  1798         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1799         return false;
  1801     ScopedJSFreePtr<UChar> pattern(cx->pod_malloc<UChar>(size + 1));
  1802     if (!pattern)
  1803         return false;
  1804     pattern[size] = '\0';
  1805     status = U_ZERO_ERROR;
  1806     udatpg_getBestPattern(gen, JSCharToUChar(skeleton),
  1807                           skeletonLen, pattern, size, &status);
  1808     if (U_FAILURE(status)) {
  1809         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1810         return false;
  1813     RootedString str(cx, JS_NewUCStringCopyZ(cx, reinterpret_cast<jschar*>(pattern.get())));
  1814     if (!str)
  1815         return false;
  1816     args.rval().setString(str);
  1817     return true;
  1820 /**
  1821  * Returns a new UDateFormat with the locale and date-time formatting options
  1822  * of the given DateTimeFormat.
  1823  */
  1824 static UDateFormat *
  1825 NewUDateFormat(JSContext *cx, HandleObject dateTimeFormat)
  1827     RootedValue value(cx);
  1829     RootedObject internals(cx);
  1830     if (!GetInternals(cx, dateTimeFormat, &internals))
  1831        return nullptr;
  1833     if (!JSObject::getProperty(cx, internals, internals, cx->names().locale, &value)) {
  1834         return nullptr;
  1836     JSAutoByteString locale(cx, value.toString());
  1837     if (!locale)
  1838         return nullptr;
  1840     // UDateFormat options with default values.
  1841     const UChar *uTimeZone = nullptr;
  1842     uint32_t uTimeZoneLength = 0;
  1843     const UChar *uPattern = nullptr;
  1844     uint32_t uPatternLength = 0;
  1846     // We don't need to look at calendar and numberingSystem - they can only be
  1847     // set via the Unicode locale extension and are therefore already set on
  1848     // locale.
  1850     RootedId id(cx, NameToId(cx->names().timeZone));
  1851     bool hasP;
  1852     if (!JSObject::hasProperty(cx, internals, id, &hasP))
  1853         return nullptr;
  1854     if (hasP) {
  1855         if (!JSObject::getProperty(cx, internals, internals, cx->names().timeZone, &value))
  1856             return nullptr;
  1857         if (!value.isUndefined()) {
  1858             uTimeZone = JSCharToUChar(JS_GetStringCharsZ(cx, value.toString()));
  1859             if (!uTimeZone)
  1860                 return nullptr;
  1861             uTimeZoneLength = u_strlen(uTimeZone);
  1864     if (!JSObject::getProperty(cx, internals, internals, cx->names().pattern, &value))
  1865         return nullptr;
  1866     uPattern = JSCharToUChar(JS_GetStringCharsZ(cx, value.toString()));
  1867     if (!uPattern)
  1868         return nullptr;
  1869     uPatternLength = u_strlen(uPattern);
  1871     UErrorCode status = U_ZERO_ERROR;
  1873     // If building with ICU headers before 50.1, use UDAT_IGNORE instead of
  1874     // UDAT_PATTERN.
  1875     UDateFormat *df =
  1876         udat_open(UDAT_PATTERN, UDAT_PATTERN, icuLocale(locale.ptr()), uTimeZone, uTimeZoneLength,
  1877                   uPattern, uPatternLength, &status);
  1878     if (U_FAILURE(status)) {
  1879         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1880         return nullptr;
  1883     // ECMAScript requires the Gregorian calendar to be used from the beginning
  1884     // of ECMAScript time.
  1885     UCalendar *cal = const_cast<UCalendar*>(udat_getCalendar(df));
  1886     ucal_setGregorianChange(cal, StartOfTime, &status);
  1888     // An error here means the calendar is not Gregorian, so we don't care.
  1890     return df;
  1893 static bool
  1894 intl_FormatDateTime(JSContext *cx, UDateFormat *df, double x, MutableHandleValue result)
  1896     if (!IsFinite(x)) {
  1897         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
  1898         return false;
  1901     StringBuffer chars(cx);
  1902     if (!chars.resize(INITIAL_STRING_BUFFER_SIZE))
  1903         return false;
  1904     UErrorCode status = U_ZERO_ERROR;
  1905     int size = udat_format(df, x, JSCharToUChar(chars.begin()), INITIAL_STRING_BUFFER_SIZE, nullptr, &status);
  1906     if (status == U_BUFFER_OVERFLOW_ERROR) {
  1907         if (!chars.resize(size))
  1908             return false;
  1909         status = U_ZERO_ERROR;
  1910         udat_format(df, x, JSCharToUChar(chars.begin()), size, nullptr, &status);
  1912     if (U_FAILURE(status)) {
  1913         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1914         return false;
  1917     // Trim any unused characters.
  1918     if (!chars.resize(size))
  1919         return false;
  1921     RootedString str(cx, chars.finishString());
  1922     if (!str)
  1923         return false;
  1925     result.setString(str);
  1926     return true;
  1929 bool
  1930 js::intl_FormatDateTime(JSContext *cx, unsigned argc, Value *vp)
  1932     CallArgs args = CallArgsFromVp(argc, vp);
  1933     JS_ASSERT(args.length() == 2);
  1934     JS_ASSERT(args[0].isObject());
  1935     JS_ASSERT(args[1].isNumber());
  1937     RootedObject dateTimeFormat(cx, &args[0].toObject());
  1939     // Obtain a UDateFormat object, cached if possible.
  1940     bool isDateTimeFormatInstance = dateTimeFormat->getClass() == &DateTimeFormatClass;
  1941     UDateFormat *df;
  1942     if (isDateTimeFormatInstance) {
  1943         df = static_cast<UDateFormat*>(dateTimeFormat->getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
  1944         if (!df) {
  1945             df = NewUDateFormat(cx, dateTimeFormat);
  1946             if (!df)
  1947                 return false;
  1948             dateTimeFormat->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(df));
  1950     } else {
  1951         // There's no good place to cache the ICU date-time format for an object
  1952         // that has been initialized as a DateTimeFormat but is not a
  1953         // DateTimeFormat instance. One possibility might be to add a
  1954         // DateTimeFormat instance as an internal property to each such object.
  1955         df = NewUDateFormat(cx, dateTimeFormat);
  1956         if (!df)
  1957             return false;
  1960     // Use the UDateFormat to actually format the time stamp.
  1961     RootedValue result(cx);
  1962     bool success = intl_FormatDateTime(cx, df, args[1].toNumber(), &result);
  1964     if (!isDateTimeFormatInstance)
  1965         udat_close(df);
  1966     if (!success)
  1967         return false;
  1968     args.rval().set(result);
  1969     return true;
  1973 /******************** Intl ********************/
  1975 const Class js::IntlClass = {
  1976     js_Object_str,
  1977     JSCLASS_HAS_CACHED_PROTO(JSProto_Intl),
  1978     JS_PropertyStub,         /* addProperty */
  1979     JS_DeletePropertyStub,   /* delProperty */
  1980     JS_PropertyStub,         /* getProperty */
  1981     JS_StrictPropertyStub,   /* setProperty */
  1982     JS_EnumerateStub,
  1983     JS_ResolveStub,
  1984     JS_ConvertStub
  1985 };
  1987 #if JS_HAS_TOSOURCE
  1988 static bool
  1989 intl_toSource(JSContext *cx, unsigned argc, Value *vp)
  1991     CallArgs args = CallArgsFromVp(argc, vp);
  1992     args.rval().setString(cx->names().Intl);
  1993     return true;
  1995 #endif
  1997 static const JSFunctionSpec intl_static_methods[] = {
  1998 #if JS_HAS_TOSOURCE
  1999     JS_FN(js_toSource_str,  intl_toSource,        0, 0),
  2000 #endif
  2001     JS_FS_END
  2002 };
  2004 /**
  2005  * Initializes the Intl Object and its standard built-in properties.
  2006  * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
  2007  */
  2008 JSObject *
  2009 js_InitIntlClass(JSContext *cx, HandleObject obj)
  2011     JS_ASSERT(obj->is<GlobalObject>());
  2012     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
  2014     // The constructors above need to be able to determine whether they've been
  2015     // called with this being "the standard built-in Intl object". The global
  2016     // object reserves slots to track standard built-in objects, but doesn't
  2017     // normally keep references to non-constructors. This makes sure there is one.
  2018     RootedObject Intl(cx, global->getOrCreateIntlObject(cx));
  2019     if (!Intl)
  2020         return nullptr;
  2022     RootedValue IntlValue(cx, ObjectValue(*Intl));
  2023     if (!JSObject::defineProperty(cx, global, cx->names().Intl, IntlValue,
  2024                                   JS_PropertyStub, JS_StrictPropertyStub, 0))
  2026         return nullptr;
  2029     if (!JS_DefineFunctions(cx, Intl, intl_static_methods))
  2030         return nullptr;
  2032     // Skip initialization of the Intl constructors during initialization of the
  2033     // self-hosting global as we may get here before self-hosted code is compiled,
  2034     // and no core code refers to the Intl classes.
  2035     if (!cx->runtime()->isSelfHostingGlobal(cx->global())) {
  2036         if (!InitCollatorClass(cx, Intl, global))
  2037             return nullptr;
  2038         if (!InitNumberFormatClass(cx, Intl, global))
  2039             return nullptr;
  2040         if (!InitDateTimeFormatClass(cx, Intl, global))
  2041             return nullptr;
  2044     global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
  2046     return Intl;
  2049 bool
  2050 GlobalObject::initIntlObject(JSContext *cx, Handle<GlobalObject*> global)
  2052     RootedObject Intl(cx);
  2053     Intl = NewObjectWithGivenProto(cx, &IntlClass, global->getOrCreateObjectPrototype(cx),
  2054                                    global, SingletonObject);
  2055     if (!Intl)
  2056         return false;
  2058     global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
  2059     return true;

mercurial