Changeset 215921 in webkit


Ignore:
Timestamp:
Apr 27, 2017 11:00:20 PM (7 years ago)
Author:
commit-queue@webkit.org
Message:

[INTL] Implement the caseFirst option for Intl.Collator
https://bugs.webkit.org/show_bug.cgi?id=158188

Patch by Andy VanWagoner <thetalecrafter@gmail.com> on 2017-04-27
Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Implements the caseFirst option and unicode locale extension.
The caseFirst option explicitly determines whether upper or lower case comes first.

  • runtime/IntlCollator.cpp:

(JSC::sortLocaleData): Added kf data.
(JSC::searchLocaleData): Added kf data.
(JSC::IntlCollator::initializeCollator): Set caseFirst option.
(JSC::IntlCollator::createCollator): Set new attributes on ICU collator.
(JSC::IntlCollator::caseFirstString): Added.
(JSC::IntlCollator::resolvedOptions): Added caseFirst property.

  • runtime/IntlCollator.h:

LayoutTests:

Updates the Intl.Collator tests to check caseFirst support.
The caseFirst option or unicode locale extension lets the user explicitly
set if lower or upper case characters should be first in order.

  • js/intl-collator-expected.txt:
  • js/script-tests/intl-collator.js:

(testCollator):

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r215916 r215921  
     12017-04-27  Andy VanWagoner  <thetalecrafter@gmail.com>
     2
     3        [INTL] Implement the caseFirst option for Intl.Collator
     4        https://bugs.webkit.org/show_bug.cgi?id=158188
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        Updates the Intl.Collator tests to check caseFirst support.
     9        The caseFirst option or unicode locale extension lets the user explicitly
     10        set if lower or upper case characters should be first in order.
     11
     12        * js/intl-collator-expected.txt:
     13        * js/script-tests/intl-collator.js:
     14        (testCollator):
     15
    1162017-04-27  Joseph Pecoraro  <pecoraro@apple.com>
    217
  • trunk/LayoutTests/js/intl-collator-expected.txt

    r215349 r215921  
    3737PASS testCollator(Intl.Collator('en-u-kn-false'), [{locale: 'en-u-kn-false', numeric: false}]) is true
    3838PASS testCollator(Intl.Collator('en-u-kn-abcd'), [{locale: 'en'}]) is true
     39PASS testCollator(Intl.Collator('en-u-kf'), [{locale: 'en'}]) is true
     40PASS testCollator(Intl.Collator('en-u-kf-upper'), [{locale: 'en-u-kf-upper', caseFirst: 'upper'}]) is true
     41PASS testCollator(Intl.Collator('en-u-kf-lower'), [{locale: 'en-u-kf-lower', caseFirst: 'lower'}]) is true
     42PASS testCollator(Intl.Collator('en-u-kf-false'), [{locale: 'en-u-kf-false', caseFirst: 'false'}]) is true
     43PASS testCollator(Intl.Collator('en-u-kf-true'), [{locale: 'en'}]) is true
    3944PASS testCollator(Intl.Collator('en-u-aa-aaaa-kn-true-bb-bbbb-co-eor-cc-cccc-y-yyd'), [{locale: 'en-u-co-eor-kn-true', collation: 'eor', numeric: true}, {locale: 'en-u-kn-true', numeric: true}]) is true
    4045PASS testCollator(Intl.Collator('en-u-kn-true-a-aa'), [{locale: 'en-u-kn-true', numeric: true}]) is true
     
    5560PASS testCollator(Intl.Collator('en', {numeric: { }}), [{locale: 'en', numeric: true}]) is true
    5661PASS Intl.Collator('en', { get numeric() { throw 42; } }) threw exception 42.
    57 PASS testCollator(Intl.Collator('en', {caseFirst: 'upper'}), [{locale: 'en'}]) is true
    58 PASS testCollator(Intl.Collator('en', {caseFirst: 'lower'}), [{locale: 'en'}]) is true
    59 PASS testCollator(Intl.Collator('en', {caseFirst: 'false'}), [{locale: 'en'}]) is true
    60 PASS testCollator(Intl.Collator('en', {caseFirst: false}), [{locale: 'en'}]) is true
     62PASS testCollator(Intl.Collator('en', {caseFirst: 'upper'}), [{locale: 'en', caseFirst: 'upper'}]) is true
     63PASS testCollator(Intl.Collator('en', {caseFirst: 'lower'}), [{locale: 'en', caseFirst: 'lower'}]) is true
     64PASS testCollator(Intl.Collator('en', {caseFirst: 'false'}), [{locale: 'en', caseFirst: 'false'}]) is true
     65PASS testCollator(Intl.Collator('en', {caseFirst: false}), [{locale: 'en', caseFirst: 'false'}]) is true
    6166PASS Intl.Collator('en', {caseFirst: 'true'}) threw exception RangeError: caseFirst must be either "upper", "lower", or "false".
    6267PASS Intl.Collator('en', { get caseFirst() { throw 42; } }) threw exception 42.
     
    7580PASS testCollator(Intl.Collator('en-u-kn-true', {numeric: true}), [{locale: 'en-u-kn-true', numeric: true}]) is true
    7681PASS testCollator(Intl.Collator('en-u-kn-false', {numeric: false}), [{locale: 'en-u-kn-false', numeric: false}]) is true
    77 PASS testCollator(Intl.Collator('en-a-aa-u-kn-false-co-eor-b-bb', {usage: 'sort', numeric: true, caseFirst: 'lower', sensitivity: 'case', ignorePunctuation: true}), [{locale: 'en-u-co-eor', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, collation: 'eor', numeric: true}, {locale: 'en', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, numeric: true}]) is true
     82PASS testCollator(Intl.Collator('en-a-aa-u-kn-false-co-eor-kf-upper-b-bb', {usage: 'sort', numeric: true, caseFirst: 'lower', sensitivity: 'case', ignorePunctuation: true}), [{locale: 'en-u-co-eor', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, collation: 'eor', numeric: true, caseFirst: 'lower'}, {locale: 'en', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, numeric: true, caseFirst: 'lower'}]) is true
    7883PASS Intl.Collator.length is 0
    7984PASS Object.getOwnPropertyDescriptor(Intl.Collator, 'prototype').writable is false
     
    181186PASS numericCompare('๒', '๑๐') is -1
    182187PASS numericCompare('๐๑', '๑') is 0
    183 PASS Intl.Collator('en', {caseFirst: 'upper'}).compare('a', 'A') is -1
     188PASS Intl.Collator('en', {caseFirst: 'upper'}).compare('a', 'A') is 1
    184189PASS Intl.Collator('en', {caseFirst: 'lower'}).compare('a', 'A') is -1
    185190PASS Intl.Collator('en', {caseFirst: 'false'}).compare('a', 'A') is -1
     
    245250PASS defaultCollator.resolvedOptions() === defaultCollator.resolvedOptions() is false
    246251PASS Intl.Collator.prototype.resolvedOptions.call(5) threw exception TypeError: Intl.Collator.prototype.resolvedOptions called on value that's not an object initialized as a Collator.
    247 PASS var options = defaultCollator.resolvedOptions(); delete options['locale']; JSON.stringify(options) is '{"usage":"sort","sensitivity":"variant","ignorePunctuation":false,"collation":"default","numeric":false}'
     252PASS var options = defaultCollator.resolvedOptions(); delete options['locale']; JSON.stringify(options) is '{"usage":"sort","sensitivity":"variant","ignorePunctuation":false,"collation":"default","numeric":false,"caseFirst":"false"}'
    248253PASS successfullyParsed is true
    249254
  • trunk/LayoutTests/js/script-tests/intl-collator.js

    r215349 r215921  
    3535            ignorePunctuation: false,
    3636            collation: "default",
    37             numeric: false
     37            numeric: false,
     38            caseFirst: "false"
    3839        }
    3940        Object.assign(defaultOptions, difference);
     
    6768shouldBeTrue("testCollator(Intl.Collator('en-u-kn-abcd'), [{locale: 'en'}])");
    6869
     70// The "kf" key is processed correctly.
     71shouldBeTrue("testCollator(Intl.Collator('en-u-kf'), [{locale: 'en'}])");
     72shouldBeTrue("testCollator(Intl.Collator('en-u-kf-upper'), [{locale: 'en-u-kf-upper', caseFirst: 'upper'}])");
     73shouldBeTrue("testCollator(Intl.Collator('en-u-kf-lower'), [{locale: 'en-u-kf-lower', caseFirst: 'lower'}])");
     74shouldBeTrue("testCollator(Intl.Collator('en-u-kf-false'), [{locale: 'en-u-kf-false', caseFirst: 'false'}])");
     75shouldBeTrue("testCollator(Intl.Collator('en-u-kf-true'), [{locale: 'en'}])");
     76
    6977// Ignores irrelevant extension keys.
    7078shouldBeTrue("testCollator(Intl.Collator('en-u-aa-aaaa-kn-true-bb-bbbb-co-eor-cc-cccc-y-yyd'), [{locale: 'en-u-co-eor-kn-true', collation: 'eor', numeric: true}, {locale: 'en-u-kn-true', numeric: true}])");
     
    96104
    97105// The option caseFirst is processed correctly.
    98 shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'upper'}), [{locale: 'en'}])");
    99 shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'lower'}), [{locale: 'en'}])");
    100 shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'false'}), [{locale: 'en'}])");
    101 shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: false}), [{locale: 'en'}])");
     106shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'upper'}), [{locale: 'en', caseFirst: 'upper'}])");
     107shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'lower'}), [{locale: 'en', caseFirst: 'lower'}])");
     108shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: 'false'}), [{locale: 'en', caseFirst: 'false'}])");
     109shouldBeTrue("testCollator(Intl.Collator('en', {caseFirst: false}), [{locale: 'en', caseFirst: 'false'}])");
    102110shouldThrow("Intl.Collator('en', {caseFirst: 'true'})", '\'RangeError: caseFirst must be either "upper", "lower", or "false"\'');
    103111shouldThrow("Intl.Collator('en', { get caseFirst() { throw 42; } })", "'42'");
     
    124132
    125133// Options and extension keys are processed correctly.
    126 shouldBeTrue("testCollator(Intl.Collator('en-a-aa-u-kn-false-co-eor-b-bb', {usage: 'sort', numeric: true, caseFirst: 'lower', sensitivity: 'case', ignorePunctuation: true}), [{locale: 'en-u-co-eor', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, collation: 'eor', numeric: true}, {locale: 'en', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, numeric: true}])");
     134shouldBeTrue("testCollator(Intl.Collator('en-a-aa-u-kn-false-co-eor-kf-upper-b-bb', {usage: 'sort', numeric: true, caseFirst: 'lower', sensitivity: 'case', ignorePunctuation: true}), [{locale: 'en-u-co-eor', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, collation: 'eor', numeric: true, caseFirst: 'lower'}, {locale: 'en', usage: 'sort', sensitivity: 'case', ignorePunctuation: true, numeric: true, caseFirst: 'lower'}])");
    127135
    128136// 10.2 Properties of the Intl.Collator Constructor
     
    325333
    326334// Test the caseFirst option.
    327 // FIXME: The result of Intl.Collator('en', {caseFirst: 'upper'}).compare('a', 'A') should be 1.
    328 shouldBe("Intl.Collator('en', {caseFirst: 'upper'}).compare('a', 'A')", "-1");
     335shouldBe("Intl.Collator('en', {caseFirst: 'upper'}).compare('a', 'A')", "1");
    329336shouldBe("Intl.Collator('en', {caseFirst: 'lower'}).compare('a', 'A')", "-1");
    330337shouldBe("Intl.Collator('en', {caseFirst: 'false'}).compare('a', 'A')", "-1");
     
    365372
    366373// Returns the default options.
    367 shouldBe("var options = defaultCollator.resolvedOptions(); delete options['locale']; JSON.stringify(options)", '\'{"usage":"sort","sensitivity":"variant","ignorePunctuation":false,"collation":"default","numeric":false}\'');
     374shouldBe("var options = defaultCollator.resolvedOptions(); delete options['locale']; JSON.stringify(options)", '\'{"usage":"sort","sensitivity":"variant","ignorePunctuation":false,"collation":"default","numeric":false,"caseFirst":"false"}\'');
  • trunk/Source/JavaScriptCore/ChangeLog

    r215919 r215921  
     12017-04-27  Andy VanWagoner  <thetalecrafter@gmail.com>
     2
     3        [INTL] Implement the caseFirst option for Intl.Collator
     4        https://bugs.webkit.org/show_bug.cgi?id=158188
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        Implements the caseFirst option and unicode locale extension.
     9        The caseFirst option explicitly determines whether upper or lower case comes first.
     10
     11        * runtime/IntlCollator.cpp:
     12        (JSC::sortLocaleData): Added kf data.
     13        (JSC::searchLocaleData): Added kf data.
     14        (JSC::IntlCollator::initializeCollator): Set caseFirst option.
     15        (JSC::IntlCollator::createCollator): Set new attributes on ICU collator.
     16        (JSC::IntlCollator::caseFirstString): Added.
     17        (JSC::IntlCollator::resolvedOptions): Added caseFirst property.
     18        * runtime/IntlCollator.h:
     19
    1202017-04-27  Mark Lam  <mark.lam@apple.com>
    221
  • trunk/Source/JavaScriptCore/runtime/IntlCollator.cpp

    r211247 r215921  
    4646const ClassInfo IntlCollator::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(IntlCollator) };
    4747
    48 // FIXME: Implement kf (caseFirst).
    49 static const char* const relevantExtensionKeys[2] = { "co", "kn" };
     48static const char* const relevantExtensionKeys[3] = { "co", "kn", "kf" };
    5049static const size_t indexOfExtensionKeyCo = 0;
    5150static const size_t indexOfExtensionKeyKn = 1;
     51static const size_t indexOfExtensionKeyKf = 2;
    5252
    5353void IntlCollator::UCollatorDeleter::operator()(UCollator* collator) const
     
    134134        keyLocaleData.uncheckedAppend(ASCIILiteral("true"));
    135135        break;
     136    case indexOfExtensionKeyKf:
     137        keyLocaleData.reserveInitialCapacity(3);
     138        keyLocaleData.uncheckedAppend(ASCIILiteral("false"));
     139        keyLocaleData.uncheckedAppend(ASCIILiteral("lower"));
     140        keyLocaleData.uncheckedAppend(ASCIILiteral("upper"));
     141        break;
    136142    default:
    137143        ASSERT_NOT_REACHED();
     
    154160        keyLocaleData.uncheckedAppend(ASCIILiteral("false"));
    155161        keyLocaleData.uncheckedAppend(ASCIILiteral("true"));
     162        break;
     163    case indexOfExtensionKeyKf:
     164        keyLocaleData.reserveInitialCapacity(3);
     165        keyLocaleData.uncheckedAppend(ASCIILiteral("false"));
     166        keyLocaleData.uncheckedAppend(ASCIILiteral("lower"));
     167        keyLocaleData.uncheckedAppend(ASCIILiteral("upper"));
    156168        break;
    157169    default:
     
    276288    const String& collation = result.get(ASCIILiteral("co"));
    277289    m_collation = collation.isNull() ? ASCIILiteral("default") : collation;
    278     m_numeric = (result.get(ASCIILiteral("kn")) == "true");
     290    m_numeric = result.get(ASCIILiteral("kn")) == "true";
     291
     292    const String& caseFirst = result.get(ASCIILiteral("kf"));
     293    if (caseFirst == "lower")
     294        m_caseFirst = CaseFirst::Lower;
     295    else if (caseFirst == "upper")
     296        m_caseFirst = CaseFirst::Upper;
     297    else
     298        m_caseFirst = CaseFirst::False;
    279299
    280300    // 24. Let s be GetOption(options, "sensitivity", "string", «"base", "accent", "case", "variant"», undefined).
     
    334354    UColAttributeValue strength = UCOL_PRIMARY;
    335355    UColAttributeValue caseLevel = UCOL_OFF;
     356    UColAttributeValue caseFirst = UCOL_OFF;
    336357    switch (m_sensitivity) {
    337358    case Sensitivity::Base:
     
    346367        strength = UCOL_TERTIARY;
    347368        break;
    348     default:
    349         ASSERT_NOT_REACHED();
    350     }
     369    }
     370    switch (m_caseFirst) {
     371    case CaseFirst::False:
     372        break;
     373    case CaseFirst::Lower:
     374        caseFirst = UCOL_LOWER_FIRST;
     375        break;
     376    case CaseFirst::Upper:
     377        caseFirst = UCOL_UPPER_FIRST;
     378        break;
     379    }
     380
    351381    ucol_setAttribute(collator.get(), UCOL_STRENGTH, strength, &status);
    352382    ucol_setAttribute(collator.get(), UCOL_CASE_LEVEL, caseLevel, &status);
    353 
     383    ucol_setAttribute(collator.get(), UCOL_CASE_FIRST, caseFirst, &status);
    354384    ucol_setAttribute(collator.get(), UCOL_NUMERIC_COLLATION, m_numeric ? UCOL_ON : UCOL_OFF, &status);
    355385
     
    416446}
    417447
     448const char* IntlCollator::caseFirstString(CaseFirst caseFirst)
     449{
     450    switch (caseFirst) {
     451    case CaseFirst::False:
     452        return "false";
     453    case CaseFirst::Lower:
     454        return "lower";
     455    case CaseFirst::Upper:
     456        return "upper";
     457    }
     458    ASSERT_NOT_REACHED();
     459    return nullptr;
     460}
     461
    418462JSObject* IntlCollator::resolvedOptions(ExecState& state)
    419463{
     
    442486    options->putDirect(vm, vm.propertyNames->collation, jsString(&state, m_collation));
    443487    options->putDirect(vm, vm.propertyNames->numeric, jsBoolean(m_numeric));
     488    options->putDirect(vm, vm.propertyNames->caseFirst, jsNontrivialString(&state, ASCIILiteral(caseFirstString(m_caseFirst))));
    444489    return options;
    445490}
  • trunk/Source/JavaScriptCore/runtime/IntlCollator.h

    r206525 r215921  
    6262    enum class Usage { Sort, Search };
    6363    enum class Sensitivity { Base, Accent, Case, Variant };
     64    enum class CaseFirst { Upper, Lower, False };
    6465
    6566    struct UCollatorDeleter {
     
    7071    static const char* usageString(Usage);
    7172    static const char* sensitivityString(Sensitivity);
     73    static const char* caseFirstString(CaseFirst);
    7274
    7375    Usage m_usage;
     
    7577    String m_collation;
    7678    Sensitivity m_sensitivity;
     79    CaseFirst m_caseFirst;
    7780    WriteBarrier<JSBoundFunction> m_boundCompare;
    7881    std::unique_ptr<UCollator, UCollatorDeleter> m_collator;
Note: See TracChangeset for help on using the changeset viewer.