[Intl] Implement Intl.ListFromat format() and formatToParts().

Spec: http://tc39.github.io/proposal-intl-list-format/

Design Doc:  go/add-intl.listformat-to-v8

Test: intl/list-format/*

R=gsathya@chromium.org, mvstanton@chromium.org

Bug: v8:7871
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I820c205ca842c228ffe37f7e1648667f30f80bd8
Reviewed-on: https://chromium-review.googlesource.com/1126683
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54991}
This commit is contained in:
Frank Tang 2018-08-08 16:04:11 -07:00 committed by Commit Bot
parent 684d085640
commit b27c3736c1
23 changed files with 892 additions and 145 deletions

View File

@ -4541,6 +4541,10 @@ void Genesis::InitializeGlobal_harmony_intl_list_format() {
SimpleInstallFunction(isolate(), prototype, "resolvedOptions",
Builtins::kListFormatPrototypeResolvedOptions, 0,
false);
SimpleInstallFunction(isolate(), prototype, "format",
Builtins::kListFormatPrototypeFormat, 1, false);
SimpleInstallFunction(isolate(), prototype, "formatToParts",
Builtins::kListFormatPrototypeFormatToParts, 1, false);
}
void Genesis::InitializeGlobal_harmony_locale() {

View File

@ -1340,6 +1340,12 @@ namespace internal {
CPP(ListFormatConstructor) \
/* ecma402 #sec-intl.listformat.prototype.resolvedoptions */ \
CPP(ListFormatPrototypeResolvedOptions) \
/* ecma402 #sec-intl-list-format.prototype.format */ \
TFJ(ListFormatPrototypeFormat, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ecma402 #sec-intl-list-format.prototype.formattoparts */ \
TFJ(ListFormatPrototypeFormatToParts, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ecma402 #sec-intl-locale-constructor */ \
CPP(LocaleConstructor) \
CPP(LocalePrototypeLanguage) \

View File

@ -6,8 +6,13 @@
#error Internationalization is expected to be enabled.
#endif // V8_INTL_SUPPORT
#include "src/builtins/builtins-iterator-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/code-stub-assembler.h"
#include "src/objects-inl.h"
#include "src/objects.h"
#include "src/objects/js-list-format-inl.h"
#include "src/objects/js-list-format.h"
namespace v8 {
namespace internal {
@ -16,6 +21,12 @@ class IntlBuiltinsAssembler : public CodeStubAssembler {
public:
explicit IntlBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
void ListFormatCommon(TNode<Context> context, TNode<Int32T> argc,
Runtime::FunctionId format_func_id,
const char* method_name);
Node* AllocateEmptyJSArray(TNode<Context> context);
};
TF_BUILTIN(StringToLowerCaseIntl, IntlBuiltinsAssembler) {
@ -129,5 +140,73 @@ TF_BUILTIN(StringPrototypeToLowerCaseIntl, IntlBuiltinsAssembler) {
Return(CallBuiltin(Builtins::kStringToLowerCaseIntl, context, string));
}
void IntlBuiltinsAssembler::ListFormatCommon(TNode<Context> context,
TNode<Int32T> argc,
Runtime::FunctionId format_func_id,
const char* method_name) {
CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
// Label has_list(this);
// 1. Let lf be this value.
// 2. If Type(lf) is not Object, throw a TypeError exception.
TNode<Object> receiver = args.GetReceiver();
// 3. If lf does not have an [[InitializedListFormat]] internal slot, throw a
// TypeError exception.
ThrowIfNotInstanceType(context, receiver, JS_INTL_LIST_FORMAT_TYPE,
method_name);
TNode<JSListFormat> list_format = CAST(receiver);
// 4. If list is not provided or is undefined, then
TNode<Object> list = args.GetOptionalArgumentValue(0);
Label has_list(this);
{
GotoIfNot(IsUndefined(list), &has_list);
if (format_func_id == Runtime::kFormatList) {
// a. Return an empty String.
args.PopAndReturn(EmptyStringConstant());
} else {
DCHECK_EQ(format_func_id, Runtime::kFormatListToParts);
// a. Return an empty Array.
args.PopAndReturn(AllocateEmptyJSArray(context));
}
}
BIND(&has_list);
{
// 5. Let x be ? IterableToList(list).
IteratorBuiltinsAssembler iterator_assembler(state());
// TODO(adamk): Consider exposing IterableToList as a buitin and calling
// it from here instead of inlining the operation.
TNode<JSArray> x = iterator_assembler.IterableToList(context, list);
// 6. Return ? FormatList(lf, x).
args.PopAndReturn(CallRuntime(format_func_id, context, list_format, x));
}
}
Node* IntlBuiltinsAssembler::AllocateEmptyJSArray(TNode<Context> context) {
Node* array = CodeStubAssembler::AllocateJSArray(
PACKED_ELEMENTS,
LoadJSArrayElementsMap(PACKED_ELEMENTS, LoadNativeContext(context)),
SmiConstant(0), SmiConstant(0));
StoreObjectFieldNoWriteBarrier(array, JSArray::kElementsOffset,
EmptyFixedArrayConstant());
return array;
}
TF_BUILTIN(ListFormatPrototypeFormat, IntlBuiltinsAssembler) {
ListFormatCommon(
CAST(Parameter(Descriptor::kContext)),
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)),
Runtime::kFormatList, "Intl.ListFormat.prototype.format");
}
TF_BUILTIN(ListFormatPrototypeFormatToParts, IntlBuiltinsAssembler) {
ListFormatCommon(
CAST(Parameter(Descriptor::kContext)),
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)),
Runtime::kFormatListToParts, "Intl.ListFormat.prototype.formatToParts");
}
} // namespace internal
} // namespace v8

View File

@ -7,14 +7,18 @@
#endif // V8_INTL_SUPPORT
#include <cmath>
#include <list>
#include <memory>
#include "src/builtins/builtins-intl.h"
#include "src/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h"
#include "src/date.h"
#include "src/elements.h"
#include "src/intl.h"
#include "src/objects-inl.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-collator-inl.h"
#include "src/objects/js-list-format-inl.h"
#include "src/objects/js-locale-inl.h"
@ -25,6 +29,7 @@
#include "unicode/decimfmt.h"
#include "unicode/fieldpos.h"
#include "unicode/fpositer.h"
#include "unicode/listformatter.h"
#include "unicode/normalizer2.h"
#include "unicode/numfmt.h"
#include "unicode/reldatefmt.h"
@ -220,56 +225,6 @@ Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
}
}
MaybeHandle<JSObject> InnerAddElement(Isolate* isolate, Handle<JSArray> array,
int index,
Handle<String> field_type_string,
const icu::UnicodeString& formatted,
int32_t begin, int32_t end) {
Factory* factory = isolate->factory();
Handle<JSObject> element = factory->NewJSObject(isolate->object_function());
Handle<String> value;
JSObject::AddProperty(isolate, element, factory->type_string(),
field_type_string, NONE);
icu::UnicodeString field(formatted.tempSubStringBetween(begin, end));
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
factory->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(field.getBuffer()),
field.length())),
JSObject);
JSObject::AddProperty(isolate, element, factory->value_string(), value, NONE);
JSObject::AddDataElement(array, index, element, NONE);
return element;
}
Maybe<bool> AddElement(Isolate* isolate, Handle<JSArray> array, int index,
Handle<String> field_type_string,
const icu::UnicodeString& formatted, int32_t begin,
int32_t end) {
RETURN_ON_EXCEPTION_VALUE(
isolate,
InnerAddElement(isolate, array, index, field_type_string, formatted,
begin, end),
Nothing<bool>());
return Just(true);
}
Maybe<bool> AddElement(Isolate* isolate, Handle<JSArray> array, int index,
Handle<String> field_type_string,
const icu::UnicodeString& formatted, int32_t begin,
int32_t end, Handle<String> name, Handle<String> value) {
Handle<JSObject> element;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, element,
InnerAddElement(isolate, array, index, field_type_string, formatted,
begin, end),
Nothing<bool>());
JSObject::AddProperty(isolate, element, name, value, NONE);
return Just(true);
}
bool cmp_NumberFormatSpan(const NumberFormatSpan& a,
const NumberFormatSpan& b) {
// Regions that start earlier should be encountered earlier.
@ -325,10 +280,12 @@ MaybeHandle<Object> FormatNumberToParts(Isolate* isolate,
part.field_id == -1
? isolate->factory()->literal_string()
: IcuNumberFieldIdToNumberType(part.field_id, number, isolate);
Maybe<bool> maybe_added_element =
AddElement(isolate, result, index, field_type_string, formatted,
part.begin_pos, part.end_pos);
MAYBE_RETURN(maybe_added_element, MaybeHandle<Object>());
Handle<String> substring;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, part.begin_pos, part.end_pos),
Object);
Intl::AddElement(isolate, result, index, field_type_string, substring);
++index;
}
JSObject::ValidateElements(*result);
@ -355,30 +312,35 @@ MaybeHandle<Object> FormatDateToParts(Isolate* isolate, icu::DateFormat* format,
int index = 0;
int32_t previous_end_pos = 0;
Handle<String> substring;
while (fp_iter.next(fp)) {
int32_t begin_pos = fp.getBeginIndex();
int32_t end_pos = fp.getEndIndex();
if (previous_end_pos < begin_pos) {
Maybe<bool> maybe_added_element = AddElement(
isolate, result, index, IcuDateFieldIdToDateType(-1, isolate),
formatted, previous_end_pos, begin_pos);
MAYBE_RETURN(maybe_added_element, MaybeHandle<Object>());
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
++index;
}
Maybe<bool> maybe_added_element =
AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate), formatted,
begin_pos, end_pos);
MAYBE_RETURN(maybe_added_element, MaybeHandle<Object>());
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, begin_pos, end_pos), Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate),
substring);
previous_end_pos = end_pos;
++index;
}
if (previous_end_pos < length) {
Maybe<bool> maybe_added_element = AddElement(
isolate, result, index, IcuDateFieldIdToDateType(-1, isolate),
formatted, previous_end_pos, length);
MAYBE_RETURN(maybe_added_element, MaybeHandle<Object>());
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, length), Object);
Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring);
}
JSObject::ValidateElements(*result);
return result;
@ -761,18 +723,16 @@ MaybeHandle<JSArray> GenerateRelativeTimeFormatParts(
Handle<JSArray> array = factory->NewJSArray(0);
int32_t found = formatted.indexOf(integer_part);
Handle<String> substring;
if (found < 0) {
// Cannot find the integer_part in the formatted.
// Return [{'type': 'literal', 'value': formatted}]
Maybe<bool> maybe_added_element =
AddElement(isolate, array,
0, // index
factory->literal_string(), // field_type_string
formatted,
0, // begin
formatted.length()); // end
MAYBE_RETURN(maybe_added_element, MaybeHandle<JSArray>());
ASSIGN_RETURN_ON_EXCEPTION(isolate, substring,
Intl::ToString(isolate, formatted), JSArray);
Intl::AddElement(isolate, array,
0, // index
factory->literal_string(), // field_type_string
substring);
} else {
// Found the formatted integer in the result.
int index = 0;
@ -781,40 +741,39 @@ MaybeHandle<JSArray> GenerateRelativeTimeFormatParts(
// 'type': 'literal',
// 'value': formatted.substring(0, found)})
if (found > 0) {
Maybe<bool> maybe_added_element =
AddElement(isolate, array, index++,
factory->literal_string(), // field_type_string
formatted,
0, // begin
found); // end
MAYBE_RETURN(maybe_added_element, MaybeHandle<JSArray>());
ASSIGN_RETURN_ON_EXCEPTION(isolate, substring,
Intl::ToString(isolate, formatted, 0, found),
JSArray);
Intl::AddElement(isolate, array, index++,
factory->literal_string(), // field_type_string
substring);
}
// array.push({
// 'type': 'integer',
// 'value': formatted.substring(found, found + integer_part.length),
// 'unit': unit})
Maybe<bool> maybe_added_element =
AddElement(isolate, array, index++,
factory->integer_string(), // field_type_string
formatted,
found, // begin
found + integer_part.length(), // end
factory->unit_string(), unit);
MAYBE_RETURN(maybe_added_element, MaybeHandle<JSArray>());
ASSIGN_RETURN_ON_EXCEPTION(isolate, substring,
Intl::ToString(isolate, formatted, found,
found + integer_part.length()),
JSArray);
Intl::AddElement(isolate, array, index++,
factory->integer_string(), // field_type_string
substring, factory->unit_string(), unit);
// array.push({
// 'type': 'literal',
// 'value': formatted.substring(
// found + integer_part.length, formatted.length)})
if (found + integer_part.length() < formatted.length()) {
Maybe<bool> maybe_added_element =
AddElement(isolate, array, index,
factory->literal_string(), // field_type_string
formatted,
found + integer_part.length(), // begin
formatted.length()); // end
MAYBE_RETURN(maybe_added_element, MaybeHandle<JSArray>());
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, found + integer_part.length(),
formatted.length()),
JSArray);
Intl::AddElement(isolate, array, index,
factory->literal_string(), // field_type_string
substring);
}
}
return array;

View File

@ -73,6 +73,7 @@
V(enqueue_string, "enqueue") \
V(entries_string, "entries") \
V(enumerable_string, "enumerable") \
V(element_string, "element") \
V(era_string, "era") \
V(Error_string, "Error") \
V(error_to_string, "[object Error]") \

View File

@ -274,6 +274,7 @@ class ErrorUtils : public AllStatic {
"Derived ArrayBuffer constructor created a buffer which was too small") \
T(ArrayBufferSpeciesThis, \
"ArrayBuffer subclass returned this from species constructor") \
T(ArrayItemNotType, "array %[%] is not type %") \
T(AwaitNotInAsyncFunction, "await is only valid in async function") \
T(AtomicsWaitNotAllowed, "Atomics.wait cannot be called in this context") \
T(BadSortComparisonFunction, \
@ -346,6 +347,7 @@ class ErrorUtils : public AllStatic {
T(LocaleNotEmpty, \
"First argument to Intl.Locale constructor can't be empty or missing") \
T(LocaleBadParameters, "Incorrect locale information provided") \
T(ListFormatBadParameters, "Incorrect ListFormat information provided") \
T(MapperFunctionNonCallable, "flatMap mapper function is not callable") \
T(MethodCalledOnWrongObject, \
"Method % called on a non-object or on a wrong type of object.") \

View File

@ -700,6 +700,59 @@ void V8BreakIterator::DeleteBreakIterator(
GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
}
MaybeHandle<String> Intl::ToString(Isolate* isolate,
const icu::UnicodeString& string) {
return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(string.getBuffer()), string.length()));
}
MaybeHandle<String> Intl::ToString(Isolate* isolate,
const icu::UnicodeString& string,
int32_t begin, int32_t end) {
return Intl::ToString(isolate, string.tempSubStringBetween(begin, end));
}
namespace {
Handle<JSObject> InnerAddElement(Isolate* isolate, Handle<JSArray> array,
int index, Handle<String> field_type_string,
Handle<String> value) {
// let element = $array[$index] = {
// type: $field_type_string,
// value: $value
// }
// return element;
Factory* factory = isolate->factory();
Handle<JSObject> element = factory->NewJSObject(isolate->object_function());
JSObject::AddProperty(isolate, element, factory->type_string(),
field_type_string, NONE);
JSObject::AddProperty(isolate, element, factory->value_string(), value, NONE);
JSObject::AddDataElement(array, index, element, NONE);
return element;
}
} // namespace
void Intl::AddElement(Isolate* isolate, Handle<JSArray> array, int index,
Handle<String> field_type_string, Handle<String> value) {
// Same as $array[$index] = {type: $field_type_string, value: $value};
InnerAddElement(isolate, array, index, field_type_string, value);
}
void Intl::AddElement(Isolate* isolate, Handle<JSArray> array, int index,
Handle<String> field_type_string, Handle<String> value,
Handle<String> additional_property_name,
Handle<String> additional_property_value) {
// Same as $array[$index] = {
// type: $field_type_string, value: $value,
// $additional_property_name: $additional_property_value
// }
Handle<JSObject> element =
InnerAddElement(isolate, array, index, field_type_string, value);
JSObject::AddProperty(isolate, element, additional_property_name,
additional_property_value, NONE);
}
// Build the shortened locale; eg, convert xx_Yyyy_ZZ to xx_ZZ.
bool Intl::RemoveLocaleScriptTag(const std::string& icu_locale,
std::string* locale_less_script) {

View File

@ -24,6 +24,7 @@ class Collator;
class DecimalFormat;
class PluralRules;
class SimpleDateFormat;
class UnicodeString;
}
namespace v8 {
@ -321,6 +322,31 @@ class Intl {
icu::Locale static CreateICULocale(Isolate* isolate,
Handle<String> bcp47_locale_str);
// Helper funciton to convert a UnicodeString to a Handle<String>
V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToString(
Isolate* isolate, const icu::UnicodeString& string);
// Helper function to convert a substring of UnicodeString to a Handle<String>
V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToString(
Isolate* isolate, const icu::UnicodeString& string, int32_t begin,
int32_t end);
// A helper function to implement formatToParts which add element to array as
// $array[$index] = { type: $field_type_string, value: $value }
static void AddElement(Isolate* isolate, Handle<JSArray> array, int index,
Handle<String> field_type_string,
Handle<String> value);
// A helper function to implement formatToParts which add element to array as
// $array[$index] = {
// type: $field_type_string, value: $value,
// $additional_property_name: $additional_property_value
// }
static void AddElement(Isolate* isolate, Handle<JSArray> array, int index,
Handle<String> field_type_string, Handle<String> value,
Handle<String> additional_property_name,
Handle<String> additional_property_value);
};
} // namespace internal

View File

@ -11,10 +11,12 @@
#include <memory>
#include <vector>
#include "src/elements.h"
#include "src/heap/factory.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-list-format-inl.h"
#include "src/objects/managed.h"
#include "unicode/listformatter.h"
@ -246,5 +248,154 @@ Handle<String> JSListFormat::TypeAsString() const {
}
}
namespace {
// TODO(ftang) remove the following hack after icu::ListFormat support
// FieldPosition.
// This is a temporary workaround until icu::ListFormat support FieldPosition
// It is inefficient and won't work correctly on the edge case that the input
// contains fraction of the list pattern.
// For example the following under English will mark the "an" incorrectly
// since the formatted is "a, b, and an".
// listFormat.formatToParts(["a", "b", "an"])
// https://ssl.icu-project.org/trac/ticket/13754
MaybeHandle<JSArray> GenerateListFormatParts(
Isolate* isolate, const icu::UnicodeString& formatted,
const icu::UnicodeString items[], int length) {
Factory* factory = isolate->factory();
int estimate_size = length * 2 + 1;
Handle<JSArray> array = factory->NewJSArray(estimate_size);
int index = 0;
int last_pos = 0;
for (int i = 0; i < length; i++) {
int found = formatted.indexOf(items[i], last_pos);
DCHECK_GE(found, 0);
if (found > last_pos) {
Handle<String> substring;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, last_pos, found), JSArray);
Intl::AddElement(isolate, array, index++, factory->literal_string(),
substring);
}
last_pos = found + items[i].length();
Handle<String> substring;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring, Intl::ToString(isolate, formatted, found, last_pos),
JSArray);
Intl::AddElement(isolate, array, index++, factory->element_string(),
substring);
}
if (last_pos < formatted.length()) {
Handle<String> substring;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring,
Intl::ToString(isolate, formatted, last_pos, formatted.length()),
JSArray);
Intl::AddElement(isolate, array, index++, factory->literal_string(),
substring);
}
return array;
}
// Extract String from JSArray into array of UnicodeString
Maybe<bool> ToUnicodeStringArray(Isolate* isolate, Handle<JSArray> array,
icu::UnicodeString items[], uint32_t length) {
Factory* factory = isolate->factory();
// In general, ElementsAccessor::Get actually isn't guaranteed to give us the
// elements in order. But given that it was created by a builtin we control,
// it shouldn't be possible for it to be problematic. Add DCHECK to ensure
// that.
DCHECK(array->HasFastPackedElements());
auto* accessor = array->GetElementsAccessor();
DCHECK(length == accessor->NumberOfElements(*array));
// ecma402 #sec-createpartsfromlist
// 2. If list contains any element value such that Type(value) is not String,
// throw a TypeError exception.
//
// Per spec it looks like we're supposed to throw a TypeError exception if the
// item isn't already a string, rather than coercing to a string. Moreover,
// the way the spec's written it looks like we're supposed to run through the
// whole list to check that they're all strings before going further.
for (uint32_t i = 0; i < length; i++) {
Handle<Object> item = accessor->Get(array, i);
DCHECK(!item.is_null());
if (!item->IsString()) {
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewTypeError(MessageTemplate::kArrayItemNotType,
factory->NewStringFromStaticChars("list"),
factory->NewNumber(i),
factory->NewStringFromStaticChars("String")),
Nothing<bool>());
}
}
for (uint32_t i = 0; i < length; i++) {
Handle<String> string = Handle<String>::cast(accessor->Get(array, i));
DisallowHeapAllocation no_gc;
string = String::Flatten(isolate, string);
std::unique_ptr<uc16[]> sap;
items[i] =
icu::UnicodeString(GetUCharBufferFromFlat(string->GetFlatContent(),
&sap, string->length()),
string->length());
}
return Just(true);
}
} // namespace
Maybe<bool> FormatListCommon(Isolate* isolate,
Handle<JSListFormat> format_holder,
Handle<JSArray> list,
icu::UnicodeString& formatted, uint32_t* length,
std::unique_ptr<icu::UnicodeString[]>& array) {
DCHECK(!list->IsUndefined());
icu::ListFormatter* formatter =
JSListFormat::UnpackFormatter(isolate, format_holder);
CHECK_NOT_NULL(formatter);
*length = list->GetElementsAccessor()->NumberOfElements(*list);
array.reset(new icu::UnicodeString[*length]);
// ecma402 #sec-createpartsfromlist
// 2. If list contains any element value such that Type(value) is not String,
// throw a TypeError exception.
MAYBE_RETURN(ToUnicodeStringArray(isolate, list, array.get(), *length),
Nothing<bool>());
UErrorCode status = U_ZERO_ERROR;
formatter->format(array.get(), *length, formatted, status);
DCHECK(U_SUCCESS(status));
return Just(true);
}
// ecma402 #sec-formatlist
MaybeHandle<String> JSListFormat::FormatList(Isolate* isolate,
Handle<JSListFormat> format_holder,
Handle<JSArray> list) {
icu::UnicodeString formatted;
uint32_t length;
std::unique_ptr<icu::UnicodeString[]> array;
MAYBE_RETURN(
FormatListCommon(isolate, format_holder, list, formatted, &length, array),
Handle<String>());
return Intl::ToString(isolate, formatted);
}
// ecma42 #sec-formatlisttoparts
MaybeHandle<JSArray> JSListFormat::FormatListToParts(
Isolate* isolate, Handle<JSListFormat> format_holder,
Handle<JSArray> list) {
icu::UnicodeString formatted;
uint32_t length;
std::unique_ptr<icu::UnicodeString[]> array;
MAYBE_RETURN(
FormatListCommon(isolate, format_holder, list, formatted, &length, array),
Handle<JSArray>());
return GenerateListFormatParts(isolate, formatted, array.get(), length);
}
} // namespace internal
} // namespace v8

View File

@ -39,6 +39,16 @@ class JSListFormat : public JSObject {
static icu::ListFormatter* UnpackFormatter(
Isolate* isolate, Handle<JSListFormat> list_format_holder);
// ecma402 #sec-formatlist
V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatList(
Isolate* isolate, Handle<JSListFormat> format_holder,
Handle<JSArray> list);
// ecma42 #sec-formatlisttoparts
V8_WARN_UNUSED_RESULT static MaybeHandle<JSArray> FormatListToParts(
Isolate* isolate, Handle<JSListFormat> format_holder,
Handle<JSArray> list);
Handle<String> StyleAsString() const;
Handle<String> TypeAsString() const;

View File

@ -20,7 +20,10 @@
#include "src/messages.h"
#include "src/objects/intl-objects-inl.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-collator-inl.h"
#include "src/objects/js-list-format-inl.h"
#include "src/objects/js-list-format.h"
#include "src/objects/js-plural-rules-inl.h"
#include "src/objects/managed.h"
#include "src/runtime/runtime-utils.h"
@ -54,6 +57,26 @@
namespace v8 {
namespace internal {
// ecma402 #sec-formatlist
RUNTIME_FUNCTION(Runtime_FormatList) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSListFormat, list_format, 0);
CONVERT_ARG_HANDLE_CHECKED(JSArray, list, 1);
RETURN_RESULT_OR_FAILURE(
isolate, JSListFormat::FormatList(isolate, list_format, list));
}
// ecma402 #sec-formatlisttoparts
RUNTIME_FUNCTION(Runtime_FormatListToParts) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSListFormat, list_format, 0);
CONVERT_ARG_HANDLE_CHECKED(JSArray, list, 1);
RETURN_RESULT_OR_FAILURE(
isolate, JSListFormat::FormatListToParts(isolate, list_format, list));
}
RUNTIME_FUNCTION(Runtime_GetNumberOption) {
HandleScope scope(isolate);
DCHECK_EQ(5, args.length());

View File

@ -218,6 +218,8 @@ namespace internal {
F(CurrencyDigits, 1, 1) \
F(DateCacheVersion, 0, 1) \
F(DefaultNumberOption, 5, 1) \
F(FormatList, 2, 1) \
F(FormatListToParts, 2, 1) \
F(GetDefaultICULocale, 0, 1) \
F(GetNumberOption, 5, 1) \
F(InternalCompare, 3, 1) \

View File

@ -362,7 +362,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(18),
B(LdaConstant), U8(14),
B(Star), R(19),

View File

@ -123,7 +123,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(19),
B(LdaConstant), U8(11),
B(Star), R(20),
@ -377,7 +377,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(19),
B(LdaConstant), U8(11),
B(Star), R(20),
@ -653,7 +653,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(19),
B(LdaConstant), U8(11),
B(Star), R(20),
@ -885,7 +885,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(17),
B(LdaConstant), U8(9),
B(Star), R(18),

View File

@ -85,7 +85,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(12),
B(LdaConstant), U8(7),
B(Star), R(13),
@ -217,7 +217,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(13),
B(LdaConstant), U8(7),
B(Star), R(14),
@ -361,7 +361,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(12),
B(LdaConstant), U8(7),
B(Star), R(13),
@ -495,7 +495,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(11),
B(LdaConstant), U8(9),
B(Star), R(12),

View File

@ -89,7 +89,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(14),
B(LdaConstant), U8(6),
B(Star), R(15),
@ -256,7 +256,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(14),
B(LdaConstant), U8(11),
B(Star), R(15),
@ -401,7 +401,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
@ -495,7 +495,7 @@ bytecodes: [
B(JumpIfUndefined), U8(6),
B(Ldar), R(6),
B(JumpIfNotNull), U8(16),
B(LdaSmi), I8(79),
B(LdaSmi), I8(81),
B(Star), R(18),
B(LdaConstant), U8(4),
B(Star), R(19),
@ -550,7 +550,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(17),
B(LdaConstant), U8(8),
B(Star), R(18),
@ -697,7 +697,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(15),
B(LdaConstant), U8(9),
B(Star), R(16),
@ -859,7 +859,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(14),
B(LdaConstant), U8(12),
B(Star), R(15),
@ -1007,7 +1007,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(20),
B(LdaConstant), U8(6),
B(Star), R(21),
@ -1218,7 +1218,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(19),
B(LdaConstant), U8(7),
B(Star), R(20),

View File

@ -203,7 +203,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(151),
B(Wide), B(LdaSmi), I16(153),
B(Star), R(14),
B(LdaConstant), U8(13),
B(Star), R(15),

View File

@ -229,7 +229,7 @@ bytecodes: [
B(JumpIfUndefined), U8(6),
B(Ldar), R(3),
B(JumpIfNotNull), U8(16),
B(LdaSmi), I8(79),
B(LdaSmi), I8(81),
B(Star), R(4),
B(LdaConstant), U8(1),
B(Star), R(5),

View File

@ -0,0 +1,119 @@
// Copyright 2018 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-list-format
// The following test are not part of the comformance. Just some output in
// English to verify the format does return something reasonable for English.
// It may be changed when we update the CLDR data.
// NOTE: These are UNSPECIFIED behavior in
// http://tc39.github.io/proposal-intl-list-time/
let enLongConjunction = new Intl.ListFormat(
["en"], {style: "long", type: 'conjunction'});
assertEquals('', enLongConjunction.format());
assertEquals('', enLongConjunction.format([]));
assertEquals('a', enLongConjunction.format(['a']));
assertEquals('b', enLongConjunction.format(['b']));
assertEquals('a and b', enLongConjunction.format(['a', 'b']));
assertEquals('a, b, and c', enLongConjunction.format(['a', 'b', 'c']));
assertEquals('a, b, c, and d', enLongConjunction.format(['a', 'b', 'c', 'd']));
assertEquals('a, b, c, d, and and', enLongConjunction.format(['a', 'b', 'c', 'd', 'and']));
let enLongDisjunction = new Intl.ListFormat(
["en"], {style: "long", type: 'disjunction'});
assertEquals('', enLongDisjunction.format());
assertEquals('', enLongDisjunction.format([]));
assertEquals('a', enLongDisjunction.format(['a']));
assertEquals('b', enLongDisjunction.format(['b']));
assertEquals('a or b', enLongDisjunction.format(['a', 'b']));
assertEquals('a, b, or c', enLongDisjunction.format(['a', 'b', 'c']));
assertEquals('a, b, c, or d', enLongDisjunction.format(['a', 'b', 'c', 'd']));
assertEquals('a, b, c, d, or or', enLongDisjunction.format(['a', 'b', 'c', 'd', 'or']));
let enLongUnit = new Intl.ListFormat(
["en"], {style: "long", type: 'unit'});
assertEquals('', enLongUnit.format());
assertEquals('', enLongUnit.format([]));
assertEquals('a', enLongUnit.format(['a']));
assertEquals('b', enLongUnit.format(['b']));
assertEquals('a, b', enLongUnit.format(['a', 'b']));
assertEquals('a, b, c', enLongUnit.format(['a', 'b', 'c']));
assertEquals('a, b, c, d', enLongUnit.format(['a', 'b', 'c', 'd']));
assertEquals('a, b, c, d, or', enLongUnit.format(['a', 'b', 'c', 'd', 'or']));
let enShortConjunction = new Intl.ListFormat(
["en"], {style: "short", type: 'conjunction'});
assertEquals('', enShortConjunction.format());
assertEquals('', enShortConjunction.format([]));
assertEquals('a', enShortConjunction.format(['a']));
assertEquals('b', enShortConjunction.format(['b']));
assertEquals('a and b', enShortConjunction.format(['a', 'b']));
assertEquals('a, b, and c', enShortConjunction.format(['a', 'b', 'c']));
assertEquals('a, b, c, and d', enShortConjunction.format(['a', 'b', 'c', 'd']));
assertEquals('a, b, c, d, and and', enShortConjunction.format(['a', 'b', 'c', 'd', 'and']));
let enShortDisjunction = new Intl.ListFormat(
["en"], {style: "short", type: 'disjunction'});
assertEquals('', enShortDisjunction.format());
assertEquals('', enShortDisjunction.format([]));
assertEquals('a', enShortDisjunction.format(['a']));
assertEquals('b', enShortDisjunction.format(['b']));
assertEquals('a or b', enShortDisjunction.format(['a', 'b']));
assertEquals('a, b, or c', enShortDisjunction.format(['a', 'b', 'c']));
assertEquals('a, b, c, or d', enShortDisjunction.format(['a', 'b', 'c', 'd']));
assertEquals('a, b, c, d, or or', enShortDisjunction.format(['a', 'b', 'c', 'd', 'or']));
let enShortUnit = new Intl.ListFormat(
["en"], {style: "short", type: 'unit'});
assertEquals('', enShortUnit.format());
assertEquals('', enShortUnit.format([]));
assertEquals('a', enShortUnit.format(['a']));
assertEquals('b', enShortUnit.format(['b']));
assertEquals('a, b', enShortUnit.format(['a', 'b']));
assertEquals('a, b, c', enShortUnit.format(['a', 'b', 'c']));
assertEquals('a, b, c, d', enShortUnit.format(['a', 'b', 'c', 'd']));
assertEquals('a, b, c, d, or', enShortUnit.format(['a', 'b', 'c', 'd', 'or']));
let enNarrowConjunction = new Intl.ListFormat(
["en"], {style: "narrow", type: 'conjunction'});
assertEquals('', enNarrowConjunction.format());
assertEquals('', enNarrowConjunction.format([]));
assertEquals('a', enNarrowConjunction.format(['a']));
assertEquals('b', enNarrowConjunction.format(['b']));
assertEquals('a and b', enNarrowConjunction.format(['a', 'b']));
assertEquals('a, b, and c', enNarrowConjunction.format(['a', 'b', 'c']));
assertEquals('a, b, c, and d', enNarrowConjunction.format(['a', 'b', 'c', 'd']));
assertEquals('a, b, c, d, and and', enNarrowConjunction.format(['a', 'b', 'c', 'd', 'and']));
let enNarrowDisjunction = new Intl.ListFormat(
["en"], {style: "narrow", type: 'disjunction'});
assertEquals('', enNarrowDisjunction.format());
assertEquals('', enNarrowDisjunction.format([]));
assertEquals('a', enNarrowDisjunction.format(['a']));
assertEquals('b', enNarrowDisjunction.format(['b']));
assertEquals('a or b', enNarrowDisjunction.format(['a', 'b']));
assertEquals('a, b, or c', enNarrowDisjunction.format(['a', 'b', 'c']));
assertEquals('a, b, c, or d', enNarrowDisjunction.format(['a', 'b', 'c', 'd']));
assertEquals('a, b, c, d, or or', enNarrowDisjunction.format(['a', 'b', 'c', 'd', 'or']));
let enNarrowUnit = new Intl.ListFormat(
["en"], {style: "narrow", type: 'unit'});
assertEquals('', enNarrowUnit.format());
assertEquals('', enNarrowUnit.format([]));
assertEquals('a', enNarrowUnit.format(['a']));
assertEquals('b', enNarrowUnit.format(['b']));
assertEquals('a b', enNarrowUnit.format(['a', 'b']));
assertEquals('a b c', enNarrowUnit.format(['a', 'b', 'c']));
assertEquals('a b c d', enNarrowUnit.format(['a', 'b', 'c', 'd']));
assertEquals('a b c d or', enNarrowUnit.format(['a', 'b', 'c', 'd', 'or']));

View File

@ -0,0 +1,92 @@
// Copyright 2018 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-list-format
function assertListFormat(listFormat, input) {
var result;
try {
result = listFormat.formatToParts(input);
} catch (e) {
fail('should not throw exception ' + e);
}
assertTrue(Array.isArray(result));
if (input) {
assertTrue(result.length >= input.length * 2 - 1);
for (var i = 0, j = 0; i < result.length; i++) {
assertEquals('string', typeof result[i].value);
assertEquals('string', typeof result[i].type);
assertTrue(result[i].type == 'literal' || result[i].type == 'element');
if (result[i].type == 'element') {
assertEquals(String(input[j++]), result[i].value);
if (i - 1 >= 0) {
assertEquals('literal', result[i - 1].type);
}
if (i + 1 < result.length) {
assertEquals('literal', result[i + 1].type);
}
}
if (result[i].type == 'literal') {
assertTrue(result[i].value.length > 0);
if (i - 1 >= 0) {
assertEquals('element', result[i - 1].type);
}
if (i + 1 < result.length) {
assertEquals('element', result[i + 1].type);
}
}
}
}
}
function testFormatter(listFormat) {
assertListFormat(listFormat, []);
assertListFormat(listFormat, undefined);
assertListFormat(listFormat, ['1']);
assertListFormat(listFormat, ['a']);
assertListFormat(listFormat, ['1', 'b']);
assertListFormat(listFormat, ['1', 'b', '3']);
assertListFormat(listFormat, ['a', 'b']);
assertListFormat(listFormat, ['a', 'b', 'c']);
assertListFormat(listFormat, ['a', 'b', 'c', 'd']);
assertListFormat(listFormat, ['作者', '譚永鋒', '1', (new Date()).toString()]);
assertListFormat(listFormat, ['作者', '譚永鋒', '1', 'b', '3']);
// Tricky cases
assertListFormat(listFormat, [' ', 'b', 'c', 'and']);
assertListFormat(listFormat, [' ', 'b', 'c', 'or']);
assertListFormat(listFormat, ['and']);
assertListFormat(listFormat, ['or']);
assertThrows(() => listFormat.formatToParts(null), TypeError);
assertThrows(() => listFormat.formatToParts([new Date()]), TypeError);
assertThrows(() => listFormat.formatToParts([1]), TypeError);
assertThrows(() => listFormat.formatToParts([1, 'b']), TypeError);
assertThrows(() => listFormat.formatToParts([1, 'b', 3]), TypeError);
assertThrows(() => listFormat.formatToParts([[3, 4]]), TypeError);
assertThrows(() => listFormat.formatToParts([undefined, 'world']), TypeError);
assertThrows(() => listFormat.formatToParts(['hello', undefined]), TypeError);
assertThrows(() => listFormat.formatToParts([undefined]), TypeError);
assertThrows(() => listFormat.formatToParts([null, 'world']), TypeError);
assertThrows(() => listFormat.formatToParts(['hello', null]), TypeError);
assertThrows(() => listFormat.formatToParts([null]), TypeError);
}
testFormatter(new Intl.ListFormat());
testFormatter(new Intl.ListFormat(["en"]));
testFormatter(new Intl.ListFormat(["en"], {style: 'long'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'short'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'narrow'}));
testFormatter(new Intl.ListFormat(["en"], {type: 'conjunction'}));
testFormatter(new Intl.ListFormat(["en"], {type: 'disjunction'}));
testFormatter(new Intl.ListFormat(["en"], {type: 'unit'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'long', type: 'conjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'short', type: 'conjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'narrow', type: 'conjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'long', type: 'disjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'short', type: 'disjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'narrow', type: 'disjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'long', type: 'unit'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'short', type: 'unit'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'narrow', type: 'unit'}));

View File

@ -0,0 +1,63 @@
// Copyright 2018 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-list-format
function assertListFormat(listFormat, input) {
try {
let result = listFormat.format(input);
assertEquals('string', typeof result);
if (input) {
for (var i = 0; i < input.length; i++) {
assertTrue(result.indexOf(input[i]) >= 0);
}
}
} catch (e) {
fail('should not throw exception ' + e);
}
}
function testFormatter(listFormat) {
assertListFormat(listFormat, []);
assertListFormat(listFormat, undefined);
assertListFormat(listFormat, ['1']);
assertListFormat(listFormat, ['a']);
assertListFormat(listFormat, ['1', 'b']);
assertListFormat(listFormat, ['1', 'b', '3']);
assertListFormat(listFormat, ['a', 'b']);
assertListFormat(listFormat, ['a', 'b', 'c']);
assertListFormat(listFormat, ['a', 'b', 'c', 'd']);
assertListFormat(listFormat, ['作者', '譚永鋒', '1', (new Date()).toString()]);
assertListFormat(listFormat, ['作者', '譚永鋒', '1', 'b', '3']);
assertThrows(() => listFormat.format(null), TypeError);
assertThrows(() => listFormat.format([new Date()]), TypeError);
assertThrows(() => listFormat.format([1]), TypeError);
assertThrows(() => listFormat.format([1, 'b']), TypeError);
assertThrows(() => listFormat.format([1, 'b', 3]), TypeError);
assertThrows(() => listFormat.format([[3, 4]]), TypeError);
assertThrows(() => listFormat.format([undefined, 'world']), TypeError);
assertThrows(() => listFormat.format(['hello', undefined]), TypeError);
assertThrows(() => listFormat.format([undefined]), TypeError);
assertThrows(() => listFormat.format([null, 'world']), TypeError);
assertThrows(() => listFormat.format(['hello', null]), TypeError);
assertThrows(() => listFormat.format([null]), TypeError);
}
testFormatter(new Intl.ListFormat());
testFormatter(new Intl.ListFormat(["en"]));
testFormatter(new Intl.ListFormat(["en"], {style: 'long'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'short'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'narrow'}));
testFormatter(new Intl.ListFormat(["en"], {type: 'conjunction'}));
testFormatter(new Intl.ListFormat(["en"], {type: 'disjunction'}));
testFormatter(new Intl.ListFormat(["en"], {type: 'unit'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'long', type: 'conjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'short', type: 'conjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'narrow', type: 'conjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'long', type: 'disjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'short', type: 'disjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'narrow', type: 'disjunction'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'long', type: 'unit'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'short', type: 'unit'}));
testFormatter(new Intl.ListFormat(["en"], {style: 'narrow', type: 'unit'}));

View File

@ -0,0 +1,157 @@
// Copyright 2018 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-list-format
// The following test are not part of the comformance. Just some output in
// Chinese to verify the format does return something reasonable for Chinese.
// It may be changed when we update the CLDR data.
// NOTE: These are UNSPECIFIED behavior in
// http://tc39.github.io/proposal-intl-list-time/
let zhLongConjunction = new Intl.ListFormat(
["zh"], {style: "long", type: 'conjunction'});
var parts;
parts = zhLongConjunction.formatToParts();
assertEquals(0, parts.length);
parts = zhLongConjunction.formatToParts([]);
assertEquals(0, parts.length);
parts = zhLongConjunction.formatToParts(['譚永鋒']);
assertEquals(1, parts.length);
assertEquals('譚永鋒', parts[0].value);
assertEquals('element', parts[0].type);
parts = zhLongConjunction.formatToParts(['譚永鋒', '劉新宇']);
assertEquals(3, parts.length);
assertEquals('譚永鋒', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('和', parts[1].value);
assertEquals('literal', parts[1].type);
assertEquals('劉新宇', parts[2].value);
assertEquals('element', parts[2].type);
parts = zhLongConjunction.formatToParts(['黄子容', '譚永鋒', '劉新宇']);
assertEquals(5, parts.length);
assertEquals('黄子容', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('、', parts[1].value);
assertEquals('literal', parts[1].type);
assertEquals('譚永鋒', parts[2].value);
assertEquals('element', parts[2].type);
assertEquals('和', parts[3].value);
assertEquals('literal', parts[3].type);
assertEquals('劉新宇', parts[4].value);
assertEquals('element', parts[4].type);
parts = zhLongConjunction.formatToParts(['黄子容', '譚永鋒', '劉新宇', '朱君毅']);
assertEquals(7, parts.length);
assertEquals('黄子容', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('、', parts[1].value);
assertEquals('literal', parts[1].type);
assertEquals('譚永鋒', parts[2].value);
assertEquals('element', parts[2].type);
assertEquals('、', parts[3].value);
assertEquals('literal', parts[3].type);
assertEquals('劉新宇', parts[4].value);
assertEquals('element', parts[4].type);
assertEquals('和', parts[5].value);
assertEquals('literal', parts[5].type);
assertEquals('朱君毅', parts[6].value);
assertEquals('element', parts[6].type);
let zhShortDisjunction = new Intl.ListFormat(
["zh"], {style: "short", type: 'disjunction'});
parts = zhShortDisjunction.formatToParts();
assertEquals(0, parts.length);
parts = zhShortDisjunction.formatToParts([]);
assertEquals(0, parts.length);
parts = zhShortDisjunction.formatToParts(['譚永鋒']);
assertEquals(1, parts.length);
assertEquals('譚永鋒', parts[0].value);
assertEquals('element', parts[0].type);
parts = zhShortDisjunction.formatToParts(['譚永鋒', '劉新宇']);
assertEquals(3, parts.length);
assertEquals('譚永鋒', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('或', parts[1].value);
assertEquals('literal', parts[1].type);
assertEquals('劉新宇', parts[2].value);
assertEquals('element', parts[2].type);
parts = zhShortDisjunction.formatToParts(['黄子容', '譚永鋒', '劉新宇']);
assertEquals(5, parts.length);
assertEquals('黄子容', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('、', parts[1].value);
assertEquals('literal', parts[1].type);
assertEquals('譚永鋒', parts[2].value);
assertEquals('element', parts[2].type);
assertEquals('或', parts[3].value);
assertEquals('literal', parts[3].type);
assertEquals('劉新宇', parts[4].value);
assertEquals('element', parts[4].type);
parts = zhShortDisjunction.formatToParts(['黄子容', '譚永鋒', '劉新宇', '朱君毅']);
assertEquals(7, parts.length);
assertEquals('黄子容', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('、', parts[1].value);
assertEquals('literal', parts[1].type);
assertEquals('譚永鋒', parts[2].value);
assertEquals('element', parts[2].type);
assertEquals('、', parts[3].value);
assertEquals('literal', parts[3].type);
assertEquals('劉新宇', parts[4].value);
assertEquals('element', parts[4].type);
assertEquals('或', parts[5].value);
assertEquals('literal', parts[5].type);
assertEquals('朱君毅', parts[6].value);
let zhNarrowUnit = new Intl.ListFormat(
["zh"], {style: "narrow", type: 'unit'});
parts = zhNarrowUnit.formatToParts();
assertEquals(0, parts.length);
parts = zhNarrowUnit.formatToParts([]);
assertEquals(0, parts.length);
parts = zhNarrowUnit.formatToParts(['3英哩']);
assertEquals(1, parts.length);
assertEquals('3英哩', parts[0].value);
assertEquals('element', parts[0].type);
parts = zhNarrowUnit.formatToParts(['3英哩', '4碼']);
assertEquals(2, parts.length);
assertEquals('3英哩', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('4碼', parts[1].value);
assertEquals('element', parts[1].type);
parts = zhNarrowUnit.formatToParts(['3英哩', '4碼', '5英尺']);
assertEquals(3, parts.length);
assertEquals('3英哩', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('4碼', parts[1].value);
assertEquals('element', parts[1].type);
assertEquals('5英尺', parts[2].value);
assertEquals('element', parts[2].type);
parts = zhNarrowUnit.formatToParts(['3英哩', '4碼', '5英尺','7英吋']);
assertEquals(4, parts.length);
assertEquals('3英哩', parts[0].value);
assertEquals('element', parts[0].type);
assertEquals('4碼', parts[1].value);
assertEquals('element', parts[1].type);
assertEquals('5英尺', parts[2].value);
assertEquals('element', parts[2].type);
assertEquals('7英吋', parts[3].value);
assertEquals('element', parts[3].type);

View File

@ -285,33 +285,33 @@ KNOWN_MAPS = {
("RO_SPACE", 0x047c1): (171, "Tuple2Map"),
("RO_SPACE", 0x04af9): (161, "InterceptorInfoMap"),
("RO_SPACE", 0x04bf1): (169, "ScriptMap"),
("RO_SPACE", 0x09ae1): (154, "AccessorInfoMap"),
("RO_SPACE", 0x09b31): (153, "AccessCheckInfoMap"),
("RO_SPACE", 0x09b81): (155, "AccessorPairMap"),
("RO_SPACE", 0x09bd1): (156, "AliasedArgumentsEntryMap"),
("RO_SPACE", 0x09c21): (157, "AllocationMementoMap"),
("RO_SPACE", 0x09c71): (158, "AsyncGeneratorRequestMap"),
("RO_SPACE", 0x09cc1): (159, "DebugInfoMap"),
("RO_SPACE", 0x09d11): (160, "FunctionTemplateInfoMap"),
("RO_SPACE", 0x09d61): (162, "InterpreterDataMap"),
("RO_SPACE", 0x09db1): (163, "ModuleInfoEntryMap"),
("RO_SPACE", 0x09e01): (164, "ModuleMap"),
("RO_SPACE", 0x09e51): (165, "ObjectTemplateInfoMap"),
("RO_SPACE", 0x09ea1): (166, "PromiseCapabilityMap"),
("RO_SPACE", 0x09ef1): (167, "PromiseReactionMap"),
("RO_SPACE", 0x09f41): (168, "PrototypeInfoMap"),
("RO_SPACE", 0x09f91): (170, "StackFrameInfoMap"),
("RO_SPACE", 0x09fe1): (172, "Tuple3Map"),
("RO_SPACE", 0x0a031): (173, "ArrayBoilerplateDescriptionMap"),
("RO_SPACE", 0x0a081): (174, "WasmDebugInfoMap"),
("RO_SPACE", 0x0a0d1): (175, "WasmExportedFunctionDataMap"),
("RO_SPACE", 0x0a121): (176, "CallableTaskMap"),
("RO_SPACE", 0x0a171): (177, "CallbackTaskMap"),
("RO_SPACE", 0x0a1c1): (178, "PromiseFulfillReactionJobTaskMap"),
("RO_SPACE", 0x0a211): (179, "PromiseRejectReactionJobTaskMap"),
("RO_SPACE", 0x0a261): (180, "PromiseResolveThenableJobTaskMap"),
("RO_SPACE", 0x0a2b1): (181, "AllocationSiteMap"),
("RO_SPACE", 0x0a301): (181, "AllocationSiteMap"),
("RO_SPACE", 0x09b79): (154, "AccessorInfoMap"),
("RO_SPACE", 0x09bc9): (153, "AccessCheckInfoMap"),
("RO_SPACE", 0x09c19): (155, "AccessorPairMap"),
("RO_SPACE", 0x09c69): (156, "AliasedArgumentsEntryMap"),
("RO_SPACE", 0x09cb9): (157, "AllocationMementoMap"),
("RO_SPACE", 0x09d09): (158, "AsyncGeneratorRequestMap"),
("RO_SPACE", 0x09d59): (159, "DebugInfoMap"),
("RO_SPACE", 0x09da9): (160, "FunctionTemplateInfoMap"),
("RO_SPACE", 0x09df9): (162, "InterpreterDataMap"),
("RO_SPACE", 0x09e49): (163, "ModuleInfoEntryMap"),
("RO_SPACE", 0x09e99): (164, "ModuleMap"),
("RO_SPACE", 0x09ee9): (165, "ObjectTemplateInfoMap"),
("RO_SPACE", 0x09f39): (166, "PromiseCapabilityMap"),
("RO_SPACE", 0x09f89): (167, "PromiseReactionMap"),
("RO_SPACE", 0x09fd9): (168, "PrototypeInfoMap"),
("RO_SPACE", 0x0a029): (170, "StackFrameInfoMap"),
("RO_SPACE", 0x0a079): (172, "Tuple3Map"),
("RO_SPACE", 0x0a0c9): (173, "ArrayBoilerplateDescriptionMap"),
("RO_SPACE", 0x0a119): (174, "WasmDebugInfoMap"),
("RO_SPACE", 0x0a169): (175, "WasmExportedFunctionDataMap"),
("RO_SPACE", 0x0a1b9): (176, "CallableTaskMap"),
("RO_SPACE", 0x0a209): (177, "CallbackTaskMap"),
("RO_SPACE", 0x0a259): (178, "PromiseFulfillReactionJobTaskMap"),
("RO_SPACE", 0x0a2a9): (179, "PromiseRejectReactionJobTaskMap"),
("RO_SPACE", 0x0a2f9): (180, "PromiseResolveThenableJobTaskMap"),
("RO_SPACE", 0x0a349): (181, "AllocationSiteMap"),
("RO_SPACE", 0x0a399): (181, "AllocationSiteMap"),
("MAP_SPACE", 0x02201): (1057, "ExternalMap"),
("MAP_SPACE", 0x02251): (1072, "JSMessageObjectMap"),
}