1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef V8_INTL_SUPPORT 6#error Internationalization is expected to be enabled. 7#endif // V8_INTL_SUPPORT 8 9#include "src/objects/js-number-format.h" 10 11#include <set> 12#include <string> 13 14#include "src/execution/isolate.h" 15#include "src/objects/intl-objects.h" 16#include "src/objects/js-number-format-inl.h" 17#include "src/objects/managed-inl.h" 18#include "src/objects/objects-inl.h" 19#include "src/objects/option-utils.h" 20#include "unicode/currunit.h" 21#include "unicode/locid.h" 22#include "unicode/numberformatter.h" 23#include "unicode/numberrangeformatter.h" 24#include "unicode/numsys.h" 25#include "unicode/ucurr.h" 26#include "unicode/uloc.h" 27#include "unicode/unumberformatter.h" 28#include "unicode/uvernum.h" // for U_ICU_VERSION_MAJOR_NUM 29 30namespace v8 { 31namespace internal { 32 33namespace { 34 35// [[Style]] is one of the values "decimal", "percent", "currency", 36// or "unit" identifying the style of the number format. 37enum class Style { DECIMAL, PERCENT, CURRENCY, UNIT }; 38 39// [[CurrencyDisplay]] is one of the values "code", "symbol", "name", 40// or "narrowSymbol" identifying the display of the currency number format. 41enum class CurrencyDisplay { 42 CODE, 43 SYMBOL, 44 NAME, 45 NARROW_SYMBOL, 46}; 47 48// [[CurrencySign]] is one of the String values "standard" or "accounting", 49// specifying whether to render negative numbers in accounting format, often 50// signified by parenthesis. It is only used when [[Style]] has the value 51// "currency" and when [[SignDisplay]] is not "never". 52enum class CurrencySign { 53 STANDARD, 54 ACCOUNTING, 55}; 56 57// [[UnitDisplay]] is one of the String values "short", "narrow", or "long", 58// specifying whether to display the unit as a symbol, narrow symbol, or 59// localized long name if formatting with the "unit" style. It is 60// only used when [[Style]] has the value "unit". 61enum class UnitDisplay { 62 SHORT, 63 NARROW, 64 LONG, 65}; 66 67// [[Notation]] is one of the String values "standard", "scientific", 68// "engineering", or "compact", specifying whether the number should be 69// displayed without scaling, scaled to the units place with the power of ten 70// in scientific notation, scaled to the nearest thousand with the power of 71// ten in scientific notation, or scaled to the nearest locale-dependent 72// compact decimal notation power of ten with the corresponding compact 73// decimal notation affix. 74 75enum class Notation { 76 STANDARD, 77 SCIENTIFIC, 78 ENGINEERING, 79 COMPACT, 80}; 81 82// [[CompactDisplay]] is one of the String values "short" or "long", 83// specifying whether to display compact notation affixes in short form ("5K") 84// or long form ("5 thousand") if formatting with the "compact" notation. It 85// is only used when [[Notation]] has the value "compact". 86enum class CompactDisplay { 87 SHORT, 88 LONG, 89}; 90 91// [[SignDisplay]] is one of the String values "auto", "always", "never", or 92// "exceptZero", specifying whether to show the sign on negative numbers 93// only, positive and negative numbers including zero, neither positive nor 94// negative numbers, or positive and negative numbers but not zero. 95enum class SignDisplay { 96 AUTO, 97 ALWAYS, 98 NEVER, 99 EXCEPT_ZERO, 100 NEGATIVE, 101}; 102 103// [[RoundingMode]] is one of the String values "ceil", "floor", "expand", 104// "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", or "halfEven", 105// specifying the rounding strategy for the number. 106// Note: To avoid name conflict with RoundingMode defined in other places, 107// prefix with Intl as IntlRoundingMode 108enum class IntlRoundingMode { 109 CEIL, 110 FLOOR, 111 EXPAND, 112 TRUNC, 113 HALF_CEIL, 114 HALF_FLOOR, 115 HALF_EXPAND, 116 HALF_TRUNC, 117 HALF_EVEN, 118}; 119 120// [[TrailingZeroDisplay]] is one of the String values "auto" or 121// "stripIfInteger", specifying the strategy for displaying trailing zeros on 122// whole number. 123enum class TrailingZeroDisplay { 124 AUTO, 125 STRIP_IF_INTEGER, 126}; 127 128// [[UseGrouping]] is .... 129enum class UseGrouping { 130 OFF, 131 MIN2, 132 AUTO, 133 ALWAYS, 134}; 135 136UNumberUnitWidth ToUNumberUnitWidth(CurrencyDisplay currency_display) { 137 switch (currency_display) { 138 case CurrencyDisplay::SYMBOL: 139 return UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT; 140 case CurrencyDisplay::CODE: 141 return UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE; 142 case CurrencyDisplay::NAME: 143 return UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME; 144 case CurrencyDisplay::NARROW_SYMBOL: 145 return UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW; 146 } 147} 148 149UNumberUnitWidth ToUNumberUnitWidth(UnitDisplay unit_display) { 150 switch (unit_display) { 151 case UnitDisplay::SHORT: 152 return UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT; 153 case UnitDisplay::LONG: 154 return UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME; 155 case UnitDisplay::NARROW: 156 return UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW; 157 } 158} 159 160UNumberSignDisplay ToUNumberSignDisplay(SignDisplay sign_display, 161 CurrencySign currency_sign) { 162 switch (sign_display) { 163 case SignDisplay::AUTO: 164 if (currency_sign == CurrencySign::ACCOUNTING) { 165 return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING; 166 } 167 DCHECK(currency_sign == CurrencySign::STANDARD); 168 return UNumberSignDisplay::UNUM_SIGN_AUTO; 169 case SignDisplay::NEVER: 170 return UNumberSignDisplay::UNUM_SIGN_NEVER; 171 case SignDisplay::ALWAYS: 172 if (currency_sign == CurrencySign::ACCOUNTING) { 173 return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS; 174 } 175 DCHECK(currency_sign == CurrencySign::STANDARD); 176 return UNumberSignDisplay::UNUM_SIGN_ALWAYS; 177 case SignDisplay::EXCEPT_ZERO: 178 if (currency_sign == CurrencySign::ACCOUNTING) { 179 return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; 180 } 181 DCHECK(currency_sign == CurrencySign::STANDARD); 182 return UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO; 183 case SignDisplay::NEGATIVE: 184 if (currency_sign == CurrencySign::ACCOUNTING) { 185 return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE; 186 } 187 DCHECK(currency_sign == CurrencySign::STANDARD); 188 return UNumberSignDisplay::UNUM_SIGN_NEGATIVE; 189 } 190} 191 192icu::number::Notation ToICUNotation(Notation notation, 193 CompactDisplay compact_display) { 194 switch (notation) { 195 case Notation::STANDARD: 196 return icu::number::Notation::simple(); 197 case Notation::SCIENTIFIC: 198 return icu::number::Notation::scientific(); 199 case Notation::ENGINEERING: 200 return icu::number::Notation::engineering(); 201 // 29. If notation is "compact", then 202 case Notation::COMPACT: 203 // 29. a. Set numberFormat.[[CompactDisplay]] to compactDisplay. 204 if (compact_display == CompactDisplay::SHORT) { 205 return icu::number::Notation::compactShort(); 206 } 207 DCHECK(compact_display == CompactDisplay::LONG); 208 return icu::number::Notation::compactLong(); 209 } 210} 211 212UNumberFormatRoundingMode ToUNumberFormatRoundingMode( 213 IntlRoundingMode rounding_mode) { 214 switch (rounding_mode) { 215 case IntlRoundingMode::CEIL: 216 return UNumberFormatRoundingMode::UNUM_ROUND_CEILING; 217 case IntlRoundingMode::FLOOR: 218 return UNumberFormatRoundingMode::UNUM_ROUND_FLOOR; 219 case IntlRoundingMode::EXPAND: 220 return UNumberFormatRoundingMode::UNUM_ROUND_UP; 221 case IntlRoundingMode::TRUNC: 222 return UNumberFormatRoundingMode::UNUM_ROUND_DOWN; 223 case IntlRoundingMode::HALF_CEIL: 224 return UNumberFormatRoundingMode::UNUM_ROUND_HALF_CEILING; 225 case IntlRoundingMode::HALF_FLOOR: 226 return UNumberFormatRoundingMode::UNUM_ROUND_HALF_FLOOR; 227 case IntlRoundingMode::HALF_EXPAND: 228 return UNumberFormatRoundingMode::UNUM_ROUND_HALFUP; 229 case IntlRoundingMode::HALF_TRUNC: 230 return UNumberFormatRoundingMode::UNUM_ROUND_HALFDOWN; 231 case IntlRoundingMode::HALF_EVEN: 232 return UNumberFormatRoundingMode::UNUM_ROUND_HALFEVEN; 233 } 234} 235 236UNumberGroupingStrategy ToUNumberGroupingStrategy(UseGrouping use_grouping) { 237 switch (use_grouping) { 238 case UseGrouping::OFF: 239 return UNumberGroupingStrategy::UNUM_GROUPING_OFF; 240 case UseGrouping::MIN2: 241 return UNumberGroupingStrategy::UNUM_GROUPING_MIN2; 242 case UseGrouping::AUTO: 243 return UNumberGroupingStrategy::UNUM_GROUPING_AUTO; 244 case UseGrouping::ALWAYS: 245 return UNumberGroupingStrategy::UNUM_GROUPING_ON_ALIGNED; 246 } 247} 248 249std::map<const std::string, icu::MeasureUnit> CreateUnitMap() { 250 UErrorCode status = U_ZERO_ERROR; 251 int32_t total = icu::MeasureUnit::getAvailable(nullptr, 0, status); 252 CHECK(U_FAILURE(status)); 253 status = U_ZERO_ERROR; 254 std::vector<icu::MeasureUnit> units(total); 255 total = icu::MeasureUnit::getAvailable(units.data(), total, status); 256 CHECK(U_SUCCESS(status)); 257 std::map<const std::string, icu::MeasureUnit> map; 258 std::set<std::string> sanctioned(Intl::SanctionedSimpleUnits()); 259 for (auto it = units.begin(); it != units.end(); ++it) { 260 // Need to skip none/percent 261 if (sanctioned.count(it->getSubtype()) > 0 && 262 strcmp("none", it->getType()) != 0) { 263 map[it->getSubtype()] = *it; 264 } 265 } 266 return map; 267} 268 269class UnitFactory { 270 public: 271 UnitFactory() : map_(CreateUnitMap()) {} 272 virtual ~UnitFactory() = default; 273 274 // ecma402 #sec-issanctionedsimpleunitidentifier 275 icu::MeasureUnit create(const std::string& unitIdentifier) { 276 // 1. If unitIdentifier is in the following list, return true. 277 auto found = map_.find(unitIdentifier); 278 if (found != map_.end()) { 279 return found->second; 280 } 281 // 2. Return false. 282 return icu::MeasureUnit(); 283 } 284 285 private: 286 std::map<const std::string, icu::MeasureUnit> map_; 287}; 288 289// ecma402 #sec-issanctionedsimpleunitidentifier 290icu::MeasureUnit IsSanctionedUnitIdentifier(const std::string& unit) { 291 static base::LazyInstance<UnitFactory>::type factory = 292 LAZY_INSTANCE_INITIALIZER; 293 return factory.Pointer()->create(unit); 294} 295 296// ecma402 #sec-iswellformedunitidentifier 297Maybe<std::pair<icu::MeasureUnit, icu::MeasureUnit>> IsWellFormedUnitIdentifier( 298 Isolate* isolate, const std::string& unit) { 299 icu::MeasureUnit result = IsSanctionedUnitIdentifier(unit); 300 icu::MeasureUnit none = icu::MeasureUnit(); 301 // 1. If the result of IsSanctionedUnitIdentifier(unitIdentifier) is true, 302 // then 303 if (result != none) { 304 // a. Return true. 305 std::pair<icu::MeasureUnit, icu::MeasureUnit> pair(result, none); 306 return Just(pair); 307 } 308 // 2. If the substring "-per-" does not occur exactly once in unitIdentifier, 309 // then 310 size_t first_per = unit.find("-per-"); 311 if (first_per == std::string::npos || 312 unit.find("-per-", first_per + 5) != std::string::npos) { 313 // a. Return false. 314 return Nothing<std::pair<icu::MeasureUnit, icu::MeasureUnit>>(); 315 } 316 // 3. Let numerator be the substring of unitIdentifier from the beginning to 317 // just before "-per-". 318 std::string numerator = unit.substr(0, first_per); 319 320 // 4. If the result of IsSanctionedUnitIdentifier(numerator) is false, then 321 result = IsSanctionedUnitIdentifier(numerator); 322 if (result == none) { 323 // a. Return false. 324 return Nothing<std::pair<icu::MeasureUnit, icu::MeasureUnit>>(); 325 } 326 // 5. Let denominator be the substring of unitIdentifier from just after 327 // "-per-" to the end. 328 std::string denominator = unit.substr(first_per + 5); 329 330 // 6. If the result of IsSanctionedUnitIdentifier(denominator) is false, then 331 icu::MeasureUnit den_result = IsSanctionedUnitIdentifier(denominator); 332 if (den_result == none) { 333 // a. Return false. 334 return Nothing<std::pair<icu::MeasureUnit, icu::MeasureUnit>>(); 335 } 336 // 7. Return true. 337 std::pair<icu::MeasureUnit, icu::MeasureUnit> pair(result, den_result); 338 return Just(pair); 339} 340 341// ecma-402/#sec-currencydigits 342// The currency is expected to an all upper case string value. 343int CurrencyDigits(const icu::UnicodeString& currency) { 344 UErrorCode status = U_ZERO_ERROR; 345 uint32_t fraction_digits = ucurr_getDefaultFractionDigits( 346 reinterpret_cast<const UChar*>(currency.getBuffer()), &status); 347 // For missing currency codes, default to the most common, 2 348 return U_SUCCESS(status) ? fraction_digits : 2; 349} 350 351bool IsAToZ(char ch) { 352 return base::IsInRange(AsciiAlphaToLower(ch), 'a', 'z'); 353} 354 355// ecma402/#sec-iswellformedcurrencycode 356bool IsWellFormedCurrencyCode(const std::string& currency) { 357 // Verifies that the input is a well-formed ISO 4217 currency code. 358 // ecma402/#sec-currency-codes 359 // 2. If the number of elements in normalized is not 3, return false. 360 if (currency.length() != 3) return false; 361 // 1. Let normalized be the result of mapping currency to upper case as 362 // described in 6.1. 363 // 364 // 3. If normalized contains any character that is not in 365 // the range "A" to "Z" (U+0041 to U+005A), return false. 366 // 367 // 4. Return true. 368 // Don't uppercase to test. It could convert invalid code into a valid one. 369 // For example \u00DFP (Eszett+P) becomes SSP. 370 return (IsAToZ(currency[0]) && IsAToZ(currency[1]) && IsAToZ(currency[2])); 371} 372 373// Return the style as a String. 374Handle<String> StyleAsString(Isolate* isolate, Style style) { 375 switch (style) { 376 case Style::PERCENT: 377 return ReadOnlyRoots(isolate).percent_string_handle(); 378 case Style::CURRENCY: 379 return ReadOnlyRoots(isolate).currency_string_handle(); 380 case Style::UNIT: 381 return ReadOnlyRoots(isolate).unit_string_handle(); 382 case Style::DECIMAL: 383 return ReadOnlyRoots(isolate).decimal_string_handle(); 384 } 385 UNREACHABLE(); 386} 387 388// Parse the 'currencyDisplay' from the skeleton. 389Handle<String> CurrencyDisplayString(Isolate* isolate, 390 const icu::UnicodeString& skeleton) { 391 // Ex: skeleton as 392 // "currency/TWD .00 rounding-mode-half-up unit-width-iso-code" 393 if (skeleton.indexOf("unit-width-iso-code") >= 0) { 394 return ReadOnlyRoots(isolate).code_string_handle(); 395 } 396 // Ex: skeleton as 397 // "currency/TWD .00 rounding-mode-half-up unit-width-full-name;" 398 if (skeleton.indexOf("unit-width-full-name") >= 0) { 399 return ReadOnlyRoots(isolate).name_string_handle(); 400 } 401 // Ex: skeleton as 402 // "currency/TWD .00 rounding-mode-half-up unit-width-narrow; 403 if (skeleton.indexOf("unit-width-narrow") >= 0) { 404 return ReadOnlyRoots(isolate).narrowSymbol_string_handle(); 405 } 406 // Ex: skeleton as "currency/TWD .00 rounding-mode-half-up" 407 return ReadOnlyRoots(isolate).symbol_string_handle(); 408} 409 410// Return true if there are no "group-off" in the skeleton. 411bool UseGroupingFromSkeleton(const icu::UnicodeString& skeleton) { 412 return skeleton.indexOf("group-off") == -1; 413} 414 415Handle<Object> UseGroupingFromSkeleton(Isolate* isolate, 416 const icu::UnicodeString& skeleton) { 417 Factory* factory = isolate->factory(); 418 static const char* group = "group-"; 419 int32_t start = skeleton.indexOf(group); 420 if (start >= 0) { 421 DCHECK_EQ(6, strlen(group)); 422 icu::UnicodeString check = skeleton.tempSubString(start + 6); 423 // Ex: skeleton as 424 // .### rounding-mode-half-up group-off 425 if (check.startsWith("off")) { 426 return factory->false_value(); 427 } 428 // Ex: skeleton as 429 // .### rounding-mode-half-up group-min2 430 if (check.startsWith("min2")) { 431 return ReadOnlyRoots(isolate).min2_string_handle(); 432 } 433 // Ex: skeleton as 434 // .### rounding-mode-half-up group-on-aligned 435 if (check.startsWith("on-aligned")) { 436 return ReadOnlyRoots(isolate).always_string_handle(); 437 } 438 } 439 // Ex: skeleton as 440 // .### 441 return ReadOnlyRoots(isolate).auto_string_handle(); 442} 443 444// Parse currency code from skeleton. For example, skeleton as 445// "currency/TWD .00 rounding-mode-half-up unit-width-full-name;" 446const icu::UnicodeString CurrencyFromSkeleton( 447 const icu::UnicodeString& skeleton) { 448 const char currency[] = "currency/"; 449 int32_t index = skeleton.indexOf(currency); 450 if (index < 0) return ""; 451 index += static_cast<int32_t>(std::strlen(currency)); 452 return skeleton.tempSubString(index, 3); 453} 454 455const icu::UnicodeString NumberingSystemFromSkeleton( 456 const icu::UnicodeString& skeleton) { 457 const char numbering_system[] = "numbering-system/"; 458 int32_t index = skeleton.indexOf(numbering_system); 459 if (index < 0) return "latn"; 460 index += static_cast<int32_t>(std::strlen(numbering_system)); 461 const icu::UnicodeString res = skeleton.tempSubString(index); 462 index = res.indexOf(" "); 463 if (index < 0) return res; 464 return res.tempSubString(0, index); 465} 466 467// Return CurrencySign as string based on skeleton. 468Handle<String> CurrencySignString(Isolate* isolate, 469 const icu::UnicodeString& skeleton) { 470 // Ex: skeleton as 471 // "currency/TWD .00 rounding-mode-half-up sign-accounting-always" OR 472 // "currency/TWD .00 rounding-mode-half-up sign-accounting-except-zero" 473 if (skeleton.indexOf("sign-accounting") >= 0) { 474 return ReadOnlyRoots(isolate).accounting_string_handle(); 475 } 476 return ReadOnlyRoots(isolate).standard_string_handle(); 477} 478 479// Return UnitDisplay as string based on skeleton. 480Handle<String> UnitDisplayString(Isolate* isolate, 481 const icu::UnicodeString& skeleton) { 482 // Ex: skeleton as 483 // "unit/length-meter .### rounding-mode-half-up unit-width-full-name" 484 if (skeleton.indexOf("unit-width-full-name") >= 0) { 485 return ReadOnlyRoots(isolate).long_string_handle(); 486 } 487 // Ex: skeleton as 488 // "unit/length-meter .### rounding-mode-half-up unit-width-narrow". 489 if (skeleton.indexOf("unit-width-narrow") >= 0) { 490 return ReadOnlyRoots(isolate).narrow_string_handle(); 491 } 492 // Ex: skeleton as 493 // "unit/length-foot .### rounding-mode-half-up" 494 return ReadOnlyRoots(isolate).short_string_handle(); 495} 496 497// Parse Notation from skeleton. 498Notation NotationFromSkeleton(const icu::UnicodeString& skeleton) { 499 // Ex: skeleton as 500 // "scientific .### rounding-mode-half-up" 501 if (skeleton.indexOf("scientific") >= 0) { 502 return Notation::SCIENTIFIC; 503 } 504 // Ex: skeleton as 505 // "engineering .### rounding-mode-half-up" 506 if (skeleton.indexOf("engineering") >= 0) { 507 return Notation::ENGINEERING; 508 } 509 // Ex: skeleton as 510 // "compact-short .### rounding-mode-half-up" or 511 // "compact-long .### rounding-mode-half-up 512 if (skeleton.indexOf("compact-") >= 0) { 513 return Notation::COMPACT; 514 } 515 // Ex: skeleton as 516 // "unit/length-foot .### rounding-mode-half-up" 517 return Notation::STANDARD; 518} 519 520Handle<String> NotationAsString(Isolate* isolate, Notation notation) { 521 switch (notation) { 522 case Notation::SCIENTIFIC: 523 return ReadOnlyRoots(isolate).scientific_string_handle(); 524 case Notation::ENGINEERING: 525 return ReadOnlyRoots(isolate).engineering_string_handle(); 526 case Notation::COMPACT: 527 return ReadOnlyRoots(isolate).compact_string_handle(); 528 case Notation::STANDARD: 529 return ReadOnlyRoots(isolate).standard_string_handle(); 530 } 531 UNREACHABLE(); 532} 533 534// Return CompactString as string based on skeleton. 535Handle<String> CompactDisplayString(Isolate* isolate, 536 const icu::UnicodeString& skeleton) { 537 // Ex: skeleton as 538 // "compact-long .### rounding-mode-half-up" 539 if (skeleton.indexOf("compact-long") >= 0) { 540 return ReadOnlyRoots(isolate).long_string_handle(); 541 } 542 // Ex: skeleton as 543 // "compact-short .### rounding-mode-half-up" 544 DCHECK_GE(skeleton.indexOf("compact-short"), 0); 545 return ReadOnlyRoots(isolate).short_string_handle(); 546} 547 548// Return SignDisplay as string based on skeleton. 549Handle<String> SignDisplayString(Isolate* isolate, 550 const icu::UnicodeString& skeleton) { 551 // Ex: skeleton as 552 // "currency/TWD .00 rounding-mode-half-up sign-never" 553 if (skeleton.indexOf("sign-never") >= 0) { 554 return ReadOnlyRoots(isolate).never_string_handle(); 555 } 556 // Ex: skeleton as 557 // ".### rounding-mode-half-up sign-always" or 558 // "currency/TWD .00 rounding-mode-half-up sign-accounting-always" 559 if (skeleton.indexOf("sign-always") >= 0 || 560 skeleton.indexOf("sign-accounting-always") >= 0) { 561 return ReadOnlyRoots(isolate).always_string_handle(); 562 } 563 // Ex: skeleton as 564 // "currency/TWD .00 rounding-mode-half-up sign-accounting-except-zero" or 565 // "currency/TWD .00 rounding-mode-half-up sign-except-zero" 566 if (skeleton.indexOf("sign-accounting-except-zero") >= 0 || 567 skeleton.indexOf("sign-except-zero") >= 0) { 568 return ReadOnlyRoots(isolate).exceptZero_string_handle(); 569 } 570 // Ex: skeleton as 571 // ".### rounding-mode-half-up sign-negative" or 572 // "currency/TWD .00 rounding-mode-half-up sign-accounting-negative" 573 if (skeleton.indexOf("sign-accounting-negative") >= 0 || 574 skeleton.indexOf("sign-negative") >= 0) { 575 return ReadOnlyRoots(isolate).negative_string_handle(); 576 } 577 return ReadOnlyRoots(isolate).auto_string_handle(); 578} 579 580// Return RoundingMode as string based on skeleton. 581Handle<String> RoundingModeString(Isolate* isolate, 582 const icu::UnicodeString& skeleton) { 583 static const char* rounding_mode = "rounding-mode-"; 584 int32_t start = skeleton.indexOf(rounding_mode); 585 if (start >= 0) { 586 DCHECK_EQ(14, strlen(rounding_mode)); 587 icu::UnicodeString check = skeleton.tempSubString(start + 14); 588 589 // Ex: skeleton as 590 // .### rounding-mode-ceiling 591 if (check.startsWith("ceiling")) { 592 return ReadOnlyRoots(isolate).ceil_string_handle(); 593 } 594 // Ex: skeleton as 595 // .### rounding-mode-down 596 if (check.startsWith("down")) { 597 return ReadOnlyRoots(isolate).trunc_string_handle(); 598 } 599 // Ex: skeleton as 600 // .### rounding-mode-floor 601 if (check.startsWith("floor")) { 602 return ReadOnlyRoots(isolate).floor_string_handle(); 603 } 604 // Ex: skeleton as 605 // .### rounding-mode-half-ceiling 606 if (check.startsWith("half-ceiling")) { 607 return ReadOnlyRoots(isolate).halfCeil_string_handle(); 608 } 609 // Ex: skeleton as 610 // .### rounding-mode-half-down 611 if (check.startsWith("half-down")) { 612 return ReadOnlyRoots(isolate).halfTrunc_string_handle(); 613 } 614 // Ex: skeleton as 615 // .### rounding-mode-half-floor 616 if (check.startsWith("half-floor")) { 617 return ReadOnlyRoots(isolate).halfFloor_string_handle(); 618 } 619 // Ex: skeleton as 620 // .### rounding-mode-half-up 621 if (check.startsWith("half-up")) { 622 return ReadOnlyRoots(isolate).halfExpand_string_handle(); 623 } 624 // Ex: skeleton as 625 // .### rounding-mode-up 626 if (check.startsWith("up")) { 627 return ReadOnlyRoots(isolate).expand_string_handle(); 628 } 629 } 630 // Ex: skeleton as 631 // .### 632 return ReadOnlyRoots(isolate).halfEven_string_handle(); 633} 634 635Handle<Object> RoundingIncrement(Isolate* isolate, 636 const icu::UnicodeString& skeleton) { 637 int32_t cur = skeleton.indexOf(u"precision-increment/"); 638 if (cur < 0) return isolate->factory()->NewNumberFromInt(1); 639 cur += 20; // length of "precision-increment/" 640 int32_t increment = 0; 641 while (cur < skeleton.length()) { 642 char16_t c = skeleton[cur++]; 643 if (c == u'.') continue; 644 if (!IsDecimalDigit(c)) break; 645 increment = increment * 10 + (c - '0'); 646 } 647 return isolate->factory()->NewNumberFromInt(increment); 648} 649 650// Return RoundingPriority as string based on skeleton. 651Handle<String> RoundingPriorityString(Isolate* isolate, 652 const icu::UnicodeString& skeleton) { 653 int32_t found; 654 // If #r or @r is followed by a SPACE or in the end of line. 655 if ((found = skeleton.indexOf("#r")) >= 0 || 656 (found = skeleton.indexOf("@r")) >= 0) { 657 if (found + 2 == skeleton.length() || skeleton[found + 2] == ' ') { 658 return ReadOnlyRoots(isolate).morePrecision_string_handle(); 659 } 660 } 661 // If #s or @s is followed by a SPACE or in the end of line. 662 if ((found = skeleton.indexOf("#s")) >= 0 || 663 (found = skeleton.indexOf("@s")) >= 0) { 664 if (found + 2 == skeleton.length() || skeleton[found + 2] == ' ') { 665 return ReadOnlyRoots(isolate).lessPrecision_string_handle(); 666 } 667 } 668 return ReadOnlyRoots(isolate).auto_string_handle(); 669} 670 671// Return trailingZeroDisplay as string based on skeleton. 672Handle<String> TrailingZeroDisplayString(Isolate* isolate, 673 const icu::UnicodeString& skeleton) { 674 int32_t found; 675 if ((found = skeleton.indexOf("/w")) >= 0) { 676 if (found + 2 == skeleton.length() || skeleton[found + 2] == ' ') { 677 return ReadOnlyRoots(isolate).stripIfInteger_string_handle(); 678 } 679 } 680 return ReadOnlyRoots(isolate).auto_string_handle(); 681} 682 683} // anonymous namespace 684 685// Return the minimum integer digits by counting the number of '0' after 686// "integer-width/*" in the skeleton. 687// Ex: Return 15 for skeleton as 688// “currency/TWD .00 rounding-mode-half-up integer-width/*000000000000000” 689// 1 690// 123456789012345 691// Return default value as 1 if there are no "integer-width/*". 692int32_t JSNumberFormat::MinimumIntegerDigitsFromSkeleton( 693 const icu::UnicodeString& skeleton) { 694 // count the number of 0 after "integer-width/*" 695 icu::UnicodeString search("integer-width/*"); 696 int32_t index = skeleton.indexOf(search); 697 if (index < 0) return 1; // return 1 if cannot find it. 698 index += search.length(); 699 int32_t matched = 0; 700 while (index < skeleton.length() && skeleton[index] == '0') { 701 matched++; 702 index++; 703 } 704 CHECK_GT(matched, 0); 705 return matched; 706} 707 708// Return true if there are fraction digits, false if not. 709// The minimum fraction digits is the number of '0' after '.' in the skeleton 710// The maximum fraction digits is the number of '#' after the above '0's plus 711// the minimum fraction digits. 712// For example, as skeleton “.000#### rounding-mode-half-up” 713// 123 714// 4567 715// Set The minimum as 3 and maximum as 7. 716bool JSNumberFormat::FractionDigitsFromSkeleton( 717 const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum) { 718 icu::UnicodeString search("."); 719 int32_t index = skeleton.indexOf(search); 720 if (index < 0) return false; 721 *minimum = 0; 722 index++; // skip the '.' 723 while (index < skeleton.length() && IsDecimalDigit(skeleton[index])) { 724 (*minimum)++; 725 index++; 726 } 727 *maximum = *minimum; 728 while (index < skeleton.length() && skeleton[index] == '#') { 729 (*maximum)++; 730 index++; 731 } 732 return true; 733} 734 735// Return true if there are significant digits, false if not. 736// The minimum significant digits is the number of '@' in the skeleton 737// The maximum significant digits is the number of '#' after these '@'s plus 738// the minimum significant digits. 739// Ex: Skeleton as "@@@@@####### rounding-mode-half-up" 740// 12345 741// 6789012 742// Set The minimum as 5 and maximum as 12. 743bool JSNumberFormat::SignificantDigitsFromSkeleton( 744 const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum) { 745 icu::UnicodeString search("@"); 746 int32_t index = skeleton.indexOf(search); 747 if (index < 0) return false; 748 *minimum = 1; 749 index++; // skip the first '@' 750 while (index < skeleton.length() && skeleton[index] == '@') { 751 (*minimum)++; 752 index++; 753 } 754 *maximum = *minimum; 755 while (index < skeleton.length() && skeleton[index] == '#') { 756 (*maximum)++; 757 index++; 758 } 759 return true; 760} 761 762namespace { 763 764// Ex: percent .### rounding-mode-half-up 765// Special case for "percent" 766// Ex: "unit/milliliter-per-acre .### rounding-mode-half-up" 767// should return "milliliter-per-acre". 768// Ex: "unit/year .### rounding-mode-half-up" should return 769// "year". 770std::string UnitFromSkeleton(const icu::UnicodeString& skeleton) { 771 std::string str; 772 str = skeleton.toUTF8String<std::string>(str); 773 std::string search("unit/"); 774 size_t begin = str.find(search); 775 if (begin == str.npos) { 776 // Special case for "percent". 777 if (str.find("percent") != str.npos) { 778 return "percent"; 779 } 780 return ""; 781 } 782 // Ex: 783 // "unit/acre .### rounding-mode-half-up" 784 // b 785 // Ex: 786 // "unit/milliliter-per-acre .### rounding-mode-half-up" 787 // b 788 begin += search.size(); 789 if (begin == str.npos) { 790 return ""; 791 } 792 // Find the end of the subtype. 793 size_t end = str.find(" ", begin); 794 // Ex: 795 // "unit/acre .### rounding-mode-half-up" 796 // b e 797 // Ex: 798 // "unit/milliliter-per-acre .### rounding-mode-half-up" 799 // b e 800 if (end == str.npos) { 801 end = str.size(); 802 } 803 return str.substr(begin, end - begin); 804} 805 806Style StyleFromSkeleton(const icu::UnicodeString& skeleton) { 807 if (skeleton.indexOf("currency/") >= 0) { 808 return Style::CURRENCY; 809 } 810 if (skeleton.indexOf("percent") >= 0) { 811 // percent precision-integer rounding-mode-half-up scale/100 812 if (skeleton.indexOf("scale/100") >= 0) { 813 return Style::PERCENT; 814 } else { 815 return Style::UNIT; 816 } 817 } 818 // Before ICU68: "measure-unit/", since ICU68 "unit/" 819 if (skeleton.indexOf("unit/") >= 0) { 820 return Style::UNIT; 821 } 822 return Style::DECIMAL; 823} 824 825icu::number::UnlocalizedNumberFormatter SetDigitOptionsToFormatterV2( 826 const icu::number::UnlocalizedNumberFormatter& settings, 827 const Intl::NumberFormatDigitOptions& digit_options) { 828 icu::number::UnlocalizedNumberFormatter result = settings; 829 if (digit_options.minimum_integer_digits > 1) { 830 result = result.integerWidth(icu::number::IntegerWidth::zeroFillTo( 831 digit_options.minimum_integer_digits)); 832 } 833 834 if (digit_options.rounding_type == Intl::RoundingType::kMorePrecision) { 835 return result; 836 } 837 icu::number::Precision precision = 838 (digit_options.minimum_significant_digits > 0) 839 ? icu::number::Precision::minMaxSignificantDigits( 840 digit_options.minimum_significant_digits, 841 digit_options.maximum_significant_digits) 842 : icu::number::Precision::minMaxFraction( 843 digit_options.minimum_fraction_digits, 844 digit_options.maximum_fraction_digits); 845 846 return result.precision(precision); 847} 848 849icu::number::UnlocalizedNumberFormatter SetDigitOptionsToFormatterV3( 850 const icu::number::UnlocalizedNumberFormatter& settings, 851 const Intl::NumberFormatDigitOptions& digit_options, int rounding_increment, 852 JSNumberFormat::ShowTrailingZeros trailing_zeros) { 853 icu::number::UnlocalizedNumberFormatter result = settings; 854 if (digit_options.minimum_integer_digits > 1) { 855 result = result.integerWidth(icu::number::IntegerWidth::zeroFillTo( 856 digit_options.minimum_integer_digits)); 857 } 858 859 icu::number::Precision precision = icu::number::Precision::unlimited(); 860 bool relaxed = false; 861 switch (digit_options.rounding_type) { 862 case Intl::RoundingType::kSignificantDigits: 863 precision = icu::number::Precision::minMaxSignificantDigits( 864 digit_options.minimum_significant_digits, 865 digit_options.maximum_significant_digits); 866 break; 867 case Intl::RoundingType::kFractionDigits: 868 precision = icu::number::Precision::minMaxFraction( 869 digit_options.minimum_fraction_digits, 870 digit_options.maximum_fraction_digits); 871 break; 872 case Intl::RoundingType::kMorePrecision: 873 relaxed = true; 874 V8_FALLTHROUGH; 875 case Intl::RoundingType::kLessPrecision: 876 precision = 877 icu::number::Precision::minMaxFraction( 878 digit_options.minimum_fraction_digits, 879 digit_options.maximum_fraction_digits) 880 .withSignificantDigits(digit_options.minimum_significant_digits, 881 digit_options.maximum_significant_digits, 882 relaxed ? UNUM_ROUNDING_PRIORITY_RELAXED 883 : UNUM_ROUNDING_PRIORITY_STRICT); 884 break; 885 } 886 if (rounding_increment != 1) { 887 double icu_increment = rounding_increment * 888 std::pow(10, -digit_options.maximum_fraction_digits); 889 precision = ::icu::number::Precision::increment(icu_increment) 890 .withMinFraction(digit_options.minimum_fraction_digits); 891 } 892 if (trailing_zeros == JSNumberFormat::ShowTrailingZeros::kHide) { 893 precision = precision.trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE); 894 } 895 return result.precision(precision); 896} 897 898} // anonymous namespace 899 900icu::number::UnlocalizedNumberFormatter 901JSNumberFormat::SetDigitOptionsToFormatter( 902 const icu::number::UnlocalizedNumberFormatter& settings, 903 const Intl::NumberFormatDigitOptions& digit_options, int rounding_increment, 904 JSNumberFormat::ShowTrailingZeros trailing_zeros) { 905 if (FLAG_harmony_intl_number_format_v3) { 906 return SetDigitOptionsToFormatterV3(settings, digit_options, 907 rounding_increment, trailing_zeros); 908 } else { 909 return SetDigitOptionsToFormatterV2(settings, digit_options); 910 } 911} 912 913// static 914// ecma402 #sec-intl.numberformat.prototype.resolvedoptions 915Handle<JSObject> JSNumberFormat::ResolvedOptions( 916 Isolate* isolate, Handle<JSNumberFormat> number_format) { 917 Factory* factory = isolate->factory(); 918 919 UErrorCode status = U_ZERO_ERROR; 920 icu::number::LocalizedNumberFormatter* icu_number_formatter = 921 number_format->icu_number_formatter().raw(); 922 icu::UnicodeString skeleton = icu_number_formatter->toSkeleton(status); 923 CHECK(U_SUCCESS(status)); 924 925 // 4. Let options be ! ObjectCreate(%ObjectPrototype%). 926 Handle<JSObject> options = factory->NewJSObject(isolate->object_function()); 927 928 Handle<String> locale = Handle<String>(number_format->locale(), isolate); 929 const icu::UnicodeString numberingSystem_ustr = 930 NumberingSystemFromSkeleton(skeleton); 931 // 5. For each row of Table 4, except the header row, in table order, do 932 // Table 4: Resolved Options of NumberFormat Instances 933 // Internal Slot Property 934 // [[Locale]] "locale" 935 // [[NumberingSystem]] "numberingSystem" 936 // [[Style]] "style" 937 // [[Currency]] "currency" 938 // [[CurrencyDisplay]] "currencyDisplay" 939 // [[CurrencySign]] "currencySign" 940 // [[Unit]] "unit" 941 // [[UnitDisplay]] "unitDisplay" 942 // [[MinimumIntegerDigits]] "minimumIntegerDigits" 943 // [[MinimumFractionDigits]] "minimumFractionDigits" 944 // [[MaximumFractionDigits]] "maximumFractionDigits" 945 // [[MinimumSignificantDigits]] "minimumSignificantDigits" 946 // [[MaximumSignificantDigits]] "maximumSignificantDigits" 947 // [[UseGrouping]] "useGrouping" 948 // [[Notation]] "notation" 949 // [[CompactDisplay]] "compactDisplay" 950 // [[SignDisplay]] "signDisplay" 951 // 952 // For v3 953 // [[RoundingMode]] "roundingMode" 954 // [[RoundingIncrement]] "roundingIncrement" 955 // [[TrailingZeroDisplay]] "trailingZeroDisplay" 956 957 CHECK(JSReceiver::CreateDataProperty(isolate, options, 958 factory->locale_string(), locale, 959 Just(kDontThrow)) 960 .FromJust()); 961 Handle<String> numberingSystem_string; 962 CHECK(Intl::ToString(isolate, numberingSystem_ustr) 963 .ToHandle(&numberingSystem_string)); 964 CHECK(JSReceiver::CreateDataProperty(isolate, options, 965 factory->numberingSystem_string(), 966 numberingSystem_string, Just(kDontThrow)) 967 .FromJust()); 968 Style style = StyleFromSkeleton(skeleton); 969 CHECK(JSReceiver::CreateDataProperty( 970 isolate, options, factory->style_string(), 971 StyleAsString(isolate, style), Just(kDontThrow)) 972 .FromJust()); 973 const icu::UnicodeString currency_ustr = CurrencyFromSkeleton(skeleton); 974 if (!currency_ustr.isEmpty()) { 975 Handle<String> currency_string; 976 CHECK(Intl::ToString(isolate, currency_ustr).ToHandle(¤cy_string)); 977 CHECK(JSReceiver::CreateDataProperty(isolate, options, 978 factory->currency_string(), 979 currency_string, Just(kDontThrow)) 980 .FromJust()); 981 982 CHECK(JSReceiver::CreateDataProperty( 983 isolate, options, factory->currencyDisplay_string(), 984 CurrencyDisplayString(isolate, skeleton), Just(kDontThrow)) 985 .FromJust()); 986 CHECK(JSReceiver::CreateDataProperty( 987 isolate, options, factory->currencySign_string(), 988 CurrencySignString(isolate, skeleton), Just(kDontThrow)) 989 .FromJust()); 990 } 991 992 if (style == Style::UNIT) { 993 std::string unit = UnitFromSkeleton(skeleton); 994 if (!unit.empty()) { 995 CHECK(JSReceiver::CreateDataProperty( 996 isolate, options, factory->unit_string(), 997 isolate->factory()->NewStringFromAsciiChecked(unit.c_str()), 998 Just(kDontThrow)) 999 .FromJust()); 1000 } 1001 CHECK(JSReceiver::CreateDataProperty( 1002 isolate, options, factory->unitDisplay_string(), 1003 UnitDisplayString(isolate, skeleton), Just(kDontThrow)) 1004 .FromJust()); 1005 } 1006 1007 CHECK( 1008 JSReceiver::CreateDataProperty( 1009 isolate, options, factory->minimumIntegerDigits_string(), 1010 factory->NewNumberFromInt(MinimumIntegerDigitsFromSkeleton(skeleton)), 1011 Just(kDontThrow)) 1012 .FromJust()); 1013 1014 int32_t minimum = 0, maximum = 0; 1015 if (SignificantDigitsFromSkeleton(skeleton, &minimum, &maximum)) { 1016 CHECK(JSReceiver::CreateDataProperty( 1017 isolate, options, factory->minimumSignificantDigits_string(), 1018 factory->NewNumberFromInt(minimum), Just(kDontThrow)) 1019 .FromJust()); 1020 CHECK(JSReceiver::CreateDataProperty( 1021 isolate, options, factory->maximumSignificantDigits_string(), 1022 factory->NewNumberFromInt(maximum), Just(kDontThrow)) 1023 .FromJust()); 1024 } else { 1025 FractionDigitsFromSkeleton(skeleton, &minimum, &maximum); 1026 CHECK(JSReceiver::CreateDataProperty( 1027 isolate, options, factory->minimumFractionDigits_string(), 1028 factory->NewNumberFromInt(minimum), Just(kDontThrow)) 1029 .FromJust()); 1030 CHECK(JSReceiver::CreateDataProperty( 1031 isolate, options, factory->maximumFractionDigits_string(), 1032 factory->NewNumberFromInt(maximum), Just(kDontThrow)) 1033 .FromJust()); 1034 } 1035 1036 if (FLAG_harmony_intl_number_format_v3) { 1037 CHECK(JSReceiver::CreateDataProperty( 1038 isolate, options, factory->useGrouping_string(), 1039 UseGroupingFromSkeleton(isolate, skeleton), Just(kDontThrow)) 1040 .FromJust()); 1041 } else { 1042 CHECK(JSReceiver::CreateDataProperty( 1043 isolate, options, factory->useGrouping_string(), 1044 factory->ToBoolean(UseGroupingFromSkeleton(skeleton)), 1045 Just(kDontThrow)) 1046 .FromJust()); 1047 } 1048 1049 Notation notation = NotationFromSkeleton(skeleton); 1050 CHECK(JSReceiver::CreateDataProperty( 1051 isolate, options, factory->notation_string(), 1052 NotationAsString(isolate, notation), Just(kDontThrow)) 1053 .FromJust()); 1054 // Only output compactDisplay when notation is compact. 1055 if (notation == Notation::COMPACT) { 1056 CHECK(JSReceiver::CreateDataProperty( 1057 isolate, options, factory->compactDisplay_string(), 1058 CompactDisplayString(isolate, skeleton), Just(kDontThrow)) 1059 .FromJust()); 1060 } 1061 CHECK(JSReceiver::CreateDataProperty( 1062 isolate, options, factory->signDisplay_string(), 1063 SignDisplayString(isolate, skeleton), Just(kDontThrow)) 1064 .FromJust()); 1065 if (FLAG_harmony_intl_number_format_v3) { 1066 CHECK(JSReceiver::CreateDataProperty( 1067 isolate, options, factory->roundingMode_string(), 1068 RoundingModeString(isolate, skeleton), Just(kDontThrow)) 1069 .FromJust()); 1070 CHECK(JSReceiver::CreateDataProperty( 1071 isolate, options, factory->roundingIncrement_string(), 1072 RoundingIncrement(isolate, skeleton), Just(kDontThrow)) 1073 .FromJust()); 1074 CHECK(JSReceiver::CreateDataProperty( 1075 isolate, options, factory->trailingZeroDisplay_string(), 1076 TrailingZeroDisplayString(isolate, skeleton), Just(kDontThrow)) 1077 .FromJust()); 1078 CHECK(JSReceiver::CreateDataProperty( 1079 isolate, options, factory->roundingPriority_string(), 1080 RoundingPriorityString(isolate, skeleton), Just(kDontThrow)) 1081 .FromJust()); 1082 } 1083 return options; 1084} 1085 1086// ecma402/#sec-unwrapnumberformat 1087MaybeHandle<JSNumberFormat> JSNumberFormat::UnwrapNumberFormat( 1088 Isolate* isolate, Handle<JSReceiver> format_holder) { 1089 // old code copy from NumberFormat::Unwrap that has no spec comment and 1090 // compiled but fail unit tests. 1091 Handle<Context> native_context = 1092 Handle<Context>(isolate->context().native_context(), isolate); 1093 Handle<JSFunction> constructor = Handle<JSFunction>( 1094 JSFunction::cast(native_context->intl_number_format_function()), isolate); 1095 Handle<Object> object; 1096 ASSIGN_RETURN_ON_EXCEPTION( 1097 isolate, object, 1098 Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor, 1099 format_holder->IsJSNumberFormat()), 1100 JSNumberFormat); 1101 // 4. If ... or nf does not have an [[InitializedNumberFormat]] internal slot, 1102 // then 1103 if (!object->IsJSNumberFormat()) { 1104 // a. Throw a TypeError exception. 1105 THROW_NEW_ERROR(isolate, 1106 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, 1107 isolate->factory()->NewStringFromAsciiChecked( 1108 "UnwrapNumberFormat")), 1109 JSNumberFormat); 1110 } 1111 // 5. Return nf. 1112 return Handle<JSNumberFormat>::cast(object); 1113} 1114 1115// 22. is in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 1116// 5000 » 1117bool IsValidRoundingIncrement(int value) { 1118 return value == 1 || value == 2 || value == 5 || value == 10 || value == 20 || 1119 value == 25 || value == 50 || value == 100 || value == 200 || 1120 value == 250 || value == 500 || value == 1000 || value == 2000 || 1121 value == 2500 || value == 5000; 1122} 1123// static 1124MaybeHandle<JSNumberFormat> JSNumberFormat::New(Isolate* isolate, 1125 Handle<Map> map, 1126 Handle<Object> locales, 1127 Handle<Object> options_obj, 1128 const char* service) { 1129 Factory* factory = isolate->factory(); 1130 1131 // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). 1132 Maybe<std::vector<std::string>> maybe_requested_locales = 1133 Intl::CanonicalizeLocaleList(isolate, locales); 1134 MAYBE_RETURN(maybe_requested_locales, Handle<JSNumberFormat>()); 1135 std::vector<std::string> requested_locales = 1136 maybe_requested_locales.FromJust(); 1137 1138 // 2. Set options to ? CoerceOptionsToObject(options). 1139 Handle<JSReceiver> options; 1140 ASSIGN_RETURN_ON_EXCEPTION( 1141 isolate, options, CoerceOptionsToObject(isolate, options_obj, service), 1142 JSNumberFormat); 1143 1144 // 3. Let opt be a new Record. 1145 // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « 1146 // "lookup", "best fit" », "best fit"). 1147 // 5. Set opt.[[localeMatcher]] to matcher. 1148 Maybe<Intl::MatcherOption> maybe_locale_matcher = 1149 Intl::GetLocaleMatcher(isolate, options, service); 1150 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSNumberFormat>()); 1151 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust(); 1152 1153 std::unique_ptr<char[]> numbering_system_str = nullptr; 1154 // 6. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`, 1155 // `"string"`, *undefined*, *undefined*). 1156 Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem( 1157 isolate, options, service, &numbering_system_str); 1158 // 7. If _numberingSystem_ is not *undefined*, then 1159 // 8. If _numberingSystem_ does not match the 1160 // `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError* 1161 // exception. 1162 MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSNumberFormat>()); 1163 1164 // 9. Let localeData be %NumberFormat%.[[LocaleData]]. 1165 // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], 1166 // requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], 1167 // localeData). 1168 std::set<std::string> relevant_extension_keys{"nu"}; 1169 Maybe<Intl::ResolvedLocale> maybe_resolve_locale = 1170 Intl::ResolveLocale(isolate, JSNumberFormat::GetAvailableLocales(), 1171 requested_locales, matcher, relevant_extension_keys); 1172 if (maybe_resolve_locale.IsNothing()) { 1173 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError), 1174 JSNumberFormat); 1175 } 1176 Intl::ResolvedLocale r = maybe_resolve_locale.FromJust(); 1177 1178 icu::Locale icu_locale = r.icu_locale; 1179 UErrorCode status = U_ZERO_ERROR; 1180 if (numbering_system_str != nullptr) { 1181 auto nu_extension_it = r.extensions.find("nu"); 1182 if (nu_extension_it != r.extensions.end() && 1183 nu_extension_it->second != numbering_system_str.get()) { 1184 icu_locale.setUnicodeKeywordValue("nu", nullptr, status); 1185 CHECK(U_SUCCESS(status)); 1186 } 1187 } 1188 1189 // 9. Set numberFormat.[[Locale]] to r.[[locale]]. 1190 Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(icu_locale); 1191 MAYBE_RETURN(maybe_locale_str, MaybeHandle<JSNumberFormat>()); 1192 Handle<String> locale_str = isolate->factory()->NewStringFromAsciiChecked( 1193 maybe_locale_str.FromJust().c_str()); 1194 1195 if (numbering_system_str != nullptr && 1196 Intl::IsValidNumberingSystem(numbering_system_str.get())) { 1197 icu_locale.setUnicodeKeywordValue("nu", numbering_system_str.get(), status); 1198 CHECK(U_SUCCESS(status)); 1199 } 1200 1201 std::string numbering_system = Intl::GetNumberingSystem(icu_locale); 1202 1203 // 11. Let dataLocale be r.[[dataLocale]]. 1204 1205 icu::number::UnlocalizedNumberFormatter settings = 1206 icu::number::UnlocalizedNumberFormatter().roundingMode(UNUM_ROUND_HALFUP); 1207 1208 // For 'latn' numbering system, skip the adoptSymbols which would cause 1209 // 10.1%-13.7% of regression of JSTests/Intl-NewIntlNumberFormat 1210 // See crbug/1052751 so we skip calling adoptSymbols and depending on the 1211 // default instead. 1212 if (!numbering_system.empty() && numbering_system != "latn") { 1213 settings = settings.adoptSymbols(icu::NumberingSystem::createInstanceByName( 1214 numbering_system.c_str(), status)); 1215 CHECK(U_SUCCESS(status)); 1216 } 1217 1218 // ==== Start SetNumberFormatUnitOptions ==== 1219 // 3. Let style be ? GetOption(options, "style", "string", « "decimal", 1220 // "percent", "currency", "unit" », "decimal"). 1221 1222 Maybe<Style> maybe_style = GetStringOption<Style>( 1223 isolate, options, "style", service, 1224 {"decimal", "percent", "currency", "unit"}, 1225 {Style::DECIMAL, Style::PERCENT, Style::CURRENCY, Style::UNIT}, 1226 Style::DECIMAL); 1227 MAYBE_RETURN(maybe_style, MaybeHandle<JSNumberFormat>()); 1228 Style style = maybe_style.FromJust(); 1229 1230 // 4. Set intlObj.[[Style]] to style. 1231 1232 // 5. Let currency be ? GetOption(options, "currency", "string", undefined, 1233 // undefined). 1234 std::unique_ptr<char[]> currency_cstr; 1235 const std::vector<const char*> empty_values = {}; 1236 Maybe<bool> found_currency = GetStringOption( 1237 isolate, options, "currency", empty_values, service, ¤cy_cstr); 1238 MAYBE_RETURN(found_currency, MaybeHandle<JSNumberFormat>()); 1239 1240 std::string currency; 1241 // 6. If currency is not undefined, then 1242 if (found_currency.FromJust()) { 1243 DCHECK_NOT_NULL(currency_cstr.get()); 1244 currency = currency_cstr.get(); 1245 // 6. a. If the result of IsWellFormedCurrencyCode(currency) is false, 1246 // throw a RangeError exception. 1247 if (!IsWellFormedCurrencyCode(currency)) { 1248 THROW_NEW_ERROR( 1249 isolate, 1250 NewRangeError(MessageTemplate::kInvalid, 1251 factory->NewStringFromStaticChars("currency code"), 1252 factory->NewStringFromAsciiChecked(currency.c_str())), 1253 JSNumberFormat); 1254 } 1255 } else { 1256 // 7. If style is "currency" and currency is undefined, throw a TypeError 1257 // exception. 1258 if (style == Style::CURRENCY) { 1259 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCurrencyCode), 1260 JSNumberFormat); 1261 } 1262 } 1263 // 8. Let currencyDisplay be ? GetOption(options, "currencyDisplay", 1264 // "string", « "code", "symbol", "name", "narrowSymbol" », "symbol"). 1265 Maybe<CurrencyDisplay> maybe_currency_display = 1266 GetStringOption<CurrencyDisplay>( 1267 isolate, options, "currencyDisplay", service, 1268 {"code", "symbol", "name", "narrowSymbol"}, 1269 {CurrencyDisplay::CODE, CurrencyDisplay::SYMBOL, 1270 CurrencyDisplay::NAME, CurrencyDisplay::NARROW_SYMBOL}, 1271 CurrencyDisplay::SYMBOL); 1272 MAYBE_RETURN(maybe_currency_display, MaybeHandle<JSNumberFormat>()); 1273 CurrencyDisplay currency_display = maybe_currency_display.FromJust(); 1274 1275 CurrencySign currency_sign = CurrencySign::STANDARD; 1276 // 9. Let currencySign be ? GetOption(options, "currencySign", "string", « 1277 // "standard", "accounting" », "standard"). 1278 Maybe<CurrencySign> maybe_currency_sign = GetStringOption<CurrencySign>( 1279 isolate, options, "currencySign", service, {"standard", "accounting"}, 1280 {CurrencySign::STANDARD, CurrencySign::ACCOUNTING}, 1281 CurrencySign::STANDARD); 1282 MAYBE_RETURN(maybe_currency_sign, MaybeHandle<JSNumberFormat>()); 1283 currency_sign = maybe_currency_sign.FromJust(); 1284 1285 // 10. Let unit be ? GetOption(options, "unit", "string", undefined, 1286 // undefined). 1287 std::unique_ptr<char[]> unit_cstr; 1288 Maybe<bool> found_unit = GetStringOption(isolate, options, "unit", 1289 empty_values, service, &unit_cstr); 1290 MAYBE_RETURN(found_unit, MaybeHandle<JSNumberFormat>()); 1291 1292 std::pair<icu::MeasureUnit, icu::MeasureUnit> unit_pair; 1293 // 11. If unit is not undefined, then 1294 if (found_unit.FromJust()) { 1295 DCHECK_NOT_NULL(unit_cstr.get()); 1296 std::string unit = unit_cstr.get(); 1297 // 11.a If the result of IsWellFormedUnitIdentifier(unit) is false, throw a 1298 // RangeError exception. 1299 Maybe<std::pair<icu::MeasureUnit, icu::MeasureUnit>> maybe_wellformed_unit = 1300 IsWellFormedUnitIdentifier(isolate, unit); 1301 if (maybe_wellformed_unit.IsNothing()) { 1302 THROW_NEW_ERROR( 1303 isolate, 1304 NewRangeError(MessageTemplate::kInvalidUnit, 1305 factory->NewStringFromAsciiChecked(service), 1306 factory->NewStringFromAsciiChecked(unit.c_str())), 1307 JSNumberFormat); 1308 } 1309 unit_pair = maybe_wellformed_unit.FromJust(); 1310 } else { 1311 // 12. If style is "unit" and unit is undefined, throw a TypeError 1312 // exception. 1313 if (style == Style::UNIT) { 1314 THROW_NEW_ERROR(isolate, 1315 NewTypeError(MessageTemplate::kInvalidUnit, 1316 factory->NewStringFromAsciiChecked(service), 1317 factory->empty_string()), 1318 JSNumberFormat); 1319 } 1320 } 1321 1322 // 13. Let unitDisplay be ? GetOption(options, "unitDisplay", "string", « 1323 // "short", "narrow", "long" », "short"). 1324 Maybe<UnitDisplay> maybe_unit_display = GetStringOption<UnitDisplay>( 1325 isolate, options, "unitDisplay", service, {"short", "narrow", "long"}, 1326 {UnitDisplay::SHORT, UnitDisplay::NARROW, UnitDisplay::LONG}, 1327 UnitDisplay::SHORT); 1328 MAYBE_RETURN(maybe_unit_display, MaybeHandle<JSNumberFormat>()); 1329 UnitDisplay unit_display = maybe_unit_display.FromJust(); 1330 1331 // 14. If style is "currency", then 1332 icu::UnicodeString currency_ustr; 1333 if (style == Style::CURRENCY) { 1334 // 14.a. If currency is undefined, throw a TypeError exception. 1335 if (!found_currency.FromJust()) { 1336 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCurrencyCode), 1337 JSNumberFormat); 1338 } 1339 // 14.a. Let currency be the result of converting currency to upper case as 1340 // specified in 6.1 1341 std::transform(currency.begin(), currency.end(), currency.begin(), toupper); 1342 currency_ustr = currency.c_str(); 1343 1344 // 14.b. Set numberFormat.[[Currency]] to currency. 1345 if (!currency_ustr.isEmpty()) { 1346 Handle<String> currency_string; 1347 ASSIGN_RETURN_ON_EXCEPTION(isolate, currency_string, 1348 Intl::ToString(isolate, currency_ustr), 1349 JSNumberFormat); 1350 1351 settings = 1352 settings.unit(icu::CurrencyUnit(currency_ustr.getBuffer(), status)); 1353 CHECK(U_SUCCESS(status)); 1354 // 14.c Set intlObj.[[CurrencyDisplay]] to currencyDisplay. 1355 // The default unitWidth is SHORT in ICU and that mapped from 1356 // Symbol so we can skip the setting for optimization. 1357 if (currency_display != CurrencyDisplay::SYMBOL) { 1358 settings = settings.unitWidth(ToUNumberUnitWidth(currency_display)); 1359 } 1360 CHECK(U_SUCCESS(status)); 1361 } 1362 } 1363 1364 // 15. If style is "unit", then 1365 if (style == Style::UNIT) { 1366 // Track newer style "unit". 1367 isolate->CountUsage(v8::Isolate::UseCounterFeature::kNumberFormatStyleUnit); 1368 1369 icu::MeasureUnit none = icu::MeasureUnit(); 1370 // 13.b Set intlObj.[[Unit]] to unit. 1371 if (unit_pair.first != none) { 1372 settings = settings.unit(unit_pair.first); 1373 } 1374 if (unit_pair.second != none) { 1375 settings = settings.perUnit(unit_pair.second); 1376 } 1377 1378 // The default unitWidth is SHORT in ICU and that mapped from 1379 // Symbol so we can skip the setting for optimization. 1380 if (unit_display != UnitDisplay::SHORT) { 1381 settings = settings.unitWidth(ToUNumberUnitWidth(unit_display)); 1382 } 1383 } 1384 1385 // === End of SetNumberFormatUnitOptions 1386 1387 if (style == Style::PERCENT) { 1388 settings = settings.unit(icu::MeasureUnit::getPercent()) 1389 .scale(icu::number::Scale::powerOfTen(2)); 1390 } 1391 1392 // 16. If style is "currency", then 1393 int mnfd_default, mxfd_default; 1394 if (style == Style::CURRENCY) { 1395 // b. Let cDigits be CurrencyDigits(currency). 1396 int c_digits = CurrencyDigits(currency_ustr); 1397 // c. Let mnfdDefault be cDigits. 1398 // d. Let mxfdDefault be cDigits. 1399 mnfd_default = c_digits; 1400 mxfd_default = c_digits; 1401 // 17. Else, 1402 } else { 1403 // a. Let mnfdDefault be 0. 1404 mnfd_default = 0; 1405 // b. If style is "percent", then 1406 if (style == Style::PERCENT) { 1407 // i. Let mxfdDefault be 0. 1408 mxfd_default = 0; 1409 } else { 1410 // c. Else, 1411 // i. Let mxfdDefault be 3. 1412 mxfd_default = 3; 1413 } 1414 } 1415 1416 Notation notation = Notation::STANDARD; 1417 // 18. Let notation be ? GetOption(options, "notation", "string", « 1418 // "standard", "scientific", "engineering", "compact" », "standard"). 1419 Maybe<Notation> maybe_notation = GetStringOption<Notation>( 1420 isolate, options, "notation", service, 1421 {"standard", "scientific", "engineering", "compact"}, 1422 {Notation::STANDARD, Notation::SCIENTIFIC, Notation::ENGINEERING, 1423 Notation::COMPACT}, 1424 Notation::STANDARD); 1425 MAYBE_RETURN(maybe_notation, MaybeHandle<JSNumberFormat>()); 1426 // 19. Set numberFormat.[[Notation]] to notation. 1427 notation = maybe_notation.FromJust(); 1428 1429 // 20. Perform ? SetNumberFormatDigitOptions(numberFormat, options, 1430 // mnfdDefault, mxfdDefault). 1431 Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options = 1432 Intl::SetNumberFormatDigitOptions(isolate, options, mnfd_default, 1433 mxfd_default, 1434 notation == Notation::COMPACT); 1435 MAYBE_RETURN(maybe_digit_options, Handle<JSNumberFormat>()); 1436 Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust(); 1437 1438 if (FLAG_harmony_intl_number_format_v3) { 1439 // 21. Let roundingIncrement be ? GetNumberOption(options, 1440 // "roundingIncrement,", 1, 5000, 1). 1441 int rounding_increment = 1; 1442 Maybe<int> maybe_rounding_increment = GetNumberOption( 1443 isolate, options, factory->roundingIncrement_string(), 1, 5000, 1); 1444 MAYBE_RETURN(maybe_rounding_increment, MaybeHandle<JSNumberFormat>()); 1445 CHECK(maybe_rounding_increment.To(&rounding_increment)); 1446 1447 // 22. If roundingIncrement is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 1448 // 250, 500, 1000, 2000, 2500, 5000 », throw a RangeError exception. 1449 if (!IsValidRoundingIncrement(rounding_increment)) { 1450 THROW_NEW_ERROR(isolate, 1451 NewRangeError(MessageTemplate::kPropertyValueOutOfRange, 1452 factory->roundingIncrement_string()), 1453 JSNumberFormat); 1454 } 1455 // 23. If roundingIncrement is not 1 and numberFormat.[[RoundingType]] is 1456 // not fractionDigits, throw a RangeError exception. 1457 if (rounding_increment != 1 && 1458 digit_options.rounding_type != Intl::RoundingType::kFractionDigits) { 1459 THROW_NEW_ERROR(isolate, 1460 NewRangeError(MessageTemplate::kPropertyValueOutOfRange, 1461 factory->roundingIncrement_string()), 1462 JSNumberFormat); 1463 } 1464 // 24. Set _numberFormat.[[RoundingIncrement]] to roundingIncrement. 1465 1466 // 25. Let trailingZeroDisplay be ? GetOption(options, 1467 // "trailingZeroDisplay", "string", « "auto", "stripIfInteger" », "auto"). 1468 Maybe<TrailingZeroDisplay> maybe_trailing_zero_display = 1469 GetStringOption<TrailingZeroDisplay>( 1470 isolate, options, "trailingZeroDisplay", service, 1471 {"auto", "stripIfInteger"}, 1472 {TrailingZeroDisplay::AUTO, TrailingZeroDisplay::STRIP_IF_INTEGER}, 1473 TrailingZeroDisplay::AUTO); 1474 MAYBE_RETURN(maybe_trailing_zero_display, MaybeHandle<JSNumberFormat>()); 1475 TrailingZeroDisplay trailing_zero_display = 1476 maybe_trailing_zero_display.FromJust(); 1477 1478 // 26. Set numberFormat.[[TrailingZeroDisplay]] to trailingZeroDisplay. 1479 settings = SetDigitOptionsToFormatterV3( 1480 settings, digit_options, rounding_increment, 1481 trailing_zero_display == TrailingZeroDisplay::STRIP_IF_INTEGER 1482 ? ShowTrailingZeros::kHide 1483 : ShowTrailingZeros::kShow); 1484 } else { 1485 settings = SetDigitOptionsToFormatterV2(settings, digit_options); 1486 } 1487 1488 // 27. Let compactDisplay be ? GetOption(options, "compactDisplay", 1489 // "string", « "short", "long" », "short"). 1490 Maybe<CompactDisplay> maybe_compact_display = GetStringOption<CompactDisplay>( 1491 isolate, options, "compactDisplay", service, {"short", "long"}, 1492 {CompactDisplay::SHORT, CompactDisplay::LONG}, CompactDisplay::SHORT); 1493 MAYBE_RETURN(maybe_compact_display, MaybeHandle<JSNumberFormat>()); 1494 CompactDisplay compact_display = maybe_compact_display.FromJust(); 1495 1496 // The default notation in ICU is Simple, which mapped from STANDARD 1497 // so we can skip setting it. 1498 if (notation != Notation::STANDARD) { 1499 settings = settings.notation(ToICUNotation(notation, compact_display)); 1500 } 1501 1502 if (!FLAG_harmony_intl_number_format_v3) { 1503 // 30. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", 1504 // undefined, true). 1505 bool use_grouping = true; 1506 Maybe<bool> found_use_grouping = 1507 GetBoolOption(isolate, options, "useGrouping", service, &use_grouping); 1508 MAYBE_RETURN(found_use_grouping, MaybeHandle<JSNumberFormat>()); 1509 // 31. Set numberFormat.[[UseGrouping]] to useGrouping. 1510 if (!use_grouping) { 1511 settings = settings.grouping(UNumberGroupingStrategy::UNUM_GROUPING_OFF); 1512 } 1513 settings = JSNumberFormat::SetDigitOptionsToFormatter( 1514 settings, digit_options, 1, ShowTrailingZeros::kShow); 1515 } else { 1516 // 28. Let defaultUseGrouping be "auto". 1517 UseGrouping default_use_grouping = UseGrouping::AUTO; 1518 1519 // 29. If notation is "compact", then 1520 if (notation == Notation::COMPACT) { 1521 // a. Set numberFormat.[[CompactDisplay]] to compactDisplay. 1522 // Done in above together 1523 // b. Set defaultUseGrouping to "min2". 1524 default_use_grouping = UseGrouping::MIN2; 1525 } 1526 1527 // 30. Let useGrouping be ? GetStringOrBooleanOption(options, "useGrouping", 1528 // « "min2", "auto", "always" », "always", false, defaultUseGrouping). 1529 Maybe<UseGrouping> maybe_use_grouping = 1530 GetStringOrBooleanOption<UseGrouping>( 1531 isolate, options, "useGrouping", service, 1532 {"min2", "auto", "always"}, 1533 {UseGrouping::MIN2, UseGrouping::AUTO, UseGrouping::ALWAYS}, 1534 UseGrouping::ALWAYS, // trueValue 1535 UseGrouping::OFF, // falseValue 1536 default_use_grouping); // fallbackValue 1537 MAYBE_RETURN(maybe_use_grouping, MaybeHandle<JSNumberFormat>()); 1538 UseGrouping use_grouping = maybe_use_grouping.FromJust(); 1539 // 31. Set numberFormat.[[UseGrouping]] to useGrouping. 1540 if (use_grouping != UseGrouping::AUTO) { 1541 settings = settings.grouping(ToUNumberGroupingStrategy(use_grouping)); 1542 } 1543 } 1544 1545 // 32. Let signDisplay be ? GetOption(options, "signDisplay", "string", « 1546 // "auto", "never", "always", "exceptZero", "negative" », "auto"). 1547 Maybe<SignDisplay> maybe_sign_display = Nothing<SignDisplay>(); 1548 if (FLAG_harmony_intl_number_format_v3) { 1549 maybe_sign_display = GetStringOption<SignDisplay>( 1550 isolate, options, "signDisplay", service, 1551 {"auto", "never", "always", "exceptZero", "negative"}, 1552 {SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS, 1553 SignDisplay::EXCEPT_ZERO, SignDisplay::NEGATIVE}, 1554 SignDisplay::AUTO); 1555 } else { 1556 maybe_sign_display = GetStringOption<SignDisplay>( 1557 isolate, options, "signDisplay", service, 1558 {"auto", "never", "always", "exceptZero"}, 1559 {SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS, 1560 SignDisplay::EXCEPT_ZERO}, 1561 SignDisplay::AUTO); 1562 } 1563 MAYBE_RETURN(maybe_sign_display, MaybeHandle<JSNumberFormat>()); 1564 SignDisplay sign_display = maybe_sign_display.FromJust(); 1565 1566 // 33. Set numberFormat.[[SignDisplay]] to signDisplay. 1567 // The default sign in ICU is UNUM_SIGN_AUTO which is mapped from 1568 // SignDisplay::AUTO and CurrencySign::STANDARD so we can skip setting 1569 // under that values for optimization. 1570 if (sign_display != SignDisplay::AUTO || 1571 currency_sign != CurrencySign::STANDARD) { 1572 settings = settings.sign(ToUNumberSignDisplay(sign_display, currency_sign)); 1573 } 1574 1575 if (FLAG_harmony_intl_number_format_v3) { 1576 // X. Let roundingMode be ? GetOption(options, "roundingMode", "string", 1577 // « "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", 1578 // "halfExpand", "halfTrunc", "halfEven" », 1579 // "halfExpand"). 1580 Maybe<IntlRoundingMode> maybe_rounding_mode = 1581 GetStringOption<IntlRoundingMode>( 1582 isolate, options, "roundingMode", service, 1583 {"ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", 1584 "halfExpand", "halfTrunc", "halfEven"}, 1585 {IntlRoundingMode::CEIL, IntlRoundingMode::FLOOR, 1586 IntlRoundingMode::EXPAND, IntlRoundingMode::TRUNC, 1587 IntlRoundingMode::HALF_CEIL, IntlRoundingMode::HALF_FLOOR, 1588 IntlRoundingMode::HALF_EXPAND, IntlRoundingMode::HALF_TRUNC, 1589 IntlRoundingMode::HALF_EVEN}, 1590 IntlRoundingMode::HALF_EXPAND); 1591 MAYBE_RETURN(maybe_rounding_mode, MaybeHandle<JSNumberFormat>()); 1592 IntlRoundingMode rounding_mode = maybe_rounding_mode.FromJust(); 1593 settings = 1594 settings.roundingMode(ToUNumberFormatRoundingMode(rounding_mode)); 1595 } 1596 1597 // 25. Let dataLocaleData be localeData.[[<dataLocale>]]. 1598 // 1599 // 26. Let patterns be dataLocaleData.[[patterns]]. 1600 // 1601 // 27. Assert: patterns is a record (see 11.3.3). 1602 // 1603 // 28. Let stylePatterns be patterns.[[<style>]]. 1604 // 1605 // 29. Set numberFormat.[[PositivePattern]] to 1606 // stylePatterns.[[positivePattern]]. 1607 // 1608 // 30. Set numberFormat.[[NegativePattern]] to 1609 // stylePatterns.[[negativePattern]]. 1610 // 1611 icu::number::LocalizedNumberFormatter icu_number_formatter = 1612 settings.locale(icu_locale); 1613 1614 icu::number::LocalizedNumberRangeFormatter icu_number_range_formatter = 1615 icu::number::UnlocalizedNumberRangeFormatter() 1616 .numberFormatterBoth(settings) 1617 .locale(icu_locale); 1618 1619 Handle<Managed<icu::number::LocalizedNumberFormatter>> 1620 managed_number_formatter = 1621 Managed<icu::number::LocalizedNumberFormatter>::FromRawPtr( 1622 isolate, 0, 1623 new icu::number::LocalizedNumberFormatter(icu_number_formatter)); 1624 1625 Handle<Managed<icu::number::LocalizedNumberRangeFormatter>> 1626 managed_number_range_formatter = 1627 Managed<icu::number::LocalizedNumberRangeFormatter>::FromRawPtr( 1628 isolate, 0, 1629 new icu::number::LocalizedNumberRangeFormatter( 1630 icu_number_range_formatter)); 1631 1632 // Now all properties are ready, so we can allocate the result object. 1633 Handle<JSNumberFormat> number_format = Handle<JSNumberFormat>::cast( 1634 isolate->factory()->NewFastOrSlowJSObjectFromMap(map)); 1635 DisallowGarbageCollection no_gc; 1636 number_format->set_locale(*locale_str); 1637 1638 number_format->set_icu_number_formatter(*managed_number_formatter); 1639 number_format->set_icu_number_range_formatter( 1640 *managed_number_range_formatter); 1641 number_format->set_bound_format(*factory->undefined_value()); 1642 1643 // 31. Return numberFormat. 1644 return number_format; 1645} 1646 1647namespace { 1648 1649Maybe<bool> IcuFormatNumber( 1650 Isolate* isolate, 1651 const icu::number::LocalizedNumberFormatter& number_format, 1652 Handle<Object> numeric_obj, icu::number::FormattedNumber* formatted) { 1653 // If it is BigInt, handle it differently. 1654 UErrorCode status = U_ZERO_ERROR; 1655 if (numeric_obj->IsBigInt()) { 1656 Handle<BigInt> big_int = Handle<BigInt>::cast(numeric_obj); 1657 Handle<String> big_int_string; 1658 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, big_int_string, 1659 BigInt::ToString(isolate, big_int), 1660 Nothing<bool>()); 1661 big_int_string = String::Flatten(isolate, big_int_string); 1662 DisallowGarbageCollection no_gc; 1663 const String::FlatContent& flat = big_int_string->GetFlatContent(no_gc); 1664 int32_t length = big_int_string->length(); 1665 DCHECK(flat.IsOneByte()); 1666 const char* char_buffer = 1667 reinterpret_cast<const char*>(flat.ToOneByteVector().begin()); 1668 *formatted = number_format.formatDecimal({char_buffer, length}, status); 1669 } else { 1670 if (FLAG_harmony_intl_number_format_v3 && numeric_obj->IsString()) { 1671 // TODO(ftang) Correct the handling of string after the resolution of 1672 // https://github.com/tc39/proposal-intl-numberformat-v3/pull/82 1673 Handle<String> string = 1674 String::Flatten(isolate, Handle<String>::cast(numeric_obj)); 1675 DisallowGarbageCollection no_gc; 1676 const String::FlatContent& flat = string->GetFlatContent(no_gc); 1677 int32_t length = string->length(); 1678 if (flat.IsOneByte()) { 1679 const char* char_buffer = 1680 reinterpret_cast<const char*>(flat.ToOneByteVector().begin()); 1681 *formatted = number_format.formatDecimal({char_buffer, length}, status); 1682 } else { 1683 // We may have two bytes string such as "漢 123456789".substring(2) 1684 // The value will be "123456789" only in ASCII range, but encoded 1685 // in two bytes string. 1686 // ICU accepts UTF8 string, so if the source is two-byte encoded, 1687 // copy into a UTF8 string via ToCString. 1688 *formatted = number_format.formatDecimal( 1689 {string->ToCString().get(), string->length()}, status); 1690 } 1691 } else { 1692 double number = numeric_obj->IsNaN() 1693 ? std::numeric_limits<double>::quiet_NaN() 1694 : numeric_obj->Number(); 1695 *formatted = number_format.formatDouble(number, status); 1696 } 1697 } 1698 if (U_FAILURE(status)) { 1699 // This happen because of icu data trimming trim out "unit". 1700 // See https://bugs.chromium.org/p/v8/issues/detail?id=8641 1701 THROW_NEW_ERROR_RETURN_VALUE( 1702 isolate, NewTypeError(MessageTemplate::kIcuError), Nothing<bool>()); 1703 } 1704 return Just(true); 1705} 1706 1707Maybe<icu::Formattable> ToFormattable(Isolate* isolate, Handle<Object> obj, 1708 const char* field) { 1709 if (obj->IsBigInt()) { 1710 Handle<BigInt> big_int = Handle<BigInt>::cast(obj); 1711 Handle<String> big_int_string; 1712 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, big_int_string, 1713 BigInt::ToString(isolate, big_int), 1714 Nothing<icu::Formattable>()); 1715 big_int_string = String::Flatten(isolate, big_int_string); 1716 { 1717 DisallowGarbageCollection no_gc; 1718 const String::FlatContent& flat = big_int_string->GetFlatContent(no_gc); 1719 int32_t length = big_int_string->length(); 1720 DCHECK(flat.IsOneByte()); 1721 const char* char_buffer = 1722 reinterpret_cast<const char*>(flat.ToOneByteVector().begin()); 1723 UErrorCode status = U_ZERO_ERROR; 1724 icu::Formattable result({char_buffer, length}, status); 1725 if (U_SUCCESS(status)) return Just(result); 1726 } 1727 THROW_NEW_ERROR_RETURN_VALUE(isolate, 1728 NewTypeError(MessageTemplate::kIcuError), 1729 Nothing<icu::Formattable>()); 1730 } 1731 // TODO(ftang) Handle the case of IsString after the resolution of 1732 // https://github.com/tc39/proposal-intl-numberformat-v3/pull/82 1733 1734 // FormatRange(|ToParts) does not allow NaN 1735 DCHECK(!obj->IsNaN()); 1736 return Just(icu::Formattable(obj->Number())); 1737} 1738 1739bool cmp_NumberFormatSpan(const NumberFormatSpan& a, 1740 const NumberFormatSpan& b) { 1741 // Regions that start earlier should be encountered earlier. 1742 if (a.begin_pos < b.begin_pos) return true; 1743 if (a.begin_pos > b.begin_pos) return false; 1744 // For regions that start in the same place, regions that last longer should 1745 // be encountered earlier. 1746 if (a.end_pos < b.end_pos) return false; 1747 if (a.end_pos > b.end_pos) return true; 1748 // For regions that are exactly the same, one of them must be the "literal" 1749 // backdrop we added, which has a field_id of -1, so consider higher field_ids 1750 // to be later. 1751 return a.field_id < b.field_id; 1752} 1753 1754} // namespace 1755 1756// Flattens a list of possibly-overlapping "regions" to a list of 1757// non-overlapping "parts". At least one of the input regions must span the 1758// entire space of possible indexes. The regions parameter will sorted in-place 1759// according to some criteria; this is done for performance to avoid copying the 1760// input. 1761std::vector<NumberFormatSpan> FlattenRegionsToParts( 1762 std::vector<NumberFormatSpan>* regions) { 1763 // The intention of this algorithm is that it's used to translate ICU "fields" 1764 // to JavaScript "parts" of a formatted string. Each ICU field and JavaScript 1765 // part has an integer field_id, which corresponds to something like "grouping 1766 // separator", "fraction", or "percent sign", and has a begin and end 1767 // position. Here's a diagram of: 1768 1769 // var nf = new Intl.NumberFormat(['de'], {style:'currency',currency:'EUR'}); 1770 // nf.formatToParts(123456.78); 1771 1772 // : 6 1773 // input regions: 0000000211 7 1774 // ('-' means -1): ------------ 1775 // formatted string: "123.456,78 €" 1776 // output parts: 0006000211-7 1777 1778 // To illustrate the requirements of this algorithm, here's a contrived and 1779 // convoluted example of inputs and expected outputs: 1780 1781 // : 4 1782 // : 22 33 3 1783 // : 11111 22 1784 // input regions: 0000000 111 1785 // : ------------ 1786 // formatted string: "abcdefghijkl" 1787 // output parts: 0221340--231 1788 // (The characters in the formatted string are irrelevant to this function.) 1789 1790 // We arrange the overlapping input regions like a mountain range where 1791 // smaller regions are "on top" of larger regions, and we output a birds-eye 1792 // view of the mountains, so that smaller regions take priority over larger 1793 // regions. 1794 std::sort(regions->begin(), regions->end(), cmp_NumberFormatSpan); 1795 std::vector<size_t> overlapping_region_index_stack; 1796 // At least one item in regions must be a region spanning the entire string. 1797 // Due to the sorting above, the first item in the vector will be one of them. 1798 overlapping_region_index_stack.push_back(0); 1799 NumberFormatSpan top_region = regions->at(0); 1800 size_t region_iterator = 1; 1801 int32_t entire_size = top_region.end_pos; 1802 1803 std::vector<NumberFormatSpan> out_parts; 1804 1805 // The "climber" is a cursor that advances from left to right climbing "up" 1806 // and "down" the mountains. Whenever the climber moves to the right, that 1807 // represents an item of output. 1808 int32_t climber = 0; 1809 while (climber < entire_size) { 1810 int32_t next_region_begin_pos; 1811 if (region_iterator < regions->size()) { 1812 next_region_begin_pos = regions->at(region_iterator).begin_pos; 1813 } else { 1814 // finish off the rest of the input by proceeding to the end. 1815 next_region_begin_pos = entire_size; 1816 } 1817 1818 if (climber < next_region_begin_pos) { 1819 while (top_region.end_pos < next_region_begin_pos) { 1820 if (climber < top_region.end_pos) { 1821 // step down 1822 out_parts.push_back(NumberFormatSpan(top_region.field_id, climber, 1823 top_region.end_pos)); 1824 climber = top_region.end_pos; 1825 } else { 1826 // drop down 1827 } 1828 overlapping_region_index_stack.pop_back(); 1829 top_region = regions->at(overlapping_region_index_stack.back()); 1830 } 1831 if (climber < next_region_begin_pos) { 1832 // cross a plateau/mesa/valley 1833 out_parts.push_back(NumberFormatSpan(top_region.field_id, climber, 1834 next_region_begin_pos)); 1835 climber = next_region_begin_pos; 1836 } 1837 } 1838 if (region_iterator < regions->size()) { 1839 overlapping_region_index_stack.push_back(region_iterator++); 1840 top_region = regions->at(overlapping_region_index_stack.back()); 1841 } 1842 } 1843 return out_parts; 1844} 1845 1846namespace { 1847Maybe<int> ConstructParts(Isolate* isolate, icu::FormattedValue* formatted, 1848 Handle<JSArray> result, int start_index, 1849 bool style_is_unit, bool is_nan, bool output_source) { 1850 UErrorCode status = U_ZERO_ERROR; 1851 icu::UnicodeString formatted_text = formatted->toString(status); 1852 if (U_FAILURE(status)) { 1853 THROW_NEW_ERROR_RETURN_VALUE( 1854 isolate, NewTypeError(MessageTemplate::kIcuError), Nothing<int>()); 1855 } 1856 int32_t length = formatted_text.length(); 1857 int index = start_index; 1858 if (length == 0) return Just(index); 1859 1860 std::vector<NumberFormatSpan> regions; 1861 // Add a "literal" backdrop for the entire string. This will be used if no 1862 // other region covers some part of the formatted string. It's possible 1863 // there's another field with exactly the same begin and end as this backdrop, 1864 // in which case the backdrop's field_id of -1 will give it lower priority. 1865 regions.push_back(NumberFormatSpan(-1, 0, formatted_text.length())); 1866 Intl::FormatRangeSourceTracker tracker; 1867 { 1868 icu::ConstrainedFieldPosition cfpos; 1869 while (formatted->nextPosition(cfpos, status)) { 1870 int32_t category = cfpos.getCategory(); 1871 int32_t field = cfpos.getField(); 1872 int32_t start = cfpos.getStart(); 1873 int32_t limit = cfpos.getLimit(); 1874 if (category == UFIELD_CATEGORY_NUMBER_RANGE_SPAN) { 1875 DCHECK_LE(field, 2); 1876 DCHECK(FLAG_harmony_intl_number_format_v3); 1877 tracker.Add(field, start, limit); 1878 } else { 1879 regions.push_back(NumberFormatSpan(field, start, limit)); 1880 } 1881 } 1882 } 1883 1884 std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(®ions); 1885 1886 for (auto it = parts.begin(); it < parts.end(); it++) { 1887 NumberFormatSpan part = *it; 1888 Handle<String> field_type_string = isolate->factory()->literal_string(); 1889 if (part.field_id != -1) { 1890 if (style_is_unit && static_cast<UNumberFormatFields>(part.field_id) == 1891 UNUM_PERCENT_FIELD) { 1892 // Special case when style is unit. 1893 field_type_string = isolate->factory()->unit_string(); 1894 } else { 1895 field_type_string = 1896 Intl::NumberFieldToType(isolate, part, formatted_text, is_nan); 1897 } 1898 } 1899 Handle<String> substring; 1900 ASSIGN_RETURN_ON_EXCEPTION_VALUE( 1901 isolate, substring, 1902 Intl::ToString(isolate, formatted_text, part.begin_pos, part.end_pos), 1903 Nothing<int>()); 1904 1905 if (output_source) { 1906 Intl::AddElement( 1907 isolate, result, index, field_type_string, substring, 1908 isolate->factory()->source_string(), 1909 Intl::SourceString(isolate, 1910 tracker.GetSource(part.begin_pos, part.end_pos))); 1911 } else { 1912 Intl::AddElement(isolate, result, index, field_type_string, substring); 1913 } 1914 ++index; 1915 } 1916 JSObject::ValidateElements(*result); 1917 return Just(index); 1918} 1919 1920bool IsPositiveInfinity(Isolate* isolate, Handle<Object> v) { 1921 if (v->IsBigInt()) return false; 1922 if (v->IsString()) { 1923 return isolate->factory()->Infinity_string()->Equals(String::cast(*v)); 1924 } 1925 CHECK(v->IsNumber()); 1926 double const value_number = v->Number(); 1927 return std::isinf(value_number) && (value_number > 0.0); 1928} 1929 1930bool IsNegativeInfinity(Isolate* isolate, Handle<Object> v) { 1931 if (v->IsBigInt()) return false; 1932 if (v->IsString()) { 1933 return isolate->factory()->minus_Infinity_string()->Equals( 1934 String::cast(*v)); 1935 } 1936 CHECK(v->IsNumber()); 1937 double const value_number = v->Number(); 1938 return std::isinf(value_number) && (value_number < 0.0); 1939} 1940 1941bool IsNegativeZero(Isolate* isolate, Handle<Object> v) { 1942 if (v->IsBigInt()) return false; 1943 if (v->IsString()) { 1944 return isolate->factory()->minus_0()->Equals(String::cast(*v)); 1945 } 1946 CHECK(v->IsNumber()); 1947 return IsMinusZero(v->Number()); 1948} 1949 1950bool LessThan(Isolate* isolate, Handle<Object> a, Handle<Object> b) { 1951 Maybe<ComparisonResult> comparison = Object::Compare(isolate, a, b); 1952 return comparison.IsJust() && 1953 comparison.FromJust() == ComparisonResult::kLessThan; 1954} 1955 1956bool IsFiniteNonMinusZeroNumberOrBigInt(Isolate* isolate, Handle<Object> v) { 1957 return !(IsPositiveInfinity(isolate, v) || IsNegativeInfinity(isolate, v) || 1958 v->IsMinusZero()); 1959} 1960 1961// #sec-partitionnumberrangepattern 1962template <typename T, MaybeHandle<T> (*F)( 1963 Isolate*, icu::FormattedValue*, 1964 const icu::number::LocalizedNumberFormatter*, bool)> 1965MaybeHandle<T> PartitionNumberRangePattern(Isolate* isolate, 1966 Handle<JSNumberFormat> number_format, 1967 Handle<Object> x, Handle<Object> y, 1968 const char* func_name) { 1969 Factory* factory = isolate->factory(); 1970 1971 // 1. If x is NaN or y is NaN, throw a RangeError exception. 1972 if (x->IsNaN()) { 1973 THROW_NEW_ERROR_RETURN_VALUE( 1974 isolate, 1975 NewRangeError(MessageTemplate::kInvalid, 1976 factory->NewStringFromStaticChars("start"), x), 1977 MaybeHandle<T>()); 1978 } 1979 if (y->IsNaN()) { 1980 THROW_NEW_ERROR_RETURN_VALUE( 1981 isolate, 1982 NewRangeError(MessageTemplate::kInvalid, 1983 factory->NewStringFromStaticChars("end"), y), 1984 MaybeHandle<T>()); 1985 } 1986 1987 // 2. If x is a mathematical value, then 1988 if (IsFiniteNonMinusZeroNumberOrBigInt(isolate, x)) { 1989 // a. If y is a mathematical value and y < x, throw a RangeError exception. 1990 if (IsFiniteNonMinusZeroNumberOrBigInt(isolate, y) && 1991 LessThan(isolate, y, x)) { 1992 THROW_NEW_ERROR_RETURN_VALUE( 1993 isolate, NewRangeError(MessageTemplate::kInvalid, x, y), 1994 MaybeHandle<T>()); 1995 } 1996 // b. Else if y is -∞, throw a RangeError exception. 1997 if (IsNegativeInfinity(isolate, y)) { 1998 THROW_NEW_ERROR_RETURN_VALUE( 1999 isolate, NewRangeError(MessageTemplate::kInvalid, x, y), 2000 MaybeHandle<T>()); 2001 } 2002 // c. Else if y is -0 and x ≥ 0, throw a RangeError exception. 2003 if (y->IsMinusZero() && 2004 !LessThan(isolate, x, Handle<Object>(Smi::zero(), isolate))) { 2005 THROW_NEW_ERROR_RETURN_VALUE( 2006 isolate, NewRangeError(MessageTemplate::kInvalid, x, y), 2007 MaybeHandle<T>()); 2008 } 2009 // 3. Else if x is +∞, then 2010 } else if (IsPositiveInfinity(isolate, x)) { 2011 // a. If y is a mathematical value, throw a RangeError exception. 2012 if (IsFiniteNonMinusZeroNumberOrBigInt(isolate, y)) { 2013 THROW_NEW_ERROR_RETURN_VALUE( 2014 isolate, NewRangeError(MessageTemplate::kInvalid, x, y), 2015 MaybeHandle<T>()); 2016 } 2017 // b. Else if y is -∞, throw a RangeError exception. 2018 if (IsNegativeInfinity(isolate, y)) { 2019 THROW_NEW_ERROR_RETURN_VALUE( 2020 isolate, NewRangeError(MessageTemplate::kInvalid, x, y), 2021 MaybeHandle<T>()); 2022 } 2023 // c. Else if y is -0, throw a RangeError exception. 2024 if (IsNegativeZero(isolate, y)) { 2025 THROW_NEW_ERROR_RETURN_VALUE( 2026 isolate, NewRangeError(MessageTemplate::kInvalid, x, y), 2027 MaybeHandle<T>()); 2028 } 2029 // 4. Else if x is -0, then 2030 } else if (IsNegativeZero(isolate, x)) { 2031 // a. If y is a mathematical value and y < 0, throw a RangeError exception. 2032 if (IsFiniteNonMinusZeroNumberOrBigInt(isolate, y) && 2033 LessThan(isolate, y, Handle<Object>(Smi::zero(), isolate))) { 2034 THROW_NEW_ERROR_RETURN_VALUE( 2035 isolate, NewRangeError(MessageTemplate::kInvalid, x, y), 2036 MaybeHandle<T>()); 2037 } 2038 // b. Else if y is -∞, throw a RangeError exception. 2039 if (IsNegativeInfinity(isolate, y)) { 2040 THROW_NEW_ERROR_RETURN_VALUE( 2041 isolate, NewRangeError(MessageTemplate::kInvalid, x, y), 2042 MaybeHandle<T>()); 2043 } 2044 } 2045 2046 Maybe<icu::Formattable> maybe_x = ToFormattable(isolate, x, "start"); 2047 MAYBE_RETURN(maybe_x, MaybeHandle<T>()); 2048 2049 Maybe<icu::Formattable> maybe_y = ToFormattable(isolate, y, "end"); 2050 MAYBE_RETURN(maybe_y, MaybeHandle<T>()); 2051 2052 icu::number::LocalizedNumberRangeFormatter* nrfmt = 2053 number_format->icu_number_range_formatter().raw(); 2054 CHECK_NOT_NULL(nrfmt); 2055 UErrorCode status = U_ZERO_ERROR; 2056 icu::number::FormattedNumberRange formatted = nrfmt->formatFormattableRange( 2057 maybe_x.FromJust(), maybe_y.FromJust(), status); 2058 if (U_FAILURE(status)) { 2059 THROW_NEW_ERROR_RETURN_VALUE( 2060 isolate, NewTypeError(MessageTemplate::kIcuError), MaybeHandle<T>()); 2061 } 2062 2063 return F(isolate, &formatted, number_format->icu_number_formatter().raw(), 2064 false /* is_nan */); 2065} 2066 2067MaybeHandle<String> FormatToString(Isolate* isolate, 2068 icu::FormattedValue* formatted, 2069 const icu::number::LocalizedNumberFormatter*, 2070 bool) { 2071 UErrorCode status = U_ZERO_ERROR; 2072 icu::UnicodeString result = formatted->toString(status); 2073 if (U_FAILURE(status)) { 2074 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String); 2075 } 2076 return Intl::ToString(isolate, result); 2077} 2078 2079MaybeHandle<JSArray> FormatToJSArray( 2080 Isolate* isolate, icu::FormattedValue* formatted, 2081 const icu::number::LocalizedNumberFormatter* nfmt, bool is_nan, 2082 bool output_source) { 2083 UErrorCode status = U_ZERO_ERROR; 2084 bool is_unit = Style::UNIT == StyleFromSkeleton(nfmt->toSkeleton(status)); 2085 CHECK(U_SUCCESS(status)); 2086 2087 Factory* factory = isolate->factory(); 2088 Handle<JSArray> result = factory->NewJSArray(0); 2089 Maybe<int> maybe_format_to_parts = ConstructParts( 2090 isolate, formatted, result, 0, is_unit, is_nan, output_source); 2091 MAYBE_RETURN(maybe_format_to_parts, Handle<JSArray>()); 2092 return result; 2093} 2094 2095MaybeHandle<JSArray> FormatRangeToJSArray( 2096 Isolate* isolate, icu::FormattedValue* formatted, 2097 const icu::number::LocalizedNumberFormatter* nfmt, bool is_nan) { 2098 return FormatToJSArray(isolate, formatted, nfmt, is_nan, true); 2099} 2100 2101} // namespace 2102 2103MaybeHandle<String> JSNumberFormat::FormatNumeric( 2104 Isolate* isolate, 2105 const icu::number::LocalizedNumberFormatter& number_format, 2106 Handle<Object> numeric_obj) { 2107 DCHECK(numeric_obj->IsNumeric() || FLAG_harmony_intl_number_format_v3); 2108 2109 icu::number::FormattedNumber formatted; 2110 Maybe<bool> maybe_format = 2111 IcuFormatNumber(isolate, number_format, numeric_obj, &formatted); 2112 MAYBE_RETURN(maybe_format, Handle<String>()); 2113 2114 return FormatToString(isolate, &formatted, &number_format, 2115 numeric_obj->IsNaN()); 2116} 2117 2118MaybeHandle<JSArray> JSNumberFormat::FormatToParts( 2119 Isolate* isolate, Handle<JSNumberFormat> number_format, 2120 Handle<Object> numeric_obj) { 2121 CHECK(numeric_obj->IsNumeric() || FLAG_harmony_intl_number_format_v3); 2122 icu::number::LocalizedNumberFormatter* fmt = 2123 number_format->icu_number_formatter().raw(); 2124 CHECK_NOT_NULL(fmt); 2125 2126 icu::number::FormattedNumber formatted; 2127 Maybe<bool> maybe_format = 2128 IcuFormatNumber(isolate, *fmt, numeric_obj, &formatted); 2129 MAYBE_RETURN(maybe_format, Handle<JSArray>()); 2130 2131 return FormatToJSArray(isolate, &formatted, fmt, numeric_obj->IsNaN(), false); 2132} 2133 2134MaybeHandle<String> JSNumberFormat::FormatNumericRange( 2135 Isolate* isolate, Handle<JSNumberFormat> number_format, 2136 Handle<Object> x_obj, Handle<Object> y_obj) { 2137 return PartitionNumberRangePattern<String, FormatToString>( 2138 isolate, number_format, x_obj, y_obj, 2139 "Intl.NumberFormat.prototype.formatRange"); 2140} 2141 2142MaybeHandle<JSArray> JSNumberFormat::FormatNumericRangeToParts( 2143 Isolate* isolate, Handle<JSNumberFormat> number_format, 2144 Handle<Object> x_obj, Handle<Object> y_obj) { 2145 return PartitionNumberRangePattern<JSArray, FormatRangeToJSArray>( 2146 isolate, number_format, x_obj, y_obj, 2147 "Intl.NumberFormat.prototype.formatRangeToParts"); 2148} 2149 2150namespace { 2151 2152struct CheckNumberElements { 2153 static const char* key() { return "NumberElements"; } 2154 static const char* path() { return nullptr; } 2155}; 2156 2157} // namespace 2158 2159const std::set<std::string>& JSNumberFormat::GetAvailableLocales() { 2160 static base::LazyInstance<Intl::AvailableLocales<CheckNumberElements>>::type 2161 available_locales = LAZY_INSTANCE_INITIALIZER; 2162 return available_locales.Pointer()->Get(); 2163} 2164 2165} // namespace internal 2166} // namespace v8 2167