Commit 12f04d81 authored by Frank Tang's avatar Frank Tang Committed by Commit Bot

[Intl] Use icu::Locale as storage in JSLocale

Remove flags and all string in JSLocale
This does not change the logic of Intl.Locale constructor
but only the way we store the information.
Preparation for logic rewrite that sync with latest spec.

Bug: v8:7684
Change-Id: Ib61705eaf00e5bcf63443c55c29f0b0b61f8e4c9
Reviewed-on: https://chromium-review.googlesource.com/c/1377996
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58386}
parent 5b607f19
......@@ -537,10 +537,9 @@ MaybeHandle<JSLocale> CreateLocale(Isolate* isolate,
Handle<String> locale_string;
// 8. If Type(tag) is Object and tag has an [[InitializedLocale]] internal
// slot, then
if (tag->IsJSLocale() && Handle<JSLocale>::cast(tag)->locale()->IsString()) {
if (tag->IsJSLocale()) {
// a. Let tag be tag.[[Locale]].
locale_string =
Handle<String>(Handle<JSLocale>::cast(tag)->locale(), isolate);
locale_string = JSLocale::ToString(isolate, Handle<JSLocale>::cast(tag));
} else { // 9. Else,
// a. Let tag be ? ToString(tag).
ASSIGN_RETURN_ON_EXCEPTION(isolate, locale_string,
......@@ -589,25 +588,25 @@ BUILTIN(LocaleConstructor) {
BUILTIN(LocalePrototypeMaximize) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.maximize");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.maximize");
Handle<JSFunction> constructor(
isolate->native_context()->intl_locale_function(), isolate);
Handle<String> locale_str = JSLocale::ToString(isolate, locale);
RETURN_RESULT_OR_FAILURE(
isolate,
CreateLocale(isolate, constructor, constructor,
JSLocale::Maximize(isolate, locale_holder->locale()),
isolate, CreateLocale(isolate, constructor, constructor,
JSLocale::Maximize(isolate, *locale_str),
isolate->factory()->NewJSObjectWithNullProto()));
}
BUILTIN(LocalePrototypeMinimize) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.minimize");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.minimize");
Handle<JSFunction> constructor(
isolate->native_context()->intl_locale_function(), isolate);
Handle<String> locale_str = JSLocale::ToString(isolate, locale);
RETURN_RESULT_OR_FAILURE(
isolate,
CreateLocale(isolate, constructor, constructor,
JSLocale::Minimize(isolate, locale_holder->locale()),
isolate, CreateLocale(isolate, constructor, constructor,
JSLocale::Minimize(isolate, *locale_str),
isolate->factory()->NewJSObjectWithNullProto()));
}
......@@ -658,89 +657,79 @@ BUILTIN(RelativeTimeFormatPrototypeFormatToParts) {
BUILTIN(LocalePrototypeLanguage) {
HandleScope scope(isolate);
// CHECK_RECEIVER will case locale_holder to JSLocale.
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.language");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.language");
return locale_holder->language();
return *JSLocale::Language(isolate, locale);
}
BUILTIN(LocalePrototypeScript) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.script");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.script");
return locale_holder->script();
return *JSLocale::Script(isolate, locale);
}
BUILTIN(LocalePrototypeRegion) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.region");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.region");
return locale_holder->region();
return *JSLocale::Region(isolate, locale);
}
BUILTIN(LocalePrototypeBaseName) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.baseName");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.baseName");
return locale_holder->base_name();
return *JSLocale::BaseName(isolate, locale);
}
BUILTIN(LocalePrototypeCalendar) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.calendar");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.calendar");
return locale_holder->calendar();
return *JSLocale::Calendar(isolate, locale);
}
BUILTIN(LocalePrototypeCaseFirst) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.caseFirst");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.caseFirst");
return *(locale_holder->CaseFirstAsString());
return *JSLocale::CaseFirst(isolate, locale);
}
BUILTIN(LocalePrototypeCollation) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.collation");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.collation");
return locale_holder->collation();
return *JSLocale::Collation(isolate, locale);
}
BUILTIN(LocalePrototypeHourCycle) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.hourCycle");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.hourCycle");
return *(locale_holder->HourCycleAsString());
return *JSLocale::HourCycle(isolate, locale);
}
BUILTIN(LocalePrototypeNumeric) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.numeric");
switch (locale_holder->numeric()) {
case JSLocale::Numeric::TRUE_VALUE:
return *(isolate->factory()->true_value());
case JSLocale::Numeric::FALSE_VALUE:
return *(isolate->factory()->false_value());
case JSLocale::Numeric::NOTSET:
return *(isolate->factory()->undefined_value());
case JSLocale::Numeric::COUNT:
UNREACHABLE();
}
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.numeric");
return *JSLocale::Numeric(isolate, locale);
}
BUILTIN(LocalePrototypeNumberingSystem) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder,
"Intl.Locale.prototype.numberingSystem");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.numberingSystem");
return locale_holder->numbering_system();
return *JSLocale::NumberingSystem(isolate, locale);
}
BUILTIN(LocalePrototypeToString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.toString");
CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.toString");
return locale_holder->locale();
return *JSLocale::ToString(isolate, locale);
}
BUILTIN(RelativeTimeFormatConstructor) {
......
......@@ -2089,16 +2089,7 @@ void JSListFormat::JSListFormatVerify(Isolate* isolate) {
void JSLocale::JSLocaleVerify(Isolate* isolate) {
JSObjectVerify(isolate);
VerifyObjectField(isolate, kLanguageOffset);
VerifyObjectField(isolate, kScriptOffset);
VerifyObjectField(isolate, kRegionOffset);
VerifyObjectField(isolate, kBaseNameOffset);
VerifyObjectField(isolate, kLocaleOffset);
// Unicode extension fields.
VerifyObjectField(isolate, kFlagsOffset);
VerifyObjectField(isolate, kCalendarOffset);
VerifyObjectField(isolate, kCollationOffset);
VerifyObjectField(isolate, kNumberingSystemOffset);
VerifyObjectField(isolate, kICULocaleOffset);
}
void JSNumberFormat::JSNumberFormatVerify(Isolate* isolate) {
......
......@@ -2065,18 +2065,8 @@ void JSListFormat::JSListFormatPrint(std::ostream& os) { // NOLINT
void JSLocale::JSLocalePrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, *this, "JSLocale");
os << "\n - language: " << Brief(language());
os << "\n - script: " << Brief(script());
os << "\n - region: " << Brief(region());
os << "\n - baseName: " << Brief(base_name());
os << "\n - locale: " << Brief(locale());
os << "\n - calendar: " << Brief(calendar());
os << "\n - caseFirst: " << CaseFirstAsString();
os << "\n - collation: " << Brief(collation());
os << "\n - hourCycle: " << HourCycleAsString();
os << "\n - numeric: " << NumericAsString();
os << "\n - numberingSystem: " << Brief(numbering_system());
os << "\n";
os << "\n - icu locale: " << Brief(icu_locale());
JSObjectPrintBody(os, *this);
}
void JSNumberFormat::JSNumberFormatPrint(std::ostream& os) { // NOLINT
......
......@@ -21,54 +21,10 @@ namespace internal {
OBJECT_CONSTRUCTORS_IMPL(JSLocale, JSObject)
// Base locale accessors.
ACCESSORS(JSLocale, language, Object, kLanguageOffset);
ACCESSORS(JSLocale, script, Object, kScriptOffset);
ACCESSORS(JSLocale, region, Object, kRegionOffset);
ACCESSORS(JSLocale, base_name, Object, kBaseNameOffset);
ACCESSORS2(JSLocale, locale, String, kLocaleOffset);
// Unicode extension accessors.
ACCESSORS(JSLocale, calendar, Object, kCalendarOffset);
ACCESSORS(JSLocale, collation, Object, kCollationOffset);
ACCESSORS(JSLocale, numbering_system, Object, kNumberingSystemOffset);
SMI_ACCESSORS(JSLocale, flags, kFlagsOffset)
ACCESSORS2(JSLocale, icu_locale, Managed<icu::Locale>, kICULocaleOffset);
CAST_ACCESSOR2(JSLocale);
inline void JSLocale::set_case_first(CaseFirst case_first) {
DCHECK_GT(CaseFirst::COUNT, case_first);
int hints = flags();
hints = CaseFirstBits::update(hints, case_first);
set_flags(hints);
}
inline JSLocale::CaseFirst JSLocale::case_first() const {
return CaseFirstBits::decode(flags());
}
inline void JSLocale::set_hour_cycle(HourCycle hour_cycle) {
DCHECK_GT(HourCycle::COUNT, hour_cycle);
int hints = flags();
hints = HourCycleBits::update(hints, hour_cycle);
set_flags(hints);
}
inline JSLocale::HourCycle JSLocale::hour_cycle() const {
return HourCycleBits::decode(flags());
}
inline void JSLocale::set_numeric(Numeric numeric) {
DCHECK_GT(Numeric::COUNT, numeric);
int hints = flags();
hints = NumericBits::update(hints, numeric);
set_flags(hints);
}
inline JSLocale::Numeric JSLocale::numeric() const {
return NumericBits::decode(flags());
}
} // namespace internal
} // namespace v8
......
......@@ -30,24 +30,36 @@ namespace internal {
namespace {
JSLocale::CaseFirst GetCaseFirst(const char* str) {
if (strcmp(str, "upper") == 0) return JSLocale::CaseFirst::UPPER;
if (strcmp(str, "lower") == 0) return JSLocale::CaseFirst::LOWER;
if (strcmp(str, "false") == 0) return JSLocale::CaseFirst::FALSE_VALUE;
UNREACHABLE();
}
JSLocale::HourCycle GetHourCycle(const char* str) {
if (strcmp(str, "h11") == 0) return JSLocale::HourCycle::H11;
if (strcmp(str, "h12") == 0) return JSLocale::HourCycle::H12;
if (strcmp(str, "h23") == 0) return JSLocale::HourCycle::H23;
if (strcmp(str, "h24") == 0) return JSLocale::HourCycle::H24;
UNREACHABLE();
// Helper function to check a language tag is valid. It will return false if
// the parsing is not the same as the tag. For example, it will return false if
// the tag is too long.
bool IsValidLanguageTag(const char* tag, int length) {
// icu::Locale::forLanguageTag won't return U_STRING_NOT_TERMINATED_WARNING
// for incorrect locale yet. So we still need the following
// uloc_forLanguageTag
// TODO(ftang): Remove once icu::Locale::forLanguageTag indicate error.
char result[ULOC_FULLNAME_CAPACITY];
UErrorCode status = U_ZERO_ERROR;
int parsed_length = 0;
int icu_length = uloc_forLanguageTag(tag, result, ULOC_FULLNAME_CAPACITY,
&parsed_length, &status);
return U_SUCCESS(status) && parsed_length == length &&
status != U_STRING_NOT_TERMINATED_WARNING && icu_length != 0;
}
JSLocale::Numeric GetNumeric(const char* str) {
return strcmp(str, "true") == 0 ? JSLocale::Numeric::TRUE_VALUE
: JSLocale::Numeric::FALSE_VALUE;
// Helper function to check a locale is valid. It will return false if
// the length of the extension fields are incorrect. For example, en-u-a or
// en-u-co-b will return false.
bool IsValidLocale(const icu::Locale& locale) {
// icu::Locale::toLanguageTag won't return U_STRING_NOT_TERMINATED_WARNING for
// incorrect locale yet. So we still need the following uloc_toLanguageTag
// TODO(ftang): Change to use icu::Locale::toLanguageTag once it indicate
// error.
char result[ULOC_FULLNAME_CAPACITY];
UErrorCode status = U_ZERO_ERROR;
uloc_toLanguageTag(locale.getName(), result, ULOC_FULLNAME_CAPACITY, true,
&status);
return U_SUCCESS(status) && status != U_STRING_NOT_TERMINATED_WARNING;
}
struct OptionData {
......@@ -60,9 +72,9 @@ struct OptionData {
// Inserts tags from options into locale string.
Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
Handle<JSReceiver> options,
char* icu_locale) {
icu::Locale* icu_locale) {
CHECK(isolate);
CHECK(icu_locale);
CHECK(!icu_locale->isBogus());
const std::vector<const char*> hour_cycle_values = {"h11", "h12", "h23",
"h24"};
......@@ -80,6 +92,7 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
// TODO(cira): Pass in values as per the spec to make this to be
// spec compliant.
UErrorCode status = U_ZERO_ERROR;
for (const auto& option_to_bcp47 : kOptionToUnicodeTagMap) {
std::unique_ptr<char[]> value_str = nullptr;
bool value_bool = false;
......@@ -109,14 +122,9 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
// Overwrite existing, or insert new key-value to the locale string.
const char* value = uloc_toLegacyType(key, value_str.get());
UErrorCode status = U_ZERO_ERROR;
if (value) {
// TODO(cira): ICU puts artificial limit on locale length, while BCP47
// doesn't. Switch to C++ API when it's ready.
// Related ICU bug - https://ssl.icu-project.org/trac/ticket/13417.
uloc_setKeywordValue(key, value, icu_locale, ULOC_FULLNAME_CAPACITY,
&status);
if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
icu_locale->setKeywordValue(key, value, status);
if (U_FAILURE(status)) {
return Just(false);
}
} else {
......@@ -124,197 +132,81 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
}
}
// Check all the unicode extension fields are in the right length.
if (!IsValidLocale(*icu_locale)) {
THROW_NEW_ERROR_RETURN_VALUE(
isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
Nothing<bool>());
}
return Just(true);
}
// Fills in the JSLocale object slots with Unicode tag/values.
bool PopulateLocaleWithUnicodeTags(Isolate* isolate, const char* icu_locale,
Handle<JSLocale> locale_holder) {
CHECK(isolate);
CHECK(icu_locale);
Factory* factory = isolate->factory();
Handle<Object> UnicodeKeywordValue(Isolate* isolate, Handle<JSLocale> locale,
const char* key) {
icu::Locale* icu_locale = locale->icu_locale()->raw();
UErrorCode status = U_ZERO_ERROR;
UEnumeration* keywords = uloc_openKeywords(icu_locale, &status);
if (!keywords) return true;
char value[ULOC_FULLNAME_CAPACITY];
while (const char* keyword = uenum_next(keywords, nullptr, &status)) {
uloc_getKeywordValue(icu_locale, keyword, value, ULOC_FULLNAME_CAPACITY,
&status);
if (U_FAILURE(status)) {
status = U_ZERO_ERROR;
continue;
}
// Ignore those we don't recognize - spec allows that.
const char* bcp47_key = uloc_toUnicodeLocaleKey(keyword);
if (bcp47_key) {
const char* bcp47_value = uloc_toUnicodeLocaleType(bcp47_key, value);
if (bcp47_value) {
if (strcmp(bcp47_key, "kn") == 0) {
locale_holder->set_numeric(GetNumeric(bcp47_value));
} else if (strcmp(bcp47_key, "ca") == 0) {
Handle<String> bcp47_handle =
factory->NewStringFromAsciiChecked(bcp47_value);
locale_holder->set_calendar(*bcp47_handle);
} else if (strcmp(bcp47_key, "kf") == 0) {
locale_holder->set_case_first(GetCaseFirst(bcp47_value));
} else if (strcmp(bcp47_key, "co") == 0) {
Handle<String> bcp47_handle =
factory->NewStringFromAsciiChecked(bcp47_value);
locale_holder->set_collation(*bcp47_handle);
} else if (strcmp(bcp47_key, "hc") == 0) {
locale_holder->set_hour_cycle(GetHourCycle(bcp47_value));
} else if (strcmp(bcp47_key, "nu") == 0) {
Handle<String> bcp47_handle =
factory->NewStringFromAsciiChecked(bcp47_value);
locale_holder->set_numbering_system(*bcp47_handle);
}
}
}
std::string value =
icu_locale->getUnicodeKeywordValue<std::string>(key, status);
CHECK(U_SUCCESS(status));
if (value == "") {
return isolate->factory()->undefined_value();
}
uenum_close(keywords);
return true;
return isolate->factory()->NewStringFromAsciiChecked(value.c_str());
}
} // namespace
MaybeHandle<JSLocale> JSLocale::Initialize(Isolate* isolate,
Handle<JSLocale> locale_holder,
Handle<String> locale,
Handle<JSLocale> locale,
Handle<String> locale_str,
Handle<JSReceiver> options) {
locale_holder->set_flags(0);
static const char* const kMethod = "Intl.Locale";
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
UErrorCode status = U_ZERO_ERROR;
// Get ICU locale format, and canonicalize it.
char icu_result[ULOC_FULLNAME_CAPACITY];
if (locale->length() == 0) {
if (locale_str->length() == 0) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kLocaleNotEmpty),
JSLocale);
}
v8::String::Utf8Value bcp47_locale(v8_isolate, v8::Utils::ToLocal(locale));
v8::String::Utf8Value bcp47_locale(v8_isolate,
v8::Utils::ToLocal(locale_str));
CHECK_LT(0, bcp47_locale.length());
CHECK_NOT_NULL(*bcp47_locale);
int parsed_length = 0;
int icu_length =
uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
&parsed_length, &status);
if (U_FAILURE(status) ||
parsed_length < static_cast<int>(bcp47_locale.length()) ||
status == U_STRING_NOT_TERMINATED_WARNING || icu_length == 0) {
THROW_NEW_ERROR(
isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters,
isolate->factory()->NewStringFromAsciiChecked(kMethod),
locale_holder),
if (!IsValidLanguageTag(*bcp47_locale, bcp47_locale.length())) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters),
JSLocale);
}
Maybe<bool> error = InsertOptionsIntoLocale(isolate, options, icu_result);
MAYBE_RETURN(error, MaybeHandle<JSLocale>());
if (!error.FromJust()) {
THROW_NEW_ERROR(
isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters,
isolate->factory()->NewStringFromAsciiChecked(kMethod),
locale_holder),
JSLocale);
}
if (!PopulateLocaleWithUnicodeTags(isolate, icu_result, locale_holder)) {
THROW_NEW_ERROR(
isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters,
isolate->factory()->NewStringFromAsciiChecked(kMethod),
locale_holder),
JSLocale);
}
// Extract language, script and region parts.
char icu_language[ULOC_LANG_CAPACITY];
uloc_getLanguage(icu_result, icu_language, ULOC_LANG_CAPACITY, &status);
char icu_script[ULOC_SCRIPT_CAPACITY];
uloc_getScript(icu_result, icu_script, ULOC_SCRIPT_CAPACITY, &status);
char icu_region[ULOC_COUNTRY_CAPACITY];
uloc_getCountry(icu_result, icu_region, ULOC_COUNTRY_CAPACITY, &status);
status = U_ZERO_ERROR;
icu::Locale icu_locale = icu::Locale::forLanguageTag(*bcp47_locale, status);
if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
THROW_NEW_ERROR(
isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters,
isolate->factory()->NewStringFromAsciiChecked(kMethod),
locale_holder),
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters),
JSLocale);
}
Factory* factory = isolate->factory();
// NOTE: One shouldn't use temporary handles, because they can go out of
// scope and be garbage collected before properly assigned.
// DON'T DO THIS: locale_holder->set_language(*f->NewStringAscii...);
Handle<String> language = factory->NewStringFromAsciiChecked(icu_language);
locale_holder->set_language(*language);
if (strlen(icu_script) != 0) {
Handle<String> script = factory->NewStringFromAsciiChecked(icu_script);
locale_holder->set_script(*script);
}
if (strlen(icu_region) != 0) {
Handle<String> region = factory->NewStringFromAsciiChecked(icu_region);
locale_holder->set_region(*region);
}
char icu_base_name[ULOC_FULLNAME_CAPACITY];
uloc_getBaseName(icu_result, icu_base_name, ULOC_FULLNAME_CAPACITY, &status);
// We need to convert it back to BCP47.
char bcp47_result[ULOC_FULLNAME_CAPACITY];
uloc_toLanguageTag(icu_base_name, bcp47_result, ULOC_FULLNAME_CAPACITY, true,
&status);
if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
THROW_NEW_ERROR(
isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters,
isolate->factory()->NewStringFromAsciiChecked(kMethod),
locale_holder),
Maybe<bool> error = InsertOptionsIntoLocale(isolate, options, &icu_locale);
MAYBE_RETURN(error, MaybeHandle<JSLocale>());
if (!error.FromJust()) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters),
JSLocale);
}
Handle<String> base_name = factory->NewStringFromAsciiChecked(bcp47_result);
locale_holder->set_base_name(*base_name);
// Produce final representation of the locale string, for toString().
uloc_toLanguageTag(icu_result, bcp47_result, ULOC_FULLNAME_CAPACITY, true,
&status);
if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
THROW_NEW_ERROR(
isolate,
NewRangeError(MessageTemplate::kLocaleBadParameters,
isolate->factory()->NewStringFromAsciiChecked(kMethod),
locale_holder),
JSLocale);
}
Handle<String> locale_handle =
factory->NewStringFromAsciiChecked(bcp47_result);
locale_holder->set_locale(*locale_handle);
// 31. Set locale.[[Locale]] to r.[[locale]].
Handle<Managed<icu::Locale>> managed_locale =
Managed<icu::Locale>::FromRawPtr(isolate, 0, icu_locale.clone());
locale->set_icu_locale(*managed_locale);
return locale_holder;
return locale;
}
namespace {
Handle<String> MorphLocale(Isolate* isolate, String locale,
void (*morph_func)(icu::Locale* l,
UErrorCode* status)) {
void (*morph_func)(icu::Locale*, UErrorCode*)) {
UErrorCode status = U_ZERO_ERROR;
icu::Locale icu_locale =
icu::Locale::forLanguageTag(locale.ToCString().get(), status);
......@@ -323,9 +215,10 @@ Handle<String> MorphLocale(Isolate* isolate, String locale,
(*morph_func)(&icu_locale, &status);
CHECK(U_SUCCESS(status));
CHECK(!icu_locale.isBogus());
return isolate->factory()->NewStringFromAsciiChecked(
Intl::ToLanguageTag(icu_locale).c_str());
std::string locale_str = Intl::ToLanguageTag(icu_locale);
return isolate->factory()->NewStringFromAsciiChecked(locale_str.c_str());
}
} // namespace
Handle<String> JSLocale::Maximize(Isolate* isolate, String locale) {
......@@ -342,45 +235,68 @@ Handle<String> JSLocale::Minimize(Isolate* isolate, String locale) {
});
}
Handle<String> JSLocale::CaseFirstAsString() const {
switch (case_first()) {
case CaseFirst::UPPER:
return GetReadOnlyRoots().upper_string_handle();
case CaseFirst::LOWER:
return GetReadOnlyRoots().lower_string_handle();
case CaseFirst::FALSE_VALUE:
return GetReadOnlyRoots().false_string_handle();
case CaseFirst::COUNT:
UNREACHABLE();
}
Handle<Object> JSLocale::Language(Isolate* isolate, Handle<JSLocale> locale) {
Factory* factory = isolate->factory();
const char* language = locale->icu_locale()->raw()->getLanguage();
if (strlen(language) == 0) return factory->undefined_value();
return factory->NewStringFromAsciiChecked(language);
}
Handle<String> JSLocale::HourCycleAsString() const {
switch (hour_cycle()) {
case HourCycle::H11:
return GetReadOnlyRoots().h11_string_handle();
case HourCycle::H12:
return GetReadOnlyRoots().h12_string_handle();
case HourCycle::H23:
return GetReadOnlyRoots().h23_string_handle();
case HourCycle::H24:
return GetReadOnlyRoots().h24_string_handle();
case HourCycle::COUNT:
UNREACHABLE();
}
Handle<Object> JSLocale::Script(Isolate* isolate, Handle<JSLocale> locale) {
Factory* factory = isolate->factory();
const char* script = locale->icu_locale()->raw()->getScript();
if (strlen(script) == 0) return factory->undefined_value();
return factory->NewStringFromAsciiChecked(script);
}
Handle<String> JSLocale::NumericAsString() const {
switch (numeric()) {
case Numeric::NOTSET:
return GetReadOnlyRoots().undefined_string_handle();
case Numeric::TRUE_VALUE:
return GetReadOnlyRoots().true_string_handle();
case Numeric::FALSE_VALUE:
return GetReadOnlyRoots().false_string_handle();
case Numeric::COUNT:
UNREACHABLE();
}
Handle<Object> JSLocale::Region(Isolate* isolate, Handle<JSLocale> locale) {
Factory* factory = isolate->factory();
const char* region = locale->icu_locale()->raw()->getCountry();
if (strlen(region) == 0) return factory->undefined_value();
return factory->NewStringFromAsciiChecked(region);
}
Handle<String> JSLocale::BaseName(Isolate* isolate, Handle<JSLocale> locale) {
icu::Locale icu_locale =
icu::Locale::createFromName(locale->icu_locale()->raw()->getBaseName());
std::string base_name = Intl::ToLanguageTag(icu_locale);
return isolate->factory()->NewStringFromAsciiChecked(base_name.c_str());
}
Handle<Object> JSLocale::Calendar(Isolate* isolate, Handle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "ca");
}
Handle<Object> JSLocale::CaseFirst(Isolate* isolate, Handle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "kf");
}
Handle<Object> JSLocale::Collation(Isolate* isolate, Handle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "co");
}
Handle<Object> JSLocale::HourCycle(Isolate* isolate, Handle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "hc");
}
Handle<Object> JSLocale::Numeric(Isolate* isolate, Handle<JSLocale> locale) {
Factory* factory = isolate->factory();
icu::Locale* icu_locale = locale->icu_locale()->raw();
UErrorCode status = U_ZERO_ERROR;
std::string numeric =
icu_locale->getUnicodeKeywordValue<std::string>("kn", status);
return (numeric == "true") ? factory->true_value() : factory->false_value();
}
Handle<Object> JSLocale::NumberingSystem(Isolate* isolate,
Handle<JSLocale> locale) {
return UnicodeKeywordValue(isolate, locale, "nu");
}
Handle<String> JSLocale::ToString(Isolate* isolate, Handle<JSLocale> locale) {
icu::Locale* icu_locale = locale->icu_locale()->raw();
std::string locale_str = Intl::ToLanguageTag(*icu_locale);
return isolate->factory()->NewStringFromAsciiChecked(locale_str.c_str());
}
} // namespace internal
......
......@@ -13,11 +13,15 @@
#include "src/heap/factory.h"
#include "src/isolate.h"
#include "src/objects.h"
#include "unicode/unistr.h"
#include "src/objects/managed.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace U_ICU_NAMESPACE {
class Locale;
}
namespace v8 {
namespace internal {
......@@ -32,97 +36,29 @@ class JSLocale : public JSObject {
static Handle<String> Maximize(Isolate* isolate, String locale);
static Handle<String> Minimize(Isolate* isolate, String locale);
Handle<String> CaseFirstAsString() const;
Handle<String> NumericAsString() const;
Handle<String> HourCycleAsString() const;
static Handle<Object> Language(Isolate* isolate, Handle<JSLocale> locale);
static Handle<Object> Script(Isolate* isolate, Handle<JSLocale> locale);
static Handle<Object> Region(Isolate* isolate, Handle<JSLocale> locale);
static Handle<String> BaseName(Isolate* isolate, Handle<JSLocale> locale);
static Handle<Object> Calendar(Isolate* isolate, Handle<JSLocale> locale);
static Handle<Object> CaseFirst(Isolate* isolate, Handle<JSLocale> locale);
static Handle<Object> Collation(Isolate* isolate, Handle<JSLocale> locale);
static Handle<Object> HourCycle(Isolate* isolate, Handle<JSLocale> locale);
static Handle<Object> Numeric(Isolate* isolate, Handle<JSLocale> locale);
static Handle<Object> NumberingSystem(Isolate* isolate,
Handle<JSLocale> locale);
static Handle<String> ToString(Isolate* isolate, Handle<JSLocale> locale);
DECL_CAST2(JSLocale)
// Locale accessors.
DECL_ACCESSORS(language, Object)
DECL_ACCESSORS(script, Object)
DECL_ACCESSORS(region, Object)
DECL_ACCESSORS(base_name, Object)
DECL_ACCESSORS2(locale, String)
// Unicode extension accessors.
DECL_ACCESSORS(calendar, Object)
DECL_ACCESSORS(collation, Object)
DECL_ACCESSORS(numbering_system, Object)
// CaseFirst: "kf"
//
// ecma402 #sec-Intl.Locale.prototype.caseFirst
enum class CaseFirst {
UPPER, // upper case sorts before lower case
LOWER, // lower case sorts before upper case
// (compiler does not like FALSE so we have to name it FALSE_VALUE)
FALSE_VALUE, // Turn the feature off
COUNT
};
inline void set_case_first(CaseFirst case_first);
inline CaseFirst case_first() const;
// Numeric: 'kn"
//
// ecma402 #sec-Intl.Locale.prototype.numeric
enum class Numeric { NOTSET, TRUE_VALUE, FALSE_VALUE, COUNT };
inline void set_numeric(Numeric numeric);
inline Numeric numeric() const;
// CaseFirst: "hc"
//
// ecma402 #sec-Intl.Locale.prototype.hourCycle
enum class HourCycle {
H11, // 12-hour format start with hour 0 and go up to 11.
H12, // 12-hour format start with hour 1 and go up to 12.
H23, // 24-hour format start with hour 0 and go up to 23.
H24, // 24-hour format start with hour 1 and go up to 24.
COUNT
};
inline void set_hour_cycle(HourCycle hour_cycle);
inline HourCycle hour_cycle() const;
// Bit positions in |flags|.
#define FLAGS_BIT_FIELDS(V, _) \
V(CaseFirstBits, CaseFirst, 2, _) \
V(NumericBits, Numeric, 2, _) \
V(HourCycleBits, HourCycle, 2, _)
DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS)
#undef FLAGS_BIT_FIELDS
STATIC_ASSERT(CaseFirst::UPPER <= CaseFirstBits::kMax);
STATIC_ASSERT(CaseFirst::LOWER <= CaseFirstBits::kMax);
STATIC_ASSERT(CaseFirst::FALSE_VALUE <= CaseFirstBits::kMax);
STATIC_ASSERT(Numeric::NOTSET <= NumericBits::kMax);
STATIC_ASSERT(Numeric::FALSE_VALUE <= NumericBits::kMax);
STATIC_ASSERT(Numeric::TRUE_VALUE <= NumericBits::kMax);
STATIC_ASSERT(HourCycle::H11 <= HourCycleBits::kMax);
STATIC_ASSERT(HourCycle::H12 <= HourCycleBits::kMax);
STATIC_ASSERT(HourCycle::H23 <= HourCycleBits::kMax);
STATIC_ASSERT(HourCycle::H24 <= HourCycleBits::kMax);
// [flags] Bit field containing various flags about the function.
DECL_INT_ACCESSORS(flags)
DECL_ACCESSORS2(icu_locale, Managed<icu::Locale>)
DECL_PRINTER(JSLocale)
DECL_VERIFIER(JSLocale)
// Layout description.
#define JS_LOCALE_FIELDS(V) \
V(kJSLocaleOffset, kTaggedSize) \
/* Locale fields. */ \
V(kLanguageOffset, kTaggedSize) \
V(kScriptOffset, kTaggedSize) \
V(kRegionOffset, kTaggedSize) \
V(kBaseNameOffset, kTaggedSize) \
V(kLocaleOffset, kTaggedSize) \
/* Unicode extension fields. */ \
V(kFlagsOffset, kTaggedSize) \
V(kCalendarOffset, kTaggedSize) \
V(kCollationOffset, kTaggedSize) \
V(kNumberingSystemOffset, kTaggedSize) \
/* Header size. */ \
V(kICULocaleOffset, kTaggedSize) \
V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, JS_LOCALE_FIELDS)
......
......@@ -19,6 +19,6 @@ let locale = new Intl.Locale('sr-cyrl-rs-t-ja-u-ca-islamic-x-whatever', {
});
let expected =
'sr-Cyrl-RS-t-ja-u-ca-buddhist-co-phonebk-hc-h23-kf-upper-kn-true-nu-roman-x-whatever';
'sr-Cyrl-RS-t-ja-u-ca-buddhist-co-phonebk-hc-h23-kf-upper-kn-nu-roman-x-whatever';
assertEquals(expected, locale.toString());
......@@ -618,10 +618,6 @@
'intl402/Locale/getters-grandfathered': [FAIL],
'intl402/Locale/likely-subtags-grandfathered': [FAIL],
# Wrong test see https://github.com/tc39/test262/pull/1835
'intl402/Locale/constructor-options-numeric-valid': [FAIL],
'intl402/Locale/constructor-options-numeric-undefined': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=6705
'built-ins/Object/assign/strings-and-symbol-order': [FAIL],
......
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