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(&currency_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, &currency_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(&regions);
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