[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:
parent
38c664fa9c
commit
bbe8db5948
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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) \
|
||||
|
55
src/intl.cc
55
src/intl.cc
@ -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);
|
||||
}
|
||||
|
||||
|
18
src/intl.h
18
src/intl.h
@ -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();
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user