Commit 8034b056 authored by Frank Tang's avatar Frank Tang Committed by Commit Bot

[Intl] Implement Intl.DateTimeFormat.prototype.formatRangeToParts

Design Doc: https://goo.gl/PGUQ1d

Use template to share code between formatRange and formatRangeToParts
Lazy crate DateIntervalFormat inside formatRange/formatRangeToParts to
reduce performance impact.

Bug: v8:7729
Change-Id: I130748a5ff7ca11235e6608195d365e58d440580
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1556573
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60930}
parent a8c73a48
......@@ -19,6 +19,7 @@
V(_, day_string, "day") \
V(_, dayPeriod_string, "dayPeriod") \
V(_, decimal_string, "decimal") \
V(_, endRange_string, "endRange") \
V(_, era_string, "era") \
V(_, first_string, "first") \
V(_, format_string, "format") \
......@@ -72,6 +73,8 @@
V(_, SegmentIterator_string, "Segment Iterator") \
V(_, sensitivity_string, "sensitivity") \
V(_, sep_string, "sep") \
V(_, shared_string, "shared") \
V(_, startRange_string, "startRange") \
V(_, strict_string, "strict") \
V(_, style_string, "style") \
V(_, term_string, "term") \
......
......@@ -31,6 +31,7 @@
#include "unicode/coll.h"
#include "unicode/datefmt.h"
#include "unicode/decimfmt.h"
#include "unicode/formattedvalue.h"
#include "unicode/locid.h"
#include "unicode/normalizer2.h"
#include "unicode/numfmt.h"
......@@ -1934,5 +1935,16 @@ Handle<String> Intl::NumberFieldToType(Isolate* isolate,
}
}
// A helper function to convert the FormattedValue for several Intl objects.
MaybeHandle<String> Intl::FormattedToString(
Isolate* isolate, const icu::FormattedValue& formatted) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString result = formatted.toString(status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String);
}
return Intl::ToString(isolate, result);
}
} // namespace internal
} // namespace v8
......@@ -26,6 +26,7 @@ namespace U_ICU_NAMESPACE {
class BreakIterator;
class Collator;
class DecimalFormat;
class FormattedValue;
class SimpleDateFormat;
class UnicodeString;
}
......@@ -186,6 +187,10 @@ class Intl {
Isolate* isolate, const icu::UnicodeString& string, int32_t begin,
int32_t end);
// Helper function to convert a FormattedValue to String
V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormattedToString(
Isolate* isolate, const icu::FormattedValue& formatted);
// Helper function to convert number field id to type string.
static Handle<String> NumberFieldToType(Isolate* isolate,
Handle<Object> numeric_obj,
......
This diff is collapsed.
......@@ -296,7 +296,7 @@ Maybe<std::vector<icu::UnicodeString>> ToUnicodeStringArray(
template <typename T>
MaybeHandle<T> FormatListCommon(
Isolate* isolate, Handle<JSListFormat> format, Handle<JSArray> list,
MaybeHandle<T> (*formatToResult)(Isolate*, const icu::FormattedList&)) {
MaybeHandle<T> (*formatToResult)(Isolate*, const icu::FormattedValue&)) {
DCHECK(!list->IsUndefined());
// ecma402 #sec-createpartsfromlist
// 2. If list contains any element value such that Type(value) is not String,
......@@ -318,18 +318,6 @@ MaybeHandle<T> FormatListCommon(
return formatToResult(isolate, formatted);
}
// A helper function to convert the FormattedList to a
// MaybeHandle<String> for the implementation of format.
MaybeHandle<String> FormattedToString(Isolate* isolate,
const icu::FormattedList& formatted) {
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString result = formatted.toString(status);
if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String);
}
return Intl::ToString(isolate, result);
}
Handle<String> IcuFieldIdToType(Isolate* isolate, int32_t field_id) {
switch (field_id) {
case ULISTFMT_LITERAL_FIELD:
......@@ -345,8 +333,8 @@ Handle<String> IcuFieldIdToType(Isolate* isolate, int32_t field_id) {
// A helper function to convert the FormattedList to a
// MaybeHandle<JSArray> for the implementation of formatToParts.
MaybeHandle<JSArray> FormattedToJSArray(Isolate* isolate,
const icu::FormattedList& formatted) {
MaybeHandle<JSArray> FormattedListToJSArray(
Isolate* isolate, const icu::FormattedValue& formatted) {
Handle<JSArray> array = isolate->factory()->NewJSArray(0);
icu::ConstrainedFieldPosition cfpos;
cfpos.constrainCategory(UFIELD_CATEGORY_LIST);
......@@ -375,13 +363,15 @@ MaybeHandle<JSArray> FormattedToJSArray(Isolate* isolate,
MaybeHandle<String> JSListFormat::FormatList(Isolate* isolate,
Handle<JSListFormat> format,
Handle<JSArray> list) {
return FormatListCommon<String>(isolate, format, list, FormattedToString);
return FormatListCommon<String>(isolate, format, list,
Intl::FormattedToString);
}
// ecma42 #sec-formatlisttoparts
MaybeHandle<JSArray> JSListFormat::FormatListToParts(
Isolate* isolate, Handle<JSListFormat> format, Handle<JSArray> list) {
return FormatListCommon<JSArray>(isolate, format, list, FormattedToJSArray);
return FormatListCommon<JSArray>(isolate, format, list,
FormattedListToJSArray);
}
const std::set<std::string>& JSListFormat::GetAvailableLocales() {
......
// 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.
// Flags: --harmony-intl-date-format-range
const date1 = new Date("2019-01-03T03:20");
const date2 = new Date("2019-01-05T19:33");
const date3 = new Date("2019-01-05T22:57");
// value: "Jan 3 – 5, 2019"
// source: hhhhShhhEhhhhhh
// type: mmmldllldllyyyy
// h: Shared, S: startRange, E: endRange
// m: month, l: literal, d: day, y: year
const expected1 = [
{type: "month", value: "Jan", source: "shared"},
{type: "literal", value: " ", source: "shared"},
{type: "day", value: "3", source: "startRange"},
{type: "literal", value: " – ", source: "shared"},
{type: "day", value: "5", source: "endRange"},
{type: "literal", value: ", ", source: "shared"},
{type: "year", value: "2019", source: "shared"}
];
var dtf = new Intl.DateTimeFormat(["en"], {year: "numeric", month: "short", day: "numeric"});
const ret1 = dtf.formatRangeToParts(date1, date2);
assertEquals(expected1, ret1);
// value: "Jan 5, 7 – 10 PM"
// source: hhhhhhhShhhEEhhh
// type: mmmldlldlllhhlpp
// h: Shared, S: startRange, E: endRange
// m: month, l: literal, d: day, h: hour, p: dayPeriod
const expected2 = [
{type: "month", value: "Jan", source: "shared"},
{type: "literal", value: " ", source: "shared"},
{type: "day", value: "5", source: "shared"},
{type: "literal", value: ", ", source: "shared"},
{type: "hour", value: "7", source: "startRange"},
{type: "literal", value: " – ", source: "shared"},
{type: "hour", value: "10", source: "endRange"},
{type: "literal", value: " ", source: "shared"},
{type: "dayPeriod", value: "PM", source: "shared"}
];
dtf = new Intl.DateTimeFormat(["en"], {month: "short", day: "numeric", hour: "numeric"});
const ret2 = dtf.formatRangeToParts(date2, date3);
assertEquals(expected2, ret2);
......@@ -11,8 +11,10 @@ assertFalse(descriptor.enumerable);
assertTrue(descriptor.configurable);
const date1 = new Date("2019-1-3");
const date2 = new Date("2019-3-4");
const dtf = new Intl.DateTimeFormat();
const date2 = new Date("2019-1-5");
const date3 = new Date("2019-3-4");
const date4 = new Date("2020-3-4");
let dtf = new Intl.DateTimeFormat();
assertThrows(() => dtf.formatRangeToParts(), RangeError);
assertThrows(() => dtf.formatRangeToParts(date1), RangeError);
assertThrows(() => dtf.formatRangeToParts(undefined, date2), RangeError);
......@@ -22,3 +24,60 @@ assertThrows(() => dtf.formatRangeToParts(date1, "2019-5-4"), RangeError);
assertThrows(() => dtf.formatRangeToParts(date2, date1), RangeError);
assertDoesNotThrow(() =>dtf.formatRangeToParts(date1, date2));
function partsToString(parts) {
return parts.map(x => x.value).join("");
}
const validSources = ["startRange", "endRange", "shared"];
const validTypes = ["literal", "year", "month", "day", "hour", "minute", "second",
"weekday", "dayPeriod", "timeZoneName", "era"];
function assertParts(parts) {
const str = partsToString(parts);
parts.forEach(function(part) {
// Check the range of part.source
assertTrue(validSources.includes(part.source),
"Invalid source '" + part.source + "' in '" + str + "' for '" + part.value + "'");
// Check the range of part.type
assertTrue(validTypes.includes(part.type),
"Invalid type '" + part.type + "' in '" + str + "' for '" + part.value + "'");
// Check the part.value is a string
assertEquals("string", typeof part.value, "Invalid value for '" + str + "'");
});
}
function verifyFormatRangeToParts(a, b, dtf) {
var parts = dtf.formatRangeToParts(a, b);
// Check each parts fulfill basic property of the parts.
assertParts(parts);
// ensure the 'value' in the parts is the same as the output of
// the formatRange.
assertEquals(dtf.formatRange(a, b), partsToString(parts));
}
verifyFormatRangeToParts(date1, date2, dtf);
verifyFormatRangeToParts(date1, date3, dtf);
verifyFormatRangeToParts(date1, date4, dtf);
verifyFormatRangeToParts(date2, date3, dtf);
verifyFormatRangeToParts(date2, date4, dtf);
verifyFormatRangeToParts(date3, date4, dtf);
dtf = new Intl.DateTimeFormat(["en"], {year: "numeric", month: "short", day: "numeric"});
verifyFormatRangeToParts(date1, date2, dtf);
verifyFormatRangeToParts(date1, date3, dtf);
verifyFormatRangeToParts(date1, date4, dtf);
verifyFormatRangeToParts(date2, date3, dtf);
verifyFormatRangeToParts(date2, date4, dtf);
verifyFormatRangeToParts(date3, date4, dtf);
// Test the sequence of ToNumber and TimeClip
var secondDateAccessed = false;
assertThrows(
() =>
dtf.formatRangeToParts(
new Date(864000000*10000000 + 1), // a date will cause TimeClip return NaN
{ get [Symbol.toPrimitive]() { secondDateAccessed = true; return {}} }),
TypeError);
assertTrue(secondDateAccessed);
......@@ -304,47 +304,47 @@ KNOWN_MAPS = {
("read_only_space", 0x026e1): (98, "EnumCacheMap"),
("read_only_space", 0x02781): (114, "ArrayBoilerplateDescriptionMap"),
("read_only_space", 0x02ad1): (101, "InterceptorInfoMap"),
("read_only_space", 0x050b9): (89, "AccessCheckInfoMap"),
("read_only_space", 0x05109): (90, "AccessorInfoMap"),
("read_only_space", 0x05159): (91, "AccessorPairMap"),
("read_only_space", 0x051a9): (92, "AliasedArgumentsEntryMap"),
("read_only_space", 0x051f9): (93, "AllocationMementoMap"),
("read_only_space", 0x05249): (94, "AsmWasmDataMap"),
("read_only_space", 0x05299): (95, "AsyncGeneratorRequestMap"),
("read_only_space", 0x052e9): (96, "ClassPositionsMap"),
("read_only_space", 0x05339): (97, "DebugInfoMap"),
("read_only_space", 0x05389): (99, "FunctionTemplateInfoMap"),
("read_only_space", 0x053d9): (100, "FunctionTemplateRareDataMap"),
("read_only_space", 0x05429): (102, "InterpreterDataMap"),
("read_only_space", 0x05479): (103, "ModuleInfoEntryMap"),
("read_only_space", 0x054c9): (104, "ModuleMap"),
("read_only_space", 0x05519): (105, "ObjectTemplateInfoMap"),
("read_only_space", 0x05569): (106, "PromiseCapabilityMap"),
("read_only_space", 0x055b9): (107, "PromiseReactionMap"),
("read_only_space", 0x05609): (108, "PrototypeInfoMap"),
("read_only_space", 0x05659): (109, "ScriptMap"),
("read_only_space", 0x056a9): (110, "StackFrameInfoMap"),
("read_only_space", 0x056f9): (111, "StackTraceFrameMap"),
("read_only_space", 0x05749): (112, "Tuple2Map"),
("read_only_space", 0x05799): (113, "Tuple3Map"),
("read_only_space", 0x057e9): (115, "WasmDebugInfoMap"),
("read_only_space", 0x05839): (116, "WasmExceptionTagMap"),
("read_only_space", 0x05889): (117, "WasmExportedFunctionDataMap"),
("read_only_space", 0x058d9): (118, "CallableTaskMap"),
("read_only_space", 0x05929): (119, "CallbackTaskMap"),
("read_only_space", 0x05979): (120, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x059c9): (121, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05a19): (122, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x05a69): (123, "FinalizationGroupCleanupJobTaskMap"),
("read_only_space", 0x05ab9): (124, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05b09): (124, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05b59): (159, "LoadHandler1Map"),
("read_only_space", 0x05ba9): (159, "LoadHandler2Map"),
("read_only_space", 0x05bf9): (159, "LoadHandler3Map"),
("read_only_space", 0x05c49): (167, "StoreHandler0Map"),
("read_only_space", 0x05c99): (167, "StoreHandler1Map"),
("read_only_space", 0x05ce9): (167, "StoreHandler2Map"),
("read_only_space", 0x05d39): (167, "StoreHandler3Map"),
("read_only_space", 0x05109): (89, "AccessCheckInfoMap"),
("read_only_space", 0x05159): (90, "AccessorInfoMap"),
("read_only_space", 0x051a9): (91, "AccessorPairMap"),
("read_only_space", 0x051f9): (92, "AliasedArgumentsEntryMap"),
("read_only_space", 0x05249): (93, "AllocationMementoMap"),
("read_only_space", 0x05299): (94, "AsmWasmDataMap"),
("read_only_space", 0x052e9): (95, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05339): (96, "ClassPositionsMap"),
("read_only_space", 0x05389): (97, "DebugInfoMap"),
("read_only_space", 0x053d9): (99, "FunctionTemplateInfoMap"),
("read_only_space", 0x05429): (100, "FunctionTemplateRareDataMap"),
("read_only_space", 0x05479): (102, "InterpreterDataMap"),
("read_only_space", 0x054c9): (103, "ModuleInfoEntryMap"),
("read_only_space", 0x05519): (104, "ModuleMap"),
("read_only_space", 0x05569): (105, "ObjectTemplateInfoMap"),
("read_only_space", 0x055b9): (106, "PromiseCapabilityMap"),
("read_only_space", 0x05609): (107, "PromiseReactionMap"),
("read_only_space", 0x05659): (108, "PrototypeInfoMap"),
("read_only_space", 0x056a9): (109, "ScriptMap"),
("read_only_space", 0x056f9): (110, "StackFrameInfoMap"),
("read_only_space", 0x05749): (111, "StackTraceFrameMap"),
("read_only_space", 0x05799): (112, "Tuple2Map"),
("read_only_space", 0x057e9): (113, "Tuple3Map"),
("read_only_space", 0x05839): (115, "WasmDebugInfoMap"),
("read_only_space", 0x05889): (116, "WasmExceptionTagMap"),
("read_only_space", 0x058d9): (117, "WasmExportedFunctionDataMap"),
("read_only_space", 0x05929): (118, "CallableTaskMap"),
("read_only_space", 0x05979): (119, "CallbackTaskMap"),
("read_only_space", 0x059c9): (120, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x05a19): (121, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05a69): (122, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x05ab9): (123, "FinalizationGroupCleanupJobTaskMap"),
("read_only_space", 0x05b09): (124, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05b59): (124, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05ba9): (159, "LoadHandler1Map"),
("read_only_space", 0x05bf9): (159, "LoadHandler2Map"),
("read_only_space", 0x05c49): (159, "LoadHandler3Map"),
("read_only_space", 0x05c99): (167, "StoreHandler0Map"),
("read_only_space", 0x05ce9): (167, "StoreHandler1Map"),
("read_only_space", 0x05d39): (167, "StoreHandler2Map"),
("read_only_space", 0x05d89): (167, "StoreHandler3Map"),
("map_space", 0x00139): (1057, "ExternalMap"),
("map_space", 0x00189): (1073, "JSMessageObjectMap"),
}
......
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