diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc index dcc691e2ef88..ceeee5f37db8 100644 --- a/src/builtins/builtins-array.cc +++ b/src/builtins/builtins-array.cc @@ -316,7 +316,7 @@ V8_WARN_UNUSED_RESULT Object* GenericArrayPush(Isolate* isolate, Handle raw_length_number; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( isolate, raw_length_number, - Object::GetLengthFromArrayLike(isolate, Handle::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. diff --git a/src/builtins/builtins-intl.cc b/src/builtins/builtins-intl.cc index 9362f73143e7..d59e4978e77c 100644 --- a/src/builtins/builtins-intl.cc +++ b/src/builtins/builtins-intl.cc @@ -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) { diff --git a/src/contexts.h b/src/contexts.h index 65a427f54563..c0a730e590a1 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -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) \ diff --git a/src/intl.cc b/src/intl.cc index 985752577211..c8548b6d4805 100644 --- a/src/intl.cc +++ b/src/intl.cc @@ -155,10 +155,8 @@ const UChar* GetUCharBufferFromFlat(const String::FlatContent& flat, } } -V8_WARN_UNUSED_RESULT Object* LocaleConvertCase(Handle s, - Isolate* isolate, - bool is_to_upper, - const char* lang) { +MaybeHandle LocaleConvertCase(Handle 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 s, Handle result; std::unique_ptr 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 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::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 s, - Isolate* isolate) { +MaybeHandle ConvertToLower(Handle 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 s, bool is_short = length < static_cast(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 result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); - return ConvertOneByteToLower(*s, *result); + return Handle(ConvertOneByteToLower(*s, *result), isolate); } -V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle s, - Isolate* isolate) { +MaybeHandle ConvertToUpper(Handle s, Isolate* isolate) { int32_t length = s->length(); if (s->HasOnlyOneByteChars() && length > 0) { Handle result = @@ -304,8 +297,9 @@ V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle s, FastAsciiConvert(reinterpret_cast(result->GetChars()), reinterpret_cast(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 s, } else { DCHECK(flat.IsTwoByte()); Vector 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 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 s, ToUpperWithSharpS(flat.ToUC16Vector(), result); } - return *result; + return result; } return LocaleConvertCase(s, isolate, true, ""); } -V8_WARN_UNUSED_RESULT Object* ConvertCase(Handle s, bool is_upper, - Isolate* isolate) { +MaybeHandle ConvertCase(Handle s, bool is_upper, + Isolate* isolate) { return is_upper ? ConvertToUpper(s, isolate) : ConvertToLower(s, isolate); } diff --git a/src/intl.h b/src/intl.h index ca6478d247d1..5ec5381f409a 100644 --- a/src/intl.h +++ b/src/intl.h @@ -37,18 +37,14 @@ enum class IcuService { const UChar* GetUCharBufferFromFlat(const String::FlatContent& flat, std::unique_ptr* dest, int32_t length); -V8_WARN_UNUSED_RESULT Object* LocaleConvertCase(Handle s, - Isolate* isolate, - bool is_to_upper, - const char* lang); -V8_WARN_UNUSED_RESULT Object* ConvertToLower(Handle s, - Isolate* isolate); -V8_WARN_UNUSED_RESULT Object* ConvertToUpper(Handle s, - Isolate* isolate); -V8_WARN_UNUSED_RESULT Object* ConvertCase(Handle s, bool is_upper, - Isolate* isolate); - -V8_WARN_UNUSED_RESULT Object* ConvertOneByteToLower(String* src, String* dst); +MaybeHandle LocaleConvertCase(Handle s, Isolate* isolate, + bool is_to_upper, const char* lang); +MaybeHandle ConvertToLower(Handle s, Isolate* isolate); +MaybeHandle ConvertToUpper(Handle s, Isolate* isolate); +MaybeHandle ConvertCase(Handle s, bool is_upper, + Isolate* isolate); + +V8_WARN_UNUSED_RESULT String* ConvertOneByteToLower(String* src, String* dst); const uint8_t* ToLatin1LowerTable(); diff --git a/src/js/intl.js b/src/js/intl.js index d9b09fff7e65..77a52ec80e37 100644 --- a/src/js/intl.js +++ b/src/js/intl.js @@ -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, diff --git a/src/json-stringifier.cc b/src/json-stringifier.cc index 0a84be99a50e..91a1b7201bfe 100644 --- a/src/json-stringifier.cc +++ b/src/json-stringifier.cc @@ -226,7 +226,9 @@ bool JsonStringifier::InitializeReplacer(Handle replacer) { Handle length_obj; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate_, length_obj, - Object::GetLengthFromArrayLike(isolate_, replacer), false); + Object::GetLengthFromArrayLike(isolate_, + Handle::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 length_object; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate_, length_object, - Object::GetLengthFromArrayLike(isolate_, object), EXCEPTION); + Object::GetLengthFromArrayLike(isolate_, + Handle::cast(object)), + EXCEPTION); uint32_t length; if (!length_object->ToUint32(&length)) { // Technically, we need to be able to handle lengths outside the diff --git a/src/objects.cc b/src/objects.cc index 587d176598ba..88fc927b95ce 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -953,11 +953,11 @@ MaybeHandle Object::CreateListFromArrayLike( // static MaybeHandle Object::GetLengthFromArrayLike(Isolate* isolate, - Handle object) { + Handle object) { Handle val; - Handle key = isolate->factory()->length_string(); + Handle 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); } diff --git a/src/objects.h b/src/objects.h index a9933df98563..bf27eee12dc4 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1289,7 +1289,7 @@ class Object { // Get length property and apply ToLength. V8_WARN_UNUSED_RESULT static MaybeHandle GetLengthFromArrayLike( - Isolate* isolate, Handle object); + Isolate* isolate, Handle object); // ES6 section 12.5.6 The typeof Operator static Handle TypeOf(Isolate* isolate, Handle object); diff --git a/src/objects/intl-objects.cc b/src/objects/intl-objects.cc index a443788a5f9a..3bd80532be6b 100644 --- a/src/objects/intl-objects.cc +++ b/src/objects/intl-objects.cc @@ -9,7 +9,9 @@ #include "src/objects/intl-objects.h" #include "src/objects/intl-objects-inl.h" +#include #include +#include #include "src/api-inl.h" #include "src/global-handles.h" @@ -1073,7 +1075,7 @@ V8_WARN_UNUSED_RESULT MaybeHandle Intl::AvailableLocalesOf( return locales; } -V8_WARN_UNUSED_RESULT Handle 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 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 input, @@ -1354,8 +1355,8 @@ MaybeHandle Intl::ResolveLocale(Isolate* isolate, const char* service, return Handle::cast(result); } -MaybeHandle Intl::CanonicalizeLocaleList(Isolate* isolate, - Handle locales) { +MaybeHandle Intl::CanonicalizeLocaleListJS(Isolate* isolate, + Handle locales) { Handle canonicalize_locale_list_function = isolate->canonicalize_locale_list(); @@ -1586,17 +1587,26 @@ bool IsGrandfatheredTagWithoutPreferredVaule(const std::string& locale) { } // anonymous namespace -MaybeHandle Intl::CanonicalizeLanguageTag(Isolate* isolate, - Handle locale_in) { +Maybe Intl::CanonicalizeLanguageTag(Isolate* isolate, + Handle locale_in) { Handle 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::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()); } else { - THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kLanguageID), - String); + THROW_NEW_ERROR_RETURN_VALUE(isolate, + NewTypeError(MessageTemplate::kLanguageID), + Nothing()); } std::string locale(locale_str->ToCString().get()); @@ -1607,24 +1617,25 @@ MaybeHandle 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()); } // 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 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()); } // Force strict BCP47 rules. @@ -1652,15 +1663,87 @@ MaybeHandle 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()); } - return isolate->factory() - ->NewStringFromOneByte(OneByteVector(result, result_len), NOT_TENURED) - .ToHandleChecked(); + return Just(std::string(result, result_len)); +} + +Maybe> Intl::CanonicalizeLocaleList( + Isolate* isolate, Handle 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()); + } + // 2. Let seen be a new empty List. + std::vector 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>(); + } + seen.push_back(canonicalized_tag); + return Just(seen); + } + // 4. Else, + // 4a. Let O be ? ToObject(locales). + Handle o; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, o, + Object::ToObject(isolate, locales), + Nothing>()); + // 5. Let len be ? ToLength(? Get(O, "length")). + Handle length_obj; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, length_obj, + Object::GetLengthFromArrayLike(isolate, o), + Nothing>()); + // 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(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 k_value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, k_value, Object::GetProperty(&it), + Nothing>()); + // 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>(); + } + // 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 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 InitializeLocaleList(Isolate* isolate, - Handle list) { - Handle result; - Handle undefined_value(ReadOnlyRoots(isolate).undefined_value(), - isolate); - Handle args[] = {list}; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, result, - Execution::Call(isolate, isolate->initialize_locale_list(), - undefined_value, arraysize(args), args), - JSArray); - return Handle::cast(result); -} - -bool IsAToZ(char ch) { - return (('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z')); -} - MaybeHandle CachedOrNewService(Isolate* isolate, Handle service, Handle locales, @@ -1777,85 +1844,40 @@ MaybeHandle Intl::StringLocaleConvertCase(Isolate* isolate, Handle s, bool to_upper, Handle locales) { - Factory* factory = isolate->factory(); - Handle 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 list; - ASSIGN_RETURN_ON_EXCEPTION(isolate, list, - InitializeLocaleList(isolate, locales), String); - Handle length; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, length, Object::GetLengthFromArrayLike(isolate, list), String); - if (length->Number() > 0) { - Handle 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 requested_locales; + if (!CanonicalizeLocaleList(isolate, locales, true).To(&requested_locales)) { + return MaybeHandle(); } - 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 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 obj(LocaleConvertCase(s, isolate, to_upper, "tr"), isolate); - return Object::ToString(isolate, obj); - } - if (V8_UNLIKELY(c1 == 'e' && c2 == 'l')) { - Handle obj(LocaleConvertCase(s, isolate, to_upper, "el"), isolate); - return Object::ToString(isolate, obj); - } - if (V8_UNLIKELY(c1 == 'l' && c2 == 't')) { - Handle obj(LocaleConvertCase(s, isolate, to_upper, "lt"), isolate); - return Object::ToString(isolate, obj); - } - if (V8_UNLIKELY(c1 == 'a' && c2 == 'z')) { - Handle obj(LocaleConvertCase(s, isolate, to_upper, "az"), 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); } - - Handle obj(ConvertCase(s, to_upper, isolate), isolate); - return Object::ToString(isolate, obj); } MaybeHandle Intl::StringLocaleCompare(Isolate* isolate, diff --git a/src/objects/intl-objects.h b/src/objects/intl-objects.h index 77c963e30b45..3666d604aacb 100644 --- a/src/objects/intl-objects.h +++ b/src/objects/intl-objects.h @@ -195,7 +195,7 @@ class Intl { static V8_WARN_UNUSED_RESULT MaybeHandle AvailableLocalesOf( Isolate* isolate, Handle service); - static V8_WARN_UNUSED_RESULT Handle DefaultLocale(Isolate* isolate); + static std::string DefaultLocale(Isolate* isolate); static void DefineWEProperty(Isolate* isolate, Handle target, Handle key, Handle 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>. // // ecma402/#sec-canonicalizelocalelist - V8_WARN_UNUSED_RESULT static MaybeHandle CanonicalizeLocaleList( + V8_WARN_UNUSED_RESULT static MaybeHandle CanonicalizeLocaleListJS( Isolate* isolate, Handle locales); // ECMA402 9.2.10. GetOption( options, property, type, values, fallback) @@ -275,9 +278,18 @@ class Intl { Isolate* isolate, Handle options, const char* property, const char* service, bool* result); - // Canonicalize the localeID. - static MaybeHandle CanonicalizeLanguageTag(Isolate* isolate, - Handle localeID); + // Canonicalize the locale. + // https://tc39.github.io/ecma402/#sec-canonicalizelanguagetag, + // including type check and structural validity check. + static Maybe CanonicalizeLanguageTag(Isolate* isolate, + Handle 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> CanonicalizeLocaleList( + Isolate* isolate, Handle locales, + bool only_return_one_result = false); // ecma-402/#sec-currencydigits // The currency is expected to an all upper case string value. diff --git a/src/objects/js-plural-rules.cc b/src/objects/js-plural-rules.cc index 852f3136ef1f..07cc62a41e60 100644 --- a/src/objects/js-plural-rules.cc +++ b/src/objects/js-plural-rules.cc @@ -89,9 +89,11 @@ MaybeHandle JSPluralRules::InitializePluralRules( Isolate* isolate, Handle plural_rules, Handle locales, Handle options_obj) { // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). + // TODO(jkummerow): Port ResolveLocale, then use the C++ version of + // CanonicalizeLocaleList here. Handle requested_locales; ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locales, - Intl::CanonicalizeLocaleList(isolate, locales), + Intl::CanonicalizeLocaleListJS(isolate, locales), JSPluralRules); // 2. If options is undefined, then diff --git a/src/runtime/runtime-intl.cc b/src/runtime/runtime-intl.cc index a080c42c8519..47ec0ecd0384 100644 --- a/src/runtime/runtime-intl.cc +++ b/src/runtime/runtime-intl.cc @@ -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) {