[Intl] Implement Intl.ListFormat constructor

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: I0dfb91b7806007e4f02f3b0438c30528c8143081
Reviewed-on: https://chromium-review.googlesource.com/1124343
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: Daniel Ehrenberg <littledan@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54668}
This commit is contained in:
Frank Tang 2018-07-24 14:07:50 -07:00 committed by Commit Bot
parent 58b98c0e98
commit ac8b5b919e
25 changed files with 682 additions and 49 deletions

View File

@ -2150,6 +2150,9 @@ v8_source_set("v8_base") {
"src/objects/js-collection.h",
"src/objects/js-generator-inl.h",
"src/objects/js-generator.h",
"src/objects/js-list-format-inl.h",
"src/objects/js-list-format.cc",
"src/objects/js-list-format.h",
"src/objects/js-locale-inl.h",
"src/objects/js-locale.cc",
"src/objects/js-locale.h",
@ -2844,6 +2847,9 @@ v8_source_set("v8_base") {
"src/objects/intl-objects-inl.h",
"src/objects/intl-objects.cc",
"src/objects/intl-objects.h",
"src/objects/js-list-format-inl.h",
"src/objects/js-list-format.cc",
"src/objects/js-list-format.h",
"src/objects/js-locale-inl.h",
"src/objects/js-locale.cc",
"src/objects/js-locale.h",

View File

@ -24,6 +24,7 @@
#include "src/objects/hash-table-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/intl-objects.h"
#include "src/objects/js-list-format.h"
#include "src/objects/js-locale.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-regexp-string-iterator.h"
@ -4514,6 +4515,31 @@ void Genesis::InitializeGlobal_harmony_await_optimization() {
}
#ifdef V8_INTL_SUPPORT
void Genesis::InitializeGlobal_harmony_intl_list_format() {
if (!FLAG_harmony_intl_list_format) return;
Handle<JSObject> intl = Handle<JSObject>::cast(
JSReceiver::GetProperty(
isolate(),
Handle<JSReceiver>(native_context()->global_object(), isolate()),
factory()->InternalizeUtf8String("Intl"))
.ToHandleChecked());
Handle<JSFunction> list_format_fun =
InstallFunction(isolate(), intl, "ListFormat", JS_INTL_LIST_FORMAT_TYPE,
JSListFormat::kSize, 0, factory()->the_hole_value(),
Builtins::kListFormatConstructor);
list_format_fun->shared()->set_length(0);
list_format_fun->shared()->DontAdaptArguments();
// Setup %ListFormatPrototype%.
Handle<JSObject> prototype(
JSObject::cast(list_format_fun->instance_prototype()), isolate());
// Install the @@toStringTag property on the {prototype}.
JSObject::AddProperty(isolate(), prototype, factory()->to_string_tag_symbol(),
factory()->Object_string(),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
}
void Genesis::InitializeGlobal_harmony_locale() {
if (!FLAG_harmony_locale) return;

View File

@ -1335,6 +1335,8 @@ namespace internal {
/* ecma402 #sec-intl.datetimeformat.prototype.formattoparts */ \
CPP(DateTimeFormatPrototypeFormatToParts) \
/* ecma402 #new proposal */ \
/* ecma402 #sec-intl-list-format-constructor */ \
CPP(ListFormatConstructor) \
/* ecma402 #sec-intl-locale-constructor */ \
CPP(LocaleConstructor) \
CPP(LocalePrototypeLanguage) \

View File

@ -15,6 +15,7 @@
#include "src/intl.h"
#include "src/objects-inl.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-list-format-inl.h"
#include "src/objects/js-locale-inl.h"
#include "src/objects/js-relative-time-format-inl.h"
@ -624,6 +625,35 @@ BUILTIN(NumberFormatInternalFormatNumber) {
isolate, number_format_holder, number));
}
BUILTIN(ListFormatConstructor) {
HandleScope scope(isolate);
// 1. If NewTarget is undefined, throw a TypeError exception.
if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
isolate->factory()->NewStringFromStaticChars(
"Intl.ListFormat")));
}
// [[Construct]]
Handle<JSFunction> target = args.target();
Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
Handle<JSObject> result;
// 2. Let listFormat be OrdinaryCreateFromConstructor(NewTarget,
// "%ListFormatPrototype%").
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
JSObject::New(target, new_target));
Handle<Object> locales = args.atOrUndefined(isolate, 1);
Handle<Object> options = args.atOrUndefined(isolate, 2);
// 3. Return InitializeListFormat(listFormat, locales, options).
RETURN_RESULT_OR_FAILURE(
isolate,
JSListFormat::InitializeListFormat(
isolate, Handle<JSListFormat>::cast(result), locales, options));
}
namespace {
MaybeHandle<JSLocale> CreateLocale(Isolate* isolate,
@ -664,6 +694,7 @@ MaybeHandle<JSLocale> CreateLocale(Isolate* isolate,
} // namespace
// Intl.Locale implementation
BUILTIN(LocaleConstructor) {
HandleScope scope(isolate);
if (args.new_target()->IsUndefined(isolate)) { // [[Call]]

View File

@ -207,6 +207,7 @@ Type::bitset BitsetType::Lub(HeapObjectType const& type) {
case JS_MESSAGE_OBJECT_TYPE:
case JS_DATE_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_INTL_LIST_FORMAT_TYPE:
case JS_INTL_LOCALE_TYPE:
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
#endif // V8_INTL_SUPPORT

View File

@ -216,9 +216,10 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_private_fields)
V(harmony_await_optimization, "harmony await taking 1 tick")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \
HARMONY_INPROGRESS_BASE(V) \
V(harmony_locale, "Intl.Locale") \
#define HARMONY_INPROGRESS(V) \
HARMONY_INPROGRESS_BASE(V) \
V(harmony_locale, "Intl.Locale") \
V(harmony_intl_list_format, "Intl.ListFormat") \
V(harmony_intl_relative_time_format, "Intl.RelativeTimeFormat")
#else
#define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V)

View File

@ -45,6 +45,7 @@
V(configurable_string, "configurable") \
V(construct_string, "construct") \
V(constructor_string, "constructor") \
V(conjunction_string, "conjunction") \
V(create_string, "create") \
V(currency_string, "currency") \
V(Date_string, "Date") \
@ -56,6 +57,7 @@
V(defineProperty_string, "defineProperty") \
V(deleteProperty_string, "deleteProperty") \
V(did_handle_string, "didHandle") \
V(disjunction_string, "disjunction") \
V(display_name_string, "displayName") \
V(done_string, "done") \
V(dot_catch_string, ".catch") \
@ -179,7 +181,6 @@
V(Script_string, "Script") \
V(script_string, "script") \
V(short_string, "short") \
V(style_string, "style") \
V(second_string, "second") \
V(set_space_string, "set ") \
V(Set_string, "Set") \
@ -196,6 +197,7 @@
V(String_string, "String") \
V(string_string, "string") \
V(string_to_string, "[object String]") \
V(style_string, "style") \
V(symbol_species_string, "[Symbol.species]") \
V(Symbol_string, "Symbol") \
V(symbol_string, "symbol") \

View File

@ -30,7 +30,8 @@ enum class IcuService {
kNumberFormat,
kPluralRules,
kResourceBundle,
kRelativeDateTimeFormatter
kRelativeDateTimeFormatter,
kListFormatter
};
const UChar* GetUCharBufferFromFlat(const String::FlatContent& flat,

View File

@ -138,6 +138,7 @@ var AVAILABLE_LOCALES = {
'breakiterator': UNDEFINED,
'pluralrules': UNDEFINED,
'relativetimeformat': UNDEFINED,
'listformat': UNDEFINED,
};
/**

View File

@ -726,6 +726,7 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) {
case JS_MESSAGE_OBJECT_TYPE:
case JS_BOUND_FUNCTION_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_INTL_LIST_FORMAT_TYPE:
case JS_INTL_LOCALE_TYPE:
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
#endif // V8_INTL_SUPPORT

View File

@ -22,6 +22,7 @@
#include "src/objects/js-generator-inl.h"
#include "src/objects/literal-objects-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-list-format-inl.h"
#include "src/objects/js-locale-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-regexp-inl.h"
@ -351,6 +352,9 @@ void HeapObject::HeapObjectVerify(Isolate* isolate) {
CodeDataContainer::cast(this)->CodeDataContainerVerify(isolate);
break;
#ifdef V8_INTL_SUPPORT
case JS_INTL_LIST_FORMAT_TYPE:
JSListFormat::cast(this)->JSListFormatVerify(isolate);
break;
case JS_INTL_LOCALE_TYPE:
JSLocale::cast(this)->JSLocaleVerify(isolate);
break;
@ -1846,6 +1850,13 @@ void InterpreterData::InterpreterDataVerify(Isolate* isolate) {
}
#ifdef V8_INTL_SUPPORT
void JSListFormat::JSListFormatVerify(Isolate* isolate) {
VerifyObjectField(isolate, kLocaleOffset);
VerifyObjectField(isolate, kStyleOffset);
VerifyObjectField(isolate, kTypeOffset);
VerifyObjectField(isolate, kFormatterOffset);
}
void JSLocale::JSLocaleVerify(Isolate* isolate) {
VerifyObjectField(isolate, kLanguageOffset);
VerifyObjectField(isolate, kScriptOffset);

View File

@ -217,6 +217,7 @@ namespace internal {
#ifdef V8_INTL_SUPPORT
#define INSTANCE_TYPE_LIST(V) \
INSTANCE_TYPE_LIST_BEFORE_INTL(V) \
V(JS_INTL_LIST_FORMAT_TYPE) \
V(JS_INTL_LOCALE_TYPE) \
V(JS_INTL_RELATIVE_TIME_FORMAT_TYPE) \
INSTANCE_TYPE_LIST_AFTER_INTL(V)

View File

@ -161,6 +161,7 @@ TYPE_CHECKER(WeakArrayList, WEAK_ARRAY_LIST_TYPE)
TYPE_CHECKER(WeakCell, WEAK_CELL_TYPE)
#ifdef V8_INTL_SUPPORT
TYPE_CHECKER(JSListFormat, JS_INTL_LIST_FORMAT_TYPE)
TYPE_CHECKER(JSLocale, JS_INTL_LOCALE_TYPE)
TYPE_CHECKER(JSRelativeTimeFormat, JS_INTL_RELATIVE_TIME_FORMAT_TYPE)
#endif // V8_INTL_SUPPORT

View File

@ -19,6 +19,7 @@
#include "src/objects/js-collection-inl.h"
#include "src/objects/js-generator-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-list-format-inl.h"
#include "src/objects/js-locale-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-regexp-inl.h"
@ -302,6 +303,9 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
JSDataView::cast(this)->JSDataViewPrint(os);
break;
#ifdef V8_INTL_SUPPORT
case JS_INTL_LIST_FORMAT_TYPE:
JSListFormat::cast(this)->JSListFormatPrint(os);
break;
case JS_INTL_LOCALE_TYPE:
JSLocale::cast(this)->JSLocalePrint(os);
break;
@ -1946,6 +1950,15 @@ void Script::ScriptPrint(std::ostream& os) { // NOLINT
}
#ifdef V8_INTL_SUPPORT
void JSListFormat::JSListFormatPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSListFormat");
os << "\n - locale: " << Brief(locale());
os << "\n - style: " << StyleAsString();
os << "\n - type: " << TypeAsString();
os << "\n - formatter: " << Brief(formatter());
os << "\n";
}
void JSLocale::JSLocalePrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "JSLocale");
os << "\n - language: " << Brief(language());

View File

@ -62,6 +62,7 @@
#include "src/objects/js-collection-inl.h"
#include "src/objects/js-generator-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-list-format.h"
#include "src/objects/js-locale.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-regexp-inl.h"
@ -1417,6 +1418,8 @@ int JSObject::GetHeaderSize(InstanceType type,
case JS_MODULE_NAMESPACE_TYPE:
return JSModuleNamespace::kHeaderSize;
#ifdef V8_INTL_SUPPORT
case JS_INTL_LIST_FORMAT_TYPE:
return JSListFormat::kSize;
case JS_INTL_LOCALE_TYPE:
return JSLocale::kSize;
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
@ -3146,6 +3149,7 @@ VisitorId Map::GetVisitorId(Map* map) {
case JS_REGEXP_TYPE:
case JS_REGEXP_STRING_ITERATOR_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_INTL_LIST_FORMAT_TYPE:
case JS_INTL_LOCALE_TYPE:
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
#endif // V8_INTL_SUPPORT

View File

@ -75,6 +75,7 @@
// - JSDate
// - JSMessageObject
// - JSModuleNamespace
// - JSListFormat // If V8_INTL_SUPPORT enabled.
// - JSLocale // If V8_INTL_SUPPORT enabled.
// - JSRelativeTimeFormat // If V8_INTL_SUPPORT enabled.
// - WasmGlobalObject
@ -582,6 +583,7 @@ enum InstanceType : uint16_t {
JS_DATA_VIEW_TYPE,
#ifdef V8_INTL_SUPPORT
JS_INTL_LIST_FORMAT_TYPE,
JS_INTL_LOCALE_TYPE,
JS_INTL_RELATIVE_TIME_FORMAT_TYPE,
#endif // V8_INTL_SUPPORT
@ -697,6 +699,7 @@ class JSAsyncGeneratorObject;
class JSGlobalObject;
class JSGlobalProxy;
#ifdef V8_INTL_SUPPORT
class JSListFormat;
class JSLocale;
class JSRelativeTimeFormat;
#endif // V8_INTL_SUPPORT
@ -910,6 +913,7 @@ template <class C> inline bool Is(Object* obj);
#ifdef V8_INTL_SUPPORT
#define HEAP_OBJECT_ORDINARY_TYPE_LIST(V) \
HEAP_OBJECT_ORDINARY_TYPE_LIST_BASE(V) \
V(JSListFormat) \
V(JSLocale) \
V(JSRelativeTimeFormat)
#else

View File

@ -1135,6 +1135,14 @@ std::set<std::string> Intl::GetAvailableLocales(const IcuService& service) {
std::inserter(locales, locales.begin()));
return locales;
}
case IcuService::kListFormatter: {
// TODO(ftang): for now just use
// icu::Locale::getAvailableLocales(count) until we migrate to
// Intl::GetAvailableLocales().
// ICU FR at https://unicode-org.atlassian.net/browse/ICU-20015
icu_available_locales = icu::Locale::getAvailableLocales(count);
break;
}
}
UErrorCode error = U_ZERO_ERROR;
@ -1177,6 +1185,8 @@ IcuService StringToIcuService(Handle<String> service) {
return IcuService::kPluralRules;
} else if (service->IsUtf8EqualTo(CStrVector("relativetimeformat"))) {
return IcuService::kRelativeDateTimeFormatter;
} else if (service->IsUtf8EqualTo(CStrVector("listformat"))) {
return IcuService::kListFormatter;
}
UNREACHABLE();
}

View File

@ -0,0 +1,62 @@
// 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.
#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif // V8_INTL_SUPPORT
#ifndef V8_OBJECTS_JS_LIST_FORMAT_INL_H_
#define V8_OBJECTS_JS_LIST_FORMAT_INL_H_
#include "src/objects-inl.h"
#include "src/objects/js-list-format.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
// Base list format accessors.
ACCESSORS(JSListFormat, locale, String, kLocaleOffset);
ACCESSORS(JSListFormat, formatter, Foreign, kFormatterOffset);
// TODO(ftang): Use bit field accessor for style and type later.
inline void JSListFormat::set_style(Style style) {
DCHECK_GT(Style::COUNT, style);
int value = static_cast<int>(style);
WRITE_FIELD(this, kStyleOffset, Smi::FromInt(value));
}
inline JSListFormat::Style JSListFormat::style() const {
Object* value = READ_FIELD(this, kStyleOffset);
int style = Smi::ToInt(value);
DCHECK_LE(0, style);
DCHECK_GT(static_cast<int>(Style::COUNT), style);
return static_cast<Style>(style);
}
inline void JSListFormat::set_type(Type type) {
DCHECK_GT(Type::COUNT, type);
int value = static_cast<int>(type);
WRITE_FIELD(this, kTypeOffset, Smi::FromInt(value));
}
inline JSListFormat::Type JSListFormat::type() const {
Object* value = READ_FIELD(this, kTypeOffset);
int type = Smi::ToInt(value);
DCHECK_LE(0, type);
DCHECK_GT(static_cast<int>(Type::COUNT), type);
return static_cast<Type>(type);
}
CAST_ACCESSOR(JSListFormat);
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_LIST_FORMAT_INL_H_

View File

@ -0,0 +1,245 @@
// 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.
#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif // V8_INTL_SUPPORT
#include "src/objects/js-list-format.h"
#include <memory>
#include <vector>
#include "src/heap/factory.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/objects/intl-objects.h"
#include "src/objects/js-list-format-inl.h"
#include "unicode/listformatter.h"
namespace v8 {
namespace internal {
namespace {
const char* kStandard = "standard";
const char* kOr = "or";
const char* kUnit = "unit";
const char* kStandardShort = "standard-short";
const char* kUnitShort = "unit-short";
const char* kUnitNarrow = "unit-narrow";
const char* GetIcuStyleString(JSListFormat::Style style,
JSListFormat::Type type) {
switch (type) {
case JSListFormat::Type::CONJUNCTION:
switch (style) {
case JSListFormat::Style::LONG:
return kStandard;
case JSListFormat::Style::SHORT:
return kStandardShort;
case JSListFormat::Style::NARROW:
// Currently, ListFormat::createInstance on "standard-narrow" will
// fail so we use "standard-short" here.
// See https://unicode.org/cldr/trac/ticket/11254
// TODO(ftang): change to return kStandardNarrow; after the above
// issue fixed in CLDR/ICU.
// CLDR bug: https://unicode.org/cldr/trac/ticket/11254
// ICU bug: https://unicode-org.atlassian.net/browse/ICU-20014
return kStandardShort;
case JSListFormat::Style::COUNT:
UNREACHABLE();
}
case JSListFormat::Type::DISJUNCTION:
switch (style) {
// Currently, ListFormat::createInstance on "or-short" and "or-narrow"
// will fail so we use "or" here.
// See https://unicode.org/cldr/trac/ticket/11254
// TODO(ftang): change to return kOr, kOrShort or kOrNarrow depend on
// style after the above issue fixed in CLDR/ICU.
// CLDR bug: https://unicode.org/cldr/trac/ticket/11254
// ICU bug: https://unicode-org.atlassian.net/browse/ICU-20014
case JSListFormat::Style::LONG:
case JSListFormat::Style::SHORT:
case JSListFormat::Style::NARROW:
return kOr;
case JSListFormat::Style::COUNT:
UNREACHABLE();
}
case JSListFormat::Type::UNIT:
switch (style) {
case JSListFormat::Style::LONG:
return kUnit;
case JSListFormat::Style::SHORT:
return kUnitShort;
case JSListFormat::Style::NARROW:
return kUnitNarrow;
case JSListFormat::Style::COUNT:
UNREACHABLE();
}
case JSListFormat::Type::COUNT:
UNREACHABLE();
}
}
} // namespace
JSListFormat::Style get_style(const char* str) {
switch (str[0]) {
case 'n':
if (strcmp(&str[1], "arrow") == 0) return JSListFormat::Style::NARROW;
break;
case 'l':
if (strcmp(&str[1], "ong") == 0) return JSListFormat::Style::LONG;
break;
case 's':
if (strcmp(&str[1], "hort") == 0) return JSListFormat::Style::SHORT;
break;
}
UNREACHABLE();
}
JSListFormat::Type get_type(const char* str) {
switch (str[0]) {
case 'c':
if (strcmp(&str[1], "onjunction") == 0)
return JSListFormat::Type::CONJUNCTION;
break;
case 'd':
if (strcmp(&str[1], "isjunction") == 0)
return JSListFormat::Type::DISJUNCTION;
break;
case 'u':
if (strcmp(&str[1], "nit") == 0) return JSListFormat::Type::UNIT;
break;
}
UNREACHABLE();
}
MaybeHandle<JSListFormat> JSListFormat::InitializeListFormat(
Isolate* isolate, Handle<JSListFormat> list_format_holder,
Handle<Object> input_locales, Handle<Object> input_options) {
Factory* factory = isolate->factory();
Handle<JSReceiver> options;
// 2. If options is undefined, then
if (input_options->IsUndefined(isolate)) {
// a. Let options be ObjectCreate(null).
options = isolate->factory()->NewJSObjectWithNullProto();
// 3. Else
} else {
// a. Let options be ? ToObject(options).
ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
Object::ToObject(isolate, input_options),
JSListFormat);
}
// 5. Let t be GetOption(options, "type", "string", «"conjunction",
// "disjunction", "unit"», "conjunction").
std::unique_ptr<char[]> type_str = nullptr;
std::vector<const char*> type_values = {"conjunction", "disjunction", "unit"};
Maybe<bool> maybe_found_type = Intl::GetStringOption(
isolate, options, "type", type_values, "Intl.ListFormat", &type_str);
Type type_enum = Type::CONJUNCTION;
MAYBE_RETURN(maybe_found_type, MaybeHandle<JSListFormat>());
if (maybe_found_type.FromJust()) {
DCHECK_NOT_NULL(type_str.get());
type_enum = get_type(type_str.get());
}
// 6. Set listFormat.[[Type]] to t.
list_format_holder->set_type(type_enum);
// 7. Let s be ? GetOption(options, "style", "string",
// «"long", "short", "narrow"», "long").
std::unique_ptr<char[]> style_str = nullptr;
std::vector<const char*> style_values = {"long", "short", "narrow"};
Maybe<bool> maybe_found_style = Intl::GetStringOption(
isolate, options, "style", style_values, "Intl.ListFormat", &style_str);
Style style_enum = Style::LONG;
MAYBE_RETURN(maybe_found_style, MaybeHandle<JSListFormat>());
if (maybe_found_style.FromJust()) {
DCHECK_NOT_NULL(style_str.get());
style_enum = get_style(style_str.get());
}
// 15. Set listFormat.[[Style]] to s.
list_format_holder->set_style(style_enum);
// 10. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]],
// requestedLocales, opt, undefined, localeData).
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "listformat", input_locales, options),
JSListFormat);
Handle<Object> locale_obj =
JSObject::GetDataProperty(r, factory->locale_string());
Handle<String> locale;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, locale, Object::ToString(isolate, locale_obj), JSListFormat);
// 18. Set listFormat.[[Locale]] to the value of r.[[Locale]].
list_format_holder->set_locale(*locale);
std::unique_ptr<char[]> locale_name = locale->ToCString();
icu::Locale icu_locale(locale_name.get());
UErrorCode status = U_ZERO_ERROR;
icu::ListFormatter* formatter = icu::ListFormatter::createInstance(
icu_locale, GetIcuStyleString(style_enum, type_enum), status);
CHECK(U_SUCCESS(status));
CHECK_NOT_NULL(formatter);
Handle<Managed<icu::ListFormatter>> managed_formatter =
Managed<icu::ListFormatter>::FromRawPtr(isolate, 0, formatter);
list_format_holder->set_formatter(*managed_formatter);
return list_format_holder;
}
Handle<JSObject> JSListFormat::ResolvedOptions(
Isolate* isolate, Handle<JSListFormat> format_holder) {
Factory* factory = isolate->factory();
Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
Handle<String> locale(format_holder->locale(), isolate);
JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
NONE);
JSObject::AddProperty(isolate, result, factory->style_string(),
format_holder->StyleAsString(), NONE);
JSObject::AddProperty(isolate, result, factory->type_string(),
format_holder->TypeAsString(), NONE);
return result;
}
icu::ListFormatter* JSListFormat::UnpackFormatter(Isolate* isolate,
Handle<JSListFormat> holder) {
return Managed<icu::ListFormatter>::cast(holder->formatter())->raw();
}
Handle<String> JSListFormat::StyleAsString() const {
switch (style()) {
case Style::LONG:
return GetReadOnlyRoots().long_string_handle();
case Style::SHORT:
return GetReadOnlyRoots().short_string_handle();
case Style::NARROW:
return GetReadOnlyRoots().narrow_string_handle();
case Style::COUNT:
UNREACHABLE();
}
}
Handle<String> JSListFormat::TypeAsString() const {
switch (type()) {
case Type::CONJUNCTION:
return GetReadOnlyRoots().conjunction_string_handle();
case Type::DISJUNCTION:
return GetReadOnlyRoots().disjunction_string_handle();
case Type::UNIT:
return GetReadOnlyRoots().unit_string_handle();
case Type::COUNT:
UNREACHABLE();
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,102 @@
// 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.
#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif // V8_INTL_SUPPORT
#ifndef V8_OBJECTS_JS_LIST_FORMAT_H_
#define V8_OBJECTS_JS_LIST_FORMAT_H_
#include "src/heap/factory.h"
#include "src/isolate.h"
#include "src/objects.h"
#include "unicode/uversion.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace U_ICU_NAMESPACE {
class ListFormatter;
}
namespace v8 {
namespace internal {
class JSListFormat : public JSObject {
public:
// Initializes relative time format object with properties derived from input
// locales and options.
static MaybeHandle<JSListFormat> InitializeListFormat(
Isolate* isolate, Handle<JSListFormat> list_format_holder,
Handle<Object> locales, Handle<Object> options);
static Handle<JSObject> ResolvedOptions(Isolate* isolate,
Handle<JSListFormat> format_holder);
// Unpacks formatter object from corresponding JavaScript object.
static icu::ListFormatter* UnpackFormatter(
Isolate* isolate, Handle<JSListFormat> list_format_holder);
Handle<String> StyleAsString() const;
Handle<String> TypeAsString() const;
DECL_CAST(JSListFormat)
// ListFormat accessors.
DECL_ACCESSORS(locale, String)
// TODO(ftang): Style requires only 2 bits and Type requires only 2 bits
// but here we're using 64 bits for each. We should fold these two fields into
// a single Flags field and use BIT_FIELD_ACCESSORS to access it.
//
// Style: identifying the relative time format style used.
//
// ecma402/#sec-properties-of-intl-listformat-instances
enum class Style {
LONG, // Everything spelled out.
SHORT, // Abbreviations used when possible.
NARROW, // Use the shortest possible form.
COUNT
};
inline void set_style(Style style);
inline Style style() const;
// Type: identifying the list of types used.
//
// ecma402/#sec-properties-of-intl-listformat-instances
enum class Type {
CONJUNCTION, // for "and"-based lists (e.g., "A, B and C")
DISJUNCTION, // for "or"-based lists (e.g., "A, B or C"),
UNIT, // for lists of values with units (e.g., "5 pounds, 12 ounces").
COUNT
};
inline void set_type(Type type);
inline Type type() const;
DECL_ACCESSORS(formatter, Foreign)
DECL_PRINTER(JSListFormat)
DECL_VERIFIER(JSListFormat)
// Layout description.
static const int kJSListFormatOffset = JSObject::kHeaderSize;
static const int kLocaleOffset = kJSListFormatOffset + kPointerSize;
static const int kStyleOffset = kLocaleOffset + kPointerSize;
static const int kTypeOffset = kStyleOffset + kPointerSize;
static const int kFormatterOffset = kTypeOffset + kPointerSize;
static const int kSize = kFormatterOffset + kPointerSize;
// Constant to access field
static const int kFormatterField = 3;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSListFormat);
};
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_LIST_FORMAT_H_

View File

@ -20,9 +20,6 @@
#include "unicode/numfmt.h"
#include "unicode/reldatefmt.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
@ -40,22 +37,22 @@ UDateRelativeDateTimeFormatterStyle getIcuStyle(
UNREACHABLE();
}
}
} // namespace
JSRelativeTimeFormat::Style getStyle(const char* str) {
JSRelativeTimeFormat::Style JSRelativeTimeFormat::getStyle(const char* str) {
if (strcmp(str, "long") == 0) return JSRelativeTimeFormat::Style::LONG;
if (strcmp(str, "short") == 0) return JSRelativeTimeFormat::Style::SHORT;
if (strcmp(str, "narrow") == 0) return JSRelativeTimeFormat::Style::NARROW;
UNREACHABLE();
}
JSRelativeTimeFormat::Numeric getNumeric(const char* str) {
JSRelativeTimeFormat::Numeric JSRelativeTimeFormat::getNumeric(
const char* str) {
if (strcmp(str, "auto") == 0) return JSRelativeTimeFormat::Numeric::AUTO;
if (strcmp(str, "always") == 0) return JSRelativeTimeFormat::Numeric::ALWAYS;
UNREACHABLE();
}
} // namespace
MaybeHandle<JSRelativeTimeFormat>
JSRelativeTimeFormat::InitializeRelativeTimeFormat(
Isolate* isolate, Handle<JSRelativeTimeFormat> relative_time_format_holder,
@ -221,5 +218,3 @@ Handle<String> JSRelativeTimeFormat::NumericAsString() const {
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"

View File

@ -103,6 +103,9 @@ class JSRelativeTimeFormat : public JSObject {
static const int kSize = kFlagsOffset + kPointerSize;
private:
static Style getStyle(const char* str);
static Numeric getNumeric(const char* str);
DISALLOW_IMPLICIT_CONSTRUCTORS(JSRelativeTimeFormat);
};

View File

@ -0,0 +1,108 @@
// 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
// ListFormat constructor can't be called as function.
assertThrows(() => Intl.ListFormat(['sr']), TypeError);
// Non-string locale.
// assertThrows(() => new Intl.ListFormat(5), TypeError);
// Invalid locale string.
assertThrows(() => new Intl.ListFormat(['abcdefghi']), RangeError);
assertDoesNotThrow(() => new Intl.ListFormat(['sr'], {}), TypeError);
assertDoesNotThrow(() => new Intl.ListFormat([], {}));
assertDoesNotThrow(() => new Intl.ListFormat(['fr', 'ar'], {}));
assertDoesNotThrow(() => new Intl.ListFormat({0: 'ja', 1:'fr'}, {}));
assertDoesNotThrow(() => new Intl.ListFormat({1: 'ja', 2:'fr'}, {}));
assertDoesNotThrow(() => new Intl.ListFormat(['sr']));
assertDoesNotThrow(() => new Intl.ListFormat());
assertDoesNotThrow(
() => new Intl.ListFormat(
['sr'], {
style: 'short',
type: 'unit'
}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'conjunction'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'disjunction'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'unit'}));
assertThrows(
() => new Intl.ListFormat(['sr'], {type: 'standard'}),
RangeError);
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {style: 'long'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {style: 'short'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {style: 'narrow'}));
assertThrows(
() => new Intl.ListFormat(['sr'], {style: 'giant'}),
RangeError);
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'conjunction', style: 'long'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'conjunction', style: 'short'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'conjunction', style: 'narrow'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'disjunction', style: 'long'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'disjunction', style: 'short'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'disjunction', style: 'narrow'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'unit', style: 'long'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'unit', style: 'short'}));
assertDoesNotThrow(
() => new Intl.ListFormat(['sr'], {type: 'unit', style: 'narrow'}));
// Throws only once during construction.
// Check for all getters to prevent regression.
// Preserve the order of getter initialization.
let getCount = 0;
let style = -1;
let type = -1;
new Intl.ListFormat(['en-US'], {
get style() {
style = ++getCount;
},
get type() {
type = ++getCount;
}
});
assertEquals(1, type);
assertEquals(2, style);

View File

@ -50,6 +50,7 @@ FEATURE_FLAGS = {
'String.prototype.matchAll': '--harmony-string-matchall',
'Symbol.matchAll': '--harmony-string-matchall',
'numeric-separator-literal': '--harmony-numeric-separator',
'Intl.ListFormat': '--harmony-intl-list-format',
'Intl.Locale': '--harmony-locale',
'Intl.RelativeTimeFormat': '--harmony-intl-relative-time-format',
'Symbol.prototype.description': '--harmony-symbol-description',

View File

@ -159,15 +159,16 @@ INSTANCE_TYPES = {
1081: "JS_WEAK_SET_TYPE",
1082: "JS_TYPED_ARRAY_TYPE",
1083: "JS_DATA_VIEW_TYPE",
1084: "JS_INTL_LOCALE_TYPE",
1085: "JS_INTL_RELATIVE_TIME_FORMAT_TYPE",
1086: "WASM_GLOBAL_TYPE",
1087: "WASM_INSTANCE_TYPE",
1088: "WASM_MEMORY_TYPE",
1089: "WASM_MODULE_TYPE",
1090: "WASM_TABLE_TYPE",
1091: "JS_BOUND_FUNCTION_TYPE",
1092: "JS_FUNCTION_TYPE",
1084: "JS_INTL_LIST_FORMAT_TYPE",
1085: "JS_INTL_LOCALE_TYPE",
1086: "JS_INTL_RELATIVE_TIME_FORMAT_TYPE",
1087: "WASM_GLOBAL_TYPE",
1088: "WASM_INSTANCE_TYPE",
1089: "WASM_MEMORY_TYPE",
1090: "WASM_MODULE_TYPE",
1091: "WASM_TABLE_TYPE",
1092: "JS_BOUND_FUNCTION_TYPE",
1093: "JS_FUNCTION_TYPE",
}
# List of known V8 maps.
@ -282,33 +283,33 @@ KNOWN_MAPS = {
("RO_SPACE", 0x047b1): (171, "Tuple2Map"),
("RO_SPACE", 0x04ae9): (161, "InterceptorInfoMap"),
("RO_SPACE", 0x04bf1): (169, "ScriptMap"),
("RO_SPACE", 0x099c9): (154, "AccessorInfoMap"),
("RO_SPACE", 0x09a19): (153, "AccessCheckInfoMap"),
("RO_SPACE", 0x09a69): (155, "AccessorPairMap"),
("RO_SPACE", 0x09ab9): (156, "AliasedArgumentsEntryMap"),
("RO_SPACE", 0x09b09): (157, "AllocationMementoMap"),
("RO_SPACE", 0x09b59): (158, "AsyncGeneratorRequestMap"),
("RO_SPACE", 0x09ba9): (159, "DebugInfoMap"),
("RO_SPACE", 0x09bf9): (160, "FunctionTemplateInfoMap"),
("RO_SPACE", 0x09c49): (162, "InterpreterDataMap"),
("RO_SPACE", 0x09c99): (163, "ModuleInfoEntryMap"),
("RO_SPACE", 0x09ce9): (164, "ModuleMap"),
("RO_SPACE", 0x09d39): (165, "ObjectTemplateInfoMap"),
("RO_SPACE", 0x09d89): (166, "PromiseCapabilityMap"),
("RO_SPACE", 0x09dd9): (167, "PromiseReactionMap"),
("RO_SPACE", 0x09e29): (168, "PrototypeInfoMap"),
("RO_SPACE", 0x09e79): (170, "StackFrameInfoMap"),
("RO_SPACE", 0x09ec9): (172, "Tuple3Map"),
("RO_SPACE", 0x09f19): (173, "ArrayBoilerplateDescriptionMap"),
("RO_SPACE", 0x09f69): (174, "WasmDebugInfoMap"),
("RO_SPACE", 0x09fb9): (175, "WasmExportedFunctionDataMap"),
("RO_SPACE", 0x0a009): (176, "CallableTaskMap"),
("RO_SPACE", 0x0a059): (177, "CallbackTaskMap"),
("RO_SPACE", 0x0a0a9): (178, "PromiseFulfillReactionJobTaskMap"),
("RO_SPACE", 0x0a0f9): (179, "PromiseRejectReactionJobTaskMap"),
("RO_SPACE", 0x0a149): (180, "PromiseResolveThenableJobTaskMap"),
("RO_SPACE", 0x0a199): (181, "AllocationSiteMap"),
("RO_SPACE", 0x09a19): (154, "AccessorInfoMap"),
("RO_SPACE", 0x09a69): (153, "AccessCheckInfoMap"),
("RO_SPACE", 0x09ab9): (155, "AccessorPairMap"),
("RO_SPACE", 0x09b09): (156, "AliasedArgumentsEntryMap"),
("RO_SPACE", 0x09b59): (157, "AllocationMementoMap"),
("RO_SPACE", 0x09ba9): (158, "AsyncGeneratorRequestMap"),
("RO_SPACE", 0x09bf9): (159, "DebugInfoMap"),
("RO_SPACE", 0x09c49): (160, "FunctionTemplateInfoMap"),
("RO_SPACE", 0x09c99): (162, "InterpreterDataMap"),
("RO_SPACE", 0x09ce9): (163, "ModuleInfoEntryMap"),
("RO_SPACE", 0x09d39): (164, "ModuleMap"),
("RO_SPACE", 0x09d89): (165, "ObjectTemplateInfoMap"),
("RO_SPACE", 0x09dd9): (166, "PromiseCapabilityMap"),
("RO_SPACE", 0x09e29): (167, "PromiseReactionMap"),
("RO_SPACE", 0x09e79): (168, "PrototypeInfoMap"),
("RO_SPACE", 0x09ec9): (170, "StackFrameInfoMap"),
("RO_SPACE", 0x09f19): (172, "Tuple3Map"),
("RO_SPACE", 0x09f69): (173, "ArrayBoilerplateDescriptionMap"),
("RO_SPACE", 0x09fb9): (174, "WasmDebugInfoMap"),
("RO_SPACE", 0x0a009): (175, "WasmExportedFunctionDataMap"),
("RO_SPACE", 0x0a059): (176, "CallableTaskMap"),
("RO_SPACE", 0x0a0a9): (177, "CallbackTaskMap"),
("RO_SPACE", 0x0a0f9): (178, "PromiseFulfillReactionJobTaskMap"),
("RO_SPACE", 0x0a149): (179, "PromiseRejectReactionJobTaskMap"),
("RO_SPACE", 0x0a199): (180, "PromiseResolveThenableJobTaskMap"),
("RO_SPACE", 0x0a1e9): (181, "AllocationSiteMap"),
("RO_SPACE", 0x0a239): (181, "AllocationSiteMap"),
("MAP_SPACE", 0x02201): (1057, "ExternalMap"),
("MAP_SPACE", 0x02251): (1072, "JSMessageObjectMap"),
}