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, ...@@ -537,10 +537,9 @@ MaybeHandle<JSLocale> CreateLocale(Isolate* isolate,
Handle<String> locale_string; Handle<String> locale_string;
// 8. If Type(tag) is Object and tag has an [[InitializedLocale]] internal // 8. If Type(tag) is Object and tag has an [[InitializedLocale]] internal
// slot, then // slot, then
if (tag->IsJSLocale() && Handle<JSLocale>::cast(tag)->locale()->IsString()) { if (tag->IsJSLocale()) {
// a. Let tag be tag.[[Locale]]. // a. Let tag be tag.[[Locale]].
locale_string = locale_string = JSLocale::ToString(isolate, Handle<JSLocale>::cast(tag));
Handle<String>(Handle<JSLocale>::cast(tag)->locale(), isolate);
} else { // 9. Else, } else { // 9. Else,
// a. Let tag be ? ToString(tag). // a. Let tag be ? ToString(tag).
ASSIGN_RETURN_ON_EXCEPTION(isolate, locale_string, ASSIGN_RETURN_ON_EXCEPTION(isolate, locale_string,
...@@ -589,25 +588,25 @@ BUILTIN(LocaleConstructor) { ...@@ -589,25 +588,25 @@ BUILTIN(LocaleConstructor) {
BUILTIN(LocalePrototypeMaximize) { BUILTIN(LocalePrototypeMaximize) {
HandleScope scope(isolate); HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.maximize"); CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.maximize");
Handle<JSFunction> constructor( Handle<JSFunction> constructor(
isolate->native_context()->intl_locale_function(), isolate); isolate->native_context()->intl_locale_function(), isolate);
Handle<String> locale_str = JSLocale::ToString(isolate, locale);
RETURN_RESULT_OR_FAILURE( RETURN_RESULT_OR_FAILURE(
isolate, isolate, CreateLocale(isolate, constructor, constructor,
CreateLocale(isolate, constructor, constructor, JSLocale::Maximize(isolate, *locale_str),
JSLocale::Maximize(isolate, locale_holder->locale()),
isolate->factory()->NewJSObjectWithNullProto())); isolate->factory()->NewJSObjectWithNullProto()));
} }
BUILTIN(LocalePrototypeMinimize) { BUILTIN(LocalePrototypeMinimize) {
HandleScope scope(isolate); HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.minimize"); CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.minimize");
Handle<JSFunction> constructor( Handle<JSFunction> constructor(
isolate->native_context()->intl_locale_function(), isolate); isolate->native_context()->intl_locale_function(), isolate);
Handle<String> locale_str = JSLocale::ToString(isolate, locale);
RETURN_RESULT_OR_FAILURE( RETURN_RESULT_OR_FAILURE(
isolate, isolate, CreateLocale(isolate, constructor, constructor,
CreateLocale(isolate, constructor, constructor, JSLocale::Minimize(isolate, *locale_str),
JSLocale::Minimize(isolate, locale_holder->locale()),
isolate->factory()->NewJSObjectWithNullProto())); isolate->factory()->NewJSObjectWithNullProto()));
} }
...@@ -658,89 +657,79 @@ BUILTIN(RelativeTimeFormatPrototypeFormatToParts) { ...@@ -658,89 +657,79 @@ BUILTIN(RelativeTimeFormatPrototypeFormatToParts) {
BUILTIN(LocalePrototypeLanguage) { BUILTIN(LocalePrototypeLanguage) {
HandleScope scope(isolate); HandleScope scope(isolate);
// CHECK_RECEIVER will case locale_holder to JSLocale. // 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) { BUILTIN(LocalePrototypeScript) {
HandleScope scope(isolate); 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) { BUILTIN(LocalePrototypeRegion) {
HandleScope scope(isolate); 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) { BUILTIN(LocalePrototypeBaseName) {
HandleScope scope(isolate); 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) { BUILTIN(LocalePrototypeCalendar) {
HandleScope scope(isolate); 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) { BUILTIN(LocalePrototypeCaseFirst) {
HandleScope scope(isolate); 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) { BUILTIN(LocalePrototypeCollation) {
HandleScope scope(isolate); 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) { BUILTIN(LocalePrototypeHourCycle) {
HandleScope scope(isolate); 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) { BUILTIN(LocalePrototypeNumeric) {
HandleScope scope(isolate); HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.numeric"); CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.numeric");
switch (locale_holder->numeric()) { return *JSLocale::Numeric(isolate, locale);
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();
}
} }
BUILTIN(LocalePrototypeNumberingSystem) { BUILTIN(LocalePrototypeNumberingSystem) {
HandleScope scope(isolate); HandleScope scope(isolate);
CHECK_RECEIVER(JSLocale, locale_holder, CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.numberingSystem");
"Intl.Locale.prototype.numberingSystem");
return locale_holder->numbering_system(); return *JSLocale::NumberingSystem(isolate, locale);
} }
BUILTIN(LocalePrototypeToString) { BUILTIN(LocalePrototypeToString) {
HandleScope scope(isolate); 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) { BUILTIN(RelativeTimeFormatConstructor) {
......
...@@ -2089,16 +2089,7 @@ void JSListFormat::JSListFormatVerify(Isolate* isolate) { ...@@ -2089,16 +2089,7 @@ void JSListFormat::JSListFormatVerify(Isolate* isolate) {
void JSLocale::JSLocaleVerify(Isolate* isolate) { void JSLocale::JSLocaleVerify(Isolate* isolate) {
JSObjectVerify(isolate); JSObjectVerify(isolate);
VerifyObjectField(isolate, kLanguageOffset); VerifyObjectField(isolate, kICULocaleOffset);
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);
} }
void JSNumberFormat::JSNumberFormatVerify(Isolate* isolate) { void JSNumberFormat::JSNumberFormatVerify(Isolate* isolate) {
......
...@@ -2065,18 +2065,8 @@ void JSListFormat::JSListFormatPrint(std::ostream& os) { // NOLINT ...@@ -2065,18 +2065,8 @@ void JSListFormat::JSListFormatPrint(std::ostream& os) { // NOLINT
void JSLocale::JSLocalePrint(std::ostream& os) { // NOLINT void JSLocale::JSLocalePrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, *this, "JSLocale"); JSObjectPrintHeader(os, *this, "JSLocale");
os << "\n - language: " << Brief(language()); os << "\n - icu locale: " << Brief(icu_locale());
os << "\n - script: " << Brief(script()); JSObjectPrintBody(os, *this);
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";
} }
void JSNumberFormat::JSNumberFormatPrint(std::ostream& os) { // NOLINT void JSNumberFormat::JSNumberFormatPrint(std::ostream& os) { // NOLINT
......
...@@ -21,54 +21,10 @@ namespace internal { ...@@ -21,54 +21,10 @@ namespace internal {
OBJECT_CONSTRUCTORS_IMPL(JSLocale, JSObject) OBJECT_CONSTRUCTORS_IMPL(JSLocale, JSObject)
// Base locale accessors. ACCESSORS2(JSLocale, icu_locale, Managed<icu::Locale>, kICULocaleOffset);
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)
CAST_ACCESSOR2(JSLocale); 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 internal
} // namespace v8 } // namespace v8
......
...@@ -30,24 +30,36 @@ namespace internal { ...@@ -30,24 +30,36 @@ namespace internal {
namespace { namespace {
JSLocale::CaseFirst GetCaseFirst(const char* str) { // Helper function to check a language tag is valid. It will return false if
if (strcmp(str, "upper") == 0) return JSLocale::CaseFirst::UPPER; // the parsing is not the same as the tag. For example, it will return false if
if (strcmp(str, "lower") == 0) return JSLocale::CaseFirst::LOWER; // the tag is too long.
if (strcmp(str, "false") == 0) return JSLocale::CaseFirst::FALSE_VALUE; bool IsValidLanguageTag(const char* tag, int length) {
UNREACHABLE(); // icu::Locale::forLanguageTag won't return U_STRING_NOT_TERMINATED_WARNING
} // for incorrect locale yet. So we still need the following
// uloc_forLanguageTag
JSLocale::HourCycle GetHourCycle(const char* str) { // TODO(ftang): Remove once icu::Locale::forLanguageTag indicate error.
if (strcmp(str, "h11") == 0) return JSLocale::HourCycle::H11; char result[ULOC_FULLNAME_CAPACITY];
if (strcmp(str, "h12") == 0) return JSLocale::HourCycle::H12; UErrorCode status = U_ZERO_ERROR;
if (strcmp(str, "h23") == 0) return JSLocale::HourCycle::H23; int parsed_length = 0;
if (strcmp(str, "h24") == 0) return JSLocale::HourCycle::H24; int icu_length = uloc_forLanguageTag(tag, result, ULOC_FULLNAME_CAPACITY,
UNREACHABLE(); &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) { // Helper function to check a locale is valid. It will return false if
return strcmp(str, "true") == 0 ? JSLocale::Numeric::TRUE_VALUE // the length of the extension fields are incorrect. For example, en-u-a or
: JSLocale::Numeric::FALSE_VALUE; // 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 { struct OptionData {
...@@ -60,9 +72,9 @@ struct OptionData { ...@@ -60,9 +72,9 @@ struct OptionData {
// Inserts tags from options into locale string. // Inserts tags from options into locale string.
Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate, Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
Handle<JSReceiver> options, Handle<JSReceiver> options,
char* icu_locale) { icu::Locale* icu_locale) {
CHECK(isolate); CHECK(isolate);
CHECK(icu_locale); CHECK(!icu_locale->isBogus());
const std::vector<const char*> hour_cycle_values = {"h11", "h12", "h23", const std::vector<const char*> hour_cycle_values = {"h11", "h12", "h23",
"h24"}; "h24"};
...@@ -80,6 +92,7 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate, ...@@ -80,6 +92,7 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
// TODO(cira): Pass in values as per the spec to make this to be // TODO(cira): Pass in values as per the spec to make this to be
// spec compliant. // spec compliant.
UErrorCode status = U_ZERO_ERROR;
for (const auto& option_to_bcp47 : kOptionToUnicodeTagMap) { for (const auto& option_to_bcp47 : kOptionToUnicodeTagMap) {
std::unique_ptr<char[]> value_str = nullptr; std::unique_ptr<char[]> value_str = nullptr;
bool value_bool = false; bool value_bool = false;
...@@ -109,14 +122,9 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate, ...@@ -109,14 +122,9 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate,
// Overwrite existing, or insert new key-value to the locale string. // Overwrite existing, or insert new key-value to the locale string.
const char* value = uloc_toLegacyType(key, value_str.get()); const char* value = uloc_toLegacyType(key, value_str.get());
UErrorCode status = U_ZERO_ERROR;
if (value) { if (value) {
// TODO(cira): ICU puts artificial limit on locale length, while BCP47 icu_locale->setKeywordValue(key, value, status);
// doesn't. Switch to C++ API when it's ready. if (U_FAILURE(status)) {
// 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) {
return Just(false); return Just(false);
} }
} else { } else {
...@@ -124,197 +132,81 @@ Maybe<bool> InsertOptionsIntoLocale(Isolate* isolate, ...@@ -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); return Just(true);
} }
// Fills in the JSLocale object slots with Unicode tag/values. Handle<Object> UnicodeKeywordValue(Isolate* isolate, Handle<JSLocale> locale,
bool PopulateLocaleWithUnicodeTags(Isolate* isolate, const char* icu_locale, const char* key) {
Handle<JSLocale> locale_holder) { icu::Locale* icu_locale = locale->icu_locale()->raw();
CHECK(isolate);
CHECK(icu_locale);
Factory* factory = isolate->factory();
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
UEnumeration* keywords = uloc_openKeywords(icu_locale, &status); std::string value =
if (!keywords) return true; icu_locale->getUnicodeKeywordValue<std::string>(key, status);
CHECK(U_SUCCESS(status));
char value[ULOC_FULLNAME_CAPACITY]; if (value == "") {
while (const char* keyword = uenum_next(keywords, nullptr, &status)) { return isolate->factory()->undefined_value();
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);
}
}
}
} }
return isolate->factory()->NewStringFromAsciiChecked(value.c_str());
uenum_close(keywords);
return true;
} }
} // namespace } // namespace
MaybeHandle<JSLocale> JSLocale::Initialize(Isolate* isolate, MaybeHandle<JSLocale> JSLocale::Initialize(Isolate* isolate,
Handle<JSLocale> locale_holder, Handle<JSLocale> locale,
Handle<String> locale, Handle<String> locale_str,
Handle<JSReceiver> options) { Handle<JSReceiver> options) {
locale_holder->set_flags(0);
static const char* const kMethod = "Intl.Locale";
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
// Get ICU locale format, and canonicalize it. if (locale_str->length() == 0) {
char icu_result[ULOC_FULLNAME_CAPACITY];
if (locale->length() == 0) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kLocaleNotEmpty), THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kLocaleNotEmpty),
JSLocale); 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_LT(0, bcp47_locale.length());
CHECK_NOT_NULL(*bcp47_locale); CHECK_NOT_NULL(*bcp47_locale);
int parsed_length = 0; if (!IsValidLanguageTag(*bcp47_locale, bcp47_locale.length())) {
int icu_length = THROW_NEW_ERROR(isolate,
uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, NewRangeError(MessageTemplate::kLocaleBadParameters),
&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),
JSLocale); JSLocale);
} }
Maybe<bool> error = InsertOptionsIntoLocale(isolate, options, icu_result); status = U_ZERO_ERROR;
MAYBE_RETURN(error, MaybeHandle<JSLocale>()); icu::Locale icu_locale = icu::Locale::forLanguageTag(*bcp47_locale, status);
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);
if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
THROW_NEW_ERROR( THROW_NEW_ERROR(isolate,
isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
NewRangeError(MessageTemplate::kLocaleBadParameters,
isolate->factory()->NewStringFromAsciiChecked(kMethod),
locale_holder),
JSLocale); JSLocale);
} }
Factory* factory = isolate->factory(); Maybe<bool> error = InsertOptionsIntoLocale(isolate, options, &icu_locale);
MAYBE_RETURN(error, MaybeHandle<JSLocale>());
// NOTE: One shouldn't use temporary handles, because they can go out of if (!error.FromJust()) {
// scope and be garbage collected before properly assigned. THROW_NEW_ERROR(isolate,
// DON'T DO THIS: locale_holder->set_language(*f->NewStringAscii...); NewRangeError(MessageTemplate::kLocaleBadParameters),
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),
JSLocale); 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(). // 31. Set locale.[[Locale]] to r.[[locale]].
uloc_toLanguageTag(icu_result, bcp47_result, ULOC_FULLNAME_CAPACITY, true, Handle<Managed<icu::Locale>> managed_locale =
&status); Managed<icu::Locale>::FromRawPtr(isolate, 0, icu_locale.clone());
if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { locale->set_icu_locale(*managed_locale);
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);
return locale_holder; return locale;
} }
namespace { namespace {
Handle<String> MorphLocale(Isolate* isolate, String locale, Handle<String> MorphLocale(Isolate* isolate, String locale,
void (*morph_func)(icu::Locale* l, void (*morph_func)(icu::Locale*, UErrorCode*)) {
UErrorCode* status)) {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
icu::Locale icu_locale = icu::Locale icu_locale =
icu::Locale::forLanguageTag(locale.ToCString().get(), status); icu::Locale::forLanguageTag(locale.ToCString().get(), status);
...@@ -323,9 +215,10 @@ Handle<String> MorphLocale(Isolate* isolate, String locale, ...@@ -323,9 +215,10 @@ Handle<String> MorphLocale(Isolate* isolate, String locale,
(*morph_func)(&icu_locale, &status); (*morph_func)(&icu_locale, &status);
CHECK(U_SUCCESS(status)); CHECK(U_SUCCESS(status));
CHECK(!icu_locale.isBogus()); CHECK(!icu_locale.isBogus());
return isolate->factory()->NewStringFromAsciiChecked( std::string locale_str = Intl::ToLanguageTag(icu_locale);
Intl::ToLanguageTag(icu_locale).c_str()); return isolate->factory()->NewStringFromAsciiChecked(locale_str.c_str());
} }
} // namespace } // namespace
Handle<String> JSLocale::Maximize(Isolate* isolate, String locale) { Handle<String> JSLocale::Maximize(Isolate* isolate, String locale) {
...@@ -342,45 +235,68 @@ Handle<String> JSLocale::Minimize(Isolate* isolate, String locale) { ...@@ -342,45 +235,68 @@ Handle<String> JSLocale::Minimize(Isolate* isolate, String locale) {
}); });
} }
Handle<String> JSLocale::CaseFirstAsString() const { Handle<Object> JSLocale::Language(Isolate* isolate, Handle<JSLocale> locale) {
switch (case_first()) { Factory* factory = isolate->factory();
case CaseFirst::UPPER: const char* language = locale->icu_locale()->raw()->getLanguage();
return GetReadOnlyRoots().upper_string_handle(); if (strlen(language) == 0) return factory->undefined_value();
case CaseFirst::LOWER: return factory->NewStringFromAsciiChecked(language);
return GetReadOnlyRoots().lower_string_handle();
case CaseFirst::FALSE_VALUE:
return GetReadOnlyRoots().false_string_handle();
case CaseFirst::COUNT:
UNREACHABLE();
}
} }
Handle<String> JSLocale::HourCycleAsString() const { Handle<Object> JSLocale::Script(Isolate* isolate, Handle<JSLocale> locale) {
switch (hour_cycle()) { Factory* factory = isolate->factory();
case HourCycle::H11: const char* script = locale->icu_locale()->raw()->getScript();
return GetReadOnlyRoots().h11_string_handle(); if (strlen(script) == 0) return factory->undefined_value();
case HourCycle::H12: return factory->NewStringFromAsciiChecked(script);
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<String> JSLocale::NumericAsString() const { Handle<Object> JSLocale::Region(Isolate* isolate, Handle<JSLocale> locale) {
switch (numeric()) { Factory* factory = isolate->factory();
case Numeric::NOTSET: const char* region = locale->icu_locale()->raw()->getCountry();
return GetReadOnlyRoots().undefined_string_handle(); if (strlen(region) == 0) return factory->undefined_value();
case Numeric::TRUE_VALUE: return factory->NewStringFromAsciiChecked(region);
return GetReadOnlyRoots().true_string_handle(); }
case Numeric::FALSE_VALUE:
return GetReadOnlyRoots().false_string_handle(); Handle<String> JSLocale::BaseName(Isolate* isolate, Handle<JSLocale> locale) {
case Numeric::COUNT: icu::Locale icu_locale =
UNREACHABLE(); 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 } // namespace internal
......
...@@ -13,11 +13,15 @@ ...@@ -13,11 +13,15 @@
#include "src/heap/factory.h" #include "src/heap/factory.h"
#include "src/isolate.h" #include "src/isolate.h"
#include "src/objects.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): // Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h" #include "src/objects/object-macros.h"
namespace U_ICU_NAMESPACE {
class Locale;
}
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -32,97 +36,29 @@ class JSLocale : public JSObject { ...@@ -32,97 +36,29 @@ class JSLocale : public JSObject {
static Handle<String> Maximize(Isolate* isolate, String locale); static Handle<String> Maximize(Isolate* isolate, String locale);
static Handle<String> Minimize(Isolate* isolate, String locale); static Handle<String> Minimize(Isolate* isolate, String locale);
Handle<String> CaseFirstAsString() const; static Handle<Object> Language(Isolate* isolate, Handle<JSLocale> locale);
Handle<String> NumericAsString() const; static Handle<Object> Script(Isolate* isolate, Handle<JSLocale> locale);
Handle<String> HourCycleAsString() const; 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) DECL_CAST2(JSLocale)
// Locale accessors. DECL_ACCESSORS2(icu_locale, Managed<icu::Locale>)
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_PRINTER(JSLocale) DECL_PRINTER(JSLocale)
DECL_VERIFIER(JSLocale) DECL_VERIFIER(JSLocale)
// Layout description. // Layout description.
#define JS_LOCALE_FIELDS(V) \ #define JS_LOCALE_FIELDS(V) \
V(kJSLocaleOffset, kTaggedSize) \ V(kICULocaleOffset, 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(kSize, 0) V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, JS_LOCALE_FIELDS) 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', { ...@@ -19,6 +19,6 @@ let locale = new Intl.Locale('sr-cyrl-rs-t-ja-u-ca-islamic-x-whatever', {
}); });
let expected = 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()); assertEquals(expected, locale.toString());
...@@ -618,10 +618,6 @@ ...@@ -618,10 +618,6 @@
'intl402/Locale/getters-grandfathered': [FAIL], 'intl402/Locale/getters-grandfathered': [FAIL],
'intl402/Locale/likely-subtags-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 # https://bugs.chromium.org/p/v8/issues/detail?id=6705
'built-ins/Object/assign/strings-and-symbol-order': [FAIL], '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