js/src/builtin/Intl.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/builtin/Intl.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2060 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * The Intl module specified by standard ECMA-402,
    1.12 + * ECMAScript Internationalization API Specification.
    1.13 + */
    1.14 +
    1.15 +#include "builtin/Intl.h"
    1.16 +
    1.17 +#include <string.h>
    1.18 +
    1.19 +#include "jsapi.h"
    1.20 +#include "jsatom.h"
    1.21 +#include "jscntxt.h"
    1.22 +#include "jsobj.h"
    1.23 +
    1.24 +#if ENABLE_INTL_API
    1.25 +#include "unicode/locid.h"
    1.26 +#include "unicode/numsys.h"
    1.27 +#include "unicode/ucal.h"
    1.28 +#include "unicode/ucol.h"
    1.29 +#include "unicode/udat.h"
    1.30 +#include "unicode/udatpg.h"
    1.31 +#include "unicode/uenum.h"
    1.32 +#include "unicode/unum.h"
    1.33 +#include "unicode/ustring.h"
    1.34 +#endif
    1.35 +#include "vm/DateTime.h"
    1.36 +#include "vm/GlobalObject.h"
    1.37 +#include "vm/Interpreter.h"
    1.38 +#include "vm/Stack.h"
    1.39 +#include "vm/StringBuffer.h"
    1.40 +
    1.41 +#include "jsobjinlines.h"
    1.42 +
    1.43 +#include "vm/ObjectImpl-inl.h"
    1.44 +
    1.45 +using namespace js;
    1.46 +
    1.47 +using mozilla::IsFinite;
    1.48 +using mozilla::IsNegativeZero;
    1.49 +
    1.50 +#if ENABLE_INTL_API
    1.51 +using icu::Locale;
    1.52 +using icu::NumberingSystem;
    1.53 +#endif
    1.54 +
    1.55 +
    1.56 +/*
    1.57 + * Pervasive note: ICU functions taking a UErrorCode in/out parameter always
    1.58 + * test that parameter before doing anything, and will return immediately if
    1.59 + * the value indicates that a failure occurred in a prior ICU call,
    1.60 + * without doing anything else. See
    1.61 + * http://userguide.icu-project.org/design#TOC-Error-Handling
    1.62 + */
    1.63 +
    1.64 +
    1.65 +/******************** ICU stubs ********************/
    1.66 +
    1.67 +#if !ENABLE_INTL_API
    1.68 +
    1.69 +/*
    1.70 + * When the Internationalization API isn't enabled, we also shouldn't link
    1.71 + * against ICU. However, we still want to compile this code in order to prevent
    1.72 + * bit rot. The following stub implementations for ICU functions make this
    1.73 + * possible. The functions using them should never be called, so they assert
    1.74 + * and return error codes. Signatures adapted from ICU header files locid.h,
    1.75 + * numsys.h, ucal.h, ucol.h, udat.h, udatpg.h, uenum.h, unum.h; see the ICU
    1.76 + * directory for license.
    1.77 + */
    1.78 +
    1.79 +static int32_t
    1.80 +u_strlen(const UChar *s)
    1.81 +{
    1.82 +    MOZ_ASSUME_UNREACHABLE("u_strlen: Intl API disabled");
    1.83 +}
    1.84 +
    1.85 +struct UEnumeration;
    1.86 +
    1.87 +static int32_t
    1.88 +uenum_count(UEnumeration *en, UErrorCode *status)
    1.89 +{
    1.90 +    MOZ_ASSUME_UNREACHABLE("uenum_count: Intl API disabled");
    1.91 +}
    1.92 +
    1.93 +static const char *
    1.94 +uenum_next(UEnumeration *en, int32_t *resultLength, UErrorCode *status)
    1.95 +{
    1.96 +    MOZ_ASSUME_UNREACHABLE("uenum_next: Intl API disabled");
    1.97 +}
    1.98 +
    1.99 +static void
   1.100 +uenum_close(UEnumeration *en)
   1.101 +{
   1.102 +    MOZ_ASSUME_UNREACHABLE("uenum_close: Intl API disabled");
   1.103 +}
   1.104 +
   1.105 +struct UCollator;
   1.106 +
   1.107 +enum UColAttribute {
   1.108 +     UCOL_ALTERNATE_HANDLING,
   1.109 +     UCOL_CASE_FIRST,
   1.110 +     UCOL_CASE_LEVEL,
   1.111 +     UCOL_NORMALIZATION_MODE,
   1.112 +     UCOL_STRENGTH,
   1.113 +     UCOL_NUMERIC_COLLATION,
   1.114 +};
   1.115 +
   1.116 +enum UColAttributeValue {
   1.117 +  UCOL_DEFAULT = -1,
   1.118 +  UCOL_PRIMARY = 0,
   1.119 +  UCOL_SECONDARY = 1,
   1.120 +  UCOL_TERTIARY = 2,
   1.121 +  UCOL_OFF = 16,
   1.122 +  UCOL_ON = 17,
   1.123 +  UCOL_SHIFTED = 20,
   1.124 +  UCOL_LOWER_FIRST = 24,
   1.125 +  UCOL_UPPER_FIRST = 25,
   1.126 +};
   1.127 +
   1.128 +enum UCollationResult {
   1.129 +  UCOL_EQUAL = 0,
   1.130 +  UCOL_GREATER = 1,
   1.131 +  UCOL_LESS = -1
   1.132 +};
   1.133 +
   1.134 +static int32_t
   1.135 +ucol_countAvailable(void)
   1.136 +{
   1.137 +    MOZ_ASSUME_UNREACHABLE("ucol_countAvailable: Intl API disabled");
   1.138 +}
   1.139 +
   1.140 +static const char *
   1.141 +ucol_getAvailable(int32_t localeIndex)
   1.142 +{
   1.143 +    MOZ_ASSUME_UNREACHABLE("ucol_getAvailable: Intl API disabled");
   1.144 +}
   1.145 +
   1.146 +static UCollator *
   1.147 +ucol_open(const char *loc, UErrorCode *status)
   1.148 +{
   1.149 +    MOZ_ASSUME_UNREACHABLE("ucol_open: Intl API disabled");
   1.150 +}
   1.151 +
   1.152 +static void
   1.153 +ucol_setAttribute(UCollator *coll, UColAttribute attr, UColAttributeValue value, UErrorCode *status)
   1.154 +{
   1.155 +    MOZ_ASSUME_UNREACHABLE("ucol_setAttribute: Intl API disabled");
   1.156 +}
   1.157 +
   1.158 +static UCollationResult
   1.159 +ucol_strcoll(const UCollator *coll, const UChar *source, int32_t sourceLength,
   1.160 +             const UChar *target, int32_t targetLength)
   1.161 +{
   1.162 +    MOZ_ASSUME_UNREACHABLE("ucol_strcoll: Intl API disabled");
   1.163 +}
   1.164 +
   1.165 +static void
   1.166 +ucol_close(UCollator *coll)
   1.167 +{
   1.168 +    MOZ_ASSUME_UNREACHABLE("ucol_close: Intl API disabled");
   1.169 +}
   1.170 +
   1.171 +static UEnumeration *
   1.172 +ucol_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed,
   1.173 +                               UErrorCode *status)
   1.174 +{
   1.175 +    MOZ_ASSUME_UNREACHABLE("ucol_getKeywordValuesForLocale: Intl API disabled");
   1.176 +}
   1.177 +
   1.178 +struct UParseError;
   1.179 +struct UFieldPosition;
   1.180 +typedef void *UNumberFormat;
   1.181 +
   1.182 +enum UNumberFormatStyle {
   1.183 +    UNUM_DECIMAL = 1,
   1.184 +    UNUM_CURRENCY,
   1.185 +    UNUM_PERCENT,
   1.186 +    UNUM_CURRENCY_ISO,
   1.187 +    UNUM_CURRENCY_PLURAL,
   1.188 +};
   1.189 +
   1.190 +enum UNumberFormatRoundingMode {
   1.191 +    UNUM_ROUND_HALFUP,
   1.192 +};
   1.193 +
   1.194 +enum UNumberFormatAttribute {
   1.195 +  UNUM_GROUPING_USED,
   1.196 +  UNUM_MIN_INTEGER_DIGITS,
   1.197 +  UNUM_MAX_FRACTION_DIGITS,
   1.198 +  UNUM_MIN_FRACTION_DIGITS,
   1.199 +  UNUM_ROUNDING_MODE,
   1.200 +  UNUM_SIGNIFICANT_DIGITS_USED,
   1.201 +  UNUM_MIN_SIGNIFICANT_DIGITS,
   1.202 +  UNUM_MAX_SIGNIFICANT_DIGITS,
   1.203 +};
   1.204 +
   1.205 +enum UNumberFormatTextAttribute {
   1.206 +  UNUM_CURRENCY_CODE,
   1.207 +};
   1.208 +
   1.209 +static int32_t
   1.210 +unum_countAvailable(void)
   1.211 +{
   1.212 +    MOZ_ASSUME_UNREACHABLE("unum_countAvailable: Intl API disabled");
   1.213 +}
   1.214 +
   1.215 +static const char *
   1.216 +unum_getAvailable(int32_t localeIndex)
   1.217 +{
   1.218 +    MOZ_ASSUME_UNREACHABLE("unum_getAvailable: Intl API disabled");
   1.219 +}
   1.220 +
   1.221 +static UNumberFormat *
   1.222 +unum_open(UNumberFormatStyle style, const UChar *pattern, int32_t patternLength,
   1.223 +          const char *locale, UParseError *parseErr, UErrorCode *status)
   1.224 +{
   1.225 +    MOZ_ASSUME_UNREACHABLE("unum_open: Intl API disabled");
   1.226 +}
   1.227 +
   1.228 +static void
   1.229 +unum_setAttribute(UNumberFormat *fmt, UNumberFormatAttribute  attr, int32_t newValue)
   1.230 +{
   1.231 +    MOZ_ASSUME_UNREACHABLE("unum_setAttribute: Intl API disabled");
   1.232 +}
   1.233 +
   1.234 +static int32_t
   1.235 +unum_formatDouble(const UNumberFormat *fmt, double number, UChar *result,
   1.236 +                  int32_t resultLength, UFieldPosition *pos, UErrorCode *status)
   1.237 +{
   1.238 +    MOZ_ASSUME_UNREACHABLE("unum_formatDouble: Intl API disabled");
   1.239 +}
   1.240 +
   1.241 +static void
   1.242 +unum_close(UNumberFormat *fmt)
   1.243 +{
   1.244 +    MOZ_ASSUME_UNREACHABLE("unum_close: Intl API disabled");
   1.245 +}
   1.246 +
   1.247 +static void
   1.248 +unum_setTextAttribute(UNumberFormat *fmt, UNumberFormatTextAttribute tag, const UChar *newValue,
   1.249 +                      int32_t newValueLength, UErrorCode *status)
   1.250 +{
   1.251 +    MOZ_ASSUME_UNREACHABLE("unum_setTextAttribute: Intl API disabled");
   1.252 +}
   1.253 +
   1.254 +class Locale {
   1.255 +  public:
   1.256 +    Locale(const char *language, const char *country = 0, const char *variant = 0,
   1.257 +           const char *keywordsAndValues = 0);
   1.258 +};
   1.259 +
   1.260 +Locale::Locale(const char *language, const char *country, const char *variant,
   1.261 +               const char *keywordsAndValues)
   1.262 +{
   1.263 +    MOZ_ASSUME_UNREACHABLE("Locale::Locale: Intl API disabled");
   1.264 +}
   1.265 +
   1.266 +class NumberingSystem {
   1.267 +  public:
   1.268 +    static NumberingSystem *createInstance(const Locale &inLocale, UErrorCode &status);
   1.269 +    const char *getName();
   1.270 +};
   1.271 +
   1.272 +NumberingSystem *
   1.273 +NumberingSystem::createInstance(const Locale &inLocale, UErrorCode &status)
   1.274 +{
   1.275 +    MOZ_ASSUME_UNREACHABLE("NumberingSystem::createInstance: Intl API disabled");
   1.276 +}
   1.277 +
   1.278 +const char *
   1.279 +NumberingSystem::getName()
   1.280 +{
   1.281 +    MOZ_ASSUME_UNREACHABLE("NumberingSystem::getName: Intl API disabled");
   1.282 +}
   1.283 +
   1.284 +typedef void *UCalendar;
   1.285 +
   1.286 +enum UCalendarType {
   1.287 +  UCAL_TRADITIONAL,
   1.288 +  UCAL_DEFAULT = UCAL_TRADITIONAL,
   1.289 +  UCAL_GREGORIAN
   1.290 +};
   1.291 +
   1.292 +static UCalendar *
   1.293 +ucal_open(const UChar *zoneID, int32_t len, const char *locale,
   1.294 +          UCalendarType type, UErrorCode *status)
   1.295 +{
   1.296 +    MOZ_ASSUME_UNREACHABLE("ucal_open: Intl API disabled");
   1.297 +}
   1.298 +
   1.299 +static const char *
   1.300 +ucal_getType(const UCalendar *cal, UErrorCode *status)
   1.301 +{
   1.302 +    MOZ_ASSUME_UNREACHABLE("ucal_getType: Intl API disabled");
   1.303 +}
   1.304 +
   1.305 +static UEnumeration *
   1.306 +ucal_getKeywordValuesForLocale(const char *key, const char *locale,
   1.307 +                               UBool commonlyUsed, UErrorCode *status)
   1.308 +{
   1.309 +    MOZ_ASSUME_UNREACHABLE("ucal_getKeywordValuesForLocale: Intl API disabled");
   1.310 +}
   1.311 +
   1.312 +static void
   1.313 +ucal_close(UCalendar *cal)
   1.314 +{
   1.315 +    MOZ_ASSUME_UNREACHABLE("ucal_close: Intl API disabled");
   1.316 +}
   1.317 +
   1.318 +typedef void *UDateTimePatternGenerator;
   1.319 +
   1.320 +static UDateTimePatternGenerator *
   1.321 +udatpg_open(const char *locale, UErrorCode *pErrorCode)
   1.322 +{
   1.323 +    MOZ_ASSUME_UNREACHABLE("udatpg_open: Intl API disabled");
   1.324 +}
   1.325 +
   1.326 +static int32_t
   1.327 +udatpg_getBestPattern(UDateTimePatternGenerator *dtpg, const UChar *skeleton,
   1.328 +                      int32_t length, UChar *bestPattern, int32_t capacity,
   1.329 +                      UErrorCode *pErrorCode)
   1.330 +{
   1.331 +    MOZ_ASSUME_UNREACHABLE("udatpg_getBestPattern: Intl API disabled");
   1.332 +}
   1.333 +
   1.334 +static void
   1.335 +udatpg_close(UDateTimePatternGenerator *dtpg)
   1.336 +{
   1.337 +    MOZ_ASSUME_UNREACHABLE("udatpg_close: Intl API disabled");
   1.338 +}
   1.339 +
   1.340 +typedef void *UCalendar;
   1.341 +typedef void *UDateFormat;
   1.342 +
   1.343 +enum UDateFormatStyle {
   1.344 +    UDAT_PATTERN = -2,
   1.345 +    UDAT_IGNORE = UDAT_PATTERN
   1.346 +};
   1.347 +
   1.348 +static int32_t
   1.349 +udat_countAvailable(void)
   1.350 +{
   1.351 +    MOZ_ASSUME_UNREACHABLE("udat_countAvailable: Intl API disabled");
   1.352 +}
   1.353 +
   1.354 +static const char *
   1.355 +udat_getAvailable(int32_t localeIndex)
   1.356 +{
   1.357 +    MOZ_ASSUME_UNREACHABLE("udat_getAvailable: Intl API disabled");
   1.358 +}
   1.359 +
   1.360 +static UDateFormat *
   1.361 +udat_open(UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const char *locale,
   1.362 +          const UChar *tzID, int32_t tzIDLength, const UChar *pattern,
   1.363 +          int32_t patternLength, UErrorCode *status)
   1.364 +{
   1.365 +    MOZ_ASSUME_UNREACHABLE("udat_open: Intl API disabled");
   1.366 +}
   1.367 +
   1.368 +static const UCalendar *
   1.369 +udat_getCalendar(const UDateFormat *fmt)
   1.370 +{
   1.371 +    MOZ_ASSUME_UNREACHABLE("udat_getCalendar: Intl API disabled");
   1.372 +}
   1.373 +
   1.374 +static void
   1.375 +ucal_setGregorianChange(UCalendar *cal, UDate date, UErrorCode *pErrorCode)
   1.376 +{
   1.377 +    MOZ_ASSUME_UNREACHABLE("ucal_setGregorianChange: Intl API disabled");
   1.378 +}
   1.379 +
   1.380 +static int32_t
   1.381 +udat_format(const UDateFormat *format, UDate dateToFormat, UChar *result,
   1.382 +            int32_t resultLength, UFieldPosition *position, UErrorCode *status)
   1.383 +{
   1.384 +    MOZ_ASSUME_UNREACHABLE("udat_format: Intl API disabled");
   1.385 +}
   1.386 +
   1.387 +static void
   1.388 +udat_close(UDateFormat *format)
   1.389 +{
   1.390 +    MOZ_ASSUME_UNREACHABLE("udat_close: Intl API disabled");
   1.391 +}
   1.392 +
   1.393 +#endif
   1.394 +
   1.395 +
   1.396 +/******************** Common to Intl constructors ********************/
   1.397 +
   1.398 +static bool
   1.399 +IntlInitialize(JSContext *cx, HandleObject obj, Handle<PropertyName*> initializer,
   1.400 +               HandleValue locales, HandleValue options)
   1.401 +{
   1.402 +    RootedValue initializerValue(cx);
   1.403 +    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), initializer, &initializerValue))
   1.404 +        return false;
   1.405 +    JS_ASSERT(initializerValue.isObject());
   1.406 +    JS_ASSERT(initializerValue.toObject().is<JSFunction>());
   1.407 +
   1.408 +    InvokeArgs args(cx);
   1.409 +    if (!args.init(3))
   1.410 +        return false;
   1.411 +
   1.412 +    args.setCallee(initializerValue);
   1.413 +    args.setThis(NullValue());
   1.414 +    args[0].setObject(*obj);
   1.415 +    args[1].set(locales);
   1.416 +    args[2].set(options);
   1.417 +
   1.418 +    return Invoke(cx, args);
   1.419 +}
   1.420 +
   1.421 +// CountAvailable and GetAvailable describe the signatures used for ICU API
   1.422 +// to determine available locales for various functionality.
   1.423 +typedef int32_t
   1.424 +(* CountAvailable)(void);
   1.425 +
   1.426 +typedef const char *
   1.427 +(* GetAvailable)(int32_t localeIndex);
   1.428 +
   1.429 +static bool
   1.430 +intl_availableLocales(JSContext *cx, CountAvailable countAvailable,
   1.431 +                      GetAvailable getAvailable, MutableHandleValue result)
   1.432 +{
   1.433 +    RootedObject locales(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, nullptr));
   1.434 +    if (!locales)
   1.435 +        return false;
   1.436 +
   1.437 +#if ENABLE_INTL_API
   1.438 +    uint32_t count = countAvailable();
   1.439 +    RootedValue t(cx, BooleanValue(true));
   1.440 +    for (uint32_t i = 0; i < count; i++) {
   1.441 +        const char *locale = getAvailable(i);
   1.442 +        ScopedJSFreePtr<char> lang(JS_strdup(cx, locale));
   1.443 +        if (!lang)
   1.444 +            return false;
   1.445 +        char *p;
   1.446 +        while ((p = strchr(lang, '_')))
   1.447 +            *p = '-';
   1.448 +        RootedAtom a(cx, Atomize(cx, lang, strlen(lang)));
   1.449 +        if (!a)
   1.450 +            return false;
   1.451 +        if (!JSObject::defineProperty(cx, locales, a->asPropertyName(), t,
   1.452 +                                      JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
   1.453 +        {
   1.454 +            return false;
   1.455 +        }
   1.456 +    }
   1.457 +#endif
   1.458 +    result.setObject(*locales);
   1.459 +    return true;
   1.460 +}
   1.461 +
   1.462 +/**
   1.463 + * Returns the object holding the internal properties for obj.
   1.464 + */
   1.465 +static bool
   1.466 +GetInternals(JSContext *cx, HandleObject obj, MutableHandleObject internals)
   1.467 +{
   1.468 +    RootedValue getInternalsValue(cx);
   1.469 +    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals, &getInternalsValue))
   1.470 +        return false;
   1.471 +    JS_ASSERT(getInternalsValue.isObject());
   1.472 +    JS_ASSERT(getInternalsValue.toObject().is<JSFunction>());
   1.473 +
   1.474 +    InvokeArgs args(cx);
   1.475 +    if (!args.init(1))
   1.476 +        return false;
   1.477 +
   1.478 +    args.setCallee(getInternalsValue);
   1.479 +    args.setThis(NullValue());
   1.480 +    args[0].setObject(*obj);
   1.481 +
   1.482 +    if (!Invoke(cx, args))
   1.483 +        return false;
   1.484 +    internals.set(&args.rval().toObject());
   1.485 +    return true;
   1.486 +}
   1.487 +
   1.488 +static bool
   1.489 +equal(const char *s1, const char *s2)
   1.490 +{
   1.491 +    return !strcmp(s1, s2);
   1.492 +}
   1.493 +
   1.494 +static bool
   1.495 +equal(JSAutoByteString &s1, const char *s2)
   1.496 +{
   1.497 +    return !strcmp(s1.ptr(), s2);
   1.498 +}
   1.499 +
   1.500 +static const char *
   1.501 +icuLocale(const char *locale)
   1.502 +{
   1.503 +    if (equal(locale, "und"))
   1.504 +        return ""; // ICU root locale
   1.505 +    return locale;
   1.506 +}
   1.507 +
   1.508 +// Simple RAII for ICU objects. MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE
   1.509 +// unfortunately doesn't work because of namespace incompatibilities
   1.510 +// (TypeSpecificDelete cannot be in icu and mozilla at the same time)
   1.511 +// and because ICU declares both UNumberFormat and UDateTimePatternGenerator
   1.512 +// as void*.
   1.513 +template <typename T>
   1.514 +class ScopedICUObject
   1.515 +{
   1.516 +    T *ptr_;
   1.517 +    void (* deleter_)(T*);
   1.518 +
   1.519 +  public:
   1.520 +    ScopedICUObject(T *ptr, void (*deleter)(T*))
   1.521 +      : ptr_(ptr),
   1.522 +        deleter_(deleter)
   1.523 +    {}
   1.524 +
   1.525 +    ~ScopedICUObject() {
   1.526 +        if (ptr_)
   1.527 +            deleter_(ptr_);
   1.528 +    }
   1.529 +
   1.530 +    // In cases where an object should be deleted on abnormal exits,
   1.531 +    // but returned to the caller if everything goes well, call forget()
   1.532 +    // to transfer the object just before returning.
   1.533 +    T *forget() {
   1.534 +        T *tmp = ptr_;
   1.535 +        ptr_ = nullptr;
   1.536 +        return tmp;
   1.537 +    }
   1.538 +};
   1.539 +
   1.540 +// As a small optimization (not important for correctness), this is the inline
   1.541 +// capacity of a StringBuffer.
   1.542 +static const size_t INITIAL_STRING_BUFFER_SIZE = 32;
   1.543 +
   1.544 +
   1.545 +/******************** Collator ********************/
   1.546 +
   1.547 +static void collator_finalize(FreeOp *fop, JSObject *obj);
   1.548 +
   1.549 +static const uint32_t UCOLLATOR_SLOT = 0;
   1.550 +static const uint32_t COLLATOR_SLOTS_COUNT = 1;
   1.551 +
   1.552 +static const Class CollatorClass = {
   1.553 +    js_Object_str,
   1.554 +    JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT),
   1.555 +    JS_PropertyStub,         /* addProperty */
   1.556 +    JS_DeletePropertyStub,   /* delProperty */
   1.557 +    JS_PropertyStub,         /* getProperty */
   1.558 +    JS_StrictPropertyStub,   /* setProperty */
   1.559 +    JS_EnumerateStub,
   1.560 +    JS_ResolveStub,
   1.561 +    JS_ConvertStub,
   1.562 +    collator_finalize
   1.563 +};
   1.564 +
   1.565 +#if JS_HAS_TOSOURCE
   1.566 +static bool
   1.567 +collator_toSource(JSContext *cx, unsigned argc, Value *vp)
   1.568 +{
   1.569 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.570 +    args.rval().setString(cx->names().Collator);
   1.571 +    return true;
   1.572 +}
   1.573 +#endif
   1.574 +
   1.575 +static const JSFunctionSpec collator_static_methods[] = {
   1.576 +    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_Collator_supportedLocalesOf", 1, 0),
   1.577 +    JS_FS_END
   1.578 +};
   1.579 +
   1.580 +static const JSFunctionSpec collator_methods[] = {
   1.581 +    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_Collator_resolvedOptions", 0, 0),
   1.582 +#if JS_HAS_TOSOURCE
   1.583 +    JS_FN(js_toSource_str, collator_toSource, 0, 0),
   1.584 +#endif
   1.585 +    JS_FS_END
   1.586 +};
   1.587 +
   1.588 +/**
   1.589 + * Collator constructor.
   1.590 + * Spec: ECMAScript Internationalization API Specification, 10.1
   1.591 + */
   1.592 +static bool
   1.593 +Collator(JSContext *cx, CallArgs args, bool construct)
   1.594 +{
   1.595 +    RootedObject obj(cx);
   1.596 +
   1.597 +    if (!construct) {
   1.598 +        // 10.1.2.1 step 3
   1.599 +        JSObject *intl = cx->global()->getOrCreateIntlObject(cx);
   1.600 +        if (!intl)
   1.601 +            return false;
   1.602 +        RootedValue self(cx, args.thisv());
   1.603 +        if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
   1.604 +            // 10.1.2.1 step 4
   1.605 +            obj = ToObject(cx, self);
   1.606 +            if (!obj)
   1.607 +                return false;
   1.608 +
   1.609 +            // 10.1.2.1 step 5
   1.610 +            bool extensible;
   1.611 +            if (!JSObject::isExtensible(cx, obj, &extensible))
   1.612 +                return false;
   1.613 +            if (!extensible)
   1.614 +                return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
   1.615 +        } else {
   1.616 +            // 10.1.2.1 step 3.a
   1.617 +            construct = true;
   1.618 +        }
   1.619 +    }
   1.620 +    if (construct) {
   1.621 +        // 10.1.3.1 paragraph 2
   1.622 +        RootedObject proto(cx, cx->global()->getOrCreateCollatorPrototype(cx));
   1.623 +        if (!proto)
   1.624 +            return false;
   1.625 +        obj = NewObjectWithGivenProto(cx, &CollatorClass, proto, cx->global());
   1.626 +        if (!obj)
   1.627 +            return false;
   1.628 +
   1.629 +        obj->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
   1.630 +    }
   1.631 +
   1.632 +    // 10.1.2.1 steps 1 and 2; 10.1.3.1 steps 1 and 2
   1.633 +    RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
   1.634 +    RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
   1.635 +
   1.636 +    // 10.1.2.1 step 6; 10.1.3.1 step 3
   1.637 +    if (!IntlInitialize(cx, obj, cx->names().InitializeCollator, locales, options))
   1.638 +        return false;
   1.639 +
   1.640 +    // 10.1.2.1 steps 3.a and 7
   1.641 +    args.rval().setObject(*obj);
   1.642 +    return true;
   1.643 +}
   1.644 +
   1.645 +static bool
   1.646 +Collator(JSContext *cx, unsigned argc, Value *vp)
   1.647 +{
   1.648 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.649 +    return Collator(cx, args, args.isConstructing());
   1.650 +}
   1.651 +
   1.652 +bool
   1.653 +js::intl_Collator(JSContext *cx, unsigned argc, Value *vp)
   1.654 +{
   1.655 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.656 +    JS_ASSERT(args.length() == 2);
   1.657 +    // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
   1.658 +    // be used with "new", but it still has to be treated as a constructor.
   1.659 +    return Collator(cx, args, true);
   1.660 +}
   1.661 +
   1.662 +static void
   1.663 +collator_finalize(FreeOp *fop, JSObject *obj)
   1.664 +{
   1.665 +    UCollator *coll = static_cast<UCollator*>(obj->getReservedSlot(UCOLLATOR_SLOT).toPrivate());
   1.666 +    if (coll)
   1.667 +        ucol_close(coll);
   1.668 +}
   1.669 +
   1.670 +static JSObject *
   1.671 +InitCollatorClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
   1.672 +{
   1.673 +    RootedFunction ctor(cx, global->createConstructor(cx, &Collator, cx->names().Collator, 0));
   1.674 +    if (!ctor)
   1.675 +        return nullptr;
   1.676 +
   1.677 +    RootedObject proto(cx, global->as<GlobalObject>().getOrCreateCollatorPrototype(cx));
   1.678 +    if (!proto)
   1.679 +        return nullptr;
   1.680 +    if (!LinkConstructorAndPrototype(cx, ctor, proto))
   1.681 +        return nullptr;
   1.682 +
   1.683 +    // 10.2.2
   1.684 +    if (!JS_DefineFunctions(cx, ctor, collator_static_methods))
   1.685 +        return nullptr;
   1.686 +
   1.687 +    // 10.3.2 and 10.3.3
   1.688 +    if (!JS_DefineFunctions(cx, proto, collator_methods))
   1.689 +        return nullptr;
   1.690 +
   1.691 +    /*
   1.692 +     * Install the getter for Collator.prototype.compare, which returns a bound
   1.693 +     * comparison function for the specified Collator object (suitable for
   1.694 +     * passing to methods like Array.prototype.sort).
   1.695 +     */
   1.696 +    RootedValue getter(cx);
   1.697 +    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CollatorCompareGet, &getter))
   1.698 +        return nullptr;
   1.699 +    if (!JSObject::defineProperty(cx, proto, cx->names().compare, UndefinedHandleValue,
   1.700 +                                  JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
   1.701 +                                  nullptr, JSPROP_GETTER | JSPROP_SHARED))
   1.702 +    {
   1.703 +        return nullptr;
   1.704 +    }
   1.705 +
   1.706 +    // 10.2.1 and 10.3
   1.707 +    if (!IntlInitialize(cx, proto, cx->names().InitializeCollator, UndefinedHandleValue,
   1.708 +                        UndefinedHandleValue))
   1.709 +    {
   1.710 +        return nullptr;
   1.711 +    }
   1.712 +
   1.713 +    // 8.1
   1.714 +    RootedValue ctorValue(cx, ObjectValue(*ctor));
   1.715 +    if (!JSObject::defineProperty(cx, Intl, cx->names().Collator, ctorValue,
   1.716 +                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
   1.717 +    {
   1.718 +        return nullptr;
   1.719 +    }
   1.720 +
   1.721 +    return ctor;
   1.722 +}
   1.723 +
   1.724 +bool
   1.725 +GlobalObject::initCollatorProto(JSContext *cx, Handle<GlobalObject*> global)
   1.726 +{
   1.727 +    RootedObject proto(cx, global->createBlankPrototype(cx, &CollatorClass));
   1.728 +    if (!proto)
   1.729 +        return false;
   1.730 +    proto->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
   1.731 +    global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*proto));
   1.732 +    return true;
   1.733 +}
   1.734 +
   1.735 +bool
   1.736 +js::intl_Collator_availableLocales(JSContext *cx, unsigned argc, Value *vp)
   1.737 +{
   1.738 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.739 +    JS_ASSERT(args.length() == 0);
   1.740 +
   1.741 +    RootedValue result(cx);
   1.742 +    if (!intl_availableLocales(cx, ucol_countAvailable, ucol_getAvailable, &result))
   1.743 +        return false;
   1.744 +    args.rval().set(result);
   1.745 +    return true;
   1.746 +}
   1.747 +
   1.748 +bool
   1.749 +js::intl_availableCollations(JSContext *cx, unsigned argc, Value *vp)
   1.750 +{
   1.751 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.752 +    JS_ASSERT(args.length() == 1);
   1.753 +    JS_ASSERT(args[0].isString());
   1.754 +
   1.755 +    JSAutoByteString locale(cx, args[0].toString());
   1.756 +    if (!locale)
   1.757 +        return false;
   1.758 +    UErrorCode status = U_ZERO_ERROR;
   1.759 +    UEnumeration *values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
   1.760 +    if (U_FAILURE(status)) {
   1.761 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   1.762 +        return false;
   1.763 +    }
   1.764 +    ScopedICUObject<UEnumeration> toClose(values, uenum_close);
   1.765 +
   1.766 +    uint32_t count = uenum_count(values, &status);
   1.767 +    if (U_FAILURE(status)) {
   1.768 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   1.769 +        return false;
   1.770 +    }
   1.771 +
   1.772 +    RootedObject collations(cx, NewDenseEmptyArray(cx));
   1.773 +    if (!collations)
   1.774 +        return false;
   1.775 +
   1.776 +    uint32_t index = 0;
   1.777 +    for (uint32_t i = 0; i < count; i++) {
   1.778 +        const char *collation = uenum_next(values, nullptr, &status);
   1.779 +        if (U_FAILURE(status)) {
   1.780 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   1.781 +            return false;
   1.782 +        }
   1.783 +
   1.784 +        // Per ECMA-402, 10.2.3, we don't include standard and search:
   1.785 +        // "The values 'standard' and 'search' must not be used as elements in
   1.786 +        // any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co
   1.787 +        // array."
   1.788 +        if (equal(collation, "standard") || equal(collation, "search"))
   1.789 +            continue;
   1.790 +
   1.791 +        // ICU returns old-style keyword values; map them to BCP 47 equivalents
   1.792 +        // (see http://bugs.icu-project.org/trac/ticket/9620).
   1.793 +        if (equal(collation, "dictionary"))
   1.794 +            collation = "dict";
   1.795 +        else if (equal(collation, "gb2312han"))
   1.796 +            collation = "gb2312";
   1.797 +        else if (equal(collation, "phonebook"))
   1.798 +            collation = "phonebk";
   1.799 +        else if (equal(collation, "traditional"))
   1.800 +            collation = "trad";
   1.801 +
   1.802 +        RootedString jscollation(cx, JS_NewStringCopyZ(cx, collation));
   1.803 +        if (!jscollation)
   1.804 +            return false;
   1.805 +        RootedValue element(cx, StringValue(jscollation));
   1.806 +        if (!JSObject::defineElement(cx, collations, index++, element))
   1.807 +            return false;
   1.808 +    }
   1.809 +
   1.810 +    args.rval().setObject(*collations);
   1.811 +    return true;
   1.812 +}
   1.813 +
   1.814 +/**
   1.815 + * Returns a new UCollator with the locale and collation options
   1.816 + * of the given Collator.
   1.817 + */
   1.818 +static UCollator *
   1.819 +NewUCollator(JSContext *cx, HandleObject collator)
   1.820 +{
   1.821 +    RootedValue value(cx);
   1.822 +
   1.823 +    RootedObject internals(cx);
   1.824 +    if (!GetInternals(cx, collator, &internals))
   1.825 +        return nullptr;
   1.826 +
   1.827 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().locale, &value))
   1.828 +        return nullptr;
   1.829 +    JSAutoByteString locale(cx, value.toString());
   1.830 +    if (!locale)
   1.831 +        return nullptr;
   1.832 +
   1.833 +    // UCollator options with default values.
   1.834 +    UColAttributeValue uStrength = UCOL_DEFAULT;
   1.835 +    UColAttributeValue uCaseLevel = UCOL_OFF;
   1.836 +    UColAttributeValue uAlternate = UCOL_DEFAULT;
   1.837 +    UColAttributeValue uNumeric = UCOL_OFF;
   1.838 +    // Normalization is always on to meet the canonical equivalence requirement.
   1.839 +    UColAttributeValue uNormalization = UCOL_ON;
   1.840 +    UColAttributeValue uCaseFirst = UCOL_DEFAULT;
   1.841 +
   1.842 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().usage, &value))
   1.843 +        return nullptr;
   1.844 +    JSAutoByteString usage(cx, value.toString());
   1.845 +    if (!usage)
   1.846 +        return nullptr;
   1.847 +    if (equal(usage, "search")) {
   1.848 +        // ICU expects search as a Unicode locale extension on locale.
   1.849 +        // Unicode locale extensions must occur before private use extensions.
   1.850 +        const char *oldLocale = locale.ptr();
   1.851 +        const char *p;
   1.852 +        size_t index;
   1.853 +        size_t localeLen = strlen(oldLocale);
   1.854 +        if ((p = strstr(oldLocale, "-x-")))
   1.855 +            index = p - oldLocale;
   1.856 +        else
   1.857 +            index = localeLen;
   1.858 +
   1.859 +        const char *insert;
   1.860 +        if ((p = strstr(oldLocale, "-u-")) && static_cast<size_t>(p - oldLocale) < index) {
   1.861 +            index = p - oldLocale + 2;
   1.862 +            insert = "-co-search";
   1.863 +        } else {
   1.864 +            insert = "-u-co-search";
   1.865 +        }
   1.866 +        size_t insertLen = strlen(insert);
   1.867 +        char *newLocale = cx->pod_malloc<char>(localeLen + insertLen + 1);
   1.868 +        if (!newLocale)
   1.869 +            return nullptr;
   1.870 +        memcpy(newLocale, oldLocale, index);
   1.871 +        memcpy(newLocale + index, insert, insertLen);
   1.872 +        memcpy(newLocale + index + insertLen, oldLocale + index, localeLen - index + 1); // '\0'
   1.873 +        locale.clear();
   1.874 +        locale.initBytes(newLocale);
   1.875 +    }
   1.876 +
   1.877 +    // We don't need to look at the collation property - it can only be set
   1.878 +    // via the Unicode locale extension and is therefore already set on
   1.879 +    // locale.
   1.880 +
   1.881 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().sensitivity, &value))
   1.882 +        return nullptr;
   1.883 +    JSAutoByteString sensitivity(cx, value.toString());
   1.884 +    if (!sensitivity)
   1.885 +        return nullptr;
   1.886 +    if (equal(sensitivity, "base")) {
   1.887 +        uStrength = UCOL_PRIMARY;
   1.888 +    } else if (equal(sensitivity, "accent")) {
   1.889 +        uStrength = UCOL_SECONDARY;
   1.890 +    } else if (equal(sensitivity, "case")) {
   1.891 +        uStrength = UCOL_PRIMARY;
   1.892 +        uCaseLevel = UCOL_ON;
   1.893 +    } else {
   1.894 +        JS_ASSERT(equal(sensitivity, "variant"));
   1.895 +        uStrength = UCOL_TERTIARY;
   1.896 +    }
   1.897 +
   1.898 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().ignorePunctuation, &value))
   1.899 +        return nullptr;
   1.900 +    // According to the ICU team, UCOL_SHIFTED causes punctuation to be
   1.901 +    // ignored. Looking at Unicode Technical Report 35, Unicode Locale Data
   1.902 +    // Markup Language, "shifted" causes whitespace and punctuation to be
   1.903 +    // ignored - that's a bit more than asked for, but there's no way to get
   1.904 +    // less.
   1.905 +    if (value.toBoolean())
   1.906 +        uAlternate = UCOL_SHIFTED;
   1.907 +
   1.908 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().numeric, &value))
   1.909 +        return nullptr;
   1.910 +    if (!value.isUndefined() && value.toBoolean())
   1.911 +        uNumeric = UCOL_ON;
   1.912 +
   1.913 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().caseFirst, &value))
   1.914 +        return nullptr;
   1.915 +    if (!value.isUndefined()) {
   1.916 +        JSAutoByteString caseFirst(cx, value.toString());
   1.917 +        if (!caseFirst)
   1.918 +            return nullptr;
   1.919 +        if (equal(caseFirst, "upper"))
   1.920 +            uCaseFirst = UCOL_UPPER_FIRST;
   1.921 +        else if (equal(caseFirst, "lower"))
   1.922 +            uCaseFirst = UCOL_LOWER_FIRST;
   1.923 +        else
   1.924 +            JS_ASSERT(equal(caseFirst, "false"));
   1.925 +    }
   1.926 +
   1.927 +    UErrorCode status = U_ZERO_ERROR;
   1.928 +    UCollator *coll = ucol_open(icuLocale(locale.ptr()), &status);
   1.929 +    if (U_FAILURE(status)) {
   1.930 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   1.931 +        return nullptr;
   1.932 +    }
   1.933 +
   1.934 +    ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
   1.935 +    ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
   1.936 +    ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
   1.937 +    ucol_setAttribute(coll, UCOL_NUMERIC_COLLATION, uNumeric, &status);
   1.938 +    ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, uNormalization, &status);
   1.939 +    ucol_setAttribute(coll, UCOL_CASE_FIRST, uCaseFirst, &status);
   1.940 +    if (U_FAILURE(status)) {
   1.941 +        ucol_close(coll);
   1.942 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
   1.943 +        return nullptr;
   1.944 +    }
   1.945 +
   1.946 +    return coll;
   1.947 +}
   1.948 +
   1.949 +static bool
   1.950 +intl_CompareStrings(JSContext *cx, UCollator *coll, HandleString str1, HandleString str2, MutableHandleValue result)
   1.951 +{
   1.952 +    JS_ASSERT(str1);
   1.953 +    JS_ASSERT(str2);
   1.954 +
   1.955 +    if (str1 == str2) {
   1.956 +        result.setInt32(0);
   1.957 +        return true;
   1.958 +    }
   1.959 +
   1.960 +    size_t length1 = str1->length();
   1.961 +    const jschar *chars1 = str1->getChars(cx);
   1.962 +    if (!chars1)
   1.963 +        return false;
   1.964 +    size_t length2 = str2->length();
   1.965 +    const jschar *chars2 = str2->getChars(cx);
   1.966 +    if (!chars2)
   1.967 +        return false;
   1.968 +
   1.969 +    UCollationResult uresult = ucol_strcoll(coll, JSCharToUChar(chars1),
   1.970 +                                            length1, JSCharToUChar(chars2),
   1.971 +                                            length2);
   1.972 +
   1.973 +    int32_t res;
   1.974 +    switch (uresult) {
   1.975 +        case UCOL_LESS: res = -1; break;
   1.976 +        case UCOL_EQUAL: res = 0; break;
   1.977 +        case UCOL_GREATER: res = 1; break;
   1.978 +        default: MOZ_ASSUME_UNREACHABLE("ucol_strcoll returned bad UCollationResult");
   1.979 +    }
   1.980 +    result.setInt32(res);
   1.981 +    return true;
   1.982 +}
   1.983 +
   1.984 +bool
   1.985 +js::intl_CompareStrings(JSContext *cx, unsigned argc, Value *vp)
   1.986 +{
   1.987 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.988 +    JS_ASSERT(args.length() == 3);
   1.989 +    JS_ASSERT(args[0].isObject());
   1.990 +    JS_ASSERT(args[1].isString());
   1.991 +    JS_ASSERT(args[2].isString());
   1.992 +
   1.993 +    RootedObject collator(cx, &args[0].toObject());
   1.994 +
   1.995 +    // Obtain a UCollator object, cached if possible.
   1.996 +    // XXX Does this handle Collator instances from other globals correctly?
   1.997 +    bool isCollatorInstance = collator->getClass() == &CollatorClass;
   1.998 +    UCollator *coll;
   1.999 +    if (isCollatorInstance) {
  1.1000 +        coll = static_cast<UCollator *>(collator->getReservedSlot(UCOLLATOR_SLOT).toPrivate());
  1.1001 +        if (!coll) {
  1.1002 +            coll = NewUCollator(cx, collator);
  1.1003 +            if (!coll)
  1.1004 +                return false;
  1.1005 +            collator->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(coll));
  1.1006 +        }
  1.1007 +    } else {
  1.1008 +        // There's no good place to cache the ICU collator for an object
  1.1009 +        // that has been initialized as a Collator but is not a Collator
  1.1010 +        // instance. One possibility might be to add a Collator instance as an
  1.1011 +        // internal property to each such object.
  1.1012 +        coll = NewUCollator(cx, collator);
  1.1013 +        if (!coll)
  1.1014 +            return false;
  1.1015 +    }
  1.1016 +
  1.1017 +    // Use the UCollator to actually compare the strings.
  1.1018 +    RootedString str1(cx, args[1].toString());
  1.1019 +    RootedString str2(cx, args[2].toString());
  1.1020 +    RootedValue result(cx);
  1.1021 +    bool success = intl_CompareStrings(cx, coll, str1, str2, &result);
  1.1022 +
  1.1023 +    if (!isCollatorInstance)
  1.1024 +        ucol_close(coll);
  1.1025 +    if (!success)
  1.1026 +        return false;
  1.1027 +    args.rval().set(result);
  1.1028 +    return true;
  1.1029 +}
  1.1030 +
  1.1031 +
  1.1032 +/******************** NumberFormat ********************/
  1.1033 +
  1.1034 +static void numberFormat_finalize(FreeOp *fop, JSObject *obj);
  1.1035 +
  1.1036 +static const uint32_t UNUMBER_FORMAT_SLOT = 0;
  1.1037 +static const uint32_t NUMBER_FORMAT_SLOTS_COUNT = 1;
  1.1038 +
  1.1039 +static const Class NumberFormatClass = {
  1.1040 +    js_Object_str,
  1.1041 +    JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT),
  1.1042 +    JS_PropertyStub,         /* addProperty */
  1.1043 +    JS_DeletePropertyStub,   /* delProperty */
  1.1044 +    JS_PropertyStub,         /* getProperty */
  1.1045 +    JS_StrictPropertyStub,   /* setProperty */
  1.1046 +    JS_EnumerateStub,
  1.1047 +    JS_ResolveStub,
  1.1048 +    JS_ConvertStub,
  1.1049 +    numberFormat_finalize
  1.1050 +};
  1.1051 +
  1.1052 +#if JS_HAS_TOSOURCE
  1.1053 +static bool
  1.1054 +numberFormat_toSource(JSContext *cx, unsigned argc, Value *vp)
  1.1055 +{
  1.1056 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1057 +    args.rval().setString(cx->names().NumberFormat);
  1.1058 +    return true;
  1.1059 +}
  1.1060 +#endif
  1.1061 +
  1.1062 +static const JSFunctionSpec numberFormat_static_methods[] = {
  1.1063 +    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_NumberFormat_supportedLocalesOf", 1, 0),
  1.1064 +    JS_FS_END
  1.1065 +};
  1.1066 +
  1.1067 +static const JSFunctionSpec numberFormat_methods[] = {
  1.1068 +    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_NumberFormat_resolvedOptions", 0, 0),
  1.1069 +#if JS_HAS_TOSOURCE
  1.1070 +    JS_FN(js_toSource_str, numberFormat_toSource, 0, 0),
  1.1071 +#endif
  1.1072 +    JS_FS_END
  1.1073 +};
  1.1074 +
  1.1075 +/**
  1.1076 + * NumberFormat constructor.
  1.1077 + * Spec: ECMAScript Internationalization API Specification, 11.1
  1.1078 + */
  1.1079 +static bool
  1.1080 +NumberFormat(JSContext *cx, CallArgs args, bool construct)
  1.1081 +{
  1.1082 +    RootedObject obj(cx);
  1.1083 +
  1.1084 +    if (!construct) {
  1.1085 +        // 11.1.2.1 step 3
  1.1086 +        JSObject *intl = cx->global()->getOrCreateIntlObject(cx);
  1.1087 +        if (!intl)
  1.1088 +            return false;
  1.1089 +        RootedValue self(cx, args.thisv());
  1.1090 +        if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
  1.1091 +            // 11.1.2.1 step 4
  1.1092 +            obj = ToObject(cx, self);
  1.1093 +            if (!obj)
  1.1094 +                return false;
  1.1095 +
  1.1096 +            // 11.1.2.1 step 5
  1.1097 +            bool extensible;
  1.1098 +            if (!JSObject::isExtensible(cx, obj, &extensible))
  1.1099 +                return false;
  1.1100 +            if (!extensible)
  1.1101 +                return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
  1.1102 +        } else {
  1.1103 +            // 11.1.2.1 step 3.a
  1.1104 +            construct = true;
  1.1105 +        }
  1.1106 +    }
  1.1107 +    if (construct) {
  1.1108 +        // 11.1.3.1 paragraph 2
  1.1109 +        RootedObject proto(cx, cx->global()->getOrCreateNumberFormatPrototype(cx));
  1.1110 +        if (!proto)
  1.1111 +            return false;
  1.1112 +        obj = NewObjectWithGivenProto(cx, &NumberFormatClass, proto, cx->global());
  1.1113 +        if (!obj)
  1.1114 +            return false;
  1.1115 +
  1.1116 +        obj->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
  1.1117 +    }
  1.1118 +
  1.1119 +    // 11.1.2.1 steps 1 and 2; 11.1.3.1 steps 1 and 2
  1.1120 +    RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
  1.1121 +    RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
  1.1122 +
  1.1123 +    // 11.1.2.1 step 6; 11.1.3.1 step 3
  1.1124 +    if (!IntlInitialize(cx, obj, cx->names().InitializeNumberFormat, locales, options))
  1.1125 +        return false;
  1.1126 +
  1.1127 +    // 11.1.2.1 steps 3.a and 7
  1.1128 +    args.rval().setObject(*obj);
  1.1129 +    return true;
  1.1130 +}
  1.1131 +
  1.1132 +static bool
  1.1133 +NumberFormat(JSContext *cx, unsigned argc, Value *vp)
  1.1134 +{
  1.1135 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1136 +    return NumberFormat(cx, args, args.isConstructing());
  1.1137 +}
  1.1138 +
  1.1139 +bool
  1.1140 +js::intl_NumberFormat(JSContext *cx, unsigned argc, Value *vp)
  1.1141 +{
  1.1142 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1143 +    JS_ASSERT(args.length() == 2);
  1.1144 +    // intl_NumberFormat is an intrinsic for self-hosted JavaScript, so it
  1.1145 +    // cannot be used with "new", but it still has to be treated as a
  1.1146 +    // constructor.
  1.1147 +    return NumberFormat(cx, args, true);
  1.1148 +}
  1.1149 +
  1.1150 +static void
  1.1151 +numberFormat_finalize(FreeOp *fop, JSObject *obj)
  1.1152 +{
  1.1153 +    UNumberFormat *nf =
  1.1154 +        static_cast<UNumberFormat*>(obj->getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
  1.1155 +    if (nf)
  1.1156 +        unum_close(nf);
  1.1157 +}
  1.1158 +
  1.1159 +static JSObject *
  1.1160 +InitNumberFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
  1.1161 +{
  1.1162 +    RootedFunction ctor(cx, global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0));
  1.1163 +    if (!ctor)
  1.1164 +        return nullptr;
  1.1165 +
  1.1166 +    RootedObject proto(cx, global->as<GlobalObject>().getOrCreateNumberFormatPrototype(cx));
  1.1167 +    if (!proto)
  1.1168 +        return nullptr;
  1.1169 +    if (!LinkConstructorAndPrototype(cx, ctor, proto))
  1.1170 +        return nullptr;
  1.1171 +
  1.1172 +    // 11.2.2
  1.1173 +    if (!JS_DefineFunctions(cx, ctor, numberFormat_static_methods))
  1.1174 +        return nullptr;
  1.1175 +
  1.1176 +    // 11.3.2 and 11.3.3
  1.1177 +    if (!JS_DefineFunctions(cx, proto, numberFormat_methods))
  1.1178 +        return nullptr;
  1.1179 +
  1.1180 +    /*
  1.1181 +     * Install the getter for NumberFormat.prototype.format, which returns a
  1.1182 +     * bound formatting function for the specified NumberFormat object (suitable
  1.1183 +     * for passing to methods like Array.prototype.map).
  1.1184 +     */
  1.1185 +    RootedValue getter(cx);
  1.1186 +    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().NumberFormatFormatGet, &getter))
  1.1187 +        return nullptr;
  1.1188 +    if (!JSObject::defineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
  1.1189 +                                  JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
  1.1190 +                                  nullptr, JSPROP_GETTER | JSPROP_SHARED))
  1.1191 +    {
  1.1192 +        return nullptr;
  1.1193 +    }
  1.1194 +
  1.1195 +    // 11.2.1 and 11.3
  1.1196 +    if (!IntlInitialize(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue,
  1.1197 +                        UndefinedHandleValue))
  1.1198 +    {
  1.1199 +        return nullptr;
  1.1200 +    }
  1.1201 +
  1.1202 +    // 8.1
  1.1203 +    RootedValue ctorValue(cx, ObjectValue(*ctor));
  1.1204 +    if (!JSObject::defineProperty(cx, Intl, cx->names().NumberFormat, ctorValue,
  1.1205 +                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
  1.1206 +    {
  1.1207 +        return nullptr;
  1.1208 +    }
  1.1209 +
  1.1210 +    return ctor;
  1.1211 +}
  1.1212 +
  1.1213 +bool
  1.1214 +GlobalObject::initNumberFormatProto(JSContext *cx, Handle<GlobalObject*> global)
  1.1215 +{
  1.1216 +    RootedObject proto(cx, global->createBlankPrototype(cx, &NumberFormatClass));
  1.1217 +    if (!proto)
  1.1218 +        return false;
  1.1219 +    proto->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
  1.1220 +    global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*proto));
  1.1221 +    return true;
  1.1222 +}
  1.1223 +
  1.1224 +bool
  1.1225 +js::intl_NumberFormat_availableLocales(JSContext *cx, unsigned argc, Value *vp)
  1.1226 +{
  1.1227 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1228 +    JS_ASSERT(args.length() == 0);
  1.1229 +
  1.1230 +    RootedValue result(cx);
  1.1231 +    if (!intl_availableLocales(cx, unum_countAvailable, unum_getAvailable, &result))
  1.1232 +        return false;
  1.1233 +    args.rval().set(result);
  1.1234 +    return true;
  1.1235 +}
  1.1236 +
  1.1237 +bool
  1.1238 +js::intl_numberingSystem(JSContext *cx, unsigned argc, Value *vp)
  1.1239 +{
  1.1240 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1241 +    JS_ASSERT(args.length() == 1);
  1.1242 +    JS_ASSERT(args[0].isString());
  1.1243 +
  1.1244 +    JSAutoByteString locale(cx, args[0].toString());
  1.1245 +    if (!locale)
  1.1246 +        return false;
  1.1247 +
  1.1248 +    // There's no C API for numbering system, so use the C++ API and hope it
  1.1249 +    // won't break. http://bugs.icu-project.org/trac/ticket/10039
  1.1250 +    Locale ulocale(locale.ptr());
  1.1251 +    UErrorCode status = U_ZERO_ERROR;
  1.1252 +    NumberingSystem *numbers = NumberingSystem::createInstance(ulocale, status);
  1.1253 +    if (U_FAILURE(status)) {
  1.1254 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1255 +        return false;
  1.1256 +    }
  1.1257 +    const char *name = numbers->getName();
  1.1258 +    RootedString jsname(cx, JS_NewStringCopyZ(cx, name));
  1.1259 +    delete numbers;
  1.1260 +    if (!jsname)
  1.1261 +        return false;
  1.1262 +    args.rval().setString(jsname);
  1.1263 +    return true;
  1.1264 +}
  1.1265 +
  1.1266 +/**
  1.1267 + * Returns a new UNumberFormat with the locale and number formatting options
  1.1268 + * of the given NumberFormat.
  1.1269 + */
  1.1270 +static UNumberFormat *
  1.1271 +NewUNumberFormat(JSContext *cx, HandleObject numberFormat)
  1.1272 +{
  1.1273 +    RootedValue value(cx);
  1.1274 +
  1.1275 +    RootedObject internals(cx);
  1.1276 +    if (!GetInternals(cx, numberFormat, &internals))
  1.1277 +       return nullptr;
  1.1278 +
  1.1279 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().locale, &value))
  1.1280 +        return nullptr;
  1.1281 +    JSAutoByteString locale(cx, value.toString());
  1.1282 +    if (!locale)
  1.1283 +        return nullptr;
  1.1284 +
  1.1285 +    // UNumberFormat options with default values
  1.1286 +    UNumberFormatStyle uStyle = UNUM_DECIMAL;
  1.1287 +    const UChar *uCurrency = nullptr;
  1.1288 +    uint32_t uMinimumIntegerDigits = 1;
  1.1289 +    uint32_t uMinimumFractionDigits = 0;
  1.1290 +    uint32_t uMaximumFractionDigits = 3;
  1.1291 +    int32_t uMinimumSignificantDigits = -1;
  1.1292 +    int32_t uMaximumSignificantDigits = -1;
  1.1293 +    bool uUseGrouping = true;
  1.1294 +
  1.1295 +    // Sprinkle appropriate rooting flavor over things the GC might care about.
  1.1296 +    RootedString currency(cx);
  1.1297 +
  1.1298 +    // We don't need to look at numberingSystem - it can only be set via
  1.1299 +    // the Unicode locale extension and is therefore already set on locale.
  1.1300 +
  1.1301 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().style, &value))
  1.1302 +        return nullptr;
  1.1303 +    JSAutoByteString style(cx, value.toString());
  1.1304 +    if (!style)
  1.1305 +        return nullptr;
  1.1306 +
  1.1307 +    if (equal(style, "currency")) {
  1.1308 +        if (!JSObject::getProperty(cx, internals, internals, cx->names().currency, &value))
  1.1309 +            return nullptr;
  1.1310 +        currency = value.toString();
  1.1311 +        MOZ_ASSERT(currency->length() == 3, "IsWellFormedCurrencyCode permits only length-3 strings");
  1.1312 +        // uCurrency remains owned by currency.
  1.1313 +        uCurrency = JSCharToUChar(JS_GetStringCharsZ(cx, currency));
  1.1314 +        if (!uCurrency)
  1.1315 +            return nullptr;
  1.1316 +
  1.1317 +        if (!JSObject::getProperty(cx, internals, internals, cx->names().currencyDisplay, &value))
  1.1318 +            return nullptr;
  1.1319 +        JSAutoByteString currencyDisplay(cx, value.toString());
  1.1320 +        if (!currencyDisplay)
  1.1321 +            return nullptr;
  1.1322 +        if (equal(currencyDisplay, "code")) {
  1.1323 +            uStyle = UNUM_CURRENCY_ISO;
  1.1324 +        } else if (equal(currencyDisplay, "symbol")) {
  1.1325 +            uStyle = UNUM_CURRENCY;
  1.1326 +        } else {
  1.1327 +            JS_ASSERT(equal(currencyDisplay, "name"));
  1.1328 +            uStyle = UNUM_CURRENCY_PLURAL;
  1.1329 +        }
  1.1330 +    } else if (equal(style, "percent")) {
  1.1331 +        uStyle = UNUM_PERCENT;
  1.1332 +    } else {
  1.1333 +        JS_ASSERT(equal(style, "decimal"));
  1.1334 +        uStyle = UNUM_DECIMAL;
  1.1335 +    }
  1.1336 +
  1.1337 +    RootedId id(cx, NameToId(cx->names().minimumSignificantDigits));
  1.1338 +    bool hasP;
  1.1339 +    if (!JSObject::hasProperty(cx, internals, id, &hasP))
  1.1340 +        return nullptr;
  1.1341 +    if (hasP) {
  1.1342 +        if (!JSObject::getProperty(cx, internals, internals, cx->names().minimumSignificantDigits,
  1.1343 +                                   &value))
  1.1344 +        {
  1.1345 +            return nullptr;
  1.1346 +        }
  1.1347 +        uMinimumSignificantDigits = int32_t(value.toNumber());
  1.1348 +        if (!JSObject::getProperty(cx, internals, internals, cx->names().maximumSignificantDigits,
  1.1349 +                                   &value))
  1.1350 +        {
  1.1351 +            return nullptr;
  1.1352 +        }
  1.1353 +        uMaximumSignificantDigits = int32_t(value.toNumber());
  1.1354 +    } else {
  1.1355 +        if (!JSObject::getProperty(cx, internals, internals, cx->names().minimumIntegerDigits,
  1.1356 +                                   &value))
  1.1357 +        {
  1.1358 +            return nullptr;
  1.1359 +        }
  1.1360 +        uMinimumIntegerDigits = int32_t(value.toNumber());
  1.1361 +        if (!JSObject::getProperty(cx, internals, internals, cx->names().minimumFractionDigits,
  1.1362 +                                   &value))
  1.1363 +        {
  1.1364 +            return nullptr;
  1.1365 +        }
  1.1366 +        uMinimumFractionDigits = int32_t(value.toNumber());
  1.1367 +        if (!JSObject::getProperty(cx, internals, internals, cx->names().maximumFractionDigits,
  1.1368 +                                   &value))
  1.1369 +        {
  1.1370 +            return nullptr;
  1.1371 +        }
  1.1372 +        uMaximumFractionDigits = int32_t(value.toNumber());
  1.1373 +    }
  1.1374 +
  1.1375 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().useGrouping, &value))
  1.1376 +        return nullptr;
  1.1377 +    uUseGrouping = value.toBoolean();
  1.1378 +
  1.1379 +    UErrorCode status = U_ZERO_ERROR;
  1.1380 +    UNumberFormat *nf = unum_open(uStyle, nullptr, 0, icuLocale(locale.ptr()), nullptr, &status);
  1.1381 +    if (U_FAILURE(status)) {
  1.1382 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1383 +        return nullptr;
  1.1384 +    }
  1.1385 +    ScopedICUObject<UNumberFormat> toClose(nf, unum_close);
  1.1386 +
  1.1387 +    if (uCurrency) {
  1.1388 +        unum_setTextAttribute(nf, UNUM_CURRENCY_CODE, uCurrency, 3, &status);
  1.1389 +        if (U_FAILURE(status)) {
  1.1390 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1391 +            return nullptr;
  1.1392 +        }
  1.1393 +    }
  1.1394 +    if (uMinimumSignificantDigits != -1) {
  1.1395 +        unum_setAttribute(nf, UNUM_SIGNIFICANT_DIGITS_USED, true);
  1.1396 +        unum_setAttribute(nf, UNUM_MIN_SIGNIFICANT_DIGITS, uMinimumSignificantDigits);
  1.1397 +        unum_setAttribute(nf, UNUM_MAX_SIGNIFICANT_DIGITS, uMaximumSignificantDigits);
  1.1398 +    } else {
  1.1399 +        unum_setAttribute(nf, UNUM_MIN_INTEGER_DIGITS, uMinimumIntegerDigits);
  1.1400 +        unum_setAttribute(nf, UNUM_MIN_FRACTION_DIGITS, uMinimumFractionDigits);
  1.1401 +        unum_setAttribute(nf, UNUM_MAX_FRACTION_DIGITS, uMaximumFractionDigits);
  1.1402 +    }
  1.1403 +    unum_setAttribute(nf, UNUM_GROUPING_USED, uUseGrouping);
  1.1404 +    unum_setAttribute(nf, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
  1.1405 +
  1.1406 +    return toClose.forget();
  1.1407 +}
  1.1408 +
  1.1409 +static bool
  1.1410 +intl_FormatNumber(JSContext *cx, UNumberFormat *nf, double x, MutableHandleValue result)
  1.1411 +{
  1.1412 +    // FormatNumber doesn't consider -0.0 to be negative.
  1.1413 +    if (IsNegativeZero(x))
  1.1414 +        x = 0.0;
  1.1415 +
  1.1416 +    StringBuffer chars(cx);
  1.1417 +    if (!chars.resize(INITIAL_STRING_BUFFER_SIZE))
  1.1418 +        return false;
  1.1419 +    UErrorCode status = U_ZERO_ERROR;
  1.1420 +    int size = unum_formatDouble(nf, x, JSCharToUChar(chars.begin()),
  1.1421 +                                 INITIAL_STRING_BUFFER_SIZE, nullptr, &status);
  1.1422 +    if (status == U_BUFFER_OVERFLOW_ERROR) {
  1.1423 +        if (!chars.resize(size))
  1.1424 +            return false;
  1.1425 +        status = U_ZERO_ERROR;
  1.1426 +        unum_formatDouble(nf, x, JSCharToUChar(chars.begin()),
  1.1427 +                          size, nullptr, &status);
  1.1428 +    }
  1.1429 +    if (U_FAILURE(status)) {
  1.1430 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1431 +        return false;
  1.1432 +    }
  1.1433 +
  1.1434 +    // Trim any unused characters.
  1.1435 +    if (!chars.resize(size))
  1.1436 +        return false;
  1.1437 +
  1.1438 +    RootedString str(cx, chars.finishString());
  1.1439 +    if (!str)
  1.1440 +        return false;
  1.1441 +
  1.1442 +    result.setString(str);
  1.1443 +    return true;
  1.1444 +}
  1.1445 +
  1.1446 +bool
  1.1447 +js::intl_FormatNumber(JSContext *cx, unsigned argc, Value *vp)
  1.1448 +{
  1.1449 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1450 +    JS_ASSERT(args.length() == 2);
  1.1451 +    JS_ASSERT(args[0].isObject());
  1.1452 +    JS_ASSERT(args[1].isNumber());
  1.1453 +
  1.1454 +    RootedObject numberFormat(cx, &args[0].toObject());
  1.1455 +
  1.1456 +    // Obtain a UNumberFormat object, cached if possible.
  1.1457 +    bool isNumberFormatInstance = numberFormat->getClass() == &NumberFormatClass;
  1.1458 +    UNumberFormat *nf;
  1.1459 +    if (isNumberFormatInstance) {
  1.1460 +        nf = static_cast<UNumberFormat*>(numberFormat->getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
  1.1461 +        if (!nf) {
  1.1462 +            nf = NewUNumberFormat(cx, numberFormat);
  1.1463 +            if (!nf)
  1.1464 +                return false;
  1.1465 +            numberFormat->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nf));
  1.1466 +        }
  1.1467 +    } else {
  1.1468 +        // There's no good place to cache the ICU number format for an object
  1.1469 +        // that has been initialized as a NumberFormat but is not a
  1.1470 +        // NumberFormat instance. One possibility might be to add a
  1.1471 +        // NumberFormat instance as an internal property to each such object.
  1.1472 +        nf = NewUNumberFormat(cx, numberFormat);
  1.1473 +        if (!nf)
  1.1474 +            return false;
  1.1475 +    }
  1.1476 +
  1.1477 +    // Use the UNumberFormat to actually format the number.
  1.1478 +    RootedValue result(cx);
  1.1479 +    bool success = intl_FormatNumber(cx, nf, args[1].toNumber(), &result);
  1.1480 +
  1.1481 +    if (!isNumberFormatInstance)
  1.1482 +        unum_close(nf);
  1.1483 +    if (!success)
  1.1484 +        return false;
  1.1485 +    args.rval().set(result);
  1.1486 +    return true;
  1.1487 +}
  1.1488 +
  1.1489 +
  1.1490 +/******************** DateTimeFormat ********************/
  1.1491 +
  1.1492 +static void dateTimeFormat_finalize(FreeOp *fop, JSObject *obj);
  1.1493 +
  1.1494 +static const uint32_t UDATE_FORMAT_SLOT = 0;
  1.1495 +static const uint32_t DATE_TIME_FORMAT_SLOTS_COUNT = 1;
  1.1496 +
  1.1497 +static const Class DateTimeFormatClass = {
  1.1498 +    js_Object_str,
  1.1499 +    JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT),
  1.1500 +    JS_PropertyStub,         /* addProperty */
  1.1501 +    JS_DeletePropertyStub,   /* delProperty */
  1.1502 +    JS_PropertyStub,         /* getProperty */
  1.1503 +    JS_StrictPropertyStub,   /* setProperty */
  1.1504 +    JS_EnumerateStub,
  1.1505 +    JS_ResolveStub,
  1.1506 +    JS_ConvertStub,
  1.1507 +    dateTimeFormat_finalize
  1.1508 +};
  1.1509 +
  1.1510 +#if JS_HAS_TOSOURCE
  1.1511 +static bool
  1.1512 +dateTimeFormat_toSource(JSContext *cx, unsigned argc, Value *vp)
  1.1513 +{
  1.1514 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1515 +    args.rval().setString(cx->names().DateTimeFormat);
  1.1516 +    return true;
  1.1517 +}
  1.1518 +#endif
  1.1519 +
  1.1520 +static const JSFunctionSpec dateTimeFormat_static_methods[] = {
  1.1521 +    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_DateTimeFormat_supportedLocalesOf", 1, 0),
  1.1522 +    JS_FS_END
  1.1523 +};
  1.1524 +
  1.1525 +static const JSFunctionSpec dateTimeFormat_methods[] = {
  1.1526 +    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_DateTimeFormat_resolvedOptions", 0, 0),
  1.1527 +#if JS_HAS_TOSOURCE
  1.1528 +    JS_FN(js_toSource_str, dateTimeFormat_toSource, 0, 0),
  1.1529 +#endif
  1.1530 +    JS_FS_END
  1.1531 +};
  1.1532 +
  1.1533 +/**
  1.1534 + * DateTimeFormat constructor.
  1.1535 + * Spec: ECMAScript Internationalization API Specification, 12.1
  1.1536 + */
  1.1537 +static bool
  1.1538 +DateTimeFormat(JSContext *cx, CallArgs args, bool construct)
  1.1539 +{
  1.1540 +    RootedObject obj(cx);
  1.1541 +
  1.1542 +    if (!construct) {
  1.1543 +        // 12.1.2.1 step 3
  1.1544 +        JSObject *intl = cx->global()->getOrCreateIntlObject(cx);
  1.1545 +        if (!intl)
  1.1546 +            return false;
  1.1547 +        RootedValue self(cx, args.thisv());
  1.1548 +        if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
  1.1549 +            // 12.1.2.1 step 4
  1.1550 +            obj = ToObject(cx, self);
  1.1551 +            if (!obj)
  1.1552 +                return false;
  1.1553 +
  1.1554 +            // 12.1.2.1 step 5
  1.1555 +            bool extensible;
  1.1556 +            if (!JSObject::isExtensible(cx, obj, &extensible))
  1.1557 +                return false;
  1.1558 +            if (!extensible)
  1.1559 +                return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
  1.1560 +        } else {
  1.1561 +            // 12.1.2.1 step 3.a
  1.1562 +            construct = true;
  1.1563 +        }
  1.1564 +    }
  1.1565 +    if (construct) {
  1.1566 +        // 12.1.3.1 paragraph 2
  1.1567 +        RootedObject proto(cx, cx->global()->getOrCreateDateTimeFormatPrototype(cx));
  1.1568 +        if (!proto)
  1.1569 +            return false;
  1.1570 +        obj = NewObjectWithGivenProto(cx, &DateTimeFormatClass, proto, cx->global());
  1.1571 +        if (!obj)
  1.1572 +            return false;
  1.1573 +
  1.1574 +        obj->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
  1.1575 +    }
  1.1576 +
  1.1577 +    // 12.1.2.1 steps 1 and 2; 12.1.3.1 steps 1 and 2
  1.1578 +    RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
  1.1579 +    RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
  1.1580 +
  1.1581 +    // 12.1.2.1 step 6; 12.1.3.1 step 3
  1.1582 +    if (!IntlInitialize(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
  1.1583 +        return false;
  1.1584 +
  1.1585 +    // 12.1.2.1 steps 3.a and 7
  1.1586 +    args.rval().setObject(*obj);
  1.1587 +    return true;
  1.1588 +}
  1.1589 +
  1.1590 +static bool
  1.1591 +DateTimeFormat(JSContext *cx, unsigned argc, Value *vp)
  1.1592 +{
  1.1593 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1594 +    return DateTimeFormat(cx, args, args.isConstructing());
  1.1595 +}
  1.1596 +
  1.1597 +bool
  1.1598 +js::intl_DateTimeFormat(JSContext *cx, unsigned argc, Value *vp)
  1.1599 +{
  1.1600 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1601 +    JS_ASSERT(args.length() == 2);
  1.1602 +    // intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it
  1.1603 +    // cannot be used with "new", but it still has to be treated as a
  1.1604 +    // constructor.
  1.1605 +    return DateTimeFormat(cx, args, true);
  1.1606 +}
  1.1607 +
  1.1608 +static void
  1.1609 +dateTimeFormat_finalize(FreeOp *fop, JSObject *obj)
  1.1610 +{
  1.1611 +    UDateFormat *df = static_cast<UDateFormat*>(obj->getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
  1.1612 +    if (df)
  1.1613 +        udat_close(df);
  1.1614 +}
  1.1615 +
  1.1616 +static JSObject *
  1.1617 +InitDateTimeFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
  1.1618 +{
  1.1619 +    RootedFunction ctor(cx, global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0));
  1.1620 +    if (!ctor)
  1.1621 +        return nullptr;
  1.1622 +
  1.1623 +    RootedObject proto(cx, global->as<GlobalObject>().getOrCreateDateTimeFormatPrototype(cx));
  1.1624 +    if (!proto)
  1.1625 +        return nullptr;
  1.1626 +    if (!LinkConstructorAndPrototype(cx, ctor, proto))
  1.1627 +        return nullptr;
  1.1628 +
  1.1629 +    // 12.2.2
  1.1630 +    if (!JS_DefineFunctions(cx, ctor, dateTimeFormat_static_methods))
  1.1631 +        return nullptr;
  1.1632 +
  1.1633 +    // 12.3.2 and 12.3.3
  1.1634 +    if (!JS_DefineFunctions(cx, proto, dateTimeFormat_methods))
  1.1635 +        return nullptr;
  1.1636 +
  1.1637 +    /*
  1.1638 +     * Install the getter for DateTimeFormat.prototype.format, which returns a
  1.1639 +     * bound formatting function for the specified DateTimeFormat object
  1.1640 +     * (suitable for passing to methods like Array.prototype.map).
  1.1641 +     */
  1.1642 +    RootedValue getter(cx);
  1.1643 +    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().DateTimeFormatFormatGet, &getter))
  1.1644 +        return nullptr;
  1.1645 +    if (!JSObject::defineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
  1.1646 +                                  JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
  1.1647 +                                  nullptr, JSPROP_GETTER | JSPROP_SHARED))
  1.1648 +    {
  1.1649 +        return nullptr;
  1.1650 +    }
  1.1651 +
  1.1652 +    // 12.2.1 and 12.3
  1.1653 +    if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue,
  1.1654 +                        UndefinedHandleValue))
  1.1655 +    {
  1.1656 +        return nullptr;
  1.1657 +    }
  1.1658 +
  1.1659 +    // 8.1
  1.1660 +    RootedValue ctorValue(cx, ObjectValue(*ctor));
  1.1661 +    if (!JSObject::defineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue,
  1.1662 +                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
  1.1663 +    {
  1.1664 +        return nullptr;
  1.1665 +    }
  1.1666 +
  1.1667 +    return ctor;
  1.1668 +}
  1.1669 +
  1.1670 +bool
  1.1671 +GlobalObject::initDateTimeFormatProto(JSContext *cx, Handle<GlobalObject*> global)
  1.1672 +{
  1.1673 +    RootedObject proto(cx, global->createBlankPrototype(cx, &DateTimeFormatClass));
  1.1674 +    if (!proto)
  1.1675 +        return false;
  1.1676 +    proto->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
  1.1677 +    global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*proto));
  1.1678 +    return true;
  1.1679 +}
  1.1680 +
  1.1681 +bool
  1.1682 +js::intl_DateTimeFormat_availableLocales(JSContext *cx, unsigned argc, Value *vp)
  1.1683 +{
  1.1684 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1685 +    JS_ASSERT(args.length() == 0);
  1.1686 +
  1.1687 +    RootedValue result(cx);
  1.1688 +    if (!intl_availableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
  1.1689 +        return false;
  1.1690 +    args.rval().set(result);
  1.1691 +    return true;
  1.1692 +}
  1.1693 +
  1.1694 +// ICU returns old-style keyword values; map them to BCP 47 equivalents
  1.1695 +// (see http://bugs.icu-project.org/trac/ticket/9620).
  1.1696 +static const char *
  1.1697 +bcp47CalendarName(const char *icuName)
  1.1698 +{
  1.1699 +    if (equal(icuName, "ethiopic-amete-alem"))
  1.1700 +        return "ethioaa";
  1.1701 +    if (equal(icuName, "gregorian"))
  1.1702 +        return "gregory";
  1.1703 +    if (equal(icuName, "islamic-civil"))
  1.1704 +        return "islamicc";
  1.1705 +    return icuName;
  1.1706 +}
  1.1707 +
  1.1708 +bool
  1.1709 +js::intl_availableCalendars(JSContext *cx, unsigned argc, Value *vp)
  1.1710 +{
  1.1711 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1712 +    JS_ASSERT(args.length() == 1);
  1.1713 +    JS_ASSERT(args[0].isString());
  1.1714 +
  1.1715 +    JSAutoByteString locale(cx, args[0].toString());
  1.1716 +    if (!locale)
  1.1717 +        return false;
  1.1718 +
  1.1719 +    RootedObject calendars(cx, NewDenseEmptyArray(cx));
  1.1720 +    if (!calendars)
  1.1721 +        return false;
  1.1722 +    uint32_t index = 0;
  1.1723 +
  1.1724 +    // We need the default calendar for the locale as the first result.
  1.1725 +    UErrorCode status = U_ZERO_ERROR;
  1.1726 +    UCalendar *cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
  1.1727 +    const char *calendar = ucal_getType(cal, &status);
  1.1728 +    if (U_FAILURE(status)) {
  1.1729 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1730 +        return false;
  1.1731 +    }
  1.1732 +    ucal_close(cal);
  1.1733 +    RootedString jscalendar(cx, JS_NewStringCopyZ(cx, bcp47CalendarName(calendar)));
  1.1734 +    if (!jscalendar)
  1.1735 +        return false;
  1.1736 +    RootedValue element(cx, StringValue(jscalendar));
  1.1737 +    if (!JSObject::defineElement(cx, calendars, index++, element))
  1.1738 +        return false;
  1.1739 +
  1.1740 +    // Now get the calendars that "would make a difference", i.e., not the default.
  1.1741 +    UEnumeration *values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
  1.1742 +    if (U_FAILURE(status)) {
  1.1743 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1744 +        return false;
  1.1745 +    }
  1.1746 +    ScopedICUObject<UEnumeration> toClose(values, uenum_close);
  1.1747 +
  1.1748 +    uint32_t count = uenum_count(values, &status);
  1.1749 +    if (U_FAILURE(status)) {
  1.1750 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1751 +        return false;
  1.1752 +    }
  1.1753 +
  1.1754 +    for (; count > 0; count--) {
  1.1755 +        calendar = uenum_next(values, nullptr, &status);
  1.1756 +        if (U_FAILURE(status)) {
  1.1757 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1758 +            return false;
  1.1759 +        }
  1.1760 +
  1.1761 +        jscalendar = JS_NewStringCopyZ(cx, bcp47CalendarName(calendar));
  1.1762 +        if (!jscalendar)
  1.1763 +            return false;
  1.1764 +        element = StringValue(jscalendar);
  1.1765 +        if (!JSObject::defineElement(cx, calendars, index++, element))
  1.1766 +            return false;
  1.1767 +    }
  1.1768 +
  1.1769 +    args.rval().setObject(*calendars);
  1.1770 +    return true;
  1.1771 +}
  1.1772 +
  1.1773 +bool
  1.1774 +js::intl_patternForSkeleton(JSContext *cx, unsigned argc, Value *vp)
  1.1775 +{
  1.1776 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1777 +    JS_ASSERT(args.length() == 2);
  1.1778 +    JS_ASSERT(args[0].isString());
  1.1779 +    JS_ASSERT(args[1].isString());
  1.1780 +
  1.1781 +    JSAutoByteString locale(cx, args[0].toString());
  1.1782 +    if (!locale)
  1.1783 +        return false;
  1.1784 +    RootedString jsskeleton(cx, args[1].toString());
  1.1785 +    const jschar *skeleton = JS_GetStringCharsZ(cx, jsskeleton);
  1.1786 +    if (!skeleton)
  1.1787 +        return false;
  1.1788 +    uint32_t skeletonLen = u_strlen(JSCharToUChar(skeleton));
  1.1789 +
  1.1790 +    UErrorCode status = U_ZERO_ERROR;
  1.1791 +    UDateTimePatternGenerator *gen = udatpg_open(icuLocale(locale.ptr()), &status);
  1.1792 +    if (U_FAILURE(status)) {
  1.1793 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1794 +        return false;
  1.1795 +    }
  1.1796 +    ScopedICUObject<UDateTimePatternGenerator> toClose(gen, udatpg_close);
  1.1797 +
  1.1798 +    int32_t size = udatpg_getBestPattern(gen, JSCharToUChar(skeleton),
  1.1799 +                                         skeletonLen, nullptr, 0, &status);
  1.1800 +    if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
  1.1801 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1802 +        return false;
  1.1803 +    }
  1.1804 +    ScopedJSFreePtr<UChar> pattern(cx->pod_malloc<UChar>(size + 1));
  1.1805 +    if (!pattern)
  1.1806 +        return false;
  1.1807 +    pattern[size] = '\0';
  1.1808 +    status = U_ZERO_ERROR;
  1.1809 +    udatpg_getBestPattern(gen, JSCharToUChar(skeleton),
  1.1810 +                          skeletonLen, pattern, size, &status);
  1.1811 +    if (U_FAILURE(status)) {
  1.1812 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1813 +        return false;
  1.1814 +    }
  1.1815 +
  1.1816 +    RootedString str(cx, JS_NewUCStringCopyZ(cx, reinterpret_cast<jschar*>(pattern.get())));
  1.1817 +    if (!str)
  1.1818 +        return false;
  1.1819 +    args.rval().setString(str);
  1.1820 +    return true;
  1.1821 +}
  1.1822 +
  1.1823 +/**
  1.1824 + * Returns a new UDateFormat with the locale and date-time formatting options
  1.1825 + * of the given DateTimeFormat.
  1.1826 + */
  1.1827 +static UDateFormat *
  1.1828 +NewUDateFormat(JSContext *cx, HandleObject dateTimeFormat)
  1.1829 +{
  1.1830 +    RootedValue value(cx);
  1.1831 +
  1.1832 +    RootedObject internals(cx);
  1.1833 +    if (!GetInternals(cx, dateTimeFormat, &internals))
  1.1834 +       return nullptr;
  1.1835 +
  1.1836 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().locale, &value)) {
  1.1837 +        return nullptr;
  1.1838 +    }
  1.1839 +    JSAutoByteString locale(cx, value.toString());
  1.1840 +    if (!locale)
  1.1841 +        return nullptr;
  1.1842 +
  1.1843 +    // UDateFormat options with default values.
  1.1844 +    const UChar *uTimeZone = nullptr;
  1.1845 +    uint32_t uTimeZoneLength = 0;
  1.1846 +    const UChar *uPattern = nullptr;
  1.1847 +    uint32_t uPatternLength = 0;
  1.1848 +
  1.1849 +    // We don't need to look at calendar and numberingSystem - they can only be
  1.1850 +    // set via the Unicode locale extension and are therefore already set on
  1.1851 +    // locale.
  1.1852 +
  1.1853 +    RootedId id(cx, NameToId(cx->names().timeZone));
  1.1854 +    bool hasP;
  1.1855 +    if (!JSObject::hasProperty(cx, internals, id, &hasP))
  1.1856 +        return nullptr;
  1.1857 +    if (hasP) {
  1.1858 +        if (!JSObject::getProperty(cx, internals, internals, cx->names().timeZone, &value))
  1.1859 +            return nullptr;
  1.1860 +        if (!value.isUndefined()) {
  1.1861 +            uTimeZone = JSCharToUChar(JS_GetStringCharsZ(cx, value.toString()));
  1.1862 +            if (!uTimeZone)
  1.1863 +                return nullptr;
  1.1864 +            uTimeZoneLength = u_strlen(uTimeZone);
  1.1865 +        }
  1.1866 +    }
  1.1867 +    if (!JSObject::getProperty(cx, internals, internals, cx->names().pattern, &value))
  1.1868 +        return nullptr;
  1.1869 +    uPattern = JSCharToUChar(JS_GetStringCharsZ(cx, value.toString()));
  1.1870 +    if (!uPattern)
  1.1871 +        return nullptr;
  1.1872 +    uPatternLength = u_strlen(uPattern);
  1.1873 +
  1.1874 +    UErrorCode status = U_ZERO_ERROR;
  1.1875 +
  1.1876 +    // If building with ICU headers before 50.1, use UDAT_IGNORE instead of
  1.1877 +    // UDAT_PATTERN.
  1.1878 +    UDateFormat *df =
  1.1879 +        udat_open(UDAT_PATTERN, UDAT_PATTERN, icuLocale(locale.ptr()), uTimeZone, uTimeZoneLength,
  1.1880 +                  uPattern, uPatternLength, &status);
  1.1881 +    if (U_FAILURE(status)) {
  1.1882 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1883 +        return nullptr;
  1.1884 +    }
  1.1885 +
  1.1886 +    // ECMAScript requires the Gregorian calendar to be used from the beginning
  1.1887 +    // of ECMAScript time.
  1.1888 +    UCalendar *cal = const_cast<UCalendar*>(udat_getCalendar(df));
  1.1889 +    ucal_setGregorianChange(cal, StartOfTime, &status);
  1.1890 +
  1.1891 +    // An error here means the calendar is not Gregorian, so we don't care.
  1.1892 +
  1.1893 +    return df;
  1.1894 +}
  1.1895 +
  1.1896 +static bool
  1.1897 +intl_FormatDateTime(JSContext *cx, UDateFormat *df, double x, MutableHandleValue result)
  1.1898 +{
  1.1899 +    if (!IsFinite(x)) {
  1.1900 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
  1.1901 +        return false;
  1.1902 +    }
  1.1903 +
  1.1904 +    StringBuffer chars(cx);
  1.1905 +    if (!chars.resize(INITIAL_STRING_BUFFER_SIZE))
  1.1906 +        return false;
  1.1907 +    UErrorCode status = U_ZERO_ERROR;
  1.1908 +    int size = udat_format(df, x, JSCharToUChar(chars.begin()), INITIAL_STRING_BUFFER_SIZE, nullptr, &status);
  1.1909 +    if (status == U_BUFFER_OVERFLOW_ERROR) {
  1.1910 +        if (!chars.resize(size))
  1.1911 +            return false;
  1.1912 +        status = U_ZERO_ERROR;
  1.1913 +        udat_format(df, x, JSCharToUChar(chars.begin()), size, nullptr, &status);
  1.1914 +    }
  1.1915 +    if (U_FAILURE(status)) {
  1.1916 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
  1.1917 +        return false;
  1.1918 +    }
  1.1919 +
  1.1920 +    // Trim any unused characters.
  1.1921 +    if (!chars.resize(size))
  1.1922 +        return false;
  1.1923 +
  1.1924 +    RootedString str(cx, chars.finishString());
  1.1925 +    if (!str)
  1.1926 +        return false;
  1.1927 +
  1.1928 +    result.setString(str);
  1.1929 +    return true;
  1.1930 +}
  1.1931 +
  1.1932 +bool
  1.1933 +js::intl_FormatDateTime(JSContext *cx, unsigned argc, Value *vp)
  1.1934 +{
  1.1935 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1936 +    JS_ASSERT(args.length() == 2);
  1.1937 +    JS_ASSERT(args[0].isObject());
  1.1938 +    JS_ASSERT(args[1].isNumber());
  1.1939 +
  1.1940 +    RootedObject dateTimeFormat(cx, &args[0].toObject());
  1.1941 +
  1.1942 +    // Obtain a UDateFormat object, cached if possible.
  1.1943 +    bool isDateTimeFormatInstance = dateTimeFormat->getClass() == &DateTimeFormatClass;
  1.1944 +    UDateFormat *df;
  1.1945 +    if (isDateTimeFormatInstance) {
  1.1946 +        df = static_cast<UDateFormat*>(dateTimeFormat->getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
  1.1947 +        if (!df) {
  1.1948 +            df = NewUDateFormat(cx, dateTimeFormat);
  1.1949 +            if (!df)
  1.1950 +                return false;
  1.1951 +            dateTimeFormat->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(df));
  1.1952 +        }
  1.1953 +    } else {
  1.1954 +        // There's no good place to cache the ICU date-time format for an object
  1.1955 +        // that has been initialized as a DateTimeFormat but is not a
  1.1956 +        // DateTimeFormat instance. One possibility might be to add a
  1.1957 +        // DateTimeFormat instance as an internal property to each such object.
  1.1958 +        df = NewUDateFormat(cx, dateTimeFormat);
  1.1959 +        if (!df)
  1.1960 +            return false;
  1.1961 +    }
  1.1962 +
  1.1963 +    // Use the UDateFormat to actually format the time stamp.
  1.1964 +    RootedValue result(cx);
  1.1965 +    bool success = intl_FormatDateTime(cx, df, args[1].toNumber(), &result);
  1.1966 +
  1.1967 +    if (!isDateTimeFormatInstance)
  1.1968 +        udat_close(df);
  1.1969 +    if (!success)
  1.1970 +        return false;
  1.1971 +    args.rval().set(result);
  1.1972 +    return true;
  1.1973 +}
  1.1974 +
  1.1975 +
  1.1976 +/******************** Intl ********************/
  1.1977 +
  1.1978 +const Class js::IntlClass = {
  1.1979 +    js_Object_str,
  1.1980 +    JSCLASS_HAS_CACHED_PROTO(JSProto_Intl),
  1.1981 +    JS_PropertyStub,         /* addProperty */
  1.1982 +    JS_DeletePropertyStub,   /* delProperty */
  1.1983 +    JS_PropertyStub,         /* getProperty */
  1.1984 +    JS_StrictPropertyStub,   /* setProperty */
  1.1985 +    JS_EnumerateStub,
  1.1986 +    JS_ResolveStub,
  1.1987 +    JS_ConvertStub
  1.1988 +};
  1.1989 +
  1.1990 +#if JS_HAS_TOSOURCE
  1.1991 +static bool
  1.1992 +intl_toSource(JSContext *cx, unsigned argc, Value *vp)
  1.1993 +{
  1.1994 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1995 +    args.rval().setString(cx->names().Intl);
  1.1996 +    return true;
  1.1997 +}
  1.1998 +#endif
  1.1999 +
  1.2000 +static const JSFunctionSpec intl_static_methods[] = {
  1.2001 +#if JS_HAS_TOSOURCE
  1.2002 +    JS_FN(js_toSource_str,  intl_toSource,        0, 0),
  1.2003 +#endif
  1.2004 +    JS_FS_END
  1.2005 +};
  1.2006 +
  1.2007 +/**
  1.2008 + * Initializes the Intl Object and its standard built-in properties.
  1.2009 + * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
  1.2010 + */
  1.2011 +JSObject *
  1.2012 +js_InitIntlClass(JSContext *cx, HandleObject obj)
  1.2013 +{
  1.2014 +    JS_ASSERT(obj->is<GlobalObject>());
  1.2015 +    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
  1.2016 +
  1.2017 +    // The constructors above need to be able to determine whether they've been
  1.2018 +    // called with this being "the standard built-in Intl object". The global
  1.2019 +    // object reserves slots to track standard built-in objects, but doesn't
  1.2020 +    // normally keep references to non-constructors. This makes sure there is one.
  1.2021 +    RootedObject Intl(cx, global->getOrCreateIntlObject(cx));
  1.2022 +    if (!Intl)
  1.2023 +        return nullptr;
  1.2024 +
  1.2025 +    RootedValue IntlValue(cx, ObjectValue(*Intl));
  1.2026 +    if (!JSObject::defineProperty(cx, global, cx->names().Intl, IntlValue,
  1.2027 +                                  JS_PropertyStub, JS_StrictPropertyStub, 0))
  1.2028 +    {
  1.2029 +        return nullptr;
  1.2030 +    }
  1.2031 +
  1.2032 +    if (!JS_DefineFunctions(cx, Intl, intl_static_methods))
  1.2033 +        return nullptr;
  1.2034 +
  1.2035 +    // Skip initialization of the Intl constructors during initialization of the
  1.2036 +    // self-hosting global as we may get here before self-hosted code is compiled,
  1.2037 +    // and no core code refers to the Intl classes.
  1.2038 +    if (!cx->runtime()->isSelfHostingGlobal(cx->global())) {
  1.2039 +        if (!InitCollatorClass(cx, Intl, global))
  1.2040 +            return nullptr;
  1.2041 +        if (!InitNumberFormatClass(cx, Intl, global))
  1.2042 +            return nullptr;
  1.2043 +        if (!InitDateTimeFormatClass(cx, Intl, global))
  1.2044 +            return nullptr;
  1.2045 +    }
  1.2046 +
  1.2047 +    global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
  1.2048 +
  1.2049 +    return Intl;
  1.2050 +}
  1.2051 +
  1.2052 +bool
  1.2053 +GlobalObject::initIntlObject(JSContext *cx, Handle<GlobalObject*> global)
  1.2054 +{
  1.2055 +    RootedObject Intl(cx);
  1.2056 +    Intl = NewObjectWithGivenProto(cx, &IntlClass, global->getOrCreateObjectPrototype(cx),
  1.2057 +                                   global, SingletonObject);
  1.2058 +    if (!Intl)
  1.2059 +        return false;
  1.2060 +
  1.2061 +    global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
  1.2062 +    return true;
  1.2063 +}

mercurial