Commit 942f3e11 authored by Frank Tang's avatar Frank Tang Committed by Commit Bot

Better GetAvilableLocales check resources

1. Check resources and not solely depend on res_index.res file
2. Performance is +2-3% for Collator, DateTimeFormat, Locale,
   -2-3% for PluralRules, RelativeTimeFormat, ListFormat, NumberFormat
   Consider we improve the performance x3 not long ago, these perf
   regression could be ignored.

Bug: v8:9340
Change-Id: Iab7cd64a77a55a03aae40f4d477523c37b3bcd3d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1655978
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarJungshik Shin <jshin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62322}
parent 15396549
......@@ -38,6 +38,7 @@
#include "unicode/numfmt.h"
#include "unicode/numsys.h"
#include "unicode/timezone.h"
#include "unicode/ures.h"
#include "unicode/ustring.h"
#include "unicode/uvernum.h" // U_ICU_VERSION_MAJOR_NUM
......@@ -502,23 +503,59 @@ bool RemoveLocaleScriptTag(const std::string& icu_locale,
return true;
}
bool ValidateResource(const icu::Locale locale, const char* path,
const char* key) {
bool result = false;
UErrorCode status = U_ZERO_ERROR;
UResourceBundle* bundle = ures_open(path, locale.getName(), &status);
if (bundle != nullptr && status == U_ZERO_ERROR) {
if (key == nullptr) {
result = true;
} else {
UResourceBundle* key_bundle =
ures_getByKey(bundle, key, nullptr, &status);
result = key_bundle != nullptr && (status == U_ZERO_ERROR);
ures_close(key_bundle);
}
}
ures_close(bundle);
if (!result) {
if ((locale.getCountry()[0] != '\0') && (locale.getScript()[0] != '\0')) {
// Fallback to try without country.
std::string without_country(locale.getLanguage());
without_country = without_country.append("-").append(locale.getScript());
return ValidateResource(without_country.c_str(), path, key);
} else if ((locale.getCountry()[0] != '\0') ||
(locale.getScript()[0] != '\0')) {
// Fallback to try with only language.
std::string language(locale.getLanguage());
return ValidateResource(language.c_str(), path, key);
}
}
return result;
}
} // namespace
std::set<std::string> Intl::BuildLocaleSet(
const icu::Locale* icu_available_locales, int32_t count) {
const icu::Locale* icu_available_locales, int32_t count, const char* path,
const char* validate_key) {
std::set<std::string> locales;
for (int32_t i = 0; i < count; ++i) {
std::string locale =
Intl::ToLanguageTag(icu_available_locales[i]).FromJust();
if (path != nullptr || validate_key != nullptr) {
if (!ValidateResource(icu_available_locales[i], path, validate_key)) {
continue;
}
}
locales.insert(locale);
std::string shortened_locale;
if (RemoveLocaleScriptTag(locale, &shortened_locale)) {
std::replace(shortened_locale.begin(), shortened_locale.end(), '_', '-');
locales.insert(shortened_locale);
}
}
return locales;
}
......@@ -1908,8 +1945,18 @@ const std::set<std::string>& Intl::GetAvailableLocalesForLocale() {
return available_locales.Pointer()->Get();
}
namespace {
struct CheckCalendar {
static const char* key() { return "calendar"; }
static const char* path() { return nullptr; }
};
} // namespace
const std::set<std::string>& Intl::GetAvailableLocalesForDateFormat() {
static base::LazyInstance<Intl::AvailableLocales<icu::DateFormat>>::type
static base::LazyInstance<
Intl::AvailableLocales<icu::DateFormat, CheckCalendar>>::type
available_locales = LAZY_INSTANCE_INITIALIZER;
return available_locales.Pointer()->Get();
}
......
......@@ -49,7 +49,8 @@ class Intl {
// script; eg, pa_Guru_IN (language=Panjabi, script=Gurmukhi, country-India)
// would include pa_IN.
static std::set<std::string> BuildLocaleSet(
const icu::Locale* icu_available_locales, int32_t count);
const icu::Locale* icu_available_locales, int32_t count, const char* path,
const char* validate_key);
static Maybe<std::string> ToLanguageTag(const icu::Locale& locale);
......@@ -281,20 +282,26 @@ class Intl {
// A helper template to implement the GetAvailableLocales
// Usage in src/objects/js-XXX.cc
//
// const std::set<std::string>& JSXxx::GetAvailableLocales() {
// static base::LazyInstance<Intl::AvailableLocales<icu::YYY>>::type
// available_locales = LAZY_INSTANCE_INITIALIZER;
// return available_locales.Pointer()->Get();
// }
template <typename T>
struct SkipResourceCheck {
static const char* key() { return nullptr; }
static const char* path() { return nullptr; }
};
template <typename T, typename C = SkipResourceCheck>
class AvailableLocales {
public:
AvailableLocales() {
int32_t num_locales = 0;
const icu::Locale* icu_available_locales =
T::getAvailableLocales(num_locales);
set = Intl::BuildLocaleSet(icu_available_locales, num_locales);
set = Intl::BuildLocaleSet(icu_available_locales, num_locales, C::path(),
C::key());
}
virtual ~AvailableLocales() {}
const std::set<std::string>& Get() const { return set; }
......
......@@ -15,7 +15,9 @@
#include "unicode/locid.h"
#include "unicode/strenum.h"
#include "unicode/ucol.h"
#include "unicode/udata.h"
#include "unicode/uloc.h"
#include "unicode/utypes.h"
namespace v8 {
namespace internal {
......@@ -475,9 +477,20 @@ MaybeHandle<JSCollator> JSCollator::New(Isolate* isolate, Handle<Map> map,
return collator;
}
namespace {
struct CheckColl {
static const char* key() { return nullptr; }
#define U_ICUDATA_COLL U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "coll"
static const char* path() { return U_ICUDATA_COLL; }
#undef U_ICUDATA_COLL
};
} // namespace
const std::set<std::string>& JSCollator::GetAvailableLocales() {
static base::LazyInstance<Intl::AvailableLocales<icu::Collator>>::type
available_locales = LAZY_INSTANCE_INITIALIZER;
static base::LazyInstance<Intl::AvailableLocales<icu::Collator, CheckColl>>::
type available_locales = LAZY_INSTANCE_INITIALIZER;
return available_locales.Pointer()->Get();
}
......
......@@ -379,11 +379,20 @@ MaybeHandle<JSArray> JSListFormat::FormatListToParts(
FormattedListToJSArray);
}
namespace {
struct CheckListPattern {
static const char* key() { return "listPattern"; }
static const char* path() { return nullptr; }
};
} // namespace
const std::set<std::string>& JSListFormat::GetAvailableLocales() {
// Since ListFormatter does not have a method to list all supported
// locales, use the one in icu::Locale per comments in
// ICU FR at https://unicode-org.atlassian.net/browse/ICU-20015
return Intl::GetAvailableLocalesForLocale();
static base::LazyInstance<
Intl::AvailableLocales<icu::Locale, CheckListPattern>>::type
available_locales = LAZY_INSTANCE_INITIALIZER;
return available_locales.Pointer()->Get();
}
} // namespace internal
......
......@@ -1505,8 +1505,18 @@ MaybeHandle<JSArray> JSNumberFormat::FormatToParts(
return result;
}
namespace {
struct CheckNumberElements {
static const char* key() { return "NumberElements"; }
static const char* path() { return nullptr; }
};
} // namespace
const std::set<std::string>& JSNumberFormat::GetAvailableLocales() {
static base::LazyInstance<Intl::AvailableLocales<icu::NumberFormat>>::type
static base::LazyInstance<
Intl::AvailableLocales<icu::NumberFormat, CheckNumberElements>>::type
available_locales = LAZY_INSTANCE_INITIALIZER;
return available_locales.Pointer()->Get();
}
......
......@@ -289,13 +289,39 @@ Handle<JSObject> JSPluralRules::ResolvedOptions(
return options;
}
namespace {
class PluralRulesAvailableLocales {
public:
PluralRulesAvailableLocales() {
UErrorCode status = U_ZERO_ERROR;
std::unique_ptr<icu::StringEnumeration> locales(
icu::PluralRules::getAvailableLocales(status));
CHECK(U_SUCCESS(status));
int32_t len = 0;
const char* locale = nullptr;
while ((locale = locales->next(&len, status)) != nullptr &&
U_SUCCESS(status)) {
std::string str(locale);
if (len > 3) {
std::replace(str.begin(), str.end(), '_', '-');
}
set_.insert(std::move(str));
}
}
const std::set<std::string>& Get() const { return set_; }
private:
std::set<std::string> set_;
};
} // namespace
const std::set<std::string>& JSPluralRules::GetAvailableLocales() {
// TODO(ftang): For PluralRules, filter out locales that
// don't support PluralRules.
// PluralRules is missing an appropriate getAvailableLocales method,
// so we should filter from all locales, but it's not clear how; see
// https://ssl.icu-project.org/trac/ticket/12756
return Intl::GetAvailableLocalesForLocale();
static base::LazyInstance<PluralRulesAvailableLocales>::type
available_locales = LAZY_INSTANCE_INITIALIZER;
return available_locales.Pointer()->Get();
// return Intl::GetAvailableLocalesForLocale();
}
} // namespace internal
......
......@@ -238,7 +238,7 @@ TEST(GetAvailableLocales) {
CHECK(locales.count("en-US"));
locales = JSPluralRules::GetAvailableLocales();
CHECK(locales.count("en-US"));
CHECK(locales.count("en"));
locales = JSRelativeTimeFormat::GetAvailableLocales();
CHECK(locales.count("en-US"));
......
......@@ -86,8 +86,10 @@ for (const service of services) {
privateuseLocale2 = service.supportedLocalesOf("x-twain");
assertEquals(undefined, privateuseLocale2[0]);
grandfatheredLocale = service.supportedLocalesOf("art-lojban");
assertEquals(undefined, grandfatheredLocale[0]);
if (service != Intl.PluralRules) {
grandfatheredLocale = service.supportedLocalesOf("art-lojban");
assertEquals(undefined, grandfatheredLocale[0]);
}
grandfatheredLocale2 = service.supportedLocalesOf("i-pwn");
assertEquals(undefined, grandfatheredLocale2[0]);
......
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