[Intl] Speed up Intl.NumberFormat constructor x4

1. Use the newer LocalizedNumberFormatter API which improve
   the performance score x3.3.
   Here are how I got the performance score:
  $ python -u tools/run_perf.py --binary-override-path \
    out/x64.release/d8 --filter "JSTests/Intl"  \
    test/js-perf-test/JSTests5.json

  Look for NewIntlNumberFormat-Intl(Score) for 3 runs.

  BEFORE: 539   507  507
   AFTER: 2009 2069 1994

2. Also add symbol and enum to prepare implementing of the unified
   number proposal.


Bug: v8:8515
Change-Id: Ie1ca1dba1e806449632cc96b81d44f0dc61b6093
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1392233
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61421}
diff --git a/src/builtins/builtins-intl.cc b/src/builtins/builtins-intl.cc
index a5ef6a3..acf8b2b 100644
--- a/src/builtins/builtins-intl.cc
+++ b/src/builtins/builtins-intl.cc
@@ -482,13 +482,14 @@
                                        Object::ToNumber(isolate, value));
   }
 
-  icu::NumberFormat* icu_number_format =
-      number_format->icu_number_format()->raw();
-  CHECK_NOT_NULL(icu_number_format);
+  icu::number::LocalizedNumberFormatter* icu_localized_number_formatter =
+      number_format->icu_number_formatter()->raw();
+  CHECK_NOT_NULL(icu_localized_number_formatter);
 
+  // Return FormatNumber(nf, x).
   RETURN_RESULT_OR_FAILURE(
-      isolate,
-      JSNumberFormat::FormatNumeric(isolate, *icu_number_format, numeric_obj));
+      isolate, JSNumberFormat::FormatNumeric(
+                   isolate, *icu_localized_number_formatter, numeric_obj));
 }
 
 BUILTIN(DateTimeFormatConstructor) {
diff --git a/src/heap-symbols.h b/src/heap-symbols.h
index 478c896..add9262 100644
--- a/src/heap-symbols.h
+++ b/src/heap-symbols.h
@@ -9,20 +9,30 @@
 #define INTERNALIZED_STRING_LIST_GENERATOR_INTL(V, _)               \
   V(_, adoptText_string, "adoptText")                               \
   V(_, baseName_string, "baseName")                                 \
+  V(_, accounting_string, "accounting")                             \
   V(_, breakType_string, "breakType")                               \
   V(_, calendar_string, "calendar")                                 \
   V(_, cardinal_string, "cardinal")                                 \
   V(_, caseFirst_string, "caseFirst")                               \
   V(_, compare_string, "compare")                                   \
   V(_, current_string, "current")                                   \
+  V(_, collation_string, "collation")                               \
+  V(_, compact_string, "compact")                                   \
+  V(_, currency_string, "currency")                                 \
+  V(_, currencyDisplay_string, "currencyDisplay")                   \
   V(_, dateStyle_string, "dateStyle")                               \
   V(_, day_string, "day")                                           \
   V(_, dayPeriod_string, "dayPeriod")                               \
   V(_, decimal_string, "decimal")                                   \
   V(_, endRange_string, "endRange")                                 \
+  V(_, engineering_string, "engineering")                           \
   V(_, era_string, "era")                                           \
   V(_, first_string, "first")                                       \
   V(_, format_string, "format")                                     \
+  V(_, except_zero_string, "except-zero")                           \
+  V(_, exponentInteger_string, "exponentInteger")                   \
+  V(_, exponentMinusSign_string, "exponentMinusSign")               \
+  V(_, exponentSeparator_string, "exponentSeparator")               \
   V(_, fraction_string, "fraction")                                 \
   V(_, full_string, "full")                                         \
   V(_, granularity_string, "granularity")                           \
@@ -35,9 +45,6 @@
   V(_, hour_string, "hour")                                         \
   V(_, hour12_string, "hour12")                                     \
   V(_, hourCycle_string, "hourCycle")                               \
-  V(_, collation_string, "collation")                               \
-  V(_, currency_string, "currency")                                 \
-  V(_, currencyDisplay_string, "currencyDisplay")                   \
   V(_, ideo_string, "ideo")                                         \
   V(_, ignorePunctuation_string, "ignorePunctuation")               \
   V(_, Invalid_Date_string, "Invalid Date")                         \
@@ -59,6 +66,8 @@
   V(_, minute_string, "minute")                                     \
   V(_, month_string, "month")                                       \
   V(_, nan_string, "nan")                                           \
+  V(_, narrow_symbol_string, "narrow-symbol")                       \
+  V(_, never_string, "never")                                       \
   V(_, none_string, "none")                                         \
   V(_, normal_string, "normal")                                     \
   V(_, numberingSystem_string, "numberingSystem")                   \
@@ -68,12 +77,14 @@
   V(_, plusSign_string, "plusSign")                                 \
   V(_, quarter_string, "quarter")                                   \
   V(_, region_string, "region")                                     \
+  V(_, scientific_string, "scientific")                             \
   V(_, second_string, "second")                                     \
   V(_, segment_string, "segment")                                   \
   V(_, SegmentIterator_string, "Segment Iterator")                  \
   V(_, sensitivity_string, "sensitivity")                           \
   V(_, sep_string, "sep")                                           \
   V(_, shared_string, "shared")                                     \
+  V(_, standard_string, "standard")                                 \
   V(_, startRange_string, "startRange")                             \
   V(_, strict_string, "strict")                                     \
   V(_, style_string, "style")                                       \
diff --git a/src/isolate.cc b/src/isolate.cc
index 56efd34..30299bc 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -4626,12 +4626,12 @@
 }
 
 #ifdef V8_INTL_SUPPORT
-icu::UObject* Isolate::get_cached_icu_object(ICUObjectCacheType cache_type) {
+icu::UMemory* Isolate::get_cached_icu_object(ICUObjectCacheType cache_type) {
   return icu_object_cache_[cache_type].get();
 }
 
 void Isolate::set_icu_object_in_cache(ICUObjectCacheType cache_type,
-                                      std::shared_ptr<icu::UObject> obj) {
+                                      std::shared_ptr<icu::UMemory> obj) {
   icu_object_cache_[cache_type] = obj;
 }
 
diff --git a/src/isolate.h b/src/isolate.h
index e35e40b..2fa2aa0 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -38,7 +38,7 @@
 #ifdef V8_INTL_SUPPORT
 #include "unicode/uversion.h"  // Define U_ICU_NAMESPACE.
 namespace U_ICU_NAMESPACE {
-class UObject;
+class UMemory;
 }  // namespace U_ICU_NAMESPACE
 #endif  // V8_INTL_SUPPORT
 
@@ -1143,9 +1143,9 @@
       kDefaultCollator, kDefaultNumberFormat, kDefaultSimpleDateFormat,
       kDefaultSimpleDateFormatForTime, kDefaultSimpleDateFormatForDate};
 
-  icu::UObject* get_cached_icu_object(ICUObjectCacheType cache_type);
+  icu::UMemory* get_cached_icu_object(ICUObjectCacheType cache_type);
   void set_icu_object_in_cache(ICUObjectCacheType cache_type,
-                               std::shared_ptr<icu::UObject> obj);
+                               std::shared_ptr<icu::UMemory> obj);
   void clear_cached_icu_object(ICUObjectCacheType cache_type);
 
 #endif  // V8_INTL_SUPPORT
@@ -1709,7 +1709,7 @@
       return static_cast<std::size_t>(a);
     }
   };
-  std::unordered_map<ICUObjectCacheType, std::shared_ptr<icu::UObject>,
+  std::unordered_map<ICUObjectCacheType, std::shared_ptr<icu::UMemory>,
                      ICUObjectCacheTypeHash>
       icu_object_cache_;
 
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index e23e570..bf20748 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -2268,7 +2268,7 @@
   CHECK(IsJSNumberFormat());
   JSObjectVerify(isolate);
   VerifyObjectField(isolate, kLocaleOffset);
-  VerifyObjectField(isolate, kIcuNumberFormatOffset);
+  VerifyObjectField(isolate, kIcuNumberFormatterOffset);
   VerifyObjectField(isolate, kBoundFormatOffset);
   CHECK(bound_format()->IsUndefined(isolate) || bound_format()->IsJSFunction());
   VerifySmiField(kFlagsOffset);
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index afdce18..f8fdb5a 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -2164,10 +2164,8 @@
 void JSNumberFormat::JSNumberFormatPrint(std::ostream& os) {  // NOLINT
   JSObjectPrintHeader(os, *this, "JSNumberFormat");
   os << "\n - locale: " << Brief(locale());
-  os << "\n - icu_number_format: " << Brief(icu_number_format());
+  os << "\n - icu_number_formatter: " << Brief(icu_number_formatter());
   os << "\n - bound_format: " << Brief(bound_format());
-  os << "\n - style: " << StyleAsString();
-  os << "\n - currency_display: " << CurrencyDisplayAsString();
   JSObjectPrintBody(os, *this);
 }
 
diff --git a/src/objects/intl-objects.cc b/src/objects/intl-objects.cc
index aa12624..c7cb8df 100644
--- a/src/objects/intl-objects.cc
+++ b/src/objects/intl-objects.cc
@@ -977,7 +977,7 @@
   if (can_cache) {
     isolate->set_icu_object_in_cache(
         Isolate::ICUObjectCacheType::kDefaultCollator,
-        std::static_pointer_cast<icu::UObject>(
+        std::static_pointer_cast<icu::UMemory>(
             collator->icu_collator()->get()));
   }
   icu::Collator* icu_collator = collator->icu_collator()->raw();
@@ -1039,9 +1039,10 @@
   bool can_cache =
       locales->IsUndefined(isolate) && options->IsUndefined(isolate);
   if (can_cache) {
-    icu::NumberFormat* cached_number_format =
-        static_cast<icu::NumberFormat*>(isolate->get_cached_icu_object(
-            Isolate::ICUObjectCacheType::kDefaultNumberFormat));
+    icu::number::LocalizedNumberFormatter* cached_number_format =
+        static_cast<icu::number::LocalizedNumberFormatter*>(
+            isolate->get_cached_icu_object(
+                Isolate::ICUObjectCacheType::kDefaultNumberFormat));
     // We may use the cached icu::NumberFormat for a fast path.
     if (cached_number_format != nullptr) {
       return JSNumberFormat::FormatNumeric(isolate, *cached_number_format,
@@ -1062,13 +1063,13 @@
   if (can_cache) {
     isolate->set_icu_object_in_cache(
         Isolate::ICUObjectCacheType::kDefaultNumberFormat,
-        std::static_pointer_cast<icu::UObject>(
-            number_format->icu_number_format()->get()));
+        std::static_pointer_cast<icu::UMemory>(
+            number_format->icu_number_formatter()->get()));
   }
 
   // Return FormatNumber(numberFormat, x).
-  icu::NumberFormat* icu_number_format =
-      number_format->icu_number_format()->raw();
+  icu::number::LocalizedNumberFormatter* icu_number_format =
+      number_format->icu_number_formatter()->raw();
   return JSNumberFormat::FormatNumeric(isolate, *icu_number_format,
                                        numeric_obj);
 }
@@ -1130,19 +1131,17 @@
 
 }  // namespace
 
-Maybe<bool> Intl::SetNumberFormatDigitOptions(Isolate* isolate,
-                                              icu::DecimalFormat* number_format,
-                                              Handle<JSReceiver> options,
-                                              int mnfd_default,
-                                              int mxfd_default) {
-  CHECK_NOT_NULL(number_format);
+Maybe<Intl::NumberFormatDigitOptions> Intl::SetNumberFormatDigitOptions(
+    Isolate* isolate, Handle<JSReceiver> options, int mnfd_default,
+    int mxfd_default) {
+  Intl::NumberFormatDigitOptions digit_options;
 
   // 5. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21,
   // 1).
   int mnid;
   if (!GetNumberOption(isolate, options, "minimumIntegerDigits", 1, 21, 1)
            .To(&mnid)) {
-    return Nothing<bool>();
+    return Nothing<NumberFormatDigitOptions>();
   }
 
   // 6. Let mnfd be ? GetNumberOption(options, "minimumFractionDigits", 0, 20,
@@ -1151,7 +1150,7 @@
   if (!GetNumberOption(isolate, options, "minimumFractionDigits", 0, 20,
                        mnfd_default)
            .To(&mnfd)) {
-    return Nothing<bool>();
+    return Nothing<NumberFormatDigitOptions>();
   }
 
   // 7. Let mxfdActualDefault be max( mnfd, mxfdDefault ).
@@ -1163,7 +1162,7 @@
   if (!GetNumberOption(isolate, options, "maximumFractionDigits", mnfd, 20,
                        mxfd_actual_default)
            .To(&mxfd)) {
-    return Nothing<bool>();
+    return Nothing<NumberFormatDigitOptions>();
   }
 
   // 9.  Let mnsd be ? Get(options, "minimumSignificantDigits").
@@ -1172,7 +1171,7 @@
       isolate->factory()->minimumSignificantDigits_string();
   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
       isolate, mnsd_obj, JSReceiver::GetProperty(isolate, options, mnsd_str),
-      Nothing<bool>());
+      Nothing<NumberFormatDigitOptions>());
 
   // 10. Let mxsd be ? Get(options, "maximumSignificantDigits").
   Handle<Object> mxsd_obj;
@@ -1180,45 +1179,43 @@
       isolate->factory()->maximumSignificantDigits_string();
   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
       isolate, mxsd_obj, JSReceiver::GetProperty(isolate, options, mxsd_str),
-      Nothing<bool>());
+      Nothing<NumberFormatDigitOptions>());
 
   // 11. Set intlObj.[[MinimumIntegerDigits]] to mnid.
-  number_format->setMinimumIntegerDigits(mnid);
+  digit_options.minimum_integer_digits = mnid;
 
   // 12. Set intlObj.[[MinimumFractionDigits]] to mnfd.
-  number_format->setMinimumFractionDigits(mnfd);
+  digit_options.minimum_fraction_digits = mnfd;
 
   // 13. Set intlObj.[[MaximumFractionDigits]] to mxfd.
-  number_format->setMaximumFractionDigits(mxfd);
+  digit_options.maximum_fraction_digits = mxfd;
 
-  bool significant_digits_used = false;
   // 14. If mnsd is not undefined or mxsd is not undefined, then
   if (!mnsd_obj->IsUndefined(isolate) || !mxsd_obj->IsUndefined(isolate)) {
     // 14. a. Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1).
     int mnsd;
     if (!DefaultNumberOption(isolate, mnsd_obj, 1, 21, 1, mnsd_str).To(&mnsd)) {
-      return Nothing<bool>();
+      return Nothing<NumberFormatDigitOptions>();
     }
 
     // 14. b. Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21).
     int mxsd;
     if (!DefaultNumberOption(isolate, mxsd_obj, mnsd, 21, 21, mxsd_str)
              .To(&mxsd)) {
-      return Nothing<bool>();
+      return Nothing<NumberFormatDigitOptions>();
     }
 
-    significant_digits_used = true;
-
     // 14. c. Set intlObj.[[MinimumSignificantDigits]] to mnsd.
-    number_format->setMinimumSignificantDigits(mnsd);
+    digit_options.minimum_significant_digits = mnsd;
 
     // 14. d. Set intlObj.[[MaximumSignificantDigits]] to mxsd.
-    number_format->setMaximumSignificantDigits(mxsd);
+    digit_options.maximum_significant_digits = mxsd;
+  } else {
+    digit_options.minimum_significant_digits = 0;
+    digit_options.maximum_significant_digits = 0;
   }
 
-  number_format->setSignificantDigitsUsed(significant_digits_used);
-  number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
-  return Just(true);
+  return Just(digit_options);
 }
 
 namespace {
@@ -1972,6 +1969,11 @@
       UNREACHABLE();
       return Handle<String>();
 
+    case UNUM_COMPACT_FIELD:
+      return isolate->factory()->compact_string();
+    case UNUM_MEASURE_UNIT_FIELD:
+      return isolate->factory()->unit_string();
+
     default:
       UNREACHABLE();
       return Handle<String>();
diff --git a/src/objects/intl-objects.h b/src/objects/intl-objects.h
index b35cb75..e14fde2 100644
--- a/src/objects/intl-objects.h
+++ b/src/objects/intl-objects.h
@@ -25,11 +25,10 @@
 namespace U_ICU_NAMESPACE {
 class BreakIterator;
 class Collator;
-class DecimalFormat;
 class FormattedValue;
 class SimpleDateFormat;
 class UnicodeString;
-}
+}  // namespace U_ICU_NAMESPACE
 
 namespace v8 {
 namespace internal {
@@ -172,9 +171,16 @@
       Handle<Object> options);
 
   // ecma402/#sec-setnfdigitoptions
-  V8_WARN_UNUSED_RESULT static Maybe<bool> SetNumberFormatDigitOptions(
-      Isolate* isolate, icu::DecimalFormat* number_format,
-      Handle<JSReceiver> options, int mnfd_default, int mxfd_default);
+  struct NumberFormatDigitOptions {
+    int minimum_integer_digits;
+    int minimum_fraction_digits;
+    int maximum_fraction_digits;
+    int minimum_significant_digits;
+    int maximum_significant_digits;
+  };
+  V8_WARN_UNUSED_RESULT static Maybe<NumberFormatDigitOptions>
+  SetNumberFormatDigitOptions(Isolate* isolate, Handle<JSReceiver> options,
+                              int mnfd_default, int mxfd_default);
 
   static icu::Locale CreateICULocale(const std::string& bcp47_locale);
 
diff --git a/src/objects/intl-objects.tq b/src/objects/intl-objects.tq
index 4b32925..67d8537 100644
--- a/src/objects/intl-objects.tq
+++ b/src/objects/intl-objects.tq
@@ -28,7 +28,8 @@
 
 extern class JSNumberFormat extends JSObject {
   locale: String;
-  icu_number_format: Foreign;  // Managed<icu::NumberFormat>
+  icu_number_formatter:
+      Foreign;  // Managed<icu::number::LocalizedNumberFormatter>
   bound_format: JSFunction | Undefined;
   flags: Smi;
 }
diff --git a/src/objects/js-date-time-format.cc b/src/objects/js-date-time-format.cc
index 8efdb2e..93fe151 100644
--- a/src/objects/js-date-time-format.cc
+++ b/src/objects/js-date-time-format.cc
@@ -658,7 +658,7 @@
 
   if (can_cache) {
     isolate->set_icu_object_in_cache(
-        cache_type, std::static_pointer_cast<icu::UObject>(
+        cache_type, std::static_pointer_cast<icu::UMemory>(
                         date_time_format->icu_simple_date_format()->get()));
   }
   // 5. Return FormatDateTime(dateFormat, x).
diff --git a/src/objects/js-number-format-inl.h b/src/objects/js-number-format-inl.h
index 491800b..0374f92 100644
--- a/src/objects/js-number-format-inl.h
+++ b/src/objects/js-number-format-inl.h
@@ -21,35 +21,51 @@
 OBJECT_CONSTRUCTORS_IMPL(JSNumberFormat, JSObject)
 
 ACCESSORS(JSNumberFormat, locale, String, kLocaleOffset)
-ACCESSORS(JSNumberFormat, icu_number_format, Managed<icu::NumberFormat>,
-          kIcuNumberFormatOffset)
+ACCESSORS(JSNumberFormat, icu_number_formatter,
+          Managed<icu::number::LocalizedNumberFormatter>,
+          kIcuNumberFormatterOffset)
 ACCESSORS(JSNumberFormat, bound_format, Object, kBoundFormatOffset)
+
+// Currenct ECMA 402 spec mandate to record (Min|Max)imumFractionDigits
+// uncondictionally while the unified number proposal eventually will only
+// record either (Min|Max)imumFractionDigits or (Min|Max)imumSignaficantDigits
+// Since LocalizedNumberFormatter can only remember one set, and during
+// 2019-1-17 ECMA402 meeting that the committee decide not to take a PR to
+// address that prior to the unified number proposal, we have to add these two
+// 5 bits int into flags to remember the (Min|Max)imumFractionDigits while
+// (Min|Max)imumSignaficantDigits is present.
+// TODO(ftang) remove the following once we ship int-number-format-unified
+//  * SMI_ACCESSORS of flags
+//  * Four inline functions: (set_)?(min|max)imum_fraction_digits
+
 SMI_ACCESSORS(JSNumberFormat, flags, kFlagsOffset)
 
-inline void JSNumberFormat::set_style(Style style) {
-  DCHECK_LT(style, Style::COUNT);
+inline int JSNumberFormat::minimum_fraction_digits() const {
+  return MinimumFractionDigitsBits::decode(flags());
+}
+
+inline void JSNumberFormat::set_minimum_fraction_digits(int digits) {
+  DCHECK_GE(MinimumFractionDigitsBits::kMax, digits);
+  DCHECK_LE(0, digits);
+  DCHECK_GE(20, digits);
   int hints = flags();
-  hints = StyleBits::update(hints, style);
+  hints = MinimumFractionDigitsBits::update(hints, digits);
   set_flags(hints);
 }
 
-inline JSNumberFormat::Style JSNumberFormat::style() const {
-  return StyleBits::decode(flags());
+inline int JSNumberFormat::maximum_fraction_digits() const {
+  return MaximumFractionDigitsBits::decode(flags());
 }
 
-inline void JSNumberFormat::set_currency_display(
-    CurrencyDisplay currency_display) {
-  DCHECK_LT(currency_display, CurrencyDisplay::COUNT);
+inline void JSNumberFormat::set_maximum_fraction_digits(int digits) {
+  DCHECK_GE(MaximumFractionDigitsBits::kMax, digits);
+  DCHECK_LE(0, digits);
+  DCHECK_GE(20, digits);
   int hints = flags();
-  hints = CurrencyDisplayBits::update(hints, currency_display);
+  hints = MaximumFractionDigitsBits::update(hints, digits);
   set_flags(hints);
 }
 
-inline JSNumberFormat::CurrencyDisplay JSNumberFormat::currency_display()
-    const {
-  return CurrencyDisplayBits::decode(flags());
-}
-
 CAST_ACCESSOR(JSNumberFormat)
 
 }  // namespace internal
diff --git a/src/objects/js-number-format.cc b/src/objects/js-number-format.cc
index 6bac26b..22fe97a 100644
--- a/src/objects/js-number-format.cc
+++ b/src/objects/js-number-format.cc
@@ -15,27 +15,106 @@
 #include "src/objects-inl.h"
 #include "src/objects/intl-objects.h"
 #include "src/objects/js-number-format-inl.h"
+#include "unicode/currunit.h"
 #include "unicode/decimfmt.h"
 #include "unicode/locid.h"
+#include "unicode/nounit.h"
+#include "unicode/numberformatter.h"
 #include "unicode/numfmt.h"
+#include "unicode/ucurr.h"
 #include "unicode/uloc.h"
+#include "unicode/unumberformatter.h"
+#include "unicode/uvernum.h"  // for U_ICU_VERSION_MAJOR_NUM
 
 namespace v8 {
 namespace internal {
 
 namespace {
 
-UNumberFormatStyle ToNumberFormatStyle(
-    JSNumberFormat::CurrencyDisplay currency_display) {
+// [[Style]] is one of the values "decimal", "percent", "currency",
+// or "unit" identifying the style of the number format.
+// Note: "unit" is added in proposal-unified-intl-numberformat
+enum class Style {
+  DECIMAL,
+  PERCENT,
+  CURRENCY,
+  UNIT,
+};
+
+// [[CurrencyDisplay]] is one of the values "code", "symbol", "name",
+// or "narrow-symbol" identifying the display of the currency number format.
+// Note: "narrow-symbol" is added in proposal-unified-intl-numberformat
+enum class CurrencyDisplay {
+  CODE,
+  SYMBOL,
+  NAME,
+  NARROW_SYMBOL,
+};
+
+// [[CurrencySign]] is one of the String values "standard" or "accounting",
+// specifying whether to render negative numbers in accounting format, often
+// signified by parenthesis. It is only used when [[Style]] has the value
+// "currency" and when [[SignDisplay]] is not "never".
+enum class CurrencySign {
+  STANDARD,
+  ACCOUNTING,
+};
+
+// [[UnitDisplay]] is one of the String values "short", "narrow", or "long",
+// specifying whether to display the unit as a symbol, narrow symbol, or
+// localized long name if formatting with the "unit" or "percent" style. It is
+// only used when [[Style]] has the value "unit" or "percent".
+enum class UnitDisplay {
+  SHORT,
+  NARROW,
+  LONG,
+};
+
+// [[Notation]] is one of the String values "standard", "scientific",
+// "engineering", or "compact", specifying whether the number should be
+// displayed without scaling, scaled to the units place with the power of ten
+// in scientific notation, scaled to the nearest thousand with the power of
+// ten in scientific notation, or scaled to the nearest locale-dependent
+// compact decimal notation power of ten with the corresponding compact
+// decimal notation affix.
+
+enum class Notation {
+  STANDARD,
+  SCIENTIFIC,
+  ENGINEERING,
+  COMPACT,
+};
+
+// [[CompactDisplay]] is one of the String values "short" or "long",
+// specifying whether to display compact notation affixes in short form ("5K")
+// or long form ("5 thousand") if formatting with the "compact" notation. It
+// is only used when [[Notation]] has the value "compact".
+enum class CompactDisplay {
+  SHORT,
+  LONG,
+};
+
+// [[SignDisplay]] is one of the String values "auto", "always", "never", or
+// "except-zero", specifying whether to show the sign on negative numbers
+// only, positive and negative numbers including zero, neither positive nor
+// negative numbers, or positive and negative numbers but not zero.
+enum class SignDisplay {
+  AUTO,
+  ALWAYS,
+  NEVER,
+  EXCEPT_ZERO,
+};
+
+UNumberUnitWidth ToUNumberUnitWidth(CurrencyDisplay currency_display) {
   switch (currency_display) {
-    case JSNumberFormat::CurrencyDisplay::SYMBOL:
-      return UNUM_CURRENCY;
-    case JSNumberFormat::CurrencyDisplay::CODE:
-      return UNUM_CURRENCY_ISO;
-    case JSNumberFormat::CurrencyDisplay::NAME:
-      return UNUM_CURRENCY_PLURAL;
-    case JSNumberFormat::CurrencyDisplay::COUNT:
-      UNREACHABLE();
+    case CurrencyDisplay::SYMBOL:
+      return UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT;
+    case CurrencyDisplay::CODE:
+      return UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE;
+    case CurrencyDisplay::NAME:
+      return UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME;
+    case CurrencyDisplay::NARROW_SYMBOL:
+      return UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW;
   }
 }
 
@@ -69,23 +148,162 @@
   return (IsAToZ(currency[0]) && IsAToZ(currency[1]) && IsAToZ(currency[2]));
 }
 
+// Parse the 'style' from the skeleton.
+Handle<String> StyleString(Isolate* isolate,
+                           const icu::UnicodeString& skeleton) {
+  // Ex: skeleton as
+  // "percent precision-integer rounding-mode-half-up scale/100"
+  if (skeleton.indexOf("percent") >= 0) {
+    return ReadOnlyRoots(isolate).percent_string_handle();
+  }
+  // Ex: skeleton as "currency/TWD .00 rounding-mode-half-up"
+  if (skeleton.indexOf("currency") >= 0) {
+    return ReadOnlyRoots(isolate).currency_string_handle();
+  }
+  // Ex: skeleton as
+  // "measure-unit/length-meter .### rounding-mode-half-up unit-width-narrow"
+  if (skeleton.indexOf("measure-unit") >= 0) {
+    return ReadOnlyRoots(isolate).unit_string_handle();
+  }
+  // Ex: skeleton as ".### rounding-mode-half-up"
+  return ReadOnlyRoots(isolate).decimal_string_handle();
+}
+
+// Parse the 'currencyDisplay' from the skeleton.
+Handle<String> CurrencyDisplayString(Isolate* isolate,
+                                     const icu::UnicodeString& skeleton) {
+  // Ex: skeleton as
+  // "currency/TWD .00 rounding-mode-half-up unit-width-iso-code"
+  if (skeleton.indexOf("unit-width-iso-code") >= 0) {
+    return ReadOnlyRoots(isolate).code_string_handle();
+  }
+  // Ex: skeleton as
+  // "currency/TWD .00 rounding-mode-half-up unit-width-full-name;
+  if (skeleton.indexOf("unit-width-full-name") >= 0) {
+    return ReadOnlyRoots(isolate).name_string_handle();
+  }
+  // Ex: skeleton as
+  // "currency/TWD .00 rounding-mode-half-up unit-width-narrow;
+  if (skeleton.indexOf("unit-width-narrow") >= 0) {
+    return ReadOnlyRoots(isolate).narrow_symbol_string_handle();
+  }
+  // Ex: skeleton as "currency/TWD .00 rounding-mode-half-up"
+  return ReadOnlyRoots(isolate).symbol_string_handle();
+}
+
+// Return true if there are no "group-off" in the skeleton.
+bool UseGroupingFromSkeleton(const icu::UnicodeString& skeleton) {
+  return skeleton.indexOf("group-off") == -1;
+}
+
+// Parse currency code from skeleton. For example, skeleton as
+// "currency/TWD .00 rounding-mode-half-up unit-width-full-name;
+std::string CurrencyFromSkeleton(const icu::UnicodeString& skeleton) {
+  std::string str;
+  str = skeleton.toUTF8String<std::string>(str);
+  std::string search("currency/");
+  size_t index = str.find(search);
+  if (index == str.npos) return "";
+  return str.substr(index + search.size(), 3);
+}
+
+// Return the minimum integer digits by counting the number of '0' after
+// "integer-width/+" in the skeleton.
+// Ex: Return 15 for skeleton as
+// “currency/TWD .00 rounding-mode-half-up integer-width/+000000000000000”
+//                                                                 1
+//                                                        123456789012345
+// Return default value as 1 if there are no "integer-width/+".
+int32_t MinimumIntegerDigitsFromSkeleton(const icu::UnicodeString& skeleton) {
+  // count the number of 0 after "integer-width/+"
+  icu::UnicodeString search("integer-width/+");
+  int32_t index = skeleton.indexOf(search);
+  if (index < 0) return 1;  // return 1 if cannot find it.
+  index += search.length();
+  int32_t matched = 0;
+  while (index < skeleton.length() && skeleton[index] == '0') {
+    matched++;
+    index++;
+  }
+  CHECK_GT(matched, 0);
+  return matched;
+}
+
+// Return true if there are fraction digits, false if not.
+// The minimum fraction digits is the number of '0' after '.' in the skeleton
+// The maximum fraction digits is the number of '#' after the above '0's plus
+// the minimum fraction digits.
+// For example, as skeleton “.000#### rounding-mode-half-up”
+//                            123
+//                               4567
+// Set The minimum as 3 and maximum as 7.
+bool FractionDigitsFromSkeleton(const icu::UnicodeString& skeleton,
+                                int32_t* minimum, int32_t* maximum) {
+  icu::UnicodeString search(".");
+  int32_t index = skeleton.indexOf(search);
+  if (index < 0) return false;
+  *minimum = 0;
+  index++;  // skip the '.'
+  while (index < skeleton.length() && skeleton[index] == '0') {
+    (*minimum)++;
+    index++;
+  }
+  *maximum = *minimum;
+  while (index < skeleton.length() && skeleton[index] == '#') {
+    (*maximum)++;
+    index++;
+  }
+  return true;
+}
+
+// Return true if there are significant digits, false if not.
+// The minimum significant digits is the number of '@' in the skeleton
+// The maximum significant digits is the number of '#' after these '@'s plus
+// the minimum significant digits.
+// Ex: Skeleton as "@@@@@####### rounding-mode-half-up"
+//                  12345
+//                       6789012
+// Set The minimum as 5 and maximum as 12.
+bool SignificantDigitsFromSkeleton(const icu::UnicodeString& skeleton,
+                                   int32_t* minimum, int32_t* maximum) {
+  icu::UnicodeString search("@");
+  int32_t index = skeleton.indexOf(search);
+  if (index < 0) return false;
+  *minimum = 1;
+  index++;  // skip the first '@'
+  while (index < skeleton.length() && skeleton[index] == '@') {
+    (*minimum)++;
+    index++;
+  }
+  *maximum = *minimum;
+  while (index < skeleton.length() && skeleton[index] == '#') {
+    (*maximum)++;
+    index++;
+  }
+  return true;
+}
+
 }  // anonymous namespace
 
 // static
 // ecma402 #sec-intl.numberformat.prototype.resolvedoptions
 Handle<JSObject> JSNumberFormat::ResolvedOptions(
-    Isolate* isolate, Handle<JSNumberFormat> number_format_holder) {
+    Isolate* isolate, Handle<JSNumberFormat> number_format) {
   Factory* factory = isolate->factory();
 
+  UErrorCode status = U_ZERO_ERROR;
+  icu::number::LocalizedNumberFormatter* icu_number_formatter =
+      number_format->icu_number_formatter()->raw();
+  icu::UnicodeString skeleton = icu_number_formatter->toSkeleton(status);
+  CHECK(U_SUCCESS(status));
+
+  std::string s_str;
+  s_str = skeleton.toUTF8String<std::string>(s_str);
+
   // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
   Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
 
-  icu::NumberFormat* number_format =
-      number_format_holder->icu_number_format()->raw();
-  CHECK_NOT_NULL(number_format);
-
-  Handle<String> locale =
-      Handle<String>(number_format_holder->locale(), isolate);
+  Handle<String> locale = Handle<String>(number_format->locale(), isolate);
 
   std::unique_ptr<char[]> locale_str = locale->ToCString();
   icu::Locale icu_locale = Intl::CreateICULocale(locale_str.get());
@@ -119,65 +337,67 @@
   }
   CHECK(JSReceiver::CreateDataProperty(
             isolate, options, factory->style_string(),
-            number_format_holder->StyleAsString(), Just(kDontThrow))
+            StyleString(isolate, skeleton), Just(kDontThrow))
             .FromJust());
-  if (number_format_holder->style() == Style::CURRENCY) {
-    icu::UnicodeString currency(number_format->getCurrency());
-    DCHECK(!currency.isEmpty());
+  std::string currency = CurrencyFromSkeleton(skeleton);
+  if (!currency.empty()) {
     CHECK(JSReceiver::CreateDataProperty(
               isolate, options, factory->currency_string(),
-              factory
-                  ->NewStringFromTwoByte(Vector<const uint16_t>(
-                      reinterpret_cast<const uint16_t*>(currency.getBuffer()),
-                      currency.length()))
-                  .ToHandleChecked(),
+              factory->NewStringFromAsciiChecked(currency.c_str()),
               Just(kDontThrow))
               .FromJust());
 
     CHECK(JSReceiver::CreateDataProperty(
               isolate, options, factory->currencyDisplay_string(),
-              number_format_holder->CurrencyDisplayAsString(), Just(kDontThrow))
+              CurrencyDisplayString(isolate, skeleton), Just(kDontThrow))
               .FromJust());
   }
+  CHECK(
+      JSReceiver::CreateDataProperty(
+          isolate, options, factory->minimumIntegerDigits_string(),
+          factory->NewNumberFromInt(MinimumIntegerDigitsFromSkeleton(skeleton)),
+          Just(kDontThrow))
+          .FromJust());
+  int32_t minimum = 0, maximum = 0;
+  if (!FractionDigitsFromSkeleton(skeleton, &minimum, &maximum)) {
+    // Currenct ECMA 402 spec mandate to record (Min|Max)imumFractionDigits
+    // uncondictionally while the unified number proposal eventually will only
+    // record either (Min|Max)imumFractionDigits or
+    // (Min|Max)imumSignaficantDigits Since LocalizedNumberFormatter can only
+    // remember one set, and during 2019-1-17 ECMA402 meeting that the committee
+    // decide not to take a PR to address that prior to the unified number
+    // proposal, we have to add these two 5 bits int into flags to remember the
+    // (Min|Max)imumFractionDigits while (Min|Max)imumSignaficantDigits is
+    // present.
+    // TODO(ftang) remove the following two lines once we ship
+    // int-number-format-unified
+    minimum = number_format->minimum_fraction_digits();
+    maximum = number_format->maximum_fraction_digits();
+  }
   CHECK(JSReceiver::CreateDataProperty(
-            isolate, options, factory->minimumIntegerDigits_string(),
-            factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()),
-            Just(kDontThrow))
+            isolate, options, factory->minimumFractionDigits_string(),
+            factory->NewNumberFromInt(minimum), Just(kDontThrow))
             .FromJust());
-  CHECK(
-      JSReceiver::CreateDataProperty(
-          isolate, options, factory->minimumFractionDigits_string(),
-          factory->NewNumberFromInt(number_format->getMinimumFractionDigits()),
-          Just(kDontThrow))
-          .FromJust());
-  CHECK(
-      JSReceiver::CreateDataProperty(
-          isolate, options, factory->maximumFractionDigits_string(),
-          factory->NewNumberFromInt(number_format->getMaximumFractionDigits()),
-          Just(kDontThrow))
-          .FromJust());
-  CHECK(number_format->getDynamicClassID() ==
-        icu::DecimalFormat::getStaticClassID());
-  icu::DecimalFormat* decimal_format =
-      static_cast<icu::DecimalFormat*>(number_format);
-  CHECK_NOT_NULL(decimal_format);
-  if (decimal_format->areSignificantDigitsUsed()) {
+  CHECK(JSReceiver::CreateDataProperty(
+            isolate, options, factory->maximumFractionDigits_string(),
+            factory->NewNumberFromInt(maximum), Just(kDontThrow))
+            .FromJust());
+  minimum = 0;
+  maximum = 0;
+  if (SignificantDigitsFromSkeleton(skeleton, &minimum, &maximum)) {
     CHECK(JSReceiver::CreateDataProperty(
               isolate, options, factory->minimumSignificantDigits_string(),
-              factory->NewNumberFromInt(
-                  decimal_format->getMinimumSignificantDigits()),
-              Just(kDontThrow))
+              factory->NewNumberFromInt(minimum), Just(kDontThrow))
               .FromJust());
     CHECK(JSReceiver::CreateDataProperty(
               isolate, options, factory->maximumSignificantDigits_string(),
-              factory->NewNumberFromInt(
-                  decimal_format->getMaximumSignificantDigits()),
-              Just(kDontThrow))
+              factory->NewNumberFromInt(maximum), Just(kDontThrow))
               .FromJust());
   }
+
   CHECK(JSReceiver::CreateDataProperty(
             isolate, options, factory->useGrouping_string(),
-            factory->ToBoolean((number_format->isGroupingUsed() == TRUE)),
+            factory->ToBoolean(UseGroupingFromSkeleton(skeleton)),
             Just(kDontThrow))
             .FromJust());
   return options;
@@ -216,8 +436,8 @@
 MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
     Isolate* isolate, Handle<JSNumberFormat> number_format,
     Handle<Object> locales, Handle<Object> options_obj) {
-  // set the flags to 0 ASAP.
   number_format->set_flags(0);
+  // set the flags to 0 ASAP.
   Factory* factory = isolate->factory();
 
   // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
@@ -299,7 +519,6 @@
   Style style = maybe_style.FromJust();
 
   // 13. Set numberFormat.[[Style]] to style.
-  number_format->set_style(style);
 
   // 14. Let currency be ? GetOption(options, "currency", "string", undefined,
   // undefined).
@@ -355,75 +574,35 @@
           CurrencyDisplay::SYMBOL);
   MAYBE_RETURN(maybe_currencyDisplay, MaybeHandle<JSNumberFormat>());
   CurrencyDisplay currency_display = maybe_currencyDisplay.FromJust();
-  UNumberFormatStyle format_style = ToNumberFormatStyle(currency_display);
 
-  std::unique_ptr<icu::NumberFormat> icu_number_format;
-  icu::Locale no_extension_locale(r.icu_locale.getBaseName());
-  if (style == Style::DECIMAL) {
-    icu_number_format.reset(
-        icu::NumberFormat::createInstance(r.icu_locale, status));
-    // If the subclass is not DecimalFormat, fallback to no extension
-    // because other subclass has not support the format() with
-    // FieldPositionIterator yet.
-    if (U_FAILURE(status) || icu_number_format.get() == nullptr ||
-        icu_number_format->getDynamicClassID() !=
-            icu::DecimalFormat::getStaticClassID()) {
-      status = U_ZERO_ERROR;
-      icu_number_format.reset(
-          icu::NumberFormat::createInstance(no_extension_locale, status));
-    }
-  } else if (style == Style::PERCENT) {
-    icu_number_format.reset(
-        icu::NumberFormat::createPercentInstance(r.icu_locale, status));
-    // If the subclass is not DecimalFormat, fallback to no extension
-    // because other subclass has not support the format() with
-    // FieldPositionIterator yet.
-    if (U_FAILURE(status) || icu_number_format.get() == nullptr ||
-        icu_number_format->getDynamicClassID() !=
-            icu::DecimalFormat::getStaticClassID()) {
-      status = U_ZERO_ERROR;
-      icu_number_format.reset(icu::NumberFormat::createPercentInstance(
-          no_extension_locale, status));
-    }
-  } else {
-    DCHECK_EQ(style, Style::CURRENCY);
-    icu_number_format.reset(
-        icu::NumberFormat::createInstance(r.icu_locale, format_style, status));
-    // If the subclass is not DecimalFormat, fallback to no extension
-    // because other subclass has not support the format() with
-    // FieldPositionIterator yet.
-    if (U_FAILURE(status) || icu_number_format.get() == nullptr ||
-        icu_number_format->getDynamicClassID() !=
-            icu::DecimalFormat::getStaticClassID()) {
-      status = U_ZERO_ERROR;
-      icu_number_format.reset(icu::NumberFormat::createInstance(
-          no_extension_locale, format_style, status));
-    }
+  icu::number::LocalizedNumberFormatter icu_number_formatter =
+      icu::number::NumberFormatter::withLocale(r.icu_locale)
+          .roundingMode(UNUM_ROUND_HALFUP);
+  if (style == Style::PERCENT) {
+    icu_number_formatter = icu_number_formatter.unit(icu::NoUnit::percent())
+                               .scale(icu::number::Scale::powerOfTen(2));
   }
 
-  if (U_FAILURE(status) || icu_number_format.get() == nullptr) {
-    status = U_ZERO_ERROR;
-    // Remove extensions and try again.
-    icu_number_format.reset(
-        icu::NumberFormat::createInstance(no_extension_locale, status));
-
-    if (U_FAILURE(status) || icu_number_format.get() == nullptr) {
-      FATAL("Failed to create ICU number_format, are ICU data files missing?");
-    }
-  }
-  DCHECK(U_SUCCESS(status));
-  CHECK_NOT_NULL(icu_number_format.get());
-  CHECK(icu_number_format->getDynamicClassID() ==
-        icu::DecimalFormat::getStaticClassID());
   if (style == Style::CURRENCY) {
     // 19. If style is "currency", set  numberFormat.[[CurrencyDisplay]] to
     // currencyDisplay.
-    number_format->set_currency_display(currency_display);
 
     // 17.b. Set numberFormat.[[Currency]] to currency.
     if (!currency_ustr.isEmpty()) {
-      status = U_ZERO_ERROR;
-      icu_number_format->setCurrency(currency_ustr.getBuffer(), status);
+      Handle<String> currency_string;
+      ASSIGN_RETURN_ON_EXCEPTION(isolate, currency_string,
+                                 Intl::ToString(isolate, currency_ustr),
+                                 JSNumberFormat);
+
+      icu_number_formatter = icu_number_formatter.unit(
+          icu::CurrencyUnit(currency_ustr.getBuffer(), status));
+      CHECK(U_SUCCESS(status));
+      // The default unitWidth is SHORT in ICU and that mapped from
+      // Symbol so we can skip the setting for optimization.
+      if (currency_display != CurrencyDisplay::SYMBOL) {
+        icu_number_formatter = icu_number_formatter.unitWidth(
+            ToUNumberUnitWidth(currency_display));
+      }
       CHECK(U_SUCCESS(status));
     }
   }
@@ -451,14 +630,45 @@
   }
   // 22. Perform ? SetNumberFormatDigitOptions(numberFormat, options,
   // mnfdDefault, mxfdDefault).
-  CHECK(icu_number_format->getDynamicClassID() ==
-        icu::DecimalFormat::getStaticClassID());
-  icu::DecimalFormat* icu_decimal_format =
-      static_cast<icu::DecimalFormat*>(icu_number_format.get());
-  Maybe<bool> maybe_set_number_for_digit_options =
-      Intl::SetNumberFormatDigitOptions(isolate, icu_decimal_format, options,
-                                        mnfd_default, mxfd_default);
-  MAYBE_RETURN(maybe_set_number_for_digit_options, Handle<JSNumberFormat>());
+  Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options =
+      Intl::SetNumberFormatDigitOptions(isolate, options, mnfd_default,
+                                        mxfd_default);
+  MAYBE_RETURN(maybe_digit_options, Handle<JSNumberFormat>());
+  Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust();
+
+  icu::number::Precision precision =
+      (digit_options.minimum_significant_digits > 0)
+          ? icu::number::Precision::minMaxSignificantDigits(
+                digit_options.minimum_significant_digits,
+                digit_options.maximum_significant_digits)
+          : icu::number::Precision::minMaxFraction(
+                digit_options.minimum_fraction_digits,
+                digit_options.maximum_fraction_digits);
+
+  if (digit_options.minimum_significant_digits > 0) {
+    // Currenct ECMA 402 spec mandate to record (Min|Max)imumFractionDigits
+    // uncondictionally while the unified number proposal eventually will only
+    // record either (Min|Max)imumFractionDigits or
+    // (Min|Max)imumSignaficantDigits Since LocalizedNumberFormatter can only
+    // remember one set, and during 2019-1-17 ECMA402 meeting that the committee
+    // decide not to take a PR to address that prior to the unified number
+    // proposal, we have to add these two 5 bits int into flags to remember the
+    // (Min|Max)imumFractionDigits while (Min|Max)imumSignaficantDigits is
+    // present.
+    // TODO(ftang) remove the following two lines once we ship
+    // int-number-format-unified
+    number_format->set_minimum_fraction_digits(
+        digit_options.minimum_fraction_digits);
+    number_format->set_maximum_fraction_digits(
+        digit_options.maximum_fraction_digits);
+  }
+
+  icu_number_formatter = icu_number_formatter.precision(precision);
+  if (digit_options.minimum_integer_digits > 1) {
+    icu_number_formatter =
+        icu_number_formatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(
+            digit_options.minimum_integer_digits));
+  }
 
   // 23. Let useGrouping be ? GetOption(options, "useGrouping", "boolean",
   // undefined, true).
@@ -467,7 +677,10 @@
       isolate, options, "useGrouping", service, &use_grouping);
   MAYBE_RETURN(found_use_grouping, MaybeHandle<JSNumberFormat>());
   // 24. Set numberFormat.[[UseGrouping]] to useGrouping.
-  icu_number_format->setGroupingUsed(use_grouping ? TRUE : FALSE);
+  if (!use_grouping) {
+    icu_number_formatter = icu_number_formatter.grouping(
+        UNumberGroupingStrategy::UNUM_GROUPING_OFF);
+  }
 
   // 25. Let dataLocaleData be localeData.[[<dataLocale>]].
   //
@@ -482,63 +695,41 @@
   //
   // 30. Set numberFormat.[[NegativePattern]] to
   // stylePatterns.[[negativePattern]].
-
-  Handle<Managed<icu::NumberFormat>> managed_number_format =
-      Managed<icu::NumberFormat>::FromUniquePtr(isolate, 0,
-                                                std::move(icu_number_format));
-  number_format->set_icu_number_format(*managed_number_format);
+  //
+  Handle<Managed<icu::number::LocalizedNumberFormatter>>
+      managed_number_formatter =
+          Managed<icu::number::LocalizedNumberFormatter>::FromRawPtr(
+              isolate, 0,
+              new icu::number::LocalizedNumberFormatter(icu_number_formatter));
+  number_format->set_icu_number_formatter(*managed_number_formatter);
   number_format->set_bound_format(*factory->undefined_value());
 
   // 31. Return numberFormat.
   return number_format;
 }
 
-Handle<String> JSNumberFormat::StyleAsString() const {
-  switch (style()) {
-    case Style::DECIMAL:
-      return GetReadOnlyRoots().decimal_string_handle();
-    case Style::PERCENT:
-      return GetReadOnlyRoots().percent_string_handle();
-    case Style::CURRENCY:
-      return GetReadOnlyRoots().currency_string_handle();
-    case Style::COUNT:
-      UNREACHABLE();
-  }
-}
-
-Handle<String> JSNumberFormat::CurrencyDisplayAsString() const {
-  switch (currency_display()) {
-    case CurrencyDisplay::CODE:
-      return GetReadOnlyRoots().code_string_handle();
-    case CurrencyDisplay::SYMBOL:
-      return GetReadOnlyRoots().symbol_string_handle();
-    case CurrencyDisplay::NAME:
-      return GetReadOnlyRoots().name_string_handle();
-    case CurrencyDisplay::COUNT:
-      UNREACHABLE();
-  }
-}
-
 namespace {
 Maybe<icu::UnicodeString> IcuFormatNumber(
-    Isolate* isolate, const icu::NumberFormat& number_format,
+    Isolate* isolate,
+    const icu::number::LocalizedNumberFormatter& number_format,
     Handle<Object> numeric_obj, icu::FieldPositionIterator* fp_iter) {
-  icu::UnicodeString result;
   // If it is BigInt, handle it differently.
   UErrorCode status = U_ZERO_ERROR;
+  icu::number::FormattedNumber formatted;
   if (numeric_obj->IsBigInt()) {
     Handle<BigInt> big_int = Handle<BigInt>::cast(numeric_obj);
     Handle<String> big_int_string;
     ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, big_int_string,
                                      BigInt::ToString(isolate, big_int),
                                      Nothing<icu::UnicodeString>());
-    number_format.format(
-        {big_int_string->ToCString().get(), big_int_string->length()}, result,
-        fp_iter, status);
+    formatted = number_format.formatDecimal(
+        {big_int_string->ToCString().get(), big_int_string->length()}, status);
   } else {
     double number = numeric_obj->Number();
-    number_format.format(number, result, fp_iter, status);
+    formatted = number_format.formatDouble(number, status);
   }
+  formatted.getAllFieldPositions(*fp_iter, status);
+  icu::UnicodeString result = formatted.toString(status);
   if (U_FAILURE(status)) {
     THROW_NEW_ERROR_RETURN_VALUE(isolate,
                                  NewTypeError(MessageTemplate::kIcuError),
@@ -550,17 +741,15 @@
 }  // namespace
 
 MaybeHandle<String> JSNumberFormat::FormatNumeric(
-    Isolate* isolate, const icu::NumberFormat& number_format,
+    Isolate* isolate,
+    const icu::number::LocalizedNumberFormatter& number_format,
     Handle<Object> numeric_obj) {
   DCHECK(numeric_obj->IsNumeric());
 
   Maybe<icu::UnicodeString> maybe_format =
       IcuFormatNumber(isolate, number_format, numeric_obj, nullptr);
   MAYBE_RETURN(maybe_format, Handle<String>());
-  icu::UnicodeString result = maybe_format.FromJust();
-
-  return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
-      reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
+  return Intl::ToString(isolate, maybe_format.FromJust());
 }
 
 namespace {
@@ -672,19 +861,12 @@
   return out_parts;
 }
 
-Maybe<int> JSNumberFormat::FormatToParts(Isolate* isolate,
-                                         Handle<JSArray> result,
-                                         int start_index,
-                                         const icu::NumberFormat& number_format,
-                                         Handle<Object> numeric_obj,
-                                         Handle<String> unit) {
+namespace {
+Maybe<int> ConstructParts(Isolate* isolate, const icu::UnicodeString& formatted,
+                          icu::FieldPositionIterator* fp_iter,
+                          Handle<JSArray> result, int start_index,
+                          Handle<Object> numeric_obj, Handle<String> unit) {
   DCHECK(numeric_obj->IsNumeric());
-  icu::FieldPositionIterator fp_iter;
-  Maybe<icu::UnicodeString> maybe_format =
-      IcuFormatNumber(isolate, number_format, numeric_obj, &fp_iter);
-  MAYBE_RETURN(maybe_format, Nothing<int>());
-  icu::UnicodeString formatted = maybe_format.FromJust();
-
   int32_t length = formatted.length();
   int index = start_index;
   if (length == 0) return Just(index);
@@ -698,7 +880,7 @@
 
   {
     icu::FieldPosition fp;
-    while (fp_iter.next(fp)) {
+    while (fp_iter->next(fp)) {
       regions.push_back(NumberFormatSpan(fp.getField(), fp.getBeginIndex(),
                                          fp.getEndIndex()));
     }
@@ -729,18 +911,26 @@
   return Just(index);
 }
 
+}  // namespace
+
 MaybeHandle<JSArray> JSNumberFormat::FormatToParts(
     Isolate* isolate, Handle<JSNumberFormat> number_format,
     Handle<Object> numeric_obj) {
   CHECK(numeric_obj->IsNumeric());
   Factory* factory = isolate->factory();
-  icu::NumberFormat* fmt = number_format->icu_number_format()->raw();
+  icu::number::LocalizedNumberFormatter* fmt =
+      number_format->icu_number_formatter()->raw();
   CHECK_NOT_NULL(fmt);
 
-  Handle<JSArray> result = factory->NewJSArray(0);
+  icu::FieldPositionIterator fp_iter;
+  Maybe<icu::UnicodeString> maybe_format =
+      IcuFormatNumber(isolate, *fmt, numeric_obj, &fp_iter);
+  MAYBE_RETURN(maybe_format, Handle<JSArray>());
 
-  Maybe<int> maybe_format_to_parts = JSNumberFormat::FormatToParts(
-      isolate, result, 0, *fmt, numeric_obj, Handle<String>());
+  Handle<JSArray> result = factory->NewJSArray(0);
+  Maybe<int> maybe_format_to_parts =
+      ConstructParts(isolate, maybe_format.FromJust(), &fp_iter, result, 0,
+                     numeric_obj, Handle<String>());
   MAYBE_RETURN(maybe_format_to_parts, Handle<JSArray>());
 
   return result;
diff --git a/src/objects/js-number-format.h b/src/objects/js-number-format.h
index 51cc401..a7940e0 100644
--- a/src/objects/js-number-format.h
+++ b/src/objects/js-number-format.h
@@ -17,12 +17,14 @@
 #include "src/objects.h"
 #include "src/objects/intl-objects.h"
 #include "src/objects/managed.h"
+#include "unicode/numberformatter.h"
 
 // Has to be the last include (doesn't have include guards):
 #include "src/objects/object-macros.h"
 
 namespace U_ICU_NAMESPACE {
 class NumberFormat;
+class UnicodeString;
 }  //  namespace U_ICU_NAMESPACE
 
 namespace v8 {
@@ -47,78 +49,55 @@
       Isolate* isolate, Handle<JSNumberFormat> number_format,
       Handle<Object> numeric_obj);
 
-  // A utility function used by the above JSNumberFormat::FormatToParts()
-  // and JSRelativeTimeFormat::FormatToParts().
-  // Format the number by using the icu::NumberFormat to get the field
-  // information. It add an object into the result array, starting from the
-  // start_index and return the total number of elements in the result array.
-  // For each object added as element, it set the substring of the field as
-  // "value", the field type as "type". If the unit is not null, it also set
-  // unit as "unit" to each added object.
-  V8_WARN_UNUSED_RESULT static Maybe<int> FormatToParts(
-      Isolate* isolate, Handle<JSArray> result, int start_index,
-      const icu::NumberFormat& fmt, Handle<Object> numeric_obj,
-      Handle<String> unit);
-
   V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatNumeric(
-      Isolate* isolate, const icu::NumberFormat& number_format,
+      Isolate* isolate,
+      const icu::number::LocalizedNumberFormatter& number_format,
       Handle<Object> numeric_obj);
 
   V8_EXPORT_PRIVATE static const std::set<std::string>& GetAvailableLocales();
 
-  Handle<String> StyleAsString() const;
-  Handle<String> CurrencyDisplayAsString() const;
-
   DECL_CAST(JSNumberFormat)
   DECL_PRINTER(JSNumberFormat)
   DECL_VERIFIER(JSNumberFormat)
 
-  // [[Style]] is one of the values "decimal", "percent" or "currency",
-  // identifying the style of the number format.
-  enum class Style {
-    DECIMAL,
-    PERCENT,
-    CURRENCY,
+  // Current ECMA 402 spec mandates to record (Min|Max)imumFractionDigits
+  // unconditionally while the unified number proposal eventually will only
+  // record either (Min|Max)imumFractionDigits or (Min|Max)imumSignaficantDigits
+  // Since LocalizedNumberFormatter can only remember one set, and during
+  // 2019-1-17 ECMA402 meeting that the committee decide not to take a PR to
+  // address that prior to the unified number proposal, we have to add these two
+  // 5 bits int into flags to remember the (Min|Max)imumFractionDigits while
+  // (Min|Max)imumSignaficantDigits is present.
+  // TODO(ftang) remove the following once we ship int-number-format-unified
+  //  * Four inline functions: (set_)?(min|max)imum_fraction_digits
+  //  * kFlagsOffset
+  //  * #define FLAGS_BIT_FIELDS
+  //  * DECL_INT_ACCESSORS(flags)
 
-    COUNT
-  };
-  inline void set_style(Style style);
-  inline Style style() const;
+  inline int minimum_fraction_digits() const;
+  inline void set_minimum_fraction_digits(int digits);
 
-  // [[CurrencyDisplay]] is one of the values "code", "symbol" or "name",
-  // identifying the display of the currency number format.
-  enum class CurrencyDisplay {
-    CODE,
-    SYMBOL,
-    NAME,
+  inline int maximum_fraction_digits() const;
+  inline void set_maximum_fraction_digits(int digits);
 
-    COUNT
-  };
-  inline void set_currency_display(CurrencyDisplay currency_display);
-  inline CurrencyDisplay currency_display() const;
-
-// Layout description.
+  // Layout description.
   DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
                                 TORQUE_GENERATED_JSNUMBER_FORMAT_FIELDS)
 
 // Bit positions in |flags|.
-#define FLAGS_BIT_FIELDS(V, _) \
-  V(StyleBits, Style, 2, _)    \
-  V(CurrencyDisplayBits, CurrencyDisplay, 2, _)
+#define FLAGS_BIT_FIELDS(V, _)            \
+  V(MinimumFractionDigitsBits, int, 5, _) \
+  V(MaximumFractionDigitsBits, int, 5, _)
 
   DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS)
 #undef FLAGS_BIT_FIELDS
 
-  STATIC_ASSERT(Style::DECIMAL <= StyleBits::kMax);
-  STATIC_ASSERT(Style::PERCENT <= StyleBits::kMax);
-  STATIC_ASSERT(Style::CURRENCY <= StyleBits::kMax);
-
-  STATIC_ASSERT(CurrencyDisplay::CODE <= CurrencyDisplayBits::kMax);
-  STATIC_ASSERT(CurrencyDisplay::SYMBOL <= CurrencyDisplayBits::kMax);
-  STATIC_ASSERT(CurrencyDisplay::NAME <= CurrencyDisplayBits::kMax);
+  STATIC_ASSERT(20 <= MinimumFractionDigitsBits::kMax);
+  STATIC_ASSERT(20 <= MaximumFractionDigitsBits::kMax);
 
   DECL_ACCESSORS(locale, String)
-  DECL_ACCESSORS(icu_number_format, Managed<icu::NumberFormat>)
+  DECL_ACCESSORS(icu_number_formatter,
+                 Managed<icu::number::LocalizedNumberFormatter>)
   DECL_ACCESSORS(bound_format, Object)
   DECL_INT_ACCESSORS(flags)
 
diff --git a/src/objects/js-plural-rules.cc b/src/objects/js-plural-rules.cc
index da349dc..bf4e3db 100644
--- a/src/objects/js-plural-rules.cc
+++ b/src/objects/js-plural-rules.cc
@@ -164,9 +164,24 @@
   CHECK_NOT_NULL(icu_decimal_format.get());
 
   // 9. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3).
-  Maybe<bool> done = Intl::SetNumberFormatDigitOptions(
-      isolate, icu_decimal_format.get(), options, 0, 3);
-  MAYBE_RETURN(done, MaybeHandle<JSPluralRules>());
+  Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options =
+      Intl::SetNumberFormatDigitOptions(isolate, options, 0, 3);
+  MAYBE_RETURN(maybe_digit_options, MaybeHandle<JSPluralRules>());
+  Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust();
+
+  icu_decimal_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
+  icu_decimal_format->setMinimumIntegerDigits(
+      digit_options.minimum_integer_digits);
+  icu_decimal_format->setMinimumFractionDigits(
+      digit_options.minimum_fraction_digits);
+  icu_decimal_format->setMaximumFractionDigits(
+      digit_options.maximum_fraction_digits);
+  if (digit_options.minimum_significant_digits > 0) {
+    icu_decimal_format->setMinimumSignificantDigits(
+        digit_options.minimum_significant_digits);
+    icu_decimal_format->setMaximumSignificantDigits(
+        digit_options.maximum_significant_digits);
+  }
 
   Handle<Managed<icu::PluralRules>> managed_plural_rules =
       Managed<icu::PluralRules>::FromUniquePtr(isolate, 0,
diff --git a/src/objects/js-plural-rules.h b/src/objects/js-plural-rules.h
index 0440bc1..8448662 100644
--- a/src/objects/js-plural-rules.h
+++ b/src/objects/js-plural-rules.h
@@ -22,6 +22,7 @@
 #include "src/objects/object-macros.h"
 
 namespace U_ICU_NAMESPACE {
+class DecimalFormat;
 class PluralRules;
 }  //  namespace U_ICU_NAMESPACE
 
diff --git a/tools/v8heapconst.py b/tools/v8heapconst.py
index b854009..3ce8b2d 100644
--- a/tools/v8heapconst.py
+++ b/tools/v8heapconst.py
@@ -306,49 +306,49 @@
   ("read_only_space", 0x014b9): (98, "EnumCacheMap"),
   ("read_only_space", 0x01509): (115, "ArrayBoilerplateDescriptionMap"),
   ("read_only_space", 0x016e1): (101, "InterceptorInfoMap"),
-  ("read_only_space", 0x03445): (89, "AccessCheckInfoMap"),
-  ("read_only_space", 0x0346d): (90, "AccessorInfoMap"),
-  ("read_only_space", 0x03495): (91, "AccessorPairMap"),
-  ("read_only_space", 0x034bd): (92, "AliasedArgumentsEntryMap"),
-  ("read_only_space", 0x034e5): (93, "AllocationMementoMap"),
-  ("read_only_space", 0x0350d): (94, "AsmWasmDataMap"),
-  ("read_only_space", 0x03535): (95, "AsyncGeneratorRequestMap"),
-  ("read_only_space", 0x0355d): (96, "ClassPositionsMap"),
-  ("read_only_space", 0x03585): (97, "DebugInfoMap"),
-  ("read_only_space", 0x035ad): (99, "FunctionTemplateInfoMap"),
-  ("read_only_space", 0x035d5): (100, "FunctionTemplateRareDataMap"),
-  ("read_only_space", 0x035fd): (102, "InterpreterDataMap"),
-  ("read_only_space", 0x03625): (103, "ModuleInfoEntryMap"),
-  ("read_only_space", 0x0364d): (104, "ModuleMap"),
-  ("read_only_space", 0x03675): (105, "ObjectTemplateInfoMap"),
-  ("read_only_space", 0x0369d): (106, "PromiseCapabilityMap"),
-  ("read_only_space", 0x036c5): (107, "PromiseReactionMap"),
-  ("read_only_space", 0x036ed): (108, "PrototypeInfoMap"),
-  ("read_only_space", 0x03715): (109, "ScriptMap"),
-  ("read_only_space", 0x0373d): (110, "SourcePositionTableWithFrameCacheMap"),
-  ("read_only_space", 0x03765): (111, "StackFrameInfoMap"),
-  ("read_only_space", 0x0378d): (112, "StackTraceFrameMap"),
-  ("read_only_space", 0x037b5): (113, "Tuple2Map"),
-  ("read_only_space", 0x037dd): (114, "Tuple3Map"),
-  ("read_only_space", 0x03805): (116, "WasmCapiFunctionDataMap"),
-  ("read_only_space", 0x0382d): (117, "WasmDebugInfoMap"),
-  ("read_only_space", 0x03855): (118, "WasmExceptionTagMap"),
-  ("read_only_space", 0x0387d): (119, "WasmExportedFunctionDataMap"),
-  ("read_only_space", 0x038a5): (120, "CallableTaskMap"),
-  ("read_only_space", 0x038cd): (121, "CallbackTaskMap"),
-  ("read_only_space", 0x038f5): (122, "PromiseFulfillReactionJobTaskMap"),
-  ("read_only_space", 0x0391d): (123, "PromiseRejectReactionJobTaskMap"),
-  ("read_only_space", 0x03945): (124, "PromiseResolveThenableJobTaskMap"),
-  ("read_only_space", 0x0396d): (125, "FinalizationGroupCleanupJobTaskMap"),
-  ("read_only_space", 0x03995): (126, "AllocationSiteWithWeakNextMap"),
-  ("read_only_space", 0x039bd): (126, "AllocationSiteWithoutWeakNextMap"),
-  ("read_only_space", 0x039e5): (161, "LoadHandler1Map"),
-  ("read_only_space", 0x03a0d): (161, "LoadHandler2Map"),
-  ("read_only_space", 0x03a35): (161, "LoadHandler3Map"),
-  ("read_only_space", 0x03a5d): (169, "StoreHandler0Map"),
-  ("read_only_space", 0x03a85): (169, "StoreHandler1Map"),
-  ("read_only_space", 0x03aad): (169, "StoreHandler2Map"),
-  ("read_only_space", 0x03ad5): (169, "StoreHandler3Map"),
+  ("read_only_space", 0x03559): (89, "AccessCheckInfoMap"),
+  ("read_only_space", 0x03581): (90, "AccessorInfoMap"),
+  ("read_only_space", 0x035a9): (91, "AccessorPairMap"),
+  ("read_only_space", 0x035d1): (92, "AliasedArgumentsEntryMap"),
+  ("read_only_space", 0x035f9): (93, "AllocationMementoMap"),
+  ("read_only_space", 0x03621): (94, "AsmWasmDataMap"),
+  ("read_only_space", 0x03649): (95, "AsyncGeneratorRequestMap"),
+  ("read_only_space", 0x03671): (96, "ClassPositionsMap"),
+  ("read_only_space", 0x03699): (97, "DebugInfoMap"),
+  ("read_only_space", 0x036c1): (99, "FunctionTemplateInfoMap"),
+  ("read_only_space", 0x036e9): (100, "FunctionTemplateRareDataMap"),
+  ("read_only_space", 0x03711): (102, "InterpreterDataMap"),
+  ("read_only_space", 0x03739): (103, "ModuleInfoEntryMap"),
+  ("read_only_space", 0x03761): (104, "ModuleMap"),
+  ("read_only_space", 0x03789): (105, "ObjectTemplateInfoMap"),
+  ("read_only_space", 0x037b1): (106, "PromiseCapabilityMap"),
+  ("read_only_space", 0x037d9): (107, "PromiseReactionMap"),
+  ("read_only_space", 0x03801): (108, "PrototypeInfoMap"),
+  ("read_only_space", 0x03829): (109, "ScriptMap"),
+  ("read_only_space", 0x03851): (110, "SourcePositionTableWithFrameCacheMap"),
+  ("read_only_space", 0x03879): (111, "StackFrameInfoMap"),
+  ("read_only_space", 0x038a1): (112, "StackTraceFrameMap"),
+  ("read_only_space", 0x038c9): (113, "Tuple2Map"),
+  ("read_only_space", 0x038f1): (114, "Tuple3Map"),
+  ("read_only_space", 0x03919): (116, "WasmCapiFunctionDataMap"),
+  ("read_only_space", 0x03941): (117, "WasmDebugInfoMap"),
+  ("read_only_space", 0x03969): (118, "WasmExceptionTagMap"),
+  ("read_only_space", 0x03991): (119, "WasmExportedFunctionDataMap"),
+  ("read_only_space", 0x039b9): (120, "CallableTaskMap"),
+  ("read_only_space", 0x039e1): (121, "CallbackTaskMap"),
+  ("read_only_space", 0x03a09): (122, "PromiseFulfillReactionJobTaskMap"),
+  ("read_only_space", 0x03a31): (123, "PromiseRejectReactionJobTaskMap"),
+  ("read_only_space", 0x03a59): (124, "PromiseResolveThenableJobTaskMap"),
+  ("read_only_space", 0x03a81): (125, "FinalizationGroupCleanupJobTaskMap"),
+  ("read_only_space", 0x03aa9): (126, "AllocationSiteWithWeakNextMap"),
+  ("read_only_space", 0x03ad1): (126, "AllocationSiteWithoutWeakNextMap"),
+  ("read_only_space", 0x03af9): (161, "LoadHandler1Map"),
+  ("read_only_space", 0x03b21): (161, "LoadHandler2Map"),
+  ("read_only_space", 0x03b49): (161, "LoadHandler3Map"),
+  ("read_only_space", 0x03b71): (169, "StoreHandler0Map"),
+  ("read_only_space", 0x03b99): (169, "StoreHandler1Map"),
+  ("read_only_space", 0x03bc1): (169, "StoreHandler2Map"),
+  ("read_only_space", 0x03be9): (169, "StoreHandler3Map"),
   ("map_space", 0x00141): (1057, "ExternalMap"),
   ("map_space", 0x00169): (1073, "JSMessageObjectMap"),
 }