Changeset 196850 in webkit


Ignore:
Timestamp:
Feb 19, 2016 5:58:06 PM (8 years ago)
Author:
Sukolsak Sakshuwong
Message:

[INTL] Implement Number Format Functions
https://bugs.webkit.org/show_bug.cgi?id=147605

Reviewed by Darin Adler.

Source/JavaScriptCore:

This patch implements Intl.NumberFormat.prototype.format() according
to the ECMAScript 2015 Internationalization API spec (ECMA-402 2nd edition.)

  • runtime/IntlNumberFormat.cpp:

(JSC::IntlNumberFormat::UNumberFormatDeleter::operator()):
(JSC::IntlNumberFormat::initializeNumberFormat):
(JSC::IntlNumberFormat::createNumberFormat):
(JSC::IntlNumberFormat::formatNumber):
(JSC::IntlNumberFormatFuncFormatNumber): Deleted.

  • runtime/IntlNumberFormat.h:
  • runtime/IntlNumberFormatPrototype.cpp:

(JSC::IntlNumberFormatFuncFormatNumber):

LayoutTests:

  • js/intl-numberformat-expected.txt:
  • js/intl-numberformat.html:
  • js/number-toLocaleString-expected.txt:
  • js/script-tests/intl-numberformat.js:
  • js/script-tests/number-toLocaleString.js:
Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r196849 r196850  
     12016-02-19  Sukolsak Sakshuwong  <sukolsak@gmail.com>
     2
     3        [INTL] Implement Number Format Functions
     4        https://bugs.webkit.org/show_bug.cgi?id=147605
     5
     6        Reviewed by Darin Adler.
     7
     8        * js/intl-numberformat-expected.txt:
     9        * js/intl-numberformat.html:
     10        * js/number-toLocaleString-expected.txt:
     11        * js/script-tests/intl-numberformat.js:
     12        * js/script-tests/number-toLocaleString.js:
     13
    1142016-02-18  Gavin Barraclough  <barraclough@apple.com>
    215
  • trunk/LayoutTests/js/intl-numberformat-expected.txt

    r196434 r196850  
    128128PASS new Intl.NumberFormat().format.call(Intl.DateTimeFormat('ar'), 1.2) is Intl.NumberFormat().format(1.2)
    129129PASS new Intl.NumberFormat().format.call(5, 1.2) is Intl.NumberFormat().format(1.2)
     130PASS Intl.NumberFormat('en').format(42) is '42'
     131PASS Intl.NumberFormat('en').format('42') is '42'
     132PASS Intl.NumberFormat('en').format({ valueOf() { return 42; } }) is '42'
     133PASS Intl.NumberFormat('en').format('one') is 'NaN'
     134PASS Intl.NumberFormat('en').format(NaN) is 'NaN'
     135PASS Intl.NumberFormat('en').format(Infinity) is '∞'
     136PASS Intl.NumberFormat('en').format(-Infinity) is '-∞'
     137PASS Intl.NumberFormat('en').format(0) is '0'
     138PASS Intl.NumberFormat('en').format(-0) is '0'
     139PASS Intl.NumberFormat('en').format(Number.MIN_VALUE) is '0'
     140PASS Intl.NumberFormat('en').format(Number.MAX_VALUE) is '179,769,313,486,232,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000'
     141PASS Intl.NumberFormat('en').format(1234.567) is '1,234.567'
     142PASS Intl.NumberFormat('es').format(1234.567) is '1.234,567'
     143PASS Intl.NumberFormat('fr').format(1234.567) is '1 234,567'
     144PASS Intl.NumberFormat('en-u-nu-latn').format(1234.567) is '1,234.567'
     145PASS Intl.NumberFormat('en-u-nu-fullwide').format(1234.567) is '1,234.567'
     146PASS Intl.NumberFormat('th-u-nu-thai').format(1234.567) is '๑,๒๓๔.๕๖๗'
     147PASS Intl.NumberFormat('zh-Hans-CN-u-nu-hanidec').format(1234.567) is '一,二三四.五六七'
     148PASS Intl.NumberFormat('en', {style: 'decimal'}).format(4.2) is '4.2'
     149PASS Intl.NumberFormat('en', {style: 'percent'}).format(4.2) is '420%'
     150PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(4.2) is '$4.20'
     151PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(4) is '$4.00'
     152PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(4.2) is '$4.20'
     153PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(-4.2) is '-$4.20'
     154PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(NaN) is 'NaN'
     155PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(Infinity) is '$∞'
     156PASS Intl.NumberFormat('en', {style: 'currency', currency: 'JPY'}).format(4.2) is '¥4'
     157PASS Intl.NumberFormat('en', {style: 'currency', currency: 'xXx'}).format(4.2) is 'XXX4.20'
     158PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD', currencyDisplay: 'code'}).format(4) is 'USD4.00'
     159PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD', currencyDisplay: 'symbol'}).format(4) is '$4.00'
     160PASS Intl.NumberFormat('en', {style: 'currency', currency: 'USD', currencyDisplay: 'name'}).format(4) is '4.00 US dollars'
     161PASS Intl.NumberFormat('en', {style: 'currency', currency: 'JPY', currencyDisplay: 'code'}).format(-4.2) is '-JPY4'
     162PASS Intl.NumberFormat('en', {style: 'currency', currency: 'JPY', currencyDisplay: 'symbol'}).format(-4.2) is '-¥4'
     163PASS Intl.NumberFormat('en', {style: 'currency', currency: 'JPY', currencyDisplay: 'name'}).format(-4.2) is '-4 Japanese yen'
     164PASS Intl.NumberFormat('fr', {style: 'currency', currency: 'USD', currencyDisplay: 'name'}).format(4) is '4,00 dollars des États-Unis'
     165PASS Intl.NumberFormat('fr', {style: 'currency', currency: 'JPY', currencyDisplay: 'name'}).format(4) is '4 yens japonais'
     166PASS Intl.NumberFormat('en', {minimumIntegerDigits: 4}).format(12) is '0,012'
     167PASS Intl.NumberFormat('en', {minimumIntegerDigits: 4}).format(12345) is '12,345'
     168PASS Intl.NumberFormat('en', {minimumFractionDigits: 3}).format(1) is '1.000'
     169PASS Intl.NumberFormat('en', {minimumFractionDigits: 3}).format(1.2) is '1.200'
     170PASS Intl.NumberFormat('en', {minimumFractionDigits: 3}).format(1.2345) is '1.235'
     171PASS Intl.NumberFormat('en', {minimumFractionDigits: 3, maximumFractionDigits: 4}).format(1.2345) is '1.2345'
     172PASS Intl.NumberFormat('en', {minimumFractionDigits: 3, maximumFractionDigits: 4}).format(1.23454) is '1.2345'
     173PASS Intl.NumberFormat('en', {minimumFractionDigits: 3, maximumFractionDigits: 4}).format(1.23455) is '1.2346'
     174PASS Intl.NumberFormat('en', {maximumFractionDigits: 0}).format(0.5) is '1'
     175PASS Intl.NumberFormat('en', {maximumFractionDigits: 0}).format(0.4) is '0'
     176PASS Intl.NumberFormat('en', {maximumFractionDigits: 0}).format(-0.4) is '-0'
     177PASS Intl.NumberFormat('en', {maximumFractionDigits: 0}).format(-0.5) is '-1'
     178PASS Intl.NumberFormat('en', {minimumSignificantDigits: 4}).format(0.12) is '0.1200'
     179PASS Intl.NumberFormat('en', {minimumSignificantDigits: 4}).format(1.2) is '1.200'
     180PASS Intl.NumberFormat('en', {minimumSignificantDigits: 4}).format(12) is '12.00'
     181PASS Intl.NumberFormat('en', {minimumSignificantDigits: 4}).format(123456) is '123,456'
     182PASS Intl.NumberFormat('en', {maximumSignificantDigits: 4}).format(0.1) is '0.1'
     183PASS Intl.NumberFormat('en', {maximumSignificantDigits: 4}).format(0.1234567) is '0.1235'
     184PASS Intl.NumberFormat('en', {maximumSignificantDigits: 4}).format(1234567) is '1,235,000'
     185PASS Intl.NumberFormat('en', {useGrouping: true}).format(1234567.123) is '1,234,567.123'
     186PASS Intl.NumberFormat('es', {useGrouping: true}).format(1234567.123) is '1.234.567,123'
     187PASS Intl.NumberFormat('fr', {useGrouping: true}).format(1234567.123) is '1 234 567,123'
     188PASS Intl.NumberFormat('en', {useGrouping: false}).format(1234567.123) is '1234567.123'
     189PASS Intl.NumberFormat('es', {useGrouping: false}).format(1234567.123) is '1234567,123'
     190PASS Intl.NumberFormat('fr', {useGrouping: false}).format(1234567.123) is '1234567,123'
    130191PASS Intl.NumberFormat.prototype.resolvedOptions.length is 0
    131192PASS Intl.NumberFormat.prototype.resolvedOptions() is an instance of Object
  • trunk/LayoutTests/js/intl-numberformat.html

    r187575 r196850  
    22<html>
    33<head>
     4<meta charset="utf-8">
    45<script src="../resources/js-test-pre.js"></script>
    56</head>
  • trunk/LayoutTests/js/number-toLocaleString-expected.txt

    r193493 r196850  
    2020PASS (0).toLocaleString() is "0"
    2121PASS new Number(1).toLocaleString() is "1"
     22PASS (0).toLocaleString('i') threw exception RangeError: invalid language tag: i.
     23PASS Infinity.toLocaleString() is "∞"
     24PASS (123456.789).toLocaleString('ar') is "١٢٣٬٤٥٦٫٧٨٩"
     25PASS (123456.789).toLocaleString('zh-Hans-CN-u-nu-hanidec') is "一二三,四五六.七八九"
     26PASS (123.456).toLocaleString('en', { maximumSignificantDigits: 3 }) is "123"
    2227PASS successfullyParsed is true
    2328
  • trunk/LayoutTests/js/script-tests/intl-numberformat.js

    r196434 r196850  
    238238shouldBe("new Intl.NumberFormat().format.call(5, 1.2)", "Intl.NumberFormat().format(1.2)");
    239239
     240// Test various values.
     241shouldBe("Intl.NumberFormat('en').format(42)", "'42'");
     242shouldBe("Intl.NumberFormat('en').format('42')", "'42'");
     243shouldBe("Intl.NumberFormat('en').format({ valueOf() { return 42; } })", "'42'");
     244shouldBe("Intl.NumberFormat('en').format('one')", "'NaN'");
     245shouldBe("Intl.NumberFormat('en').format(NaN)", "'NaN'");
     246shouldBe("Intl.NumberFormat('en').format(Infinity)", "'∞'");
     247shouldBe("Intl.NumberFormat('en').format(-Infinity)", "'-∞'");
     248shouldBe("Intl.NumberFormat('en').format(0)", "'0'");
     249shouldBe("Intl.NumberFormat('en').format(-0)", "'0'");
     250shouldBe("Intl.NumberFormat('en').format(Number.MIN_VALUE)", "'0'");
     251shouldBe("Intl.NumberFormat('en').format(Number.MAX_VALUE)", "'179,769,313,486,232,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000'");
     252
     253// Test locales.
     254shouldBe("Intl.NumberFormat('en').format(1234.567)", "'1,234.567'");
     255shouldBe("Intl.NumberFormat('es').format(1234.567)", "'1.234,567'");
     256shouldBe("Intl.NumberFormat('fr').format(1234.567)", "'1 234,567'");
     257
     258// Test numbering systems.
     259shouldBe("Intl.NumberFormat('en-u-nu-latn').format(1234.567)", "'1,234.567'");
     260shouldBe("Intl.NumberFormat('en-u-nu-fullwide').format(1234.567)", "'1,234.567'");
     261shouldBe("Intl.NumberFormat('th-u-nu-thai').format(1234.567)", "'๑,๒๓๔.๕๖๗'");
     262shouldBe("Intl.NumberFormat('zh-Hans-CN-u-nu-hanidec').format(1234.567)", "'一,二三四.五六七'");
     263
     264// Test the style option.
     265shouldBe("Intl.NumberFormat('en', {style: 'decimal'}).format(4.2)", "'4.2'");
     266shouldBe("Intl.NumberFormat('en', {style: 'percent'}).format(4.2)", "'420%'");
     267shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(4.2)", "'$4.20'");
     268
     269// Test the currency option.
     270shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(4)", "'$4.00'");
     271shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(4.2)", "'$4.20'");
     272shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(-4.2)", "'-$4.20'");
     273shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(NaN)", "'NaN'");
     274shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD'}).format(Infinity)", "'$∞'");
     275shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'JPY'}).format(4.2)", "'¥4'");
     276shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'xXx'}).format(4.2)", "'XXX4.20'");
     277
     278// Test the currencyDisplay option.
     279shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD', currencyDisplay: 'code'}).format(4)", "'USD4.00'");
     280shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD', currencyDisplay: 'symbol'}).format(4)", "'$4.00'");
     281shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'USD', currencyDisplay: 'name'}).format(4)", "'4.00 US dollars'");
     282shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'JPY', currencyDisplay: 'code'}).format(-4.2)", "'-JPY4'");
     283shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'JPY', currencyDisplay: 'symbol'}).format(-4.2)", "'-¥4'");
     284shouldBe("Intl.NumberFormat('en', {style: 'currency', currency: 'JPY', currencyDisplay: 'name'}).format(-4.2)", "'-4 Japanese yen'");
     285shouldBe("Intl.NumberFormat('fr', {style: 'currency', currency: 'USD', currencyDisplay: 'name'}).format(4)", "'4,00 dollars des États-Unis'");
     286shouldBe("Intl.NumberFormat('fr', {style: 'currency', currency: 'JPY', currencyDisplay: 'name'}).format(4)", "'4 yens japonais'");
     287
     288// Test the minimumIntegerDigits option.
     289shouldBe("Intl.NumberFormat('en', {minimumIntegerDigits: 4}).format(12)", "'0,012'");
     290shouldBe("Intl.NumberFormat('en', {minimumIntegerDigits: 4}).format(12345)", "'12,345'");
     291
     292// Test the minimumFractionDigits option.
     293shouldBe("Intl.NumberFormat('en', {minimumFractionDigits: 3}).format(1)", "'1.000'");
     294shouldBe("Intl.NumberFormat('en', {minimumFractionDigits: 3}).format(1.2)", "'1.200'");
     295shouldBe("Intl.NumberFormat('en', {minimumFractionDigits: 3}).format(1.2345)", "'1.235'");
     296
     297// Test the maximumFractionDigits option.
     298shouldBe("Intl.NumberFormat('en', {minimumFractionDigits: 3, maximumFractionDigits: 4}).format(1.2345)", "'1.2345'");
     299shouldBe("Intl.NumberFormat('en', {minimumFractionDigits: 3, maximumFractionDigits: 4}).format(1.23454)", "'1.2345'");
     300shouldBe("Intl.NumberFormat('en', {minimumFractionDigits: 3, maximumFractionDigits: 4}).format(1.23455)", "'1.2346'");
     301shouldBe("Intl.NumberFormat('en', {maximumFractionDigits: 0}).format(0.5)", "'1'");
     302shouldBe("Intl.NumberFormat('en', {maximumFractionDigits: 0}).format(0.4)", "'0'");
     303shouldBe("Intl.NumberFormat('en', {maximumFractionDigits: 0}).format(-0.4)", "'-0'");
     304shouldBe("Intl.NumberFormat('en', {maximumFractionDigits: 0}).format(-0.5)", "'-1'");
     305
     306// Test the minimumSignificantDigits option.
     307shouldBe("Intl.NumberFormat('en', {minimumSignificantDigits: 4}).format(0.12)", "'0.1200'");
     308shouldBe("Intl.NumberFormat('en', {minimumSignificantDigits: 4}).format(1.2)", "'1.200'");
     309shouldBe("Intl.NumberFormat('en', {minimumSignificantDigits: 4}).format(12)", "'12.00'");
     310shouldBe("Intl.NumberFormat('en', {minimumSignificantDigits: 4}).format(123456)", "'123,456'");
     311
     312// Test the maximumSignificantDigits option.
     313shouldBe("Intl.NumberFormat('en', {maximumSignificantDigits: 4}).format(0.1)", "'0.1'");
     314shouldBe("Intl.NumberFormat('en', {maximumSignificantDigits: 4}).format(0.1234567)", "'0.1235'");
     315shouldBe("Intl.NumberFormat('en', {maximumSignificantDigits: 4}).format(1234567)", "'1,235,000'");
     316
     317// Test the useGrouping option.
     318shouldBe("Intl.NumberFormat('en', {useGrouping: true}).format(1234567.123)", "'1,234,567.123'");
     319shouldBe("Intl.NumberFormat('es', {useGrouping: true}).format(1234567.123)", "'1.234.567,123'");
     320shouldBe("Intl.NumberFormat('fr', {useGrouping: true}).format(1234567.123)", "'1 234 567,123'");
     321shouldBe("Intl.NumberFormat('en', {useGrouping: false}).format(1234567.123)", "'1234567.123'");
     322shouldBe("Intl.NumberFormat('es', {useGrouping: false}).format(1234567.123)", "'1234567,123'");
     323shouldBe("Intl.NumberFormat('fr', {useGrouping: false}).format(1234567.123)", "'1234567,123'");
     324
    240325// 11.3.5 Intl.NumberFormat.prototype.resolvedOptions ()
    241326
  • trunk/LayoutTests/js/script-tests/number-toLocaleString.js

    r193493 r196850  
    2121shouldBeEqualToString("new Number(1).toLocaleString()", "1");
    2222
    23 // FIXME: Test for NumberFormat behavior once implemented.
    24 // shouldThrow("(0).toLocaleString('i')");
    25 // shouldBeEqualToString("Infinity.toLocaleString()", "∞");
     23// Test for NumberFormat behavior.
     24shouldThrow("(0).toLocaleString('i')", "'RangeError: invalid language tag: i'");
     25shouldBeEqualToString("Infinity.toLocaleString()", "∞");
    2626
    2727// Test that locale parameter is passed through properly.
    28 // shouldBeEqualToString("(123456.789).toLocaleString('ar')", "١٢٣٤٥٦٫٧٨٩");
    29 // shouldBeEqualToString("(123456.789).toLocaleString('zh-Hans-CN-u-nu-hanidec')", "一二三,四五六.七八九");
     28shouldBeEqualToString("(123456.789).toLocaleString('ar')", "١٢٣٬٤٥٦٫٧٨٩");
     29shouldBeEqualToString("(123456.789).toLocaleString('zh-Hans-CN-u-nu-hanidec')", "一二三,四五六.七八九");
    3030
    3131// Test that options parameter is passed through properly.
    32 // shouldBeEqualToString("(123.456).toLocaleString('en', { maximumSignificantDigits: 3 })", "123");
     32shouldBeEqualToString("(123.456).toLocaleString('en', { maximumSignificantDigits: 3 })", "123");
  • trunk/Source/JavaScriptCore/ChangeLog

    r196849 r196850  
     12016-02-19  Sukolsak Sakshuwong  <sukolsak@gmail.com>
     2
     3        [INTL] Implement Number Format Functions
     4        https://bugs.webkit.org/show_bug.cgi?id=147605
     5
     6        Reviewed by Darin Adler.
     7
     8        This patch implements Intl.NumberFormat.prototype.format() according
     9        to the ECMAScript 2015 Internationalization API spec (ECMA-402 2nd edition.)
     10
     11        * runtime/IntlNumberFormat.cpp:
     12        (JSC::IntlNumberFormat::UNumberFormatDeleter::operator()):
     13        (JSC::IntlNumberFormat::initializeNumberFormat):
     14        (JSC::IntlNumberFormat::createNumberFormat):
     15        (JSC::IntlNumberFormat::formatNumber):
     16        (JSC::IntlNumberFormatFuncFormatNumber): Deleted.
     17        * runtime/IntlNumberFormat.h:
     18        * runtime/IntlNumberFormatPrototype.cpp:
     19        (JSC::IntlNumberFormatFuncFormatNumber):
     20
    1212016-02-18  Gavin Barraclough  <barraclough@apple.com>
    222
  • trunk/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp

    r196434 r196850  
    4646
    4747static const char* const relevantExtensionKeys[1] = { "nu" };
     48
     49void IntlNumberFormat::UNumberFormatDeleter::operator()(UNumberFormat* numberFormat) const
     50{
     51    if (numberFormat)
     52        unum_close(numberFormat);
     53}
    4854
    4955IntlNumberFormat* IntlNumberFormat::create(VM& vm, IntlNumberFormatConstructor* constructor)
     
    341347    m_useGrouping = useGrouping;
    342348
    343     // FIXME: Implement Steps 46 - 51.
     349    // Steps 46 - 51 are not necessary to our implementation.
    344350    // 46. Let dataLocaleData be Get(localeData, dataLocale).
    345351    // 47. Let patterns be Get(dataLocaleData, "patterns").
     
    356362}
    357363
    358 EncodedJSValue JSC_HOST_CALL IntlNumberFormatFuncFormatNumber(ExecState* state)
    359 {
    360     // 11.3.4 Format Number Functions (ECMA-402 2.0)
    361     // 1. Let nf be the this value.
    362     IntlNumberFormat* format = jsDynamicCast<IntlNumberFormat*>(state->thisValue());
    363     // 2. Assert: Type(nf) is Object and nf has an [[initializedNumberFormat]] internal slot whose value is true.
    364     if (!format)
    365         return JSValue::encode(throwTypeError(state));
    366 
    367     // 3. If value is not provided, let value be undefined.
    368     // 4. Let x be ToNumber(value).
    369     double value = state->argument(0).toNumber(state);
    370     // 5. ReturnIfAbrupt(x).
    371     if (state->hadException())
    372         return JSValue::encode(jsUndefined());
    373 
    374     // 6. Return FormatNumber(nf, x).
    375    
     364void IntlNumberFormat::createNumberFormat(ExecState& state)
     365{
     366    ASSERT(!m_numberFormat);
     367
     368    if (!m_initializedNumberFormat) {
     369        initializeNumberFormat(state, jsUndefined(), jsUndefined());
     370        ASSERT(!state.hadException());
     371    }
     372
     373    UNumberFormatStyle style;
     374    switch (m_style) {
     375    case Style::Decimal:
     376        style = UNUM_DECIMAL;
     377        break;
     378    case Style::Percent:
     379        style = UNUM_PERCENT;
     380        break;
     381    case Style::Currency:
     382        switch (m_currencyDisplay) {
     383        case CurrencyDisplay::Code:
     384            style = UNUM_CURRENCY_ISO;
     385            break;
     386        case CurrencyDisplay::Symbol:
     387            style = UNUM_CURRENCY;
     388            break;
     389        case CurrencyDisplay::Name:
     390            style = UNUM_CURRENCY_PLURAL;
     391            break;
     392        default:
     393            ASSERT_NOT_REACHED();
     394        }
     395        break;
     396    default:
     397        ASSERT_NOT_REACHED();
     398    }
     399
     400    UErrorCode status = U_ZERO_ERROR;
     401    auto numberFormat = std::unique_ptr<UNumberFormat, UNumberFormatDeleter>(unum_open(style, nullptr, 0, m_locale.utf8().data(), nullptr, &status));
     402    if (U_FAILURE(status))
     403        return;
     404
     405    if (m_style == Style::Currency)
     406        unum_setTextAttribute(numberFormat.get(), UNUM_CURRENCY_CODE, StringView(m_currency).upconvertedCharacters(), 3, &status);
     407    if (!m_minimumSignificantDigits) {
     408        unum_setAttribute(numberFormat.get(), UNUM_MIN_INTEGER_DIGITS, m_minimumIntegerDigits);
     409        unum_setAttribute(numberFormat.get(), UNUM_MIN_FRACTION_DIGITS, m_minimumFractionDigits);
     410        unum_setAttribute(numberFormat.get(), UNUM_MAX_FRACTION_DIGITS, m_maximumFractionDigits);
     411    } else {
     412        unum_setAttribute(numberFormat.get(), UNUM_SIGNIFICANT_DIGITS_USED, true);
     413        unum_setAttribute(numberFormat.get(), UNUM_MIN_SIGNIFICANT_DIGITS, m_minimumSignificantDigits);
     414        unum_setAttribute(numberFormat.get(), UNUM_MAX_SIGNIFICANT_DIGITS, m_maximumSignificantDigits);
     415    }
     416    unum_setAttribute(numberFormat.get(), UNUM_GROUPING_USED, m_useGrouping);
     417    unum_setAttribute(numberFormat.get(), UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
     418    if (U_FAILURE(status))
     419        return;
     420
     421    m_numberFormat = WTFMove(numberFormat);
     422}
     423
     424JSValue IntlNumberFormat::formatNumber(ExecState& state, double number)
     425{
    376426    // 11.3.4 FormatNumber abstract operation (ECMA-402 2.0)
    377     // FIXME: Implement FormatNumber.
    378 
    379     return JSValue::encode(jsNumber(value).toString(state));
     427    if (!m_numberFormat) {
     428        createNumberFormat(state);
     429        if (!m_numberFormat)
     430            return state.vm().throwException(&state, createError(&state, ASCIILiteral("Failed to format a number.")));
     431    }
     432
     433    // Map negative zero to positive zero.
     434    if (!number)
     435        number = 0.0;
     436
     437    UErrorCode status = U_ZERO_ERROR;
     438    Vector<UChar, 32> buffer(32);
     439    auto length = unum_formatDouble(m_numberFormat.get(), number, buffer.data(), buffer.size(), nullptr, &status);
     440    if (status == U_BUFFER_OVERFLOW_ERROR) {
     441        buffer.grow(length);
     442        status = U_ZERO_ERROR;
     443        unum_formatDouble(m_numberFormat.get(), number, buffer.data(), length, nullptr, &status);
     444    }
     445    if (U_FAILURE(status))
     446        return state.vm().throwException(&state, createError(&state, ASCIILiteral("Failed to format a number.")));
     447
     448    return jsString(&state, String(buffer.data(), length));
    380449}
    381450
  • trunk/Source/JavaScriptCore/runtime/IntlNumberFormat.h

    r196434 r196850  
    3030
    3131#include "JSDestructibleObject.h"
     32#include <unicode/unum.h>
    3233
    3334namespace JSC {
     
    4647
    4748    void initializeNumberFormat(ExecState&, JSValue locales, JSValue optionsValue);
     49    JSValue formatNumber(ExecState&, double number);
    4850    JSObject* resolvedOptions(ExecState&);
    4951
     
    6163    enum class CurrencyDisplay { Code, Symbol, Name };
    6264
    63     const char* styleString(Style);
    64     const char* currencyDisplayString(CurrencyDisplay);
     65    struct UNumberFormatDeleter {
     66        void operator()(UNumberFormat*) const;
     67    };
     68
     69    void createNumberFormat(ExecState&);
     70    static const char* styleString(Style);
     71    static const char* currencyDisplayString(CurrencyDisplay);
    6572
    6673    String m_locale;
     
    7481    unsigned m_minimumSignificantDigits { 0 };
    7582    unsigned m_maximumSignificantDigits { 0 };
     83    std::unique_ptr<UNumberFormat, UNumberFormatDeleter> m_numberFormat;
    7684    WriteBarrier<JSBoundFunction> m_boundFormat;
    7785    bool m_useGrouping { true };
    7886    bool m_initializedNumberFormat { false };
    7987};
    80    
    81 EncodedJSValue JSC_HOST_CALL IntlNumberFormatFuncFormatNumber(ExecState*);
    8288
    8389} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp

    r196434 r196850  
    8484}
    8585
     86static EncodedJSValue JSC_HOST_CALL IntlNumberFormatFuncFormatNumber(ExecState* state)
     87{
     88    // 11.3.4 Format Number Functions (ECMA-402 2.0)
     89    // 1. Let nf be the this value.
     90    // 2. Assert: Type(nf) is Object and nf has an [[initializedNumberFormat]] internal slot whose value  true.
     91    IntlNumberFormat* numberFormat = jsCast<IntlNumberFormat*>(state->thisValue());
     92
     93    // 3. If value is not provided, let value be undefined.
     94    // 4. Let x be ToNumber(value).
     95    double number = state->argument(0).toNumber(state);
     96    // 5. ReturnIfAbrupt(x).
     97    if (state->hadException())
     98        return JSValue::encode(jsUndefined());
     99
     100    // 6. Return FormatNumber(nf, x).
     101    return JSValue::encode(numberFormat->formatNumber(*state, number));
     102}
     103
    86104EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeGetterFormat(ExecState* state)
    87105{
Note: See TracChangeset for help on using the changeset viewer.