Diff
Modified: trunk/JSTests/ChangeLog (281373 => 281374)
--- trunk/JSTests/ChangeLog 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/JSTests/ChangeLog 2021-08-21 14:33:08 UTC (rev 281374)
@@ -1,5 +1,19 @@
2021-08-21 Yusuke Suzuki <ysuz...@apple.com>
+ [JSC] Intl Locale Info
+ https://bugs.webkit.org/show_bug.cgi?id=227830
+
+ Reviewed by Ross Kirsling.
+
+ * stress/intl-locale-info.js: Added.
+ (shouldBe):
+ (throw.new.Error):
+ (let.enGB.new.Intl.Locale.shouldBe):
+ (let.l.new.Intl.Locale.shouldBe):
+ * test262/config.yaml:
+
+2021-08-21 Yusuke Suzuki <ysuz...@apple.com>
+
[JSC] Extend Intl TimeZoneName Option
https://bugs.webkit.org/show_bug.cgi?id=227831
Added: trunk/JSTests/stress/intl-locale-info.js (0 => 281374)
--- trunk/JSTests/stress/intl-locale-info.js (rev 0)
+++ trunk/JSTests/stress/intl-locale-info.js 2021-08-21 14:33:08 UTC (rev 281374)
@@ -0,0 +1,106 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${expected} but got ${actual}`);
+}
+
+{
+ let he = new Intl.Locale("he")
+ shouldBe(JSON.stringify(he.weekInfo), `{"firstDay":7,"weekendStart":5,"weekendEnd":6,"minimalDays":1}`);
+ let af = new Intl.Locale("af")
+ shouldBe(JSON.stringify(af.weekInfo), `{"firstDay":7,"weekendStart":6,"weekendEnd":7,"minimalDays":1}`);
+ let enGB = new Intl.Locale("en-GB")
+ shouldBe(JSON.stringify(enGB.weekInfo), `{"firstDay":1,"weekendStart":6,"weekendEnd":7,"minimalDays":4}`);
+}
+{
+ let l = new Intl.Locale("ar")
+ shouldBe(JSON.stringify(l.textInfo), `{"direction":"rtl"}`);
+}
+{
+ let locale = new Intl.Locale("ar")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory","coptic","islamic","islamicc","islamic-tbla"]`);
+ shouldBe(JSON.stringify(locale.collations), `["compat","emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+ let ns = JSON.stringify(locale.numberingSystems);
+ shouldBe(ns === `["arab"]` || ns === `["latn"]`, true);
+ shouldBe(locale.timeZones, undefined);
+}
+{
+ let locale = new Intl.Locale("ar-EG")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory","coptic","islamic","islamicc","islamic-tbla"]`);
+ shouldBe(JSON.stringify(locale.collations), `["compat","emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["arab"]`);
+ shouldBe(JSON.stringify(locale.timeZones), `["Africa/Cairo"]`);
+}
+{
+ let locale = new Intl.Locale("ar-SA")
+ shouldBe(JSON.stringify(locale.calendars), `["islamic-umalqura","islamic-rgsa","islamic","gregory"]`);
+ shouldBe(JSON.stringify(locale.collations), `["compat","emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["arab"]`);
+ shouldBe(JSON.stringify(locale.timeZones), `["Asia/Riyadh"]`);
+}
+{
+ let locale = new Intl.Locale("ja")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory","japanese"]`);
+ shouldBe(JSON.stringify(locale.collations), `["unihan","emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h23"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+ shouldBe(locale.timeZones, undefined);
+}
+{
+ let locale = new Intl.Locale("ja-JP")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory","japanese"]`);
+ shouldBe(JSON.stringify(locale.collations), `["unihan","emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h23"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+ shouldBe(JSON.stringify(locale.timeZones), `["Asia/Tokyo"]`);
+}
+{
+ let locale = new Intl.Locale("en-US")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory"]`);
+ shouldBe(JSON.stringify(locale.collations), `["emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+ shouldBe(JSON.stringify(locale.timeZones), `["America/Adak","America/Anchorage","America/Boise","America/Chicago","America/Denver","America/Detroit","America/Indiana/Knox","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Tell_City","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Indianapolis","America/Juneau","America/Kentucky/Monticello","America/Los_Angeles","America/Louisville","America/Menominee","America/Metlakatla","America/New_York","America/Nome","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Phoenix","America/Sitka","America/Yakutat","Pacific/Honolulu"]`);
+}
+{
+ let locale = new Intl.Locale("en-NZ")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory"]`);
+ shouldBe(JSON.stringify(locale.collations), `["emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+ shouldBe(JSON.stringify(locale.timeZones), `["Pacific/Auckland","Pacific/Chatham"]`);
+}
+{
+ let locale = new Intl.Locale("zh")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory","chinese"]`);
+ shouldBe(JSON.stringify(locale.collations), `["pinyin","big5han","gb2312","stroke","unihan","zhuyin","emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+ shouldBe(locale.timeZones, undefined);
+}
+{
+ let locale = new Intl.Locale("zh-TW")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory","roc","chinese"]`);
+ shouldBe(JSON.stringify(locale.collations), `["stroke","big5han","gb2312","pinyin","unihan","zhuyin","emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+ shouldBe(JSON.stringify(locale.timeZones), `["Asia/Taipei"]`);
+}
+{
+ let locale = new Intl.Locale("zh-HK")
+ shouldBe(JSON.stringify(locale.calendars), `["gregory","chinese"]`);
+ shouldBe(JSON.stringify(locale.collations), `["stroke","big5han","gb2312","pinyin","unihan","zhuyin","emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+ shouldBe(JSON.stringify(locale.timeZones), `["Asia/Hong_Kong"]`);
+}
+{
+ let locale = new Intl.Locale("fa")
+ shouldBe(JSON.stringify(locale.calendars), `["persian","gregory","islamic","islamicc","islamic-tbla"]`);
+ shouldBe(JSON.stringify(locale.collations), `["emoji","eor"]`);
+ shouldBe(JSON.stringify(locale.hourCycles), `["h23"]`);
+ shouldBe(JSON.stringify(locale.numberingSystems), `["arabext"]`);
+ shouldBe(locale.timeZones, undefined);
+}
Modified: trunk/JSTests/test262/config.yaml (281373 => 281374)
--- trunk/JSTests/test262/config.yaml 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/JSTests/test262/config.yaml 2021-08-21 14:33:08 UTC (rev 281374)
@@ -24,7 +24,6 @@
# https://bugs.webkit.org/show_bug.cgi?id=174931
- regexp-lookbehind
- cleanupSome
- - Intl.Locale-info
- resizable-arraybuffer
- Object.hasOwn
- Temporal
Modified: trunk/Source/_javascript_Core/ChangeLog (281373 => 281374)
--- trunk/Source/_javascript_Core/ChangeLog 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-08-21 14:33:08 UTC (rev 281374)
@@ -1,5 +1,32 @@
2021-08-21 Yusuke Suzuki <ysuz...@apple.com>
+ [JSC] Intl Locale Info
+ https://bugs.webkit.org/show_bug.cgi?id=227830
+
+ Reviewed by Ross Kirsling.
+
+ This patch implements Intl.Locale's extension (Intl Locale Info proposal)[1], which is already stage 3.
+ Intl.Locale#{calendars,collations,hourCycles,numberingSystems,timeZones} can return array of preferred
+ configuration for the given locale. And Intl.Locale#textInfo can return text layout direction and Intl.Locale#weekInfo
+ can return weekday information (e.g. when weekend starts).
+
+ [1]: https://github.com/tc39/proposal-intl-locale-info
+
+ * runtime/IntlLocale.cpp:
+ (JSC::createArrayFromStringVector):
+ (JSC::IntlLocale::calendars):
+ (JSC::IntlLocale::collations):
+ (JSC::IntlLocale::hourCycles):
+ (JSC::IntlLocale::numberingSystems):
+ (JSC::IntlLocale::timeZones):
+ (JSC::IntlLocale::textInfo):
+ (JSC::IntlLocale::weekInfo):
+ * runtime/IntlLocale.h:
+ * runtime/IntlLocalePrototype.cpp:
+ (JSC::JSC_DEFINE_CUSTOM_GETTER):
+
+2021-08-21 Yusuke Suzuki <ysuz...@apple.com>
+
[JSC] Extend Intl TimeZoneName Option
https://bugs.webkit.org/show_bug.cgi?id=227831
Modified: trunk/Source/_javascript_Core/runtime/IntlCollator.cpp (281373 => 281374)
--- trunk/Source/_javascript_Core/runtime/IntlCollator.cpp 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/_javascript_Core/runtime/IntlCollator.cpp 2021-08-21 14:33:08 UTC (rev 281374)
@@ -90,23 +90,17 @@
UErrorCode status = U_ZERO_ERROR;
auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucol_getKeywordValuesForLocale("collation", locale.utf8().data(), false, &status));
if (U_SUCCESS(status)) {
- const char* collation;
- while ((collation = uenum_next(enumeration.get(), nullptr, &status)) && U_SUCCESS(status)) {
+ const char* pointer;
+ int32_t length = 0;
+ while ((pointer = uenum_next(enumeration.get(), &length, &status)) && U_SUCCESS(status)) {
// 10.2.3 "The values "standard" and "search" must not be used as elements in any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co array."
- if (!strcmp(collation, "standard") || !strcmp(collation, "search"))
+ String collation(pointer, length);
+ if (collation == "standard"_s || collation == "search"_s)
continue;
-
- // Map keyword values to BCP 47 equivalents.
- if (!strcmp(collation, "dictionary"))
- keyLocaleData.append("dict"_s);
- else if (!strcmp(collation, "gb2312han"))
- keyLocaleData.append("gb2312"_s);
- else if (!strcmp(collation, "phonebook"))
- keyLocaleData.append("phonebk"_s);
- else if (!strcmp(collation, "traditional"))
- keyLocaleData.append("trad"_s);
+ if (auto mapped = mapICUCollationKeywordToBCP47(collation))
+ keyLocaleData.append(WTFMove(mapped.value()));
else
- keyLocaleData.append(collation);
+ keyLocaleData.append(WTFMove(collation));
}
}
break;
Modified: trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp (281373 => 281374)
--- trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp 2021-08-21 14:33:08 UTC (rev 281374)
@@ -165,13 +165,8 @@
ASSERT(U_SUCCESS(status));
String calendar = String(availableName, nameLength);
keyLocaleData.append(calendar);
- // Ensure aliases used in language tag are allowed.
- if (calendar == "gregorian")
- keyLocaleData.append("gregory"_s);
- else if (calendar == "islamic-civil")
- keyLocaleData.append("islamicc"_s);
- else if (calendar == "ethiopic-amete-alem")
- keyLocaleData.append("ethioaa"_s);
+ if (auto mapped = mapICUCalendarKeywordToBCP47(calendar))
+ keyLocaleData.append(WTFMove(mapped.value()));
}
uenum_close(calendars);
break;
Modified: trunk/Source/_javascript_Core/runtime/IntlLocale.cpp (281373 => 281374)
--- trunk/Source/_javascript_Core/runtime/IntlLocale.cpp 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/_javascript_Core/runtime/IntlLocale.cpp 2021-08-21 14:33:08 UTC (rev 281374)
@@ -29,7 +29,12 @@
#include "IntlObjectInlines.h"
#include "JSCInlines.h"
+#include <unicode/ucal.h>
+#include <unicode/ucol.h>
+#include <unicode/udat.h>
+#include <unicode/udatpg.h>
#include <unicode/uloc.h>
+#include <unicode/unumsys.h>
#include <wtf/unicode/icu/ICUHelpers.h>
namespace JSC {
@@ -532,4 +537,333 @@
return m_numeric;
}
+static inline JSArray* createArrayFromStringVector(JSGlobalObject* globalObject, Vector<String, 1>&& elements)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSArray* result = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), elements.size());
+ if (!result) {
+ throwOutOfMemoryError(globalObject, scope);
+ return nullptr;
+ }
+ for (unsigned index = 0; index < elements.size(); ++index) {
+ result->putDirectIndex(globalObject, index, jsString(vm, WTFMove(elements[index])));
+ RETURN_IF_EXCEPTION(scope, { });
+ }
+ return result;
+}
+
+JSArray* IntlLocale::calendars(JSGlobalObject* globalObject)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Vector<String, 1> elements;
+
+ String preferred = calendar();
+ if (!preferred.isEmpty()) {
+ elements.append(WTFMove(preferred));
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ constexpr bool commonlyUsed = true;
+ auto calendars = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucal_getKeywordValuesForLocale("calendar", m_localeID.data(), commonlyUsed, &status));
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+
+ const char* pointer;
+ int32_t length = 0;
+ while ((pointer = uenum_next(calendars.get(), &length, &status)) && U_SUCCESS(status)) {
+ String calendar(pointer, length);
+ if (auto mapped = mapICUCalendarKeywordToBCP47(calendar))
+ elements.append(WTFMove(mapped.value()));
+ else
+ elements.append(WTFMove(calendar));
+ }
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSArray* IntlLocale::collations(JSGlobalObject* globalObject)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Vector<String, 1> elements;
+
+ String preferred = collation();
+ if (!preferred.isEmpty()) {
+ elements.append(WTFMove(preferred));
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ constexpr bool commonlyUsed = true;
+ auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucol_getKeywordValuesForLocale("collation", m_localeID.data(), commonlyUsed, &status));
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+
+ const char* pointer;
+ int32_t length = 0;
+ while ((pointer = uenum_next(enumeration.get(), &length, &status)) && U_SUCCESS(status)) {
+ String collation(pointer, length);
+ // 1.1.3 step 4, The values "standard" and "search" must be excluded from list.
+ if (collation == "standard"_s || collation == "search"_s)
+ continue;
+ if (auto mapped = mapICUCollationKeywordToBCP47(collation))
+ elements.append(WTFMove(mapped.value()));
+ else
+ elements.append(WTFMove(collation));
+ }
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSArray* IntlLocale::hourCycles(JSGlobalObject* globalObject)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Vector<String, 1> elements;
+
+ String preferred = hourCycle();
+ if (!preferred.isEmpty()) {
+ elements.append(WTFMove(preferred));
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ auto generator = std::unique_ptr<UDateTimePatternGenerator, ICUDeleter<udatpg_close>>(udatpg_open(m_localeID.data(), &status));
+ if (U_FAILURE(status))
+ return nullptr;
+
+ // Use "j" skeleton and parse pattern to retrieve the configured hour-cycle information.
+ constexpr const UChar skeleton[] = { 'j', 0 };
+ Vector<UChar, 32> pattern;
+ status = callBufferProducingFunction(udatpg_getBestPatternWithOptions, generator.get(), skeleton, 1, UDATPG_MATCH_HOUR_FIELD_LENGTH, pattern);
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+
+ for (unsigned i = 0; i < pattern.size(); ++i) {
+ UChar currentCharacter = pattern[i];
+ if (!isASCIIAlpha(currentCharacter))
+ continue;
+
+ while (i + 1 < pattern.size() && pattern[i + 1] == currentCharacter)
+ ++i;
+
+ switch (currentCharacter) {
+ case 'h': {
+ elements.append("h12"_s);
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+ }
+ case 'H': {
+ elements.append("h23"_s);
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+ }
+ case 'k': {
+ elements.append("h24"_s);
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+ }
+ case 'K': {
+ elements.append("h11"_s);
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+ }
+ default:
+ break;
+ }
+ }
+
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSArray* IntlLocale::numberingSystems(JSGlobalObject* globalObject)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Vector<String, 1> elements;
+ String preferred = numberingSystem();
+ if (!preferred.isEmpty()) {
+ elements.append(WTFMove(preferred));
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ auto numberingSystem = std::unique_ptr<UNumberingSystem, ICUDeleter<unumsys_close>>(unumsys_open(m_localeID.data(), &status));
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+ elements.append(unumsys_getName(numberingSystem.get()));
+
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSValue IntlLocale::timeZones(JSGlobalObject* globalObject)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Vector<String, 1> elements;
+
+ // 11.6-3 Let region be the substring of locale corresponding to the unicode_region_subtag production of the unicode_language_id.
+ String region = this->region();
+ if (region.isEmpty())
+ return jsUndefined();
+
+ UErrorCode status = U_ZERO_ERROR;
+ auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, region.utf8().data(), nullptr, &status));
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return { };
+ }
+
+ int32_t length;
+ const char* collation;
+ while ((collation = uenum_next(enumeration.get(), &length, &status)) && U_SUCCESS(status))
+ elements.constructAndAppend(collation, length);
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return { };
+ }
+
+ RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSObject* IntlLocale::textInfo(JSGlobalObject* globalObject)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ UErrorCode status = U_ZERO_ERROR;
+ ULayoutType layout = uloc_getCharacterOrientation(m_localeID.data(), &status);
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+
+ JSString* layoutString = nullptr;
+ switch (layout) {
+ default:
+ case ULOC_LAYOUT_LTR:
+ layoutString = jsString(vm, "ltr"_s);
+ break;
+ case ULOC_LAYOUT_RTL:
+ layoutString = jsString(vm, "rtl"_s);
+ break;
+ case ULOC_LAYOUT_TTB:
+ layoutString = jsString(vm, "ttb"_s);
+ break;
+ case ULOC_LAYOUT_BTT:
+ layoutString = jsString(vm, "btt"_s);
+ break;
+ }
+
+ JSObject* result = constructEmptyObject(globalObject);
+ result->putDirect(vm, Identifier::fromString(vm, "direction"), layoutString);
+ return result;
+}
+
+JSObject* IntlLocale::weekInfo(JSGlobalObject* globalObject)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ UErrorCode status = U_ZERO_ERROR;
+ auto calendar = std::unique_ptr<UCalendar, ICUDeleter<ucal_close>>(ucal_open(nullptr, 0, m_localeID.data(), UCAL_DEFAULT, &status));
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+
+ int32_t firstDayOfWeek = ucal_getAttribute(calendar.get(), UCAL_FIRST_DAY_OF_WEEK);
+ int32_t minimalDays = ucal_getAttribute(calendar.get(), UCAL_MINIMAL_DAYS_IN_FIRST_WEEK);
+
+ auto canonicalizeDayOfWeekType = [](UCalendarWeekdayType type) {
+ switch (type) {
+ // UCAL_WEEKEND_ONSET is a day that starts as a weekday and transitions to the weekend. It means this is WeekDay.
+ case UCAL_WEEKEND_ONSET:
+ case UCAL_WEEKDAY:
+ return UCAL_WEEKDAY;
+ // UCAL_WEEKEND_CEASE is a day that starts as the weekend and transitions to a weekday. It means this is WeekEnd.
+ case UCAL_WEEKEND_CEASE:
+ case UCAL_WEEKEND:
+ return UCAL_WEEKEND;
+ default:
+ return UCAL_WEEKEND;
+ }
+ };
+
+ static_assert(UCAL_SUNDAY == 1);
+ static_assert(UCAL_SATURDAY == 7);
+ UCalendarWeekdayType previous = canonicalizeDayOfWeekType(ucal_getDayOfWeekType(calendar.get(), UCAL_SATURDAY, &status));
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+
+ int32_t weekendStart = 0;
+ int32_t weekendEnd = 0;
+ for (int32_t day = UCAL_SUNDAY; day <= UCAL_SATURDAY; ++day) {
+ UCalendarWeekdayType type = canonicalizeDayOfWeekType(ucal_getDayOfWeekType(calendar.get(), static_cast<UCalendarDaysOfWeek>(day), &status));
+ if (!U_SUCCESS(status)) {
+ throwTypeError(globalObject, scope, "invalid locale"_s);
+ return nullptr;
+ }
+ if (previous != type) {
+ switch (type) {
+ case UCAL_WEEKDAY: // WeekEnd => WeekDay
+ if (day == UCAL_SUNDAY)
+ weekendEnd = UCAL_SATURDAY;
+ else
+ weekendEnd = day - 1;
+ break;
+ case UCAL_WEEKEND: // WeekDay => WeekEnd
+ weekendStart = day;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ }
+ previous = type;
+ }
+
+ auto convertUCalendarDaysOfWeekToMondayBasedDay = [](int32_t day) -> int32_t {
+ // Convert from
+ // Sunday => 1
+ // Saturday => 7
+ // to
+ // Monday => 1
+ // Sunday => 7
+ if (day == UCAL_SUNDAY)
+ return 7;
+ return day - 1;
+ };
+
+ JSObject* result = constructEmptyObject(globalObject);
+ result->putDirect(vm, Identifier::fromString(vm, "firstDay"), jsNumber(convertUCalendarDaysOfWeekToMondayBasedDay(firstDayOfWeek)));
+ result->putDirect(vm, Identifier::fromString(vm, "weekendStart"), jsNumber(convertUCalendarDaysOfWeekToMondayBasedDay(weekendStart)));
+ result->putDirect(vm, Identifier::fromString(vm, "weekendEnd"), jsNumber(convertUCalendarDaysOfWeekToMondayBasedDay(weekendEnd)));
+ result->putDirect(vm, Identifier::fromString(vm, "minimalDays"), jsNumber(minimalDays));
+ return result;
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/IntlLocale.h (281373 => 281374)
--- trunk/Source/_javascript_Core/runtime/IntlLocale.h 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/_javascript_Core/runtime/IntlLocale.h 2021-08-21 14:33:08 UTC (rev 281374)
@@ -69,6 +69,14 @@
const String& numberingSystem();
TriState numeric();
+ JSArray* calendars(JSGlobalObject*);
+ JSArray* collations(JSGlobalObject*);
+ JSArray* hourCycles(JSGlobalObject*);
+ JSArray* numberingSystems(JSGlobalObject*);
+ JSValue timeZones(JSGlobalObject*);
+ JSObject* textInfo(JSGlobalObject*);
+ JSObject* weekInfo(JSGlobalObject*);
+
private:
IntlLocale(VM&, Structure*);
void finishCreation(VM&);
Modified: trunk/Source/_javascript_Core/runtime/IntlLocalePrototype.cpp (281373 => 281374)
--- trunk/Source/_javascript_Core/runtime/IntlLocalePrototype.cpp 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/_javascript_Core/runtime/IntlLocalePrototype.cpp 2021-08-21 14:33:08 UTC (rev 281374)
@@ -36,14 +36,21 @@
static JSC_DECLARE_HOST_FUNCTION(IntlLocalePrototypeFuncToString);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterBaseName);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCalendar);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCalendars);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCaseFirst);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCollation);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCollations);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterHourCycle);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterHourCycles);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumeric);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumberingSystem);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumberingSystems);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterLanguage);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterScript);
static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterRegion);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterTimeZones);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterTextInfo);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterWeekInfo);
}
@@ -60,14 +67,21 @@
toString IntlLocalePrototypeFuncToString DontEnum|Function 0
baseName IntlLocalePrototypeGetterBaseName DontEnum|ReadOnly|CustomAccessor
calendar IntlLocalePrototypeGetterCalendar DontEnum|ReadOnly|CustomAccessor
+ calendars IntlLocalePrototypeGetterCalendars DontEnum|ReadOnly|CustomAccessor
caseFirst IntlLocalePrototypeGetterCaseFirst DontEnum|ReadOnly|CustomAccessor
collation IntlLocalePrototypeGetterCollation DontEnum|ReadOnly|CustomAccessor
+ collations IntlLocalePrototypeGetterCollations DontEnum|ReadOnly|CustomAccessor
hourCycle IntlLocalePrototypeGetterHourCycle DontEnum|ReadOnly|CustomAccessor
+ hourCycles IntlLocalePrototypeGetterHourCycles DontEnum|ReadOnly|CustomAccessor
numeric IntlLocalePrototypeGetterNumeric DontEnum|ReadOnly|CustomAccessor
numberingSystem IntlLocalePrototypeGetterNumberingSystem DontEnum|ReadOnly|CustomAccessor
+ numberingSystems IntlLocalePrototypeGetterNumberingSystems DontEnum|ReadOnly|CustomAccessor
language IntlLocalePrototypeGetterLanguage DontEnum|ReadOnly|CustomAccessor
script IntlLocalePrototypeGetterScript DontEnum|ReadOnly|CustomAccessor
region IntlLocalePrototypeGetterRegion DontEnum|ReadOnly|CustomAccessor
+ timeZones IntlLocalePrototypeGetterTimeZones DontEnum|ReadOnly|CustomAccessor
+ textInfo IntlLocalePrototypeGetterTextInfo DontEnum|ReadOnly|CustomAccessor
+ weekInfo IntlLocalePrototypeGetterWeekInfo DontEnum|ReadOnly|CustomAccessor
@end
*/
@@ -169,6 +183,19 @@
RELEASE_AND_RETURN(scope, JSValue::encode(calendar.isNull() ? jsUndefined() : jsString(vm, calendar)));
}
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.calendars
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterCalendars, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+ if (!locale)
+ return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.calendars called on value that's not an object initialized as a Locale"_s);
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(locale->calendars(globalObject)));
+}
+
// https://tc39.es/ecma402/#sec-Intl.Locale.prototype.caseFirst
JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterCaseFirst, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
{
@@ -197,6 +224,19 @@
RELEASE_AND_RETURN(scope, JSValue::encode(collation.isNull() ? jsUndefined() : jsString(vm, collation)));
}
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.collations
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterCollations, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+ if (!locale)
+ return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.collations called on value that's not an object initialized as a Locale"_s);
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(locale->collations(globalObject)));
+}
+
// https://tc39.es/ecma402/#sec-Intl.Locale.prototype.hourCycle
JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterHourCycle, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
{
@@ -211,6 +251,19 @@
RELEASE_AND_RETURN(scope, JSValue::encode(hourCycle.isNull() ? jsUndefined() : jsString(vm, hourCycle)));
}
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.hourcycles
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterHourCycles, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+ if (!locale)
+ return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.hourCycles called on value that's not an object initialized as a Locale"_s);
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(locale->hourCycles(globalObject)));
+}
+
// https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numeric
JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumeric, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
{
@@ -238,6 +291,19 @@
RELEASE_AND_RETURN(scope, JSValue::encode(numberingSystem.isNull() ? jsUndefined() : jsString(vm, numberingSystem)));
}
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.numberingSystems
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumberingSystems, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+ if (!locale)
+ return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.numberingSystems called on value that's not an object initialized as a Locale"_s);
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(locale->numberingSystems(globalObject)));
+}
+
// https://tc39.es/ecma402/#sec-Intl.Locale.prototype.language
JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterLanguage, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
{
@@ -280,4 +346,43 @@
RELEASE_AND_RETURN(scope, JSValue::encode(region.isEmpty() ? jsUndefined() : jsString(vm, region)));
}
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.timezones
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterTimeZones, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+ if (!locale)
+ return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.timeZones called on value that's not an object initialized as a Locale"_s);
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(locale->timeZones(globalObject)));
+}
+
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.textInfo
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterTextInfo, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+ if (!locale)
+ return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.textInfo called on value that's not an object initialized as a Locale"_s);
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(locale->textInfo(globalObject)));
+}
+
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.weekInfo
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterWeekInfo, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+ if (!locale)
+ return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.weekInfo called on value that's not an object initialized as a Locale"_s);
+
+ RELEASE_AND_RETURN(scope, JSValue::encode(locale->weekInfo(globalObject)));
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/IntlObject.cpp (281373 => 281374)
--- trunk/Source/_javascript_Core/runtime/IntlObject.cpp 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/_javascript_Core/runtime/IntlObject.cpp 2021-08-21 14:33:08 UTC (rev 281374)
@@ -1458,6 +1458,31 @@
#endif
}
+std::optional<String> mapICUCalendarKeywordToBCP47(const String& calendar)
+{
+ if (calendar == "gregorian"_s)
+ return "gregory"_s;
+ if (calendar == "islamic-civil"_s)
+ return "islamicc"_s;
+ if (calendar == "ethiopic-amete-alem"_s)
+ return "ethioaa"_s;
+ return std::nullopt;
+}
+
+std::optional<String> mapICUCollationKeywordToBCP47(const String& collation)
+{
+ // Map keyword values to BCP 47 equivalents.
+ if (collation == "dictionary"_s)
+ return "dict"_s;
+ if (collation == "gb2312han"_s)
+ return "gb2312"_s;
+ if (collation == "phonebook"_s)
+ return "phonebk"_s;
+ if (collation == "traditional"_s)
+ return "trad"_s;
+ return std::nullopt;
+}
+
JSC_DEFINE_HOST_FUNCTION(intlObjectFuncGetCanonicalLocales, (JSGlobalObject* globalObject, CallFrame* callFrame))
{
// Intl.getCanonicalLocales(locales)
Modified: trunk/Source/_javascript_Core/runtime/IntlObject.h (281373 => 281374)
--- trunk/Source/_javascript_Core/runtime/IntlObject.h 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/_javascript_Core/runtime/IntlObject.h 2021-08-21 14:33:08 UTC (rev 281374)
@@ -136,4 +136,7 @@
void operator()(UFieldPositionIterator*) const;
};
+std::optional<String> mapICUCollationKeywordToBCP47(const String&);
+std::optional<String> mapICUCalendarKeywordToBCP47(const String&);
+
} // namespace JSC