[Intl] Only use DecimalFormat

Force to use locale with extension if the created NumberFormat
is not a DecimalFormat.
Check the dynamic class id.
Guard DecimalFormat casting code

Bug: v8:9035
Change-Id: Id32a3f652b93ddfca82f95f30ad2107b364ee7fc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1536571
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60452}
This commit is contained in:
Frank Tang 2019-03-23 12:22:58 -07:00 committed by Commit Bot
parent 31d7e1d366
commit 7b2d7d4528
2 changed files with 67 additions and 4 deletions

View File

@ -83,9 +83,6 @@ Handle<JSObject> JSNumberFormat::ResolvedOptions(
icu::NumberFormat* number_format =
number_format_holder->icu_number_format()->raw();
CHECK_NOT_NULL(number_format);
icu::DecimalFormat* decimal_format =
static_cast<icu::DecimalFormat*>(number_format);
CHECK_NOT_NULL(decimal_format);
Handle<String> locale =
Handle<String>(number_format_holder->locale(), isolate);
@ -159,6 +156,11 @@ Handle<JSObject> JSNumberFormat::ResolvedOptions(
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->minimumSignificantDigits_string(),
@ -335,22 +337,52 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
UErrorCode status = U_ZERO_ERROR;
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));
}
}
if (U_FAILURE(status) || icu_number_format.get() == nullptr) {
status = U_ZERO_ERROR;
// Remove extensions and try again.
icu::Locale no_extension_locale(r.icu_locale.getBaseName());
icu_number_format.reset(
icu::NumberFormat::createInstance(no_extension_locale, status));
@ -360,6 +392,8 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
}
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.
@ -396,6 +430,8 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
}
// 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 =

27
test/intl/regress-9035.js Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// These Numbering Systems (nu) are listed in
// http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/number.xml
// with " — algorithmic" in the description but are NOT listed in
// https://www.ecma-international.org/ecma-402/#table-numbering-system-digits
// So there are no mandate to support these nu in a particular way.
// Therefore in here we are only assure it won't throw exception in the
// constructor and format method.
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-armn')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-armnlow')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-cyrl')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-ethi')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-geor')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-grek')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-greklow')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-hans')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-hansfin')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-hant')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-hantfin')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-hebr')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-jpanfin')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-roman')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-romanlow')).format(0));
assertDoesNotThrow(() => (new Intl.NumberFormat('en-u-nu-taml')).format(0));