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