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.

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

mercurial