Commit 38c5394c authored by littledan's avatar littledan Committed by Commit bot

[intl] Fix NumberFormat options handling spec compliance issues

The goal of this patch was to refactor NumberFormat parameter handling
to be usable by a PluralRules implementation. Along the way, I found
and fixed a couple minor issues where options handling differed from
the specification, and removed some dead code. Regression tests are
added as test262 tests. With this change, the overall flow more closely
resembles the specification plus this editorial change which is out
for review: https://github.com/tc39/ecma402/pull/130/files

BUG=v8:6015,v8:6016
R=yangguo,jungshik

Review-Url: https://codereview.chromium.org/2717613005
Cr-Commit-Position: refs/heads/master@{#44571}
parent 7deb6821
...@@ -274,23 +274,6 @@ icu::DecimalFormat* CreateICUNumberFormat( ...@@ -274,23 +274,6 @@ icu::DecimalFormat* CreateICUNumberFormat(
delete number_format; delete number_format;
return NULL; return NULL;
} }
UErrorCode status_digits = U_ZERO_ERROR;
#if U_ICU_VERSION_MAJOR_NUM >= 59
uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
icu::toUCharPtr(currency.getTerminatedBuffer()), &status_digits);
#else
uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
currency.getTerminatedBuffer(), &status_digits);
#endif
if (U_SUCCESS(status_digits)) {
number_format->setMinimumFractionDigits(fraction_digits);
number_format->setMaximumFractionDigits(fraction_digits);
} else {
// Set min & max to default values (previously in i18n.js)
number_format->setMinimumFractionDigits(0);
number_format->setMaximumFractionDigits(3);
}
} else if (style == UNICODE_STRING_SIMPLE("percent")) { } else if (style == UNICODE_STRING_SIMPLE("percent")) {
number_format = static_cast<icu::DecimalFormat*>( number_format = static_cast<icu::DecimalFormat*>(
icu::NumberFormat::createPercentInstance(icu_locale, status)); icu::NumberFormat::createPercentInstance(icu_locale, status));
......
...@@ -32,6 +32,7 @@ var IntlFallbackSymbol = utils.ImportNow("intl_fallback_symbol"); ...@@ -32,6 +32,7 @@ var IntlFallbackSymbol = utils.ImportNow("intl_fallback_symbol");
var InstallFunctions = utils.InstallFunctions; var InstallFunctions = utils.InstallFunctions;
var InstallGetter = utils.InstallGetter; var InstallGetter = utils.InstallGetter;
var InternalArray = utils.InternalArray; var InternalArray = utils.InternalArray;
var MaxSimple;
var ObjectHasOwnProperty = utils.ImportNow("ObjectHasOwnProperty"); var ObjectHasOwnProperty = utils.ImportNow("ObjectHasOwnProperty");
var OverrideFunction = utils.OverrideFunction; var OverrideFunction = utils.OverrideFunction;
var patternSymbol = utils.ImportNow("intl_pattern_symbol"); var patternSymbol = utils.ImportNow("intl_pattern_symbol");
...@@ -43,6 +44,7 @@ var StringSubstring = GlobalString.prototype.substring; ...@@ -43,6 +44,7 @@ var StringSubstring = GlobalString.prototype.substring;
utils.Import(function(from) { utils.Import(function(from) {
ArrayJoin = from.ArrayJoin; ArrayJoin = from.ArrayJoin;
ArrayPush = from.ArrayPush; ArrayPush = from.ArrayPush;
MaxSimple = from.MaxSimple;
}); });
// Utilities for definitions // Utilities for definitions
...@@ -934,16 +936,6 @@ function BuildLanguageTagREs() { ...@@ -934,16 +936,6 @@ function BuildLanguageTagREs() {
LANGUAGE_TAG_RE = new GlobalRegExp(languageTag, 'i'); LANGUAGE_TAG_RE = new GlobalRegExp(languageTag, 'i');
} }
var resolvedAccessor = {
get() {
%IncrementUseCounter(kIntlResolved);
return this[resolvedSymbol];
},
set(value) {
this[resolvedSymbol] = value;
}
};
// ECMA 402 section 8.2.1 // ECMA 402 section 8.2.1
InstallFunction(GlobalIntl, 'getCanonicalLocales', function(locales) { InstallFunction(GlobalIntl, 'getCanonicalLocales', function(locales) {
return makeArray(canonicalizeLocaleList(locales)); return makeArray(canonicalizeLocaleList(locales));
...@@ -1137,7 +1129,6 @@ function defaultNumberOption(value, min, max, fallback, property) { ...@@ -1137,7 +1129,6 @@ function defaultNumberOption(value, min, max, fallback, property) {
return fallback; return fallback;
} }
/** /**
* Returns the valid digit count for a property, or throws RangeError on * Returns the valid digit count for a property, or throws RangeError on
* a value out of the range. * a value out of the range.
...@@ -1147,15 +1138,35 @@ function getNumberOption(options, property, min, max, fallback) { ...@@ -1147,15 +1138,35 @@ function getNumberOption(options, property, min, max, fallback) {
return defaultNumberOption(value, min, max, fallback, property); return defaultNumberOption(value, min, max, fallback, property);
} }
var patternAccessor = { // ECMA 402 #sec-setnfdigitoptions
get() { // SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault )
%IncrementUseCounter(kIntlPattern); function SetNumberFormatDigitOptions(internalOptions, options,
return this[patternSymbol]; mnfdDefault, mxfdDefault) {
}, // Digit ranges.
set(value) { var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
this[patternSymbol] = value; defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
var mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20,
mnfdDefault);
defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
var mxfdActualDefault = MaxSimple(mnfd, mxfdDefault);
var mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20,
mxfdActualDefault);
defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
var mnsd = options['minimumSignificantDigits'];
var mxsd = options['maximumSignificantDigits'];
if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) {
mnsd = defaultNumberOption(mnsd, 1, 21, 1, 'minimumSignificantDigits');
defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
mxsd = defaultNumberOption(mxsd, mnsd, 21, 21, 'maximumSignificantDigits');
defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
} }
}; }
/** /**
* Initializes the given object so it's a valid NumberFormat instance. * Initializes the given object so it's a valid NumberFormat instance.
...@@ -1183,41 +1194,22 @@ function CreateNumberFormat(locales, options) { ...@@ -1183,41 +1194,22 @@ function CreateNumberFormat(locales, options) {
throw %make_type_error(kCurrencyCode); throw %make_type_error(kCurrencyCode);
} }
var mnfdDefault, mxfdDefault;
var currencyDisplay = getOption( var currencyDisplay = getOption(
'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol'); 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
if (internalOptions.style === 'currency') { if (internalOptions.style === 'currency') {
defineWEProperty(internalOptions, 'currency', %StringToUpperCaseI18N(currency)); defineWEProperty(internalOptions, 'currency', %StringToUpperCaseI18N(currency));
defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay); defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay);
}
// Digit ranges.
var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
var mnfd = options['minimumFractionDigits'];
var mxfd = options['maximumFractionDigits'];
if (!IS_UNDEFINED(mnfd) || internalOptions.style !== 'currency') {
mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0);
defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
}
if (!IS_UNDEFINED(mxfd) || internalOptions.style !== 'currency') { mnfdDefault = mxfdDefault = %CurrencyDigits(internalOptions.currency);
var min_mxfd = internalOptions.style === 'percent' ? 0 : 3; } else {
mnfd = IS_UNDEFINED(mnfd) ? 0 : mnfd; mnfdDefault = 0;
var fallback_limit = (mnfd > min_mxfd) ? mnfd : min_mxfd; mxfdDefault = internalOptions.style === 'percent' ? 0 : 3;
mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, fallback_limit);
defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
} }
var mnsd = options['minimumSignificantDigits']; SetNumberFormatDigitOptions(internalOptions, options, mnfdDefault,
var mxsd = options['maximumSignificantDigits']; mxfdDefault);
if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) {
mnsd = defaultNumberOption(mnsd, 1, 21, 1, 'minimumSignificantDigits');
defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
mxsd = defaultNumberOption(mxsd, mnsd, 21, 21, 'maximumSignificantDigits');
defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
}
// Grouping. // Grouping.
defineWEProperty(internalOptions, 'useGrouping', getOption( defineWEProperty(internalOptions, 'useGrouping', getOption(
......
...@@ -532,6 +532,29 @@ RUNTIME_FUNCTION(Runtime_InternalNumberFormat) { ...@@ -532,6 +532,29 @@ RUNTIME_FUNCTION(Runtime_InternalNumberFormat) {
result.length()))); result.length())));
} }
RUNTIME_FUNCTION(Runtime_CurrencyDigits) {
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, currency, 0);
// TODO(littledan): Avoid transcoding the string twice
v8::String::Utf8Value currency_string(v8::Utils::ToLocal(currency));
icu::UnicodeString currency_icu =
icu::UnicodeString::fromUTF8(*currency_string);
DisallowHeapAllocation no_gc;
UErrorCode status = U_ZERO_ERROR;
#if U_ICU_VERSION_MAJOR_NUM >= 59
uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
icu::toUCharPtr(currency_icu.getTerminatedBuffer()), &status);
#else
uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
currency_icu.getTerminatedBuffer(), &status);
#endif
// For missing currency codes, default to the most common, 2
if (!U_SUCCESS(status)) fraction_digits = 2;
return Smi::FromInt(fraction_digits);
}
RUNTIME_FUNCTION(Runtime_CreateCollator) { RUNTIME_FUNCTION(Runtime_CreateCollator) {
HandleScope scope(isolate); HandleScope scope(isolate);
......
...@@ -265,6 +265,7 @@ namespace internal { ...@@ -265,6 +265,7 @@ namespace internal {
F(InternalDateFormatToParts, 2, 1) \ F(InternalDateFormatToParts, 2, 1) \
F(CreateNumberFormat, 3, 1) \ F(CreateNumberFormat, 3, 1) \
F(InternalNumberFormat, 2, 1) \ F(InternalNumberFormat, 2, 1) \
F(CurrencyDigits, 1, 1) \
F(CreateCollator, 3, 1) \ F(CreateCollator, 3, 1) \
F(InternalCompare, 3, 1) \ F(InternalCompare, 3, 1) \
F(CreateBreakIterator, 3, 1) \ F(CreateBreakIterator, 3, 1) \
......
...@@ -546,9 +546,6 @@ ...@@ -546,9 +546,6 @@
'built-ins/Object/internals/DefineOwnProperty/consistent-value-function-caller': [FAIL_SLOPPY], 'built-ins/Object/internals/DefineOwnProperty/consistent-value-function-caller': [FAIL_SLOPPY],
'built-ins/Object/internals/DefineOwnProperty/consistent-value-function-arguments': [FAIL_SLOPPY], 'built-ins/Object/internals/DefineOwnProperty/consistent-value-function-arguments': [FAIL_SLOPPY],
# https://bugs.chromium.org/p/v8/issues/detail?id=6016
'intl402/NumberFormat/dft-currency-mnfd-range-check-mxfd': [FAIL],
######################## NEEDS INVESTIGATION ########################### ######################## NEEDS INVESTIGATION ###########################
# These test failures are specific to the intl402 suite and need investigation # These test failures are specific to the intl402 suite and need investigation
...@@ -568,8 +565,6 @@ ...@@ -568,8 +565,6 @@
'intl402/NumberFormat/11.2.3_b': [FAIL], 'intl402/NumberFormat/11.2.3_b': [FAIL],
'intl402/NumberFormat/prototype/11.3_a': [FAIL], 'intl402/NumberFormat/prototype/11.3_a': [FAIL],
'intl402/String/prototype/localeCompare/13.1.1_7': [PASS, FAIL], 'intl402/String/prototype/localeCompare/13.1.1_7': [PASS, FAIL],
'intl402/NumberFormat/default-currency-maximum-fraction-digits': [PASS, FAIL],
'intl402/NumberFormat/fraction-digit-options-read-once': [PASS, FAIL],
##################### DELIBERATE INCOMPATIBILITIES ##################### ##################### DELIBERATE INCOMPATIBILITIES #####################
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment