Fix DateTimeFormat::formatRange bug

Using hack mentioned in https://unicode-org.atlassian.net/browse/ICU-20710
to address the short coming in the ICU IntervalFormat

Bug: v8:11411
Change-Id: I38e54d3617f24afbd9dc4355f946850d7a506116
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2713573
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73240}
This commit is contained in:
Frank Tang 2021-03-04 20:05:14 -08:00 committed by Commit Bot
parent 13568594c0
commit 2590dc5a50
4 changed files with 74 additions and 22 deletions

View File

@ -149,8 +149,8 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) {
isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
}
RETURN_RESULT_OR_FAILURE(
isolate, JSDateTimeFormat::FormatToParts(isolate, dtf, date_value));
RETURN_RESULT_OR_FAILURE(isolate, JSDateTimeFormat::FormatToParts(
isolate, dtf, date_value, false));
}
// Common code for DateTimeFormatPrototypeFormtRange(|ToParts)

View File

@ -1955,7 +1955,7 @@ Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
MaybeHandle<JSArray> JSDateTimeFormat::FormatToParts(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
double date_value) {
double date_value, bool output_source) {
Factory* factory = isolate->factory();
icu::SimpleDateFormat* format =
date_time_format->icu_simple_date_format().raw();
@ -1986,16 +1986,30 @@ MaybeHandle<JSArray> JSDateTimeFormat::FormatToParts(
isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
JSArray);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
if (output_source) {
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring,
isolate->factory()->source_string(),
isolate->factory()->shared_string());
} else {
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
}
++index;
}
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, begin_pos, end_pos), JSArray);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate),
substring);
if (output_source) {
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate),
substring, isolate->factory()->source_string(),
isolate->factory()->shared_string());
} else {
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate),
substring);
}
previous_end_pos = end_pos;
++index;
}
@ -2003,8 +2017,15 @@ MaybeHandle<JSArray> JSDateTimeFormat::FormatToParts(
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, length), JSArray);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
if (output_source) {
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring,
isolate->factory()->source_string(),
isolate->factory()->shared_string());
} else {
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
}
}
JSObject::ValidateElements(*result);
return result;
@ -2092,10 +2113,29 @@ Maybe<bool> AddPartForFormatRange(Isolate* isolate, Handle<JSArray> array,
return Just(true);
}
MaybeHandle<String> FormattedToString(Isolate* isolate,
const icu::FormattedValue& formatted,
bool* outputRange) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString result = formatted.toString(status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String);
}
*outputRange = false;
icu::ConstrainedFieldPosition cfpos;
while (formatted.nextPosition(cfpos, status)) {
if (cfpos.getCategory() == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
*outputRange = true;
break;
}
}
return Intl::ToString(isolate, result);
}
// A helper function to convert the FormattedDateInterval to a
// MaybeHandle<JSArray> for the implementation of formatRangeToParts.
MaybeHandle<JSArray> FormattedDateIntervalToJSArray(
Isolate* isolate, const icu::FormattedValue& formatted) {
Isolate* isolate, const icu::FormattedValue& formatted, bool* outputRange) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString result = formatted.toString(status);
@ -2105,6 +2145,7 @@ MaybeHandle<JSArray> FormattedDateIntervalToJSArray(
int index = 0;
int32_t previous_end_pos = 0;
SourceTracker tracker;
*outputRange = false;
while (formatted.nextPosition(cfpos, status)) {
int32_t category = cfpos.getCategory();
int32_t field = cfpos.getField();
@ -2113,6 +2154,7 @@ MaybeHandle<JSArray> FormattedDateIntervalToJSArray(
if (category == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
DCHECK_LE(field, 2);
*outputRange = true;
tracker.Add(field, start, limit);
} else {
DCHECK(category == UFIELD_CATEGORY_DATE);
@ -2154,7 +2196,9 @@ template <typename T>
MaybeHandle<T> FormatRangeCommon(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
double y,
MaybeHandle<T> (*formatToResult)(Isolate*, const icu::FormattedValue&)) {
MaybeHandle<T> (*formatToResult)(Isolate*, const icu::FormattedValue&,
bool*),
bool* outputRange) {
// Track newer feature formateRange and formatRangeToParts
isolate->CountUsage(v8::Isolate::UseCounterFeature::kDateTimeFormatRange);
@ -2197,7 +2241,7 @@ MaybeHandle<T> FormatRangeCommon(
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T);
}
return formatToResult(isolate, formatted);
return formatToResult(isolate, formatted, outputRange);
}
} // namespace
@ -2205,15 +2249,27 @@ MaybeHandle<T> FormatRangeCommon(
MaybeHandle<String> JSDateTimeFormat::FormatRange(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
double y) {
return FormatRangeCommon<String>(isolate, date_time_format, x, y,
Intl::FormattedToString);
bool outputRange = true;
MaybeHandle<String> ret = FormatRangeCommon<String>(
isolate, date_time_format, x, y, FormattedToString, &outputRange);
if (outputRange) {
return ret;
}
return FormatDateTime(isolate,
*(date_time_format->icu_simple_date_format().raw()), x);
}
MaybeHandle<JSArray> JSDateTimeFormat::FormatRangeToParts(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
double y) {
return FormatRangeCommon<JSArray>(isolate, date_time_format, x, y,
FormattedDateIntervalToJSArray);
bool outputRange = true;
MaybeHandle<JSArray> ret =
FormatRangeCommon<JSArray>(isolate, date_time_format, x, y,
FormattedDateIntervalToJSArray, &outputRange);
if (outputRange) {
return ret;
}
return JSDateTimeFormat::FormatToParts(isolate, date_time_format, x, true);
}
} // namespace internal

View File

@ -60,7 +60,7 @@ class JSDateTimeFormat
// ecma402/#sec-Intl.DateTimeFormat.prototype.formatToParts
V8_WARN_UNUSED_RESULT static MaybeHandle<JSArray> FormatToParts(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
double date_value);
double date_value, bool output_source);
// ecma402/#sec-intl.datetimeformat.prototype.formatRange
V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatRange(

View File

@ -583,10 +583,6 @@
# Temporarily disabled until upstream tests are changed to use /d
'built-ins/RegExp/match-indices/*': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=11411
'intl402/DateTimeFormat/prototype/formatRange/date-same-returns-single-date': [FAIL],
'intl402/DateTimeFormat/prototype/formatRangeToParts/date-same-returns-single-date': [FAIL],
# http://crbug/v8/11466
# https://github.com/tc39/test262/issues/2950
'intl402/Segmenter/constructor/constructor/options-toobject': [FAIL],