Changeset 281513 in webkit


Ignore:
Timestamp:
Aug 24, 2021 12:28:30 PM (11 months ago)
Author:
ysuzuki@apple.com
Message:

[JSC] Add Intl Enumeration APIs
https://bugs.webkit.org/show_bug.cgi?id=214795

Reviewed by Ross Kirsling.

JSTests:

  • stress/intl-datetimeformat.js:
  • stress/intl-enumeration.js: Added.

(shouldBe):
(shouldThrow):

  • stress/intl-locale-info.js:

(let.l.new.Intl.Locale.shouldBe):
(shouldBe):

Source/JavaScriptCore:

This patch implements Intl enumeration API[1] which is in stage-3.
Supported keys are, "calendar", "collation", "currency", "numberingSystem", "timeZone", and "unit".

[1]: https://github.com/tc39/proposal-intl-enumeration

  • runtime/IntlDateTimeFormat.cpp:

(JSC::IntlDateTimeFormat::localeData):
(JSC::IntlDateTimeFormat::initializeDateTimeFormat):

  • runtime/IntlLocale.cpp:

(JSC::createArrayFromStringVector): Deleted.

  • runtime/IntlNumberFormat.cpp:

(JSC::sanctionedSimpleUnitIdentifier):

  • runtime/IntlObject.cpp:

(JSC::IntlObject::finishCreation):
(JSC::mapICUCalendarKeywordToBCP47):
(JSC::availableCalendars):
(JSC::availableCollations):
(JSC::availableCurrencies):
(JSC::availableNumberingSystems):
(JSC::canonicalizeTimeZoneNameFromICUTimeZone):
(JSC::availableTimeZones):
(JSC::availableUnits):
(JSC::JSC_DEFINE_HOST_FUNCTION):
(JSC::createArrayFromStringVector):

  • runtime/IntlObject.h:
  • runtime/OptionsList.h:

Source/WTF:

  • wtf/text/StringImpl.cpp:

(WTF::StringImpl::createFromLiteral):

  • wtf/text/StringImpl.h:
Location:
trunk
Files:
1 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r281500 r281513  
     12021-08-24  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Add Intl Enumeration APIs
     4        https://bugs.webkit.org/show_bug.cgi?id=214795
     5
     6        Reviewed by Ross Kirsling.
     7
     8        * stress/intl-datetimeformat.js:
     9        * stress/intl-enumeration.js: Added.
     10        (shouldBe):
     11        (shouldThrow):
     12        * stress/intl-locale-info.js:
     13        (let.l.new.Intl.Locale.shouldBe):
     14        (shouldBe):
     15
    1162021-08-24  Keith Miller  <keith_miller@apple.com>
    217
  • trunk/JSTests/stress/intl-datetimeformat.js

    r276285 r281513  
    315315shouldBe(Intl.DateTimeFormat('en-u-ca-coptic').resolvedOptions().calendar, 'coptic');
    316316shouldBe(Intl.DateTimeFormat('en-u-ca-dangi').resolvedOptions().calendar, 'dangi');
    317 shouldBeForICUVersion(62, Intl.DateTimeFormat('en-u-ca-ethioaa').resolvedOptions().calendar, 'ethiopic-amete-alem');
     317shouldBeForICUVersion(62, Intl.DateTimeFormat('en-u-ca-ethioaa').resolvedOptions().calendar, 'ethioaa');
    318318shouldBe(Intl.DateTimeFormat('en-u-ca-ethiopic').resolvedOptions().calendar, 'ethiopic');
    319319shouldBe(Intl.DateTimeFormat('ar-SA-u-ca-gregory').resolvedOptions().calendar, 'gregory');
     
    326326shouldBe(Intl.DateTimeFormat('en-u-ca-persian').resolvedOptions().calendar, 'persian');
    327327shouldBe(Intl.DateTimeFormat('en-u-ca-roc').resolvedOptions().calendar, 'roc');
    328 shouldBeForICUVersion(62, Intl.DateTimeFormat('en-u-ca-ethiopic-amete-alem').resolvedOptions().calendar, 'ethiopic-amete-alem');
     328shouldBeForICUVersion(62, Intl.DateTimeFormat('en-u-ca-ethiopic-amete-alem').resolvedOptions().calendar, 'ethioaa');
    329329shouldBe(Intl.DateTimeFormat('en-u-ca-islamic-umalqura').resolvedOptions().calendar, 'islamic-umalqura');
    330330shouldBe(Intl.DateTimeFormat('en-u-ca-islamic-tbla').resolvedOptions().calendar, 'islamic-tbla');
  • trunk/JSTests/stress/intl-locale-info.js

    r281421 r281513  
    1818{
    1919    let locale = new Intl.Locale("ar")
    20     shouldBe(JSON.stringify(locale.calendars), `["gregory","coptic","islamic","islamicc","islamic-tbla"]`);
     20    shouldBe(JSON.stringify(locale.calendars), `["gregory","coptic","islamic","islamic-civil","islamic-tbla"]`);
    2121    shouldBe(JSON.stringify(locale.collations), `["compat","emoji","eor"]`);
    2222    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
     
    2727{
    2828    let locale = new Intl.Locale("ar-EG")
    29     shouldBe(JSON.stringify(locale.calendars), `["gregory","coptic","islamic","islamicc","islamic-tbla"]`);
     29    shouldBe(JSON.stringify(locale.calendars), `["gregory","coptic","islamic","islamic-civil","islamic-tbla"]`);
    3030    shouldBe(JSON.stringify(locale.collations), `["compat","emoji","eor"]`);
    3131    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
     
    100100{
    101101    let locale = new Intl.Locale("fa")
    102     shouldBe(JSON.stringify(locale.calendars), `["persian","gregory","islamic","islamicc","islamic-tbla"]`);
     102    shouldBe(JSON.stringify(locale.calendars), `["persian","gregory","islamic","islamic-civil","islamic-tbla"]`);
    103103    shouldBe(JSON.stringify(locale.collations), `["emoji","eor"]`);
    104104    shouldBe(JSON.stringify(locale.hourCycles), `["h23"]`);
  • trunk/Source/JavaScriptCore/ChangeLog

    r281500 r281513  
     12021-08-24  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Add Intl Enumeration APIs
     4        https://bugs.webkit.org/show_bug.cgi?id=214795
     5
     6        Reviewed by Ross Kirsling.
     7
     8        This patch implements Intl enumeration API[1] which is in stage-3.
     9        Supported keys are, "calendar", "collation", "currency", "numberingSystem", "timeZone", and "unit".
     10
     11        [1]: https://github.com/tc39/proposal-intl-enumeration
     12
     13        * runtime/IntlDateTimeFormat.cpp:
     14        (JSC::IntlDateTimeFormat::localeData):
     15        (JSC::IntlDateTimeFormat::initializeDateTimeFormat):
     16        * runtime/IntlLocale.cpp:
     17        (JSC::createArrayFromStringVector): Deleted.
     18        * runtime/IntlNumberFormat.cpp:
     19        (JSC::sanctionedSimpleUnitIdentifier):
     20        * runtime/IntlObject.cpp:
     21        (JSC::IntlObject::finishCreation):
     22        (JSC::mapICUCalendarKeywordToBCP47):
     23        (JSC::availableCalendars):
     24        (JSC::availableCollations):
     25        (JSC::availableCurrencies):
     26        (JSC::availableNumberingSystems):
     27        (JSC::canonicalizeTimeZoneNameFromICUTimeZone):
     28        (JSC::availableTimeZones):
     29        (JSC::availableUnits):
     30        (JSC::JSC_DEFINE_HOST_FUNCTION):
     31        (JSC::createArrayFromStringVector):
     32        * runtime/IntlObject.h:
     33        * runtime/OptionsList.h:
     34
    1352021-08-24  Keith Miller  <keith_miller@apple.com>
    236
  • trunk/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp

    r281383 r281513  
    166166            String calendar = String(availableName, nameLength);
    167167            keyLocaleData.append(calendar);
     168            // Adding "islamicc" candidate for backward compatibility.
     169            if (calendar == "islamic-civil"_s)
     170                keyLocaleData.append("islamicc"_s);
    168171            if (auto mapped = mapICUCalendarKeywordToBCP47(calendar))
    169172                keyLocaleData.append(WTFMove(mapped.value()));
     
    580583    }
    581584
    582     m_calendar = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Ca)];
    583     if (m_calendar == "gregorian")
    584         m_calendar = "gregory"_s;
    585     else if (m_calendar == "islamicc")
    586         m_calendar = "islamic-civil"_s;
    587     else if (m_calendar == "ethioaa")
    588         m_calendar = "ethiopic-amete-alem"_s;
     585    {
     586        String calendar = resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Ca)];
     587        if (auto mapped = mapICUCalendarKeywordToBCP47(calendar))
     588            m_calendar = WTFMove(mapped.value());
     589        else
     590            m_calendar = WTFMove(calendar);
     591    }
    589592
    590593    hourCycle = parseHourCycle(resolved.extensions[static_cast<unsigned>(RelevantExtensionKey::Hc)]);
  • trunk/Source/JavaScriptCore/runtime/IntlLocale.cpp

    r281374 r281513  
    538538}
    539539
    540 static inline JSArray* createArrayFromStringVector(JSGlobalObject* globalObject, Vector<String, 1>&& elements)
    541 {
    542     VM& vm = globalObject->vm();
    543     auto scope = DECLARE_THROW_SCOPE(vm);
    544 
    545     JSArray* result = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), elements.size());
    546     if (!result) {
    547         throwOutOfMemoryError(globalObject, scope);
    548         return nullptr;
    549     }
    550     for (unsigned index = 0; index < elements.size(); ++index) {
    551         result->putDirectIndex(globalObject, index, jsString(vm, WTFMove(elements[index])));
    552         RETURN_IF_EXCEPTION(scope, { });
    553     }
    554     return result;
    555 }
    556 
    557540JSArray* IntlLocale::calendars(JSGlobalObject* globalObject)
    558541{
  • trunk/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp

    r278697 r281513  
    152152}
    153153
    154 // Create MeasureUnit like ICU4J.
    155 struct MeasureUnit {
    156     ASCIILiteral type;
    157     ASCIILiteral subType;
    158 };
    159 
    160154static std::optional<MeasureUnit> sanctionedSimpleUnitIdentifier(StringView unitIdentifier)
    161155{
    162     static constexpr MeasureUnit simpleUnits[] = {
    163         { "area"_s, "acre"_s },
    164         { "digital"_s, "bit"_s },
    165         { "digital"_s, "byte"_s },
    166         { "temperature"_s, "celsius"_s },
    167         { "length"_s, "centimeter"_s },
    168         { "duration"_s, "day"_s },
    169         { "angle"_s, "degree"_s },
    170         { "temperature"_s, "fahrenheit"_s },
    171         { "volume"_s, "fluid-ounce"_s },
    172         { "length"_s, "foot"_s },
    173         { "volume"_s, "gallon"_s },
    174         { "digital"_s, "gigabit"_s },
    175         { "digital"_s, "gigabyte"_s },
    176         { "mass"_s, "gram"_s },
    177         { "area"_s, "hectare"_s },
    178         { "duration"_s, "hour"_s },
    179         { "length"_s, "inch"_s },
    180         { "digital"_s, "kilobit"_s },
    181         { "digital"_s, "kilobyte"_s },
    182         { "mass"_s, "kilogram"_s },
    183         { "length"_s, "kilometer"_s },
    184         { "volume"_s, "liter"_s },
    185         { "digital"_s, "megabit"_s },
    186         { "digital"_s, "megabyte"_s },
    187         { "length"_s, "meter"_s },
    188         { "length"_s, "mile"_s },
    189         { "length"_s, "mile-scandinavian"_s },
    190         { "volume"_s, "milliliter"_s },
    191         { "length"_s, "millimeter"_s },
    192         { "duration"_s, "millisecond"_s },
    193         { "duration"_s, "minute"_s },
    194         { "duration"_s, "month"_s },
    195         { "mass"_s, "ounce"_s },
    196         { "concentr"_s, "percent"_s },
    197         { "digital"_s, "petabyte"_s },
    198         { "mass"_s, "pound"_s },
    199         { "duration"_s, "second"_s },
    200         { "mass"_s, "stone"_s },
    201         { "digital"_s, "terabit"_s },
    202         { "digital"_s, "terabyte"_s },
    203         { "duration"_s, "week"_s },
    204         { "length"_s, "yard"_s },
    205         { "duration"_s, "year"_s },
    206     };
    207156    ASSERT(
    208157        std::is_sorted(std::begin(simpleUnits), std::end(simpleUnits),
  • trunk/Source/JavaScriptCore/runtime/IntlObject.cpp

    r281375 r281513  
    5959#include "Options.h"
    6060#include <unicode/ubrk.h>
     61#include <unicode/ucal.h>
    6162#include <unicode/ucol.h>
    6263#include <unicode/ufieldpositer.h>
     
    7677
    7778static JSC_DECLARE_HOST_FUNCTION(intlObjectFuncGetCanonicalLocales);
     79static JSC_DECLARE_HOST_FUNCTION(intlObjectFuncSupportedValuesOf);
    7880
    7981static JSValue createCollatorConstructor(VM& vm, JSObject* object)
     
    173175}
    174176
     177const MeasureUnit simpleUnits[43] = {
     178    { "area"_s, "acre"_s },
     179    { "digital"_s, "bit"_s },
     180    { "digital"_s, "byte"_s },
     181    { "temperature"_s, "celsius"_s },
     182    { "length"_s, "centimeter"_s },
     183    { "duration"_s, "day"_s },
     184    { "angle"_s, "degree"_s },
     185    { "temperature"_s, "fahrenheit"_s },
     186    { "volume"_s, "fluid-ounce"_s },
     187    { "length"_s, "foot"_s },
     188    { "volume"_s, "gallon"_s },
     189    { "digital"_s, "gigabit"_s },
     190    { "digital"_s, "gigabyte"_s },
     191    { "mass"_s, "gram"_s },
     192    { "area"_s, "hectare"_s },
     193    { "duration"_s, "hour"_s },
     194    { "length"_s, "inch"_s },
     195    { "digital"_s, "kilobit"_s },
     196    { "digital"_s, "kilobyte"_s },
     197    { "mass"_s, "kilogram"_s },
     198    { "length"_s, "kilometer"_s },
     199    { "volume"_s, "liter"_s },
     200    { "digital"_s, "megabit"_s },
     201    { "digital"_s, "megabyte"_s },
     202    { "length"_s, "meter"_s },
     203    { "length"_s, "mile"_s },
     204    { "length"_s, "mile-scandinavian"_s },
     205    { "volume"_s, "milliliter"_s },
     206    { "length"_s, "millimeter"_s },
     207    { "duration"_s, "millisecond"_s },
     208    { "duration"_s, "minute"_s },
     209    { "duration"_s, "month"_s },
     210    { "mass"_s, "ounce"_s },
     211    { "concentr"_s, "percent"_s },
     212    { "digital"_s, "petabyte"_s },
     213    { "mass"_s, "pound"_s },
     214    { "duration"_s, "second"_s },
     215    { "mass"_s, "stone"_s },
     216    { "digital"_s, "terabit"_s },
     217    { "digital"_s, "terabyte"_s },
     218    { "duration"_s, "week"_s },
     219    { "length"_s, "yard"_s },
     220    { "duration"_s, "year"_s },
     221};
     222
    175223IntlObject::IntlObject(VM& vm, Structure* structure)
    176224    : Base(vm, structure)
     
    185233}
    186234
    187 void IntlObject::finishCreation(VM& vm, JSGlobalObject*)
     235void IntlObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
    188236{
    189237    Base::finishCreation(vm);
     
    200248    UNUSED_PARAM(&createListFormatConstructor);
    201249#endif
     250    if (Options::useIntlEnumeration())
     251        JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("supportedValuesOf", intlObjectFuncSupportedValuesOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
    202252}
    203253
     
    14691519    if (calendar == "gregorian"_s)
    14701520        return "gregory"_s;
    1471     if (calendar == "islamic-civil"_s)
    1472         return "islamicc"_s;
     1521    // islamicc is deprecated in BCP47, and islamic-civil is preferred.
     1522    // https://github.com/unicode-org/cldr/blob/master/common/bcp47/calendar.xml
    14731523    if (calendar == "ethiopic-amete-alem"_s)
    14741524        return "ethioaa"_s;
     
    15251575}
    15261576
     1577// https://tc39.es/proposal-intl-enumeration/#sec-availablecalendars
     1578static JSArray* availableCalendars(JSGlobalObject* globalObject)
     1579{
     1580    VM& vm = globalObject->vm();
     1581    auto scope = DECLARE_THROW_SCOPE(vm);
     1582
     1583    UErrorCode status = U_ZERO_ERROR;
     1584    auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucal_getKeywordValuesForLocale("calendars", "und", false, &status));
     1585    if (U_FAILURE(status)) {
     1586        throwTypeError(globalObject, scope, "failed to enumerate available calendars"_s);
     1587        return { };
     1588    }
     1589
     1590    int32_t count = uenum_count(enumeration.get(), &status);
     1591    if (U_FAILURE(status)) {
     1592        throwTypeError(globalObject, scope, "failed to enumerate available calendars"_s);
     1593        return { };
     1594    }
     1595
     1596    Vector<String, 1> elements;
     1597    elements.reserveInitialCapacity(count);
     1598    for (int32_t index = 0; index < count; ++index) {
     1599        int32_t length = 0;
     1600        const char* pointer = uenum_next(enumeration.get(), &length, &status);
     1601        if (U_FAILURE(status)) {
     1602            throwTypeError(globalObject, scope, "failed to enumerate available calendars"_s);
     1603            return { };
     1604        }
     1605        String calendar(pointer, length);
     1606        if (auto mapped = mapICUCalendarKeywordToBCP47(calendar))
     1607            elements.append(WTFMove(mapped.value()));
     1608        else
     1609            elements.append(WTFMove(calendar));
     1610    }
     1611
     1612    // The AvailableCalendars abstract operation returns a List, ordered as if an Array of the same
     1613    // values had been sorted using %Array.prototype.sort% using undefined as comparefn
     1614    std::sort(elements.begin(), elements.end(),
     1615        [](const String& a, const String& b) {
     1616            return WTF::codePointCompare(a, b) < 0;
     1617        });
     1618
     1619    return createArrayFromStringVector(globalObject, WTFMove(elements));
     1620}
     1621
     1622// https://tc39.es/proposal-intl-enumeration/#sec-availablecollations
     1623static JSArray* availableCollations(JSGlobalObject* globalObject)
     1624{
     1625    VM& vm = globalObject->vm();
     1626    auto scope = DECLARE_THROW_SCOPE(vm);
     1627
     1628    UErrorCode status = U_ZERO_ERROR;
     1629    auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucol_getKeywordValues("collation", &status));
     1630    if (U_FAILURE(status)) {
     1631        throwTypeError(globalObject, scope, "failed to enumerate available collations"_s);
     1632        return { };
     1633    }
     1634
     1635    int32_t count = uenum_count(enumeration.get(), &status);
     1636    if (U_FAILURE(status)) {
     1637        throwTypeError(globalObject, scope, "failed to enumerate available collations"_s);
     1638        return { };
     1639    }
     1640
     1641    Vector<String, 1> elements;
     1642    elements.reserveInitialCapacity(count);
     1643    for (int32_t index = 0; index < count; ++index) {
     1644        int32_t length = 0;
     1645        const char* pointer = uenum_next(enumeration.get(), &length, &status);
     1646        if (U_FAILURE(status)) {
     1647            throwTypeError(globalObject, scope, "failed to enumerate available collations"_s);
     1648            return { };
     1649        }
     1650        String collation(pointer, length);
     1651        if (collation == "standard"_s || collation == "search"_s)
     1652            continue;
     1653        if (auto mapped = mapICUCollationKeywordToBCP47(collation))
     1654            elements.append(WTFMove(mapped.value()));
     1655        else
     1656            elements.append(WTFMove(collation));
     1657    }
     1658
     1659    // The AvailableCollations abstract operation returns a List, ordered as if an Array of the same
     1660    // values had been sorted using %Array.prototype.sort% using undefined as comparefn
     1661    std::sort(elements.begin(), elements.end(),
     1662        [](const String& a, const String& b) {
     1663            return WTF::codePointCompare(a, b) < 0;
     1664        });
     1665
     1666    return createArrayFromStringVector(globalObject, WTFMove(elements));
     1667}
     1668
     1669// https://tc39.es/proposal-intl-enumeration/#sec-availablecurrencies
     1670static JSArray* availableCurrencies(JSGlobalObject* globalObject)
     1671{
     1672    VM& vm = globalObject->vm();
     1673    auto scope = DECLARE_THROW_SCOPE(vm);
     1674
     1675    UErrorCode status = U_ZERO_ERROR;
     1676    auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucurr_openISOCurrencies(UCURR_COMMON | UCURR_NON_DEPRECATED, &status));
     1677    if (U_FAILURE(status)) {
     1678        throwTypeError(globalObject, scope, "failed to enumerate available currencies"_s);
     1679        return { };
     1680    }
     1681
     1682    int32_t count = uenum_count(enumeration.get(), &status);
     1683    if (U_FAILURE(status)) {
     1684        throwTypeError(globalObject, scope, "failed to enumerate available currencies"_s);
     1685        return { };
     1686    }
     1687
     1688    Vector<String, 1> elements;
     1689    elements.reserveInitialCapacity(count);
     1690    for (int32_t index = 0; index < count; ++index) {
     1691        int32_t length = 0;
     1692        const char* currency = uenum_next(enumeration.get(), &length, &status);
     1693        if (U_FAILURE(status)) {
     1694            throwTypeError(globalObject, scope, "failed to enumerate available currencies"_s);
     1695            return { };
     1696        }
     1697        elements.constructAndAppend(currency, length);
     1698    }
     1699
     1700    // The AvailableCurrencies abstract operation returns a List, ordered as if an Array of the same
     1701    // values had been sorted using %Array.prototype.sort% using undefined as comparefn
     1702    std::sort(elements.begin(), elements.end(),
     1703        [](const String& a, const String& b) {
     1704            return WTF::codePointCompare(a, b) < 0;
     1705        });
     1706
     1707    return createArrayFromStringVector(globalObject, WTFMove(elements));
     1708}
     1709
     1710// https://tc39.es/proposal-intl-enumeration/#sec-availablenumberingsystems
     1711static JSArray* availableNumberingSystems(JSGlobalObject* globalObject)
     1712{
     1713    VM& vm = globalObject->vm();
     1714    auto scope = DECLARE_THROW_SCOPE(vm);
     1715
     1716    UErrorCode status = U_ZERO_ERROR;
     1717    auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(unumsys_openAvailableNames(&status));
     1718    if (U_FAILURE(status)) {
     1719        throwTypeError(globalObject, scope, "failed to enumerate available numbering systems"_s);
     1720        return { };
     1721    }
     1722
     1723    int32_t count = uenum_count(enumeration.get(), &status);
     1724    if (U_FAILURE(status)) {
     1725        throwTypeError(globalObject, scope, "failed to enumerate available numbering systems"_s);
     1726        return { };
     1727    }
     1728
     1729    Vector<String, 1> elements;
     1730    elements.reserveInitialCapacity(count);
     1731    for (int32_t index = 0; index < count; ++index) {
     1732        int32_t length = 0;
     1733        const char* numberingSystem = uenum_next(enumeration.get(), &length, &status);
     1734        if (U_FAILURE(status)) {
     1735            throwTypeError(globalObject, scope, "failed to enumerate available numbering systems"_s);
     1736            return { };
     1737        }
     1738        elements.constructAndAppend(numberingSystem, length);
     1739    }
     1740
     1741    // The AvailableNumberingSystems abstract operation returns a List, ordered as if an Array of the same
     1742    // values had been sorted using %Array.prototype.sort% using undefined as comparefn
     1743    std::sort(elements.begin(), elements.end(),
     1744        [](const String& a, const String& b) {
     1745            return WTF::codePointCompare(a, b) < 0;
     1746        });
     1747
     1748    return createArrayFromStringVector(globalObject, WTFMove(elements));
     1749}
     1750
     1751// https://tc39.es/proposal-intl-enumeration/#sec-canonicalizetimezonename
     1752static std::optional<String> canonicalizeTimeZoneNameFromICUTimeZone(const String& timeZoneName)
     1753{
     1754    // Some time zone names are included in ICU, but they are not included in the IANA Time Zone Database.
     1755    // We need to filter them out.
     1756    if (timeZoneName.startsWith("SystemV/"))
     1757        return std::nullopt;
     1758    if (timeZoneName.startsWith("Etc/"))
     1759        return std::nullopt;
     1760    // IANA time zone names include '/'. Some of them are not including, but it is in backward links.
     1761    // And ICU already resolved these backward links.
     1762    if (!timeZoneName.contains('/'))
     1763        return std::nullopt;
     1764
     1765    return timeZoneName;
     1766}
     1767
     1768// https://tc39.es/proposal-intl-enumeration/#sec-availabletimezones
     1769static JSArray* availableTimeZones(JSGlobalObject* globalObject)
     1770{
     1771    VM& vm = globalObject->vm();
     1772    auto scope = DECLARE_THROW_SCOPE(vm);
     1773
     1774    UErrorCode status = U_ZERO_ERROR;
     1775    auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, nullptr, nullptr, &status));
     1776    if (U_FAILURE(status)) {
     1777        throwTypeError(globalObject, scope, "failed to enumerate available timezones"_s);
     1778        return { };
     1779    }
     1780
     1781    int32_t count = uenum_count(enumeration.get(), &status);
     1782    if (U_FAILURE(status)) {
     1783        throwTypeError(globalObject, scope, "failed to enumerate available timezones"_s);
     1784        return { };
     1785    }
     1786
     1787    Vector<String, 1> elements;
     1788    elements.reserveInitialCapacity(count);
     1789    for (int32_t index = 0; index < count; ++index) {
     1790        int32_t length = 0;
     1791        const char* pointer = uenum_next(enumeration.get(), &length, &status);
     1792        if (U_FAILURE(status)) {
     1793            throwTypeError(globalObject, scope, "failed to enumerate available timezones"_s);
     1794            return { };
     1795        }
     1796        String timeZone(pointer, length);
     1797        if (auto mapped = canonicalizeTimeZoneNameFromICUTimeZone(timeZone))
     1798            elements.append(WTFMove(mapped.value()));
     1799    }
     1800
     1801    // 4. Sort result in order as if an Array of the same values had been sorted using %Array.prototype.sort% using undefined as comparefn.
     1802    std::sort(elements.begin(), elements.end(),
     1803        [](const String& a, const String& b) {
     1804            return WTF::codePointCompare(a, b) < 0;
     1805        });
     1806
     1807    return createArrayFromStringVector(globalObject, WTFMove(elements));
     1808}
     1809
     1810// https://tc39.es/proposal-intl-enumeration/#sec-availableunits
     1811static JSArray* availableUnits(JSGlobalObject* globalObject)
     1812{
     1813    VM& vm = globalObject->vm();
     1814    auto scope = DECLARE_THROW_SCOPE(vm);
     1815
     1816    JSArray* result = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), std::size(simpleUnits));
     1817    if (!result) {
     1818        throwOutOfMemoryError(globalObject, scope);
     1819        return { };
     1820    }
     1821
     1822    ASSERT(
     1823        std::is_sorted(std::begin(simpleUnits), std::end(simpleUnits),
     1824            [](const MeasureUnit& a, const MeasureUnit& b) {
     1825                return WTF::codePointCompare(StringView(a.subType), StringView(b.subType)) < 0;
     1826            }));
     1827
     1828    int32_t index = 0;
     1829    for (const MeasureUnit& unit : simpleUnits) {
     1830        result->putDirectIndex(globalObject, index++, jsString(vm, StringImpl::createFromLiteral(unit.subType)));
     1831        RETURN_IF_EXCEPTION(scope, { });
     1832    }
     1833    return result;
     1834}
     1835
     1836// https://tc39.es/proposal-intl-enumeration/#sec-intl.supportedvaluesof
     1837JSC_DEFINE_HOST_FUNCTION(intlObjectFuncSupportedValuesOf, (JSGlobalObject* globalObject, CallFrame* callFrame))
     1838{
     1839    VM& vm = globalObject->vm();
     1840    auto scope = DECLARE_THROW_SCOPE(vm);
     1841
     1842    String key = callFrame->argument(0).toWTFString(globalObject);
     1843    RETURN_IF_EXCEPTION(scope, { });
     1844
     1845    if (key == "calendar"_s)
     1846        RELEASE_AND_RETURN(scope, JSValue::encode(availableCalendars(globalObject)));
     1847
     1848    if (key == "collation"_s)
     1849        RELEASE_AND_RETURN(scope, JSValue::encode(availableCollations(globalObject)));
     1850
     1851    if (key == "currency"_s)
     1852        RELEASE_AND_RETURN(scope, JSValue::encode(availableCurrencies(globalObject)));
     1853
     1854    if (key == "numberingSystem"_s)
     1855        RELEASE_AND_RETURN(scope, JSValue::encode(availableNumberingSystems(globalObject)));
     1856
     1857    if (key == "timeZone"_s)
     1858        RELEASE_AND_RETURN(scope, JSValue::encode(availableTimeZones(globalObject)));
     1859
     1860    if (key == "unit"_s)
     1861        RELEASE_AND_RETURN(scope, JSValue::encode(availableUnits(globalObject)));
     1862
     1863    throwRangeError(globalObject, scope, "Unknown key for Intl.supportedValuesOf"_s);
     1864    return { };
     1865}
     1866
     1867JSArray* createArrayFromStringVector(JSGlobalObject* globalObject, Vector<String, 1>&& elements)
     1868{
     1869    VM& vm = globalObject->vm();
     1870    auto scope = DECLARE_THROW_SCOPE(vm);
     1871
     1872    JSArray* result = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), elements.size());
     1873    if (!result) {
     1874        throwOutOfMemoryError(globalObject, scope);
     1875        return nullptr;
     1876    }
     1877    for (unsigned index = 0; index < elements.size(); ++index) {
     1878        result->putDirectIndex(globalObject, index, jsString(vm, WTFMove(elements[index])));
     1879        RETURN_IF_EXCEPTION(scope, { });
     1880    }
     1881    return result;
     1882}
     1883
    15271884} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/IntlObject.h

    r281375 r281513  
    6060static constexpr uint8_t numberOfRelevantExtensionKeys = 0 JSC_INTL_RELEVANT_EXTENSION_KEYS(JSC_COUNT_INTL_RELEVANT_EXTENSION_KEYS);
    6161#undef JSC_COUNT_INTL_RELEVANT_EXTENSION_KEYS
     62
     63struct MeasureUnit {
     64    ASCIILiteral type;
     65    ASCIILiteral subType;
     66};
     67
     68extern JS_EXPORT_PRIVATE const MeasureUnit simpleUnits[43];
    6269
    6370class IntlObject final : public JSNonFinalObject {
     
    142149std::optional<String> mapBCP47ToICUCalendarKeyword(const String&);
    143150
     151JSArray* createArrayFromStringVector(JSGlobalObject*, Vector<String, 1>&&);
     152
    144153} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/OptionsList.h

    r281438 r281513  
    542542    v(Bool, useTemporal, false, Normal, "Expose the Temporal object.") \
    543543    v(Bool, useArrayFindLastMethod, true, Normal, "Expose the findLast() and findLastIndex() methods on Array and %TypedArray%.") \
     544    v(Bool, useIntlEnumeration, true, Normal, "Expose the Intl enumeration APIs.") \
    544545
    545546
  • trunk/Source/WTF/ChangeLog

    r281490 r281513  
     12021-08-24  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Add Intl Enumeration APIs
     4        https://bugs.webkit.org/show_bug.cgi?id=214795
     5
     6        Reviewed by Ross Kirsling.
     7
     8        * wtf/text/StringImpl.cpp:
     9        (WTF::StringImpl::createFromLiteral):
     10        * wtf/text/StringImpl.h:
     11
    1122021-08-24  Tim Nguyen  <ntim@apple.com>
    213
  • trunk/Source/WTF/wtf/text/StringImpl.cpp

    r277355 r281513  
    162162{
    163163    return createFromLiteral(characters, strlen(characters));
     164}
     165
     166Ref<StringImpl> StringImpl::createFromLiteral(ASCIILiteral literal)
     167{
     168    return createFromLiteral(literal.characters(), literal.length());
    164169}
    165170
  • trunk/Source/WTF/wtf/text/StringImpl.h

    r281091 r281513  
    3434#include <wtf/Vector.h>
    3535#include <wtf/text/ASCIIFastPath.h>
     36#include <wtf/text/ASCIILiteral.h>
    3637#include <wtf/text/ConversionMode.h>
    3738#include <wtf/text/StringBuffer.h>
     
    250251    WTF_EXPORT_PRIVATE static Ref<StringImpl> createFromLiteral(const char*, unsigned length);
    251252    WTF_EXPORT_PRIVATE static Ref<StringImpl> createFromLiteral(const char*);
     253    WTF_EXPORT_PRIVATE static Ref<StringImpl> createFromLiteral(ASCIILiteral);
    252254
    253255    WTF_EXPORT_PRIVATE static Ref<StringImpl> createWithoutCopying(const UChar*, unsigned length);
Note: See TracChangeset for help on using the changeset viewer.