[intl] Port CanonicalizeLocaleList to C++

This CL also contains some drive-by cleanup of related code.

Bug: v8:5751, v8:7987
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I6144d16c1d85922efc1dc419cce8a2eba2a60056
Reviewed-on: https://chromium-review.googlesource.com/1161545
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54952}
This commit is contained in:
Jakob Kummerow 2018-08-06 16:02:30 -07:00 committed by Commit Bot
parent 38c664fa9c
commit bbe8db5948
13 changed files with 200 additions and 173 deletions

View File

@ -316,7 +316,7 @@ V8_WARN_UNUSED_RESULT Object* GenericArrayPush(Isolate* isolate,
Handle<Object> raw_length_number;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, raw_length_number,
Object::GetLengthFromArrayLike(isolate, Handle<Object>::cast(receiver)));
Object::GetLengthFromArrayLike(isolate, receiver));
// 3. Let args be a List whose elements are, in left to right order,
// the arguments that were passed to this function invocation.

View File

@ -41,7 +41,7 @@ BUILTIN(StringPrototypeToUpperCaseIntl) {
HandleScope scope(isolate);
TO_THIS_STRING(string, "String.prototype.toUpperCase");
string = String::Flatten(isolate, string);
return ConvertCase(string, true, isolate);
RETURN_RESULT_OR_FAILURE(isolate, ConvertCase(string, true, isolate));
}
BUILTIN(StringPrototypeNormalizeIntl) {

View File

@ -90,7 +90,6 @@ enum ContextLookupFlags {
V(MAP_HAS_INDEX, JSFunction, map_has) \
V(MAP_SET_INDEX, JSFunction, map_set) \
V(FUNCTION_HAS_INSTANCE_INDEX, JSFunction, function_has_instance) \
V(INITIALIZE_LOCALE_LIST_FUNCTION_INDEX, JSFunction, initialize_locale_list) \
V(OBJECT_VALUE_OF, JSFunction, object_value_of) \
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \

View File

@ -155,10 +155,8 @@ const UChar* GetUCharBufferFromFlat(const String::FlatContent& flat,
}
}
V8_WARN_UNUSED_RESULT Object* LocaleConvertCase(Handle<String> s,
Isolate* isolate,
bool is_to_upper,
const char* lang) {
MaybeHandle<String> LocaleConvertCase(Handle<String> s, Isolate* isolate,
bool is_to_upper, const char* lang) {
auto case_converter = is_to_upper ? u_strToUpper : u_strToLower;
int32_t src_length = s->length();
int32_t dest_length = src_length;
@ -166,15 +164,16 @@ V8_WARN_UNUSED_RESULT Object* LocaleConvertCase(Handle<String> s,
Handle<SeqTwoByteString> result;
std::unique_ptr<uc16[]> sap;
if (dest_length == 0) return ReadOnlyRoots(isolate).empty_string();
if (dest_length == 0) return ReadOnlyRoots(isolate).empty_string_handle();
// This is not a real loop. It'll be executed only once (no overflow) or
// twice (overflow).
for (int i = 0; i < 2; ++i) {
// Case conversion can increase the string length (e.g. sharp-S => SS) so
// that we have to handle RangeError exceptions here.
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawTwoByteString(dest_length));
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawTwoByteString(dest_length),
String);
DisallowHeapAllocation no_gc;
DCHECK(s->IsFlat());
String::FlatContent flat = s->GetFlatContent();
@ -192,21 +191,17 @@ V8_WARN_UNUSED_RESULT Object* LocaleConvertCase(Handle<String> s,
DCHECK(U_SUCCESS(status));
if (V8_LIKELY(status == U_STRING_NOT_TERMINATED_WARNING)) {
DCHECK(dest_length == result->length());
return *result;
return result;
}
if (U_SUCCESS(status)) {
DCHECK(dest_length < result->length());
return *Handle<SeqTwoByteString>::cast(
SeqString::Truncate(result, dest_length));
}
return *s;
DCHECK(dest_length < result->length());
return SeqString::Truncate(result, dest_length);
}
// A stripped-down version of ConvertToLower that can only handle flat one-byte
// strings and does not allocate. Note that {src} could still be, e.g., a
// one-byte sliced string with a two-byte parent string.
// Called from TF builtins.
V8_WARN_UNUSED_RESULT Object* ConvertOneByteToLower(String* src, String* dst) {
V8_WARN_UNUSED_RESULT String* ConvertOneByteToLower(String* src, String* dst) {
DCHECK_EQ(src->length(), dst->length());
DCHECK(src->HasOnlyOneByteChars());
DCHECK(src->IsFlat());
@ -251,8 +246,7 @@ V8_WARN_UNUSED_RESULT Object* ConvertOneByteToLower(String* src, String* dst) {
return dst;
}
V8_WARN_UNUSED_RESULT Object* ConvertToLower(Handle<String> s,
Isolate* isolate) {
MaybeHandle<String> ConvertToLower(Handle<String> s, Isolate* isolate) {
if (!s->HasOnlyOneByteChars()) {
// Use a slower implementation for strings with characters beyond U+00FF.
return LocaleConvertCase(s, isolate, false, "");
@ -274,17 +268,16 @@ V8_WARN_UNUSED_RESULT Object* ConvertToLower(Handle<String> s,
bool is_short = length < static_cast<int>(sizeof(uintptr_t));
if (is_short) {
bool is_lower_ascii = FindFirstUpperOrNonAscii(*s, length) == length;
if (is_lower_ascii) return *s;
if (is_lower_ascii) return s;
}
Handle<SeqOneByteString> result =
isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
return ConvertOneByteToLower(*s, *result);
return Handle<String>(ConvertOneByteToLower(*s, *result), isolate);
}
V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle<String> s,
Isolate* isolate) {
MaybeHandle<String> ConvertToUpper(Handle<String> s, Isolate* isolate) {
int32_t length = s->length();
if (s->HasOnlyOneByteChars() && length > 0) {
Handle<SeqOneByteString> result =
@ -304,8 +297,9 @@ V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle<String> s,
FastAsciiConvert<false>(reinterpret_cast<char*>(result->GetChars()),
reinterpret_cast<const char*>(src.start()),
length, &has_changed_character);
if (index_to_first_unprocessed == length)
return has_changed_character ? *result : *s;
if (index_to_first_unprocessed == length) {
return has_changed_character ? result : s;
}
// If not ASCII, we keep the result up to index_to_first_unprocessed and
// process the rest.
is_result_single_byte =
@ -314,7 +308,7 @@ V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle<String> s,
} else {
DCHECK(flat.IsTwoByte());
Vector<const uint16_t> src = flat.ToUC16Vector();
if (ToUpperFastASCII(src, result)) return *result;
if (ToUpperFastASCII(src, result)) return result;
is_result_single_byte = ToUpperOneByte(src, dest, &sharp_s_count);
}
}
@ -325,13 +319,14 @@ V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle<String> s,
return LocaleConvertCase(s, isolate, true, "");
}
if (sharp_s_count == 0) return *result;
if (sharp_s_count == 0) return result;
// We have sharp_s_count sharp-s characters, but the result is still
// in the Latin-1 range.
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
isolate->factory()->NewRawOneByteString(length + sharp_s_count));
isolate->factory()->NewRawOneByteString(length + sharp_s_count),
String);
DisallowHeapAllocation no_gc;
String::FlatContent flat = s->GetFlatContent();
if (flat.IsOneByte()) {
@ -340,14 +335,14 @@ V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle<String> s,
ToUpperWithSharpS(flat.ToUC16Vector(), result);
}
return *result;
return result;
}
return LocaleConvertCase(s, isolate, true, "");
}
V8_WARN_UNUSED_RESULT Object* ConvertCase(Handle<String> s, bool is_upper,
Isolate* isolate) {
MaybeHandle<String> ConvertCase(Handle<String> s, bool is_upper,
Isolate* isolate) {
return is_upper ? ConvertToUpper(s, isolate) : ConvertToLower(s, isolate);
}

View File

@ -37,18 +37,14 @@ enum class IcuService {
const UChar* GetUCharBufferFromFlat(const String::FlatContent& flat,
std::unique_ptr<uc16[]>* dest,
int32_t length);
V8_WARN_UNUSED_RESULT Object* LocaleConvertCase(Handle<String> s,
Isolate* isolate,
bool is_to_upper,
const char* lang);
V8_WARN_UNUSED_RESULT Object* ConvertToLower(Handle<String> s,
Isolate* isolate);
V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle<String> s,
Isolate* isolate);
V8_WARN_UNUSED_RESULT Object* ConvertCase(Handle<String> s, bool is_upper,
Isolate* isolate);
MaybeHandle<String> LocaleConvertCase(Handle<String> s, Isolate* isolate,
bool is_to_upper, const char* lang);
MaybeHandle<String> ConvertToLower(Handle<String> s, Isolate* isolate);
MaybeHandle<String> ConvertToUpper(Handle<String> s, Isolate* isolate);
MaybeHandle<String> ConvertCase(Handle<String> s, bool is_upper,
Isolate* isolate);
V8_WARN_UNUSED_RESULT Object* ConvertOneByteToLower(String* src, String* dst);
V8_WARN_UNUSED_RESULT String* ConvertOneByteToLower(String* src, String* dst);
const uint8_t* ToLatin1LowerTable();

View File

@ -772,13 +772,6 @@ function initializeLocaleList(locales) {
return freezeArray(canonicalizeLocaleList(locales));
}
// TODO(ftang): remove the %InstallToContext once
// initializeLocaleList is available in C++
// https://bugs.chromium.org/p/v8/issues/detail?id=7987
%InstallToContext([
"initialize_locale_list", initializeLocaleList
]);
// ECMA 402 section 8.2.1
DEFINE_METHOD(
GlobalIntl,

View File

@ -226,7 +226,9 @@ bool JsonStringifier::InitializeReplacer(Handle<Object> replacer) {
Handle<Object> length_obj;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, length_obj,
Object::GetLengthFromArrayLike(isolate_, replacer), false);
Object::GetLengthFromArrayLike(isolate_,
Handle<JSReceiver>::cast(replacer)),
false);
uint32_t length;
if (!length_obj->ToUint32(&length)) length = kMaxUInt32;
for (uint32_t i = 0; i < length; i++) {
@ -720,7 +722,9 @@ JsonStringifier::Result JsonStringifier::SerializeJSProxy(
Handle<Object> length_object;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, length_object,
Object::GetLengthFromArrayLike(isolate_, object), EXCEPTION);
Object::GetLengthFromArrayLike(isolate_,
Handle<JSReceiver>::cast(object)),
EXCEPTION);
uint32_t length;
if (!length_object->ToUint32(&length)) {
// Technically, we need to be able to handle lengths outside the

View File

@ -953,11 +953,11 @@ MaybeHandle<FixedArray> Object::CreateListFromArrayLike(
// static
MaybeHandle<Object> Object::GetLengthFromArrayLike(Isolate* isolate,
Handle<Object> object) {
Handle<JSReceiver> object) {
Handle<Object> val;
Handle<Object> key = isolate->factory()->length_string();
Handle<Name> key = isolate->factory()->length_string();
ASSIGN_RETURN_ON_EXCEPTION(
isolate, val, Runtime::GetObjectProperty(isolate, object, key), Object);
isolate, val, JSReceiver::GetProperty(isolate, object, key), Object);
return Object::ToLength(isolate, val);
}

View File

@ -1289,7 +1289,7 @@ class Object {
// Get length property and apply ToLength.
V8_WARN_UNUSED_RESULT static MaybeHandle<Object> GetLengthFromArrayLike(
Isolate* isolate, Handle<Object> object);
Isolate* isolate, Handle<JSReceiver> object);
// ES6 section 12.5.6 The typeof Operator
static Handle<String> TypeOf(Isolate* isolate, Handle<Object> object);

View File

@ -9,7 +9,9 @@
#include "src/objects/intl-objects.h"
#include "src/objects/intl-objects-inl.h"
#include <algorithm>
#include <memory>
#include <string>
#include "src/api-inl.h"
#include "src/global-handles.h"
@ -1073,7 +1075,7 @@ V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> Intl::AvailableLocalesOf(
return locales;
}
V8_WARN_UNUSED_RESULT Handle<String> Intl::DefaultLocale(Isolate* isolate) {
std::string Intl::DefaultLocale(Isolate* isolate) {
if (isolate->default_locale().empty()) {
icu::Locale default_locale;
// Translate ICU's fallback locale to a well-known locale.
@ -1091,8 +1093,7 @@ V8_WARN_UNUSED_RESULT Handle<String> Intl::DefaultLocale(Isolate* isolate) {
}
DCHECK(!isolate->default_locale().empty());
}
return isolate->factory()->NewStringFromAsciiChecked(
isolate->default_locale().c_str());
return isolate->default_locale();
}
bool Intl::IsObjectOfType(Isolate* isolate, Handle<Object> input,
@ -1354,8 +1355,8 @@ MaybeHandle<JSObject> Intl::ResolveLocale(Isolate* isolate, const char* service,
return Handle<JSObject>::cast(result);
}
MaybeHandle<JSObject> Intl::CanonicalizeLocaleList(Isolate* isolate,
Handle<Object> locales) {
MaybeHandle<JSObject> Intl::CanonicalizeLocaleListJS(Isolate* isolate,
Handle<Object> locales) {
Handle<JSFunction> canonicalize_locale_list_function =
isolate->canonicalize_locale_list();
@ -1586,17 +1587,26 @@ bool IsGrandfatheredTagWithoutPreferredVaule(const std::string& locale) {
} // anonymous namespace
MaybeHandle<String> Intl::CanonicalizeLanguageTag(Isolate* isolate,
Handle<Object> locale_in) {
Maybe<std::string> Intl::CanonicalizeLanguageTag(Isolate* isolate,
Handle<Object> locale_in) {
Handle<String> locale_str;
// This does part of the validity checking spec'ed in CanonicalizeLocaleList:
// 7c ii. If Type(kValue) is not String or Object, throw a TypeError
// exception.
// 7c iii. Let tag be ? ToString(kValue).
// 7c iv. If IsStructurallyValidLanguageTag(tag) is false, throw a
// RangeError exception.
if (locale_in->IsString()) {
locale_str = Handle<String>::cast(locale_in);
} else if (locale_in->IsJSReceiver()) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, locale_str,
Object::ToString(isolate, locale_in), String);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, locale_str,
Object::ToString(isolate, locale_in),
Nothing<std::string>());
} else {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kLanguageID),
String);
THROW_NEW_ERROR_RETURN_VALUE(isolate,
NewTypeError(MessageTemplate::kLanguageID),
Nothing<std::string>());
}
std::string locale(locale_str->ToCString().get());
@ -1607,24 +1617,25 @@ MaybeHandle<String> Intl::CanonicalizeLanguageTag(Isolate* isolate,
// fast-track 'fil' (3-letter canonical code).
if ((IsTwoLetterLanguage(locale) && !IsDeprecatedLanguage(locale)) ||
locale == "fil") {
return locale_str;
return Just(locale);
}
// Because per BCP 47 2.1.1 language tags are case-insensitive, lowercase
// the input before any more check.
std::transform(locale.begin(), locale.end(), locale.begin(), AsciiToLower);
if (!IsStructurallyValidLanguageTag(isolate, locale)) {
THROW_NEW_ERROR(
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewRangeError(MessageTemplate::kInvalidLanguageTag, locale_str),
String);
Nothing<std::string>());
}
// ICU maps a few grandfathered tags to what looks like a regular language
// tag even though IANA language tag registry does not have a preferred
// entry map for them. Return them as they're with lowercasing.
if (IsGrandfatheredTagWithoutPreferredVaule(locale))
return isolate->factory()->NewStringFromAsciiChecked(locale.data());
if (IsGrandfatheredTagWithoutPreferredVaule(locale)) {
return Just(locale);
}
// // ECMA 402 6.2.3
// TODO(jshin): uloc_{for,to}TanguageTag can fail even for a structually valid
@ -1640,10 +1651,10 @@ MaybeHandle<String> Intl::CanonicalizeLanguageTag(Isolate* isolate,
if (U_FAILURE(error) || error == U_STRING_NOT_TERMINATED_WARNING) {
// TODO(jshin): This should not happen because the structural validity
// is already checked. If that's the case, remove this.
THROW_NEW_ERROR(
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewRangeError(MessageTemplate::kInvalidLanguageTag, locale_str),
String);
Nothing<std::string>());
}
// Force strict BCP47 rules.
@ -1652,15 +1663,87 @@ MaybeHandle<String> Intl::CanonicalizeLanguageTag(Isolate* isolate,
ULOC_FULLNAME_CAPACITY, TRUE, &error);
if (U_FAILURE(error)) {
THROW_NEW_ERROR(
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewRangeError(MessageTemplate::kInvalidLanguageTag, locale_str),
String);
Nothing<std::string>());
}
return isolate->factory()
->NewStringFromOneByte(OneByteVector(result, result_len), NOT_TENURED)
.ToHandleChecked();
return Just(std::string(result, result_len));
}
Maybe<std::vector<std::string>> Intl::CanonicalizeLocaleList(
Isolate* isolate, Handle<Object> locales, bool only_return_one_result) {
// 1. If locales is undefined, then
if (locales->IsUndefined(isolate)) {
// 1a. Return a new empty List.
return Just(std::vector<std::string>());
}
// 2. Let seen be a new empty List.
std::vector<std::string> seen;
// 3. If Type(locales) is String, then
if (locales->IsString()) {
// 3a. Let O be CreateArrayFromList(« locales »).
// Instead of creating a one-element array and then iterating over it,
// we inline the body of the iteration:
std::string canonicalized_tag;
if (!CanonicalizeLanguageTag(isolate, locales).To(&canonicalized_tag)) {
return Nothing<std::vector<std::string>>();
}
seen.push_back(canonicalized_tag);
return Just(seen);
}
// 4. Else,
// 4a. Let O be ? ToObject(locales).
Handle<JSReceiver> o;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, o,
Object::ToObject(isolate, locales),
Nothing<std::vector<std::string>>());
// 5. Let len be ? ToLength(? Get(O, "length")).
Handle<Object> length_obj;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, length_obj,
Object::GetLengthFromArrayLike(isolate, o),
Nothing<std::vector<std::string>>());
// TODO(jkummerow): Spec violation: strictly speaking, we have to iterate
// up to 2^53-1 if {length_obj} says so. Since cases above 2^32 probably
// don't happen in practice (and would be very slow if they do), we'll keep
// the code simple for now by using a saturating to-uint32 conversion.
double raw_length = length_obj->Number();
uint32_t len =
raw_length >= kMaxUInt32 ? kMaxUInt32 : static_cast<uint32_t>(raw_length);
// 6. Let k be 0.
// 7. Repeat, while k < len
for (uint32_t k = 0; k < len; k++) {
// 7a. Let Pk be ToString(k).
// 7b. Let kPresent be ? HasProperty(O, Pk).
LookupIterator it(isolate, o, k);
// 7c. If kPresent is true, then
if (!it.IsFound()) continue;
// 7c i. Let kValue be ? Get(O, Pk).
Handle<Object> k_value;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, k_value, Object::GetProperty(&it),
Nothing<std::vector<std::string>>());
// 7c ii. If Type(kValue) is not String or Object, throw a TypeError
// exception.
// 7c iii. Let tag be ? ToString(kValue).
// 7c iv. If IsStructurallyValidLanguageTag(tag) is false, throw a
// RangeError exception.
// 7c v. Let canonicalizedTag be CanonicalizeLanguageTag(tag).
std::string canonicalized_tag;
if (!CanonicalizeLanguageTag(isolate, k_value).To(&canonicalized_tag)) {
return Nothing<std::vector<std::string>>();
}
// 7c vi. If canonicalizedTag is not an element of seen, append
// canonicalizedTag as the last element of seen.
if (std::find(seen.begin(), seen.end(), canonicalized_tag) == seen.end()) {
seen.push_back(canonicalized_tag);
}
// 7d. Increase k by 1. (See loop header.)
// Optimization: some callers only need one result.
if (only_return_one_result) return Just(seen);
}
// 8. Return seen.
return Just(seen);
}
// ecma-402/#sec-currencydigits
@ -1707,32 +1790,16 @@ MaybeHandle<JSObject> Intl::CreateNumberFormat(Isolate* isolate,
namespace {
// Remove the following after we port InitializeLocaleList from src/js/intl.js
// to c++ https://bugs.chromium.org/p/v8/issues/detail?id=7987
bool IsAToZ(char ch) {
return IsInRange(AsciiAlphaToLower(ch), 'a', 'z');
}
// The following are temporary function calling back into js code in
// src/js/intl.js to call pre-existing functions until they are all moved to C++
// under src/objects/*.
// TODO(ftang): remove these temp function after bstell move them from js into
// C++
MaybeHandle<JSObject> InitializeLocaleList(Isolate* isolate,
Handle<Object> list) {
Handle<Object> result;
Handle<Object> undefined_value(ReadOnlyRoots(isolate).undefined_value(),
isolate);
Handle<Object> args[] = {list};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, isolate->initialize_locale_list(),
undefined_value, arraysize(args), args),
JSArray);
return Handle<JSObject>::cast(result);
}
bool IsAToZ(char ch) {
return (('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z'));
}
MaybeHandle<JSObject> CachedOrNewService(Isolate* isolate,
Handle<String> service,
Handle<Object> locales,
@ -1777,85 +1844,40 @@ MaybeHandle<String> Intl::StringLocaleConvertCase(Isolate* isolate,
Handle<String> s,
bool to_upper,
Handle<Object> locales) {
Factory* factory = isolate->factory();
Handle<String> requested_locale;
if (locales->IsUndefined()) {
requested_locale = Intl::DefaultLocale(isolate);
} else if (locales->IsString()) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locale,
CanonicalizeLanguageTag(isolate, locales),
String);
} else {
Handle<JSObject> list;
ASSIGN_RETURN_ON_EXCEPTION(isolate, list,
InitializeLocaleList(isolate, locales), String);
Handle<Object> length;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, length, Object::GetLengthFromArrayLike(isolate, list), String);
if (length->Number() > 0) {
Handle<Object> element;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, element,
JSObject::GetPropertyOrElement(
isolate, list, factory->NumberToString(factory->NewNumber(0))),
String);
ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locale,
Object::ToString(isolate, element), String);
} else {
requested_locale = Intl::DefaultLocale(isolate);
}
std::vector<std::string> requested_locales;
if (!CanonicalizeLocaleList(isolate, locales, true).To(&requested_locales)) {
return MaybeHandle<String>();
}
int dash = String::IndexOf(isolate, requested_locale,
factory->NewStringFromStaticChars("-"), 0);
if (dash > 0) {
requested_locale = factory->NewSubString(requested_locale, 0, dash);
std::string requested_locale = requested_locales.size() == 0
? Intl::DefaultLocale(isolate)
: requested_locales[0];
size_t dash = requested_locale.find("-");
if (dash != std::string::npos) {
requested_locale = requested_locale.substr(0, dash);
}
// Primary language tag can be up to 8 characters long in theory.
// https://tools.ietf.org/html/bcp47#section-2.2.1
DCHECK_LE(requested_locale->length(), 8);
requested_locale = String::Flatten(isolate, requested_locale);
DCHECK_LE(requested_locale.length(), 8);
s = String::Flatten(isolate, s);
// All the languages requiring special-handling have two-letter codes.
// Note that we have to check for '!= 2' here because private-use language
// tags (x-foo) or grandfathered irregular tags (e.g. i-enochian) would have
// only 'x' or 'i' when they get here.
if (V8_UNLIKELY(requested_locale->length() != 2)) {
Handle<Object> obj(ConvertCase(s, to_upper, isolate), isolate);
return Object::ToString(isolate, obj);
}
char c1, c2;
{
DisallowHeapAllocation no_gc;
String::FlatContent lang = requested_locale->GetFlatContent();
c1 = lang.Get(0);
c2 = lang.Get(1);
if (V8_UNLIKELY(requested_locale.length() != 2)) {
return ConvertCase(s, to_upper, isolate);
}
// TODO(jshin): Consider adding a fast path for ASCII or Latin-1. The fastpath
// in the root locale needs to be adjusted for az, lt and tr because even case
// mapping of ASCII range characters are different in those locales.
// Greek (el) does not require any adjustment.
if (V8_UNLIKELY(c1 == 't' && c2 == 'r')) {
Handle<Object> obj(LocaleConvertCase(s, isolate, to_upper, "tr"), isolate);
return Object::ToString(isolate, obj);
if (V8_UNLIKELY((requested_locale == "tr") || (requested_locale == "el") ||
(requested_locale == "lt") || (requested_locale == "az"))) {
return LocaleConvertCase(s, isolate, to_upper, requested_locale.c_str());
} else {
return ConvertCase(s, to_upper, isolate);
}
if (V8_UNLIKELY(c1 == 'e' && c2 == 'l')) {
Handle<Object> obj(LocaleConvertCase(s, isolate, to_upper, "el"), isolate);
return Object::ToString(isolate, obj);
}
if (V8_UNLIKELY(c1 == 'l' && c2 == 't')) {
Handle<Object> obj(LocaleConvertCase(s, isolate, to_upper, "lt"), isolate);
return Object::ToString(isolate, obj);
}
if (V8_UNLIKELY(c1 == 'a' && c2 == 'z')) {
Handle<Object> obj(LocaleConvertCase(s, isolate, to_upper, "az"), isolate);
return Object::ToString(isolate, obj);
}
Handle<Object> obj(ConvertCase(s, to_upper, isolate), isolate);
return Object::ToString(isolate, obj);
}
MaybeHandle<Object> Intl::StringLocaleCompare(Isolate* isolate,

View File

@ -195,7 +195,7 @@ class Intl {
static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> AvailableLocalesOf(
Isolate* isolate, Handle<String> service);
static V8_WARN_UNUSED_RESULT Handle<String> DefaultLocale(Isolate* isolate);
static std::string DefaultLocale(Isolate* isolate);
static void DefineWEProperty(Isolate* isolate, Handle<JSObject> target,
Handle<Name> key, Handle<Object> value);
@ -234,9 +234,12 @@ class Intl {
// This currently calls out to the JavaScript implementation of
// CanonicalizeLocaleList.
// Note: This is deprecated glue code, required only as long as ResolveLocale
// still calls a JS implementation. The C++ successor is the overloaded
// version below that returns a Maybe<std::vector<std::string>>.
//
// ecma402/#sec-canonicalizelocalelist
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CanonicalizeLocaleList(
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CanonicalizeLocaleListJS(
Isolate* isolate, Handle<Object> locales);
// ECMA402 9.2.10. GetOption( options, property, type, values, fallback)
@ -275,9 +278,18 @@ class Intl {
Isolate* isolate, Handle<JSReceiver> options, const char* property,
const char* service, bool* result);
// Canonicalize the localeID.
static MaybeHandle<String> CanonicalizeLanguageTag(Isolate* isolate,
Handle<Object> localeID);
// Canonicalize the locale.
// https://tc39.github.io/ecma402/#sec-canonicalizelanguagetag,
// including type check and structural validity check.
static Maybe<std::string> CanonicalizeLanguageTag(Isolate* isolate,
Handle<Object> locale_in);
// https://tc39.github.io/ecma402/#sec-canonicalizelocalelist
// {only_return_one_result} is an optimization for callers that only
// care about the first result.
static Maybe<std::vector<std::string>> CanonicalizeLocaleList(
Isolate* isolate, Handle<Object> locales,
bool only_return_one_result = false);
// ecma-402/#sec-currencydigits
// The currency is expected to an all upper case string value.

View File

@ -89,9 +89,11 @@ MaybeHandle<JSPluralRules> JSPluralRules::InitializePluralRules(
Isolate* isolate, Handle<JSPluralRules> plural_rules,
Handle<Object> locales, Handle<Object> options_obj) {
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
// TODO(jkummerow): Port ResolveLocale, then use the C++ version of
// CanonicalizeLocaleList here.
Handle<JSObject> requested_locales;
ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locales,
Intl::CanonicalizeLocaleList(isolate, locales),
Intl::CanonicalizeLocaleListJS(isolate, locales),
JSPluralRules);
// 2. If options is undefined, then

View File

@ -94,8 +94,11 @@ RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag) {
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, locale, 0);
RETURN_RESULT_OR_FAILURE(isolate,
Intl::CanonicalizeLanguageTag(isolate, locale));
std::string canonicalized;
if (!Intl::CanonicalizeLanguageTag(isolate, locale).To(&canonicalized)) {
return ReadOnlyRoots(isolate).exception();
}
return *isolate->factory()->NewStringFromAsciiChecked(canonicalized.c_str());
}
RUNTIME_FUNCTION(Runtime_AvailableLocalesOf) {
@ -112,7 +115,8 @@ RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
return *Intl::DefaultLocale(isolate);
return *isolate->factory()->NewStringFromAsciiChecked(
Intl::DefaultLocale(isolate).c_str());
}
RUNTIME_FUNCTION(Runtime_IsWellFormedCurrencyCode) {
@ -476,7 +480,7 @@ RUNTIME_FUNCTION(Runtime_StringToLowerCaseIntl) {
DCHECK_EQ(args.length(), 1);
CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
s = String::Flatten(isolate, s);
return ConvertToLower(s, isolate);
RETURN_RESULT_OR_FAILURE(isolate, ConvertToLower(s, isolate));
}
RUNTIME_FUNCTION(Runtime_StringToUpperCaseIntl) {
@ -484,7 +488,7 @@ RUNTIME_FUNCTION(Runtime_StringToUpperCaseIntl) {
DCHECK_EQ(args.length(), 1);
CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
s = String::Flatten(isolate, s);
return ConvertToUpper(s, isolate);
RETURN_RESULT_OR_FAILURE(isolate, ConvertToUpper(s, isolate));
}
RUNTIME_FUNCTION(Runtime_DateCacheVersion) {