summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/Intl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/Intl.cpp')
-rw-r--r--js/src/builtin/Intl.cpp483
1 files changed, 447 insertions, 36 deletions
diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp
index 990a4acdb..cd808cb10 100644
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -102,6 +102,18 @@ Char16ToUChar(char16_t* chars)
MOZ_CRASH("Char16ToUChar: Intl API disabled");
}
+inline char16_t*
+UCharToChar16(UChar* chars)
+{
+ MOZ_CRASH("UCharToChar16: Intl API disabled");
+}
+
+inline const char16_t*
+UCharToChar16(const UChar* chars)
+{
+ MOZ_CRASH("UCharToChar16: Intl API disabled");
+}
+
static int32_t
u_strlen(const UChar* s)
{
@@ -356,6 +368,27 @@ enum UCalendarDateFields {
UCAL_DAY_OF_MONTH = UCAL_DATE
};
+enum UCalendarMonths {
+ UCAL_JANUARY,
+ UCAL_FEBRUARY,
+ UCAL_MARCH,
+ UCAL_APRIL,
+ UCAL_MAY,
+ UCAL_JUNE,
+ UCAL_JULY,
+ UCAL_AUGUST,
+ UCAL_SEPTEMBER,
+ UCAL_OCTOBER,
+ UCAL_NOVEMBER,
+ UCAL_DECEMBER,
+ UCAL_UNDECIMBER
+};
+
+enum UCalendarAMPMs {
+ UCAL_AM,
+ UCAL_PM
+};
+
static UCalendar*
ucal_open(const UChar* zoneID, int32_t len, const char* locale,
UCalendarType type, UErrorCode* status)
@@ -420,6 +453,13 @@ ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* statu
MOZ_CRASH("ucal_getDefaultTimeZone: Intl API disabled");
}
+enum UDateTimePatternField {
+ UDATPG_YEAR_FIELD,
+ UDATPG_MONTH_FIELD,
+ UDATPG_WEEK_OF_YEAR_FIELD,
+ UDATPG_DAY_FIELD,
+};
+
typedef void* UDateTimePatternGenerator;
static UDateTimePatternGenerator*
@@ -436,6 +476,14 @@ udatpg_getBestPattern(UDateTimePatternGenerator* dtpg, const UChar* skeleton,
MOZ_CRASH("udatpg_getBestPattern: Intl API disabled");
}
+static const UChar *
+udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg,
+ UDateTimePatternField field,
+ int32_t *pLength)
+{
+ MOZ_CRASH("udatpg_getAppendItemName: Intl API disabled");
+}
+
static void
udatpg_close(UDateTimePatternGenerator* dtpg)
{
@@ -488,10 +536,46 @@ enum UDateFormatField {
};
enum UDateFormatStyle {
+ UDAT_FULL,
+ UDAT_LONG,
+ UDAT_MEDIUM,
+ UDAT_SHORT,
+ UDAT_DEFAULT = UDAT_MEDIUM,
UDAT_PATTERN = -2,
UDAT_IGNORE = UDAT_PATTERN
};
+enum UDateFormatSymbolType {
+ UDAT_ERAS,
+ UDAT_MONTHS,
+ UDAT_SHORT_MONTHS,
+ UDAT_WEEKDAYS,
+ UDAT_SHORT_WEEKDAYS,
+ UDAT_AM_PMS,
+ UDAT_LOCALIZED_CHARS,
+ UDAT_ERA_NAMES,
+ UDAT_NARROW_MONTHS,
+ UDAT_NARROW_WEEKDAYS,
+ UDAT_STANDALONE_MONTHS,
+ UDAT_STANDALONE_SHORT_MONTHS,
+ UDAT_STANDALONE_NARROW_MONTHS,
+ UDAT_STANDALONE_WEEKDAYS,
+ UDAT_STANDALONE_SHORT_WEEKDAYS,
+ UDAT_STANDALONE_NARROW_WEEKDAYS,
+ UDAT_QUARTERS,
+ UDAT_SHORT_QUARTERS,
+ UDAT_STANDALONE_QUARTERS,
+ UDAT_STANDALONE_SHORT_QUARTERS,
+ UDAT_SHORTER_WEEKDAYS,
+ UDAT_STANDALONE_SHORTER_WEEKDAYS,
+ UDAT_CYCLIC_YEARS_WIDE,
+ UDAT_CYCLIC_YEARS_ABBREVIATED,
+ UDAT_CYCLIC_YEARS_NARROW,
+ UDAT_ZODIAC_NAMES_WIDE,
+ UDAT_ZODIAC_NAMES_ABBREVIATED,
+ UDAT_ZODIAC_NAMES_NARROW
+};
+
static int32_t
udat_countAvailable()
{
@@ -563,6 +647,13 @@ udat_close(UDateFormat* format)
MOZ_CRASH("udat_close: Intl API disabled");
}
+static int32_t
+udat_getSymbols(const UDateFormat *fmt, UDateFormatSymbolType type, int32_t symbolIndex,
+ UChar *result, int32_t resultLength, UErrorCode *status)
+{
+ MOZ_CRASH("udat_getSymbols: Intl API disabled");
+}
+
#endif
@@ -765,42 +856,53 @@ static const JSFunctionSpec collator_methods[] = {
};
/**
- * Collator constructor.
- * Spec: ECMAScript Internationalization API Specification, 10.1
+ * 10.1.2 Intl.Collator([ locales [, options]])
+ *
+ * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
Collator(JSContext* cx, const CallArgs& args, bool construct)
{
RootedObject obj(cx);
+ // We're following ECMA-402 1st Edition when Collator is called because of
+ // backward compatibility issues.
+ // See https://github.com/tc39/ecma402/issues/57
if (!construct) {
- // 10.1.2.1 step 3
+ // ES Intl 1st ed., 10.1.2.1 step 3
JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
if (!intl)
return false;
RootedValue self(cx, args.thisv());
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
- // 10.1.2.1 step 4
+ // ES Intl 1st ed., 10.1.2.1 step 4
obj = ToObject(cx, self);
if (!obj)
return false;
- // 10.1.2.1 step 5
+ // ES Intl 1st ed., 10.1.2.1 step 5
bool extensible;
if (!IsExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
- // 10.1.2.1 step 3.a
+ // ES Intl 1st ed., 10.1.2.1 step 3.a
construct = true;
}
}
if (construct) {
- // 10.1.3.1 paragraph 2
- RootedObject proto(cx, cx->global()->getOrCreateCollatorPrototype(cx));
- if (!proto)
+ // Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
+ RootedObject proto(cx);
+ if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
+
+ if (!proto) {
+ proto = cx->global()->getOrCreateCollatorPrototype(cx);
+ if (!proto)
+ return false;
+ }
+
obj = NewObjectWithGivenProto(cx, &CollatorClass, proto);
if (!obj)
return false;
@@ -808,15 +910,13 @@ Collator(JSContext* cx, const CallArgs& args, bool construct)
obj->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
}
- // 10.1.2.1 steps 1 and 2; 10.1.3.1 steps 1 and 2
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
- // 10.1.2.1 step 6; 10.1.3.1 step 3
+ // Step 6.
if (!IntlInitialize(cx, obj, cx->names().InitializeCollator, locales, options))
return false;
- // 10.1.2.1 steps 3.a and 7
args.rval().setObject(*obj);
return true;
}
@@ -833,6 +933,7 @@ js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
+ MOZ_ASSERT(!args.isConstructing());
// intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
// be used with "new", but it still has to be treated as a constructor.
return Collator(cx, args, true);
@@ -1257,42 +1358,53 @@ static const JSFunctionSpec numberFormat_methods[] = {
};
/**
- * NumberFormat constructor.
- * Spec: ECMAScript Internationalization API Specification, 11.1
+ * 11.2.1 Intl.NumberFormat([ locales [, options]])
+ *
+ * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
{
RootedObject obj(cx);
+ // We're following ECMA-402 1st Edition when NumberFormat is called
+ // because of backward compatibility issues.
+ // See https://github.com/tc39/ecma402/issues/57
if (!construct) {
- // 11.1.2.1 step 3
+ // ES Intl 1st ed., 11.1.2.1 step 3
JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
if (!intl)
return false;
RootedValue self(cx, args.thisv());
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
- // 11.1.2.1 step 4
+ // ES Intl 1st ed., 11.1.2.1 step 4
obj = ToObject(cx, self);
if (!obj)
return false;
- // 11.1.2.1 step 5
+ // ES Intl 1st ed., 11.1.2.1 step 5
bool extensible;
if (!IsExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
- // 11.1.2.1 step 3.a
+ // ES Intl 1st ed., 11.1.2.1 step 3.a
construct = true;
}
}
if (construct) {
- // 11.1.3.1 paragraph 2
- RootedObject proto(cx, cx->global()->getOrCreateNumberFormatPrototype(cx));
- if (!proto)
+ // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
+ RootedObject proto(cx);
+ if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
+
+ if (!proto) {
+ proto = cx->global()->getOrCreateNumberFormatPrototype(cx);
+ if (!proto)
+ return false;
+ }
+
obj = NewObjectWithGivenProto(cx, &NumberFormatClass, proto);
if (!obj)
return false;
@@ -1300,15 +1412,13 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
obj->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
}
- // 11.1.2.1 steps 1 and 2; 11.1.3.1 steps 1 and 2
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
- // 11.1.2.1 step 6; 11.1.3.1 step 3
+ // Step 3.
if (!IntlInitialize(cx, obj, cx->names().InitializeNumberFormat, locales, options))
return false;
- // 11.1.2.1 steps 3.a and 7
args.rval().setObject(*obj);
return true;
}
@@ -1325,6 +1435,7 @@ js::intl_NumberFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
+ MOZ_ASSERT(!args.isConstructing());
// intl_NumberFormat is an intrinsic for self-hosted JavaScript, so it
// cannot be used with "new", but it still has to be treated as a
// constructor.
@@ -1725,42 +1836,53 @@ static const JSFunctionSpec dateTimeFormat_methods[] = {
};
/**
- * DateTimeFormat constructor.
- * Spec: ECMAScript Internationalization API Specification, 12.1
+ * 12.2.1 Intl.DateTimeFormat([ locales [, options]])
+ *
+ * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
{
RootedObject obj(cx);
+ // We're following ECMA-402 1st Edition when DateTimeFormat is called
+ // because of backward compatibility issues.
+ // See https://github.com/tc39/ecma402/issues/57
if (!construct) {
- // 12.1.2.1 step 3
+ // ES Intl 1st ed., 12.1.2.1 step 3
JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
if (!intl)
return false;
RootedValue self(cx, args.thisv());
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
- // 12.1.2.1 step 4
+ // ES Intl 1st ed., 12.1.2.1 step 4
obj = ToObject(cx, self);
if (!obj)
return false;
- // 12.1.2.1 step 5
+ // ES Intl 1st ed., 12.1.2.1 step 5
bool extensible;
if (!IsExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
- // 12.1.2.1 step 3.a
+ // ES Intl 1st ed., 12.1.2.1 step 3.a
construct = true;
}
}
if (construct) {
- // 12.1.3.1 paragraph 2
- RootedObject proto(cx, cx->global()->getOrCreateDateTimeFormatPrototype(cx));
- if (!proto)
+ // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
+ RootedObject proto(cx);
+ if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
+
+ if (!proto) {
+ proto = cx->global()->getOrCreateDateTimeFormatPrototype(cx);
+ if (!proto)
+ return false;
+ }
+
obj = NewObjectWithGivenProto(cx, &DateTimeFormatClass, proto);
if (!obj)
return false;
@@ -1768,15 +1890,13 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
obj->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
}
- // 12.1.2.1 steps 1 and 2; 12.1.3.1 steps 1 and 2
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
- // 12.1.2.1 step 6; 12.1.3.1 step 3
+ // Step 3.
if (!IntlInitialize(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
return false;
- // 12.1.2.1 steps 3.a and 7
args.rval().setObject(*obj);
return true;
}
@@ -1793,6 +1913,7 @@ js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
+ MOZ_ASSERT(!args.isConstructing());
// intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it
// cannot be used with "new", but it still has to be treated as a
// constructor.
@@ -2891,6 +3012,296 @@ js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
return true;
}
+template<size_t N>
+inline bool
+MatchPart(const char** pattern, const char (&part)[N])
+{
+ if (strncmp(*pattern, part, N - 1))
+ return false;
+
+ *pattern += N - 1;
+ return true;
+}
+
+bool
+js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 3);
+ // 1. Assert: locale is a string.
+ MOZ_ASSERT(args[0].isString());
+ // 2. Assert: style is a string.
+ MOZ_ASSERT(args[1].isString());
+ // 3. Assert: keys is an Array.
+ MOZ_ASSERT(args[2].isObject());
+
+ JSAutoByteString locale(cx, args[0].toString());
+ if (!locale)
+ return false;
+
+ JSAutoByteString style(cx, args[1].toString());
+ if (!style)
+ return false;
+
+ RootedArrayObject keys(cx, &args[2].toObject().as<ArrayObject>());
+ if (!keys)
+ return false;
+
+ // 4. Let result be ArrayCreate(0).
+ RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length()));
+ if (!result)
+ return false;
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ UDateFormat* fmt =
+ udat_open(UDAT_DEFAULT, UDAT_DEFAULT, icuLocale(locale.ptr()),
+ nullptr, 0, nullptr, 0, &status);
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+ ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
+
+ // UDateTimePatternGenerator will be needed for translations of date and
+ // time fields like "month", "week", "day" etc.
+ UDateTimePatternGenerator* dtpg = udatpg_open(icuLocale(locale.ptr()), &status);
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+ ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg);
+
+ RootedValue keyValue(cx);
+ RootedString keyValStr(cx);
+ RootedValue wordVal(cx);
+ Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
+ if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
+ return false;
+
+ // 5. For each element of keys,
+ for (uint32_t i = 0; i < keys->length(); i++) {
+ /**
+ * We iterate over keys array looking for paths that we have code
+ * branches for.
+ *
+ * For any unknown path branch, the wordVal will keep NullValue and
+ * we'll throw at the end.
+ */
+
+ if (!GetElement(cx, keys, keys, i, &keyValue))
+ return false;
+
+ JSAutoByteString pattern;
+ keyValStr = keyValue.toString();
+ if (!pattern.encodeUtf8(cx, keyValStr))
+ return false;
+
+ wordVal.setNull();
+
+ // 5.a. Perform an implementation dependent algorithm to map a key to a
+ // corresponding display name.
+ const char* pat = pattern.ptr();
+
+ if (!MatchPart(&pat, "dates")) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ if (!MatchPart(&pat, "/")) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ if (MatchPart(&pat, "fields")) {
+ if (!MatchPart(&pat, "/")) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ UDateTimePatternField fieldType;
+
+ if (MatchPart(&pat, "year")) {
+ fieldType = UDATPG_YEAR_FIELD;
+ } else if (MatchPart(&pat, "month")) {
+ fieldType = UDATPG_MONTH_FIELD;
+ } else if (MatchPart(&pat, "week")) {
+ fieldType = UDATPG_WEEK_OF_YEAR_FIELD;
+ } else if (MatchPart(&pat, "day")) {
+ fieldType = UDATPG_DAY_FIELD;
+ } else {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ // This part must be the final part with no trailing data.
+ if (*pat != '\0') {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ int32_t resultSize;
+
+ const UChar* value = udatpg_getAppendItemName(dtpg, fieldType, &resultSize);
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+
+ JSString* word = NewStringCopyN<CanGC>(cx, UCharToChar16(value), resultSize);
+ if (!word)
+ return false;
+
+ wordVal.setString(word);
+ } else if (MatchPart(&pat, "gregorian")) {
+ if (!MatchPart(&pat, "/")) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ UDateFormatSymbolType symbolType;
+ int32_t index;
+
+ if (MatchPart(&pat, "months")) {
+ if (!MatchPart(&pat, "/")) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ if (equal(style, "narrow")) {
+ symbolType = UDAT_STANDALONE_NARROW_MONTHS;
+ } else if (equal(style, "short")) {
+ symbolType = UDAT_STANDALONE_SHORT_MONTHS;
+ } else {
+ MOZ_ASSERT(equal(style, "long"));
+ symbolType = UDAT_STANDALONE_MONTHS;
+ }
+
+ if (MatchPart(&pat, "january")) {
+ index = UCAL_JANUARY;
+ } else if (MatchPart(&pat, "february")) {
+ index = UCAL_FEBRUARY;
+ } else if (MatchPart(&pat, "march")) {
+ index = UCAL_MARCH;
+ } else if (MatchPart(&pat, "april")) {
+ index = UCAL_APRIL;
+ } else if (MatchPart(&pat, "may")) {
+ index = UCAL_MAY;
+ } else if (MatchPart(&pat, "june")) {
+ index = UCAL_JUNE;
+ } else if (MatchPart(&pat, "july")) {
+ index = UCAL_JULY;
+ } else if (MatchPart(&pat, "august")) {
+ index = UCAL_AUGUST;
+ } else if (MatchPart(&pat, "september")) {
+ index = UCAL_SEPTEMBER;
+ } else if (MatchPart(&pat, "october")) {
+ index = UCAL_OCTOBER;
+ } else if (MatchPart(&pat, "november")) {
+ index = UCAL_NOVEMBER;
+ } else if (MatchPart(&pat, "december")) {
+ index = UCAL_DECEMBER;
+ } else {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+ } else if (MatchPart(&pat, "weekdays")) {
+ if (!MatchPart(&pat, "/")) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ if (equal(style, "narrow")) {
+ symbolType = UDAT_STANDALONE_NARROW_WEEKDAYS;
+ } else if (equal(style, "short")) {
+ symbolType = UDAT_STANDALONE_SHORT_WEEKDAYS;
+ } else {
+ MOZ_ASSERT(equal(style, "long"));
+ symbolType = UDAT_STANDALONE_WEEKDAYS;
+ }
+
+ if (MatchPart(&pat, "monday")) {
+ index = UCAL_MONDAY;
+ } else if (MatchPart(&pat, "tuesday")) {
+ index = UCAL_TUESDAY;
+ } else if (MatchPart(&pat, "wednesday")) {
+ index = UCAL_WEDNESDAY;
+ } else if (MatchPart(&pat, "thursday")) {
+ index = UCAL_THURSDAY;
+ } else if (MatchPart(&pat, "friday")) {
+ index = UCAL_FRIDAY;
+ } else if (MatchPart(&pat, "saturday")) {
+ index = UCAL_SATURDAY;
+ } else if (MatchPart(&pat, "sunday")) {
+ index = UCAL_SUNDAY;
+ } else {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+ } else if (MatchPart(&pat, "dayperiods")) {
+ if (!MatchPart(&pat, "/")) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ symbolType = UDAT_AM_PMS;
+
+ if (MatchPart(&pat, "am")) {
+ index = UCAL_AM;
+ } else if (MatchPart(&pat, "pm")) {
+ index = UCAL_PM;
+ } else {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+ } else {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ // This part must be the final part with no trailing data.
+ if (*pat != '\0') {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ int32_t resultSize =
+ udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()),
+ INITIAL_CHAR_BUFFER_SIZE, &status);
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ if (!chars.resize(resultSize))
+ return false;
+ status = U_ZERO_ERROR;
+ udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()),
+ resultSize, &status);
+ }
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+
+ JSString* word = NewStringCopyN<CanGC>(cx, chars.begin(), resultSize);
+ if (!word)
+ return false;
+
+ wordVal.setString(word);
+ } else {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
+ return false;
+ }
+
+ MOZ_ASSERT(wordVal.isString());
+
+ // 5.b. Append the result string to result.
+ if (!DefineElement(cx, result, i, wordVal))
+ return false;
+ }
+
+ // 6. Return result.
+ args.rval().setObject(*result);
+ return true;
+}
+
/******************** Intl ********************/
const Class js::IntlClass = {