Commit 4d3a3e6a authored by Frank Tang's avatar Frank Tang Committed by Commit Bot

[Intl] Move LocaleConvertCase/String.toLocale(Lower|Upper)Case to C++

This also move the function body of Runtime_StringLocaleConvertCase
into IntlFunc::StringLocaleConvertCase


Bug: v8:7958
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ibc081150babdd7d50043383b0f7375d46cfcf4a5
Reviewed-on: https://chromium-review.googlesource.com/1144525
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarJungshik Shin <jshin@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54737}
parent fbedc72a
......@@ -2077,18 +2077,18 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kStringPrototypeTrimStart, 0, false);
SimpleInstallFunction(isolate_, prototype, "trimRight",
Builtins::kStringPrototypeTrimEnd, 0, false);
#ifdef V8_INTL_SUPPORT
SimpleInstallFunction(isolate_, prototype, "toLowerCase",
Builtins::kStringPrototypeToLowerCaseIntl, 0, true);
SimpleInstallFunction(isolate_, prototype, "toUpperCase",
Builtins::kStringPrototypeToUpperCaseIntl, 0, false);
#else
SimpleInstallFunction(isolate_, prototype, "toLocaleLowerCase",
Builtins::kStringPrototypeToLocaleLowerCase, 0,
false);
SimpleInstallFunction(isolate_, prototype, "toLocaleUpperCase",
Builtins::kStringPrototypeToLocaleUpperCase, 0,
false);
#ifdef V8_INTL_SUPPORT
SimpleInstallFunction(isolate_, prototype, "toLowerCase",
Builtins::kStringPrototypeToLowerCaseIntl, 0, true);
SimpleInstallFunction(isolate_, prototype, "toUpperCase",
Builtins::kStringPrototypeToUpperCaseIntl, 0, false);
#else
SimpleInstallFunction(isolate_, prototype, "toLowerCase",
Builtins::kStringPrototypeToLowerCase, 0, false);
SimpleInstallFunction(isolate_, prototype, "toUpperCase",
......
......@@ -1367,7 +1367,11 @@ namespace internal {
/* ecma402 #sec-intl.RelativeTimeFormat.prototype.format */ \
CPP(RelativeTimeFormatPrototypeFormat) \
/* ecma402 #sec-intl.RelativeTimeFormat.prototype.formatToParts */ \
CPP(RelativeTimeFormatPrototypeFormatToParts)
CPP(RelativeTimeFormatPrototypeFormatToParts) \
/* ecma402 #sup-string.prototype.tolocalelowercase */ \
CPP(StringPrototypeToLocaleLowerCase) \
/* ecma402 #sup-string.prototype.tolocaleuppercase */ \
CPP(StringPrototypeToLocaleUpperCase)
#else
#define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
BUILTIN_LIST_BASE(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
......
......@@ -1079,5 +1079,21 @@ BUILTIN(RelativeTimeFormatPrototypeResolvedOptions) {
return *JSRelativeTimeFormat::ResolvedOptions(isolate, format_holder);
}
BUILTIN(StringPrototypeToLocaleLowerCase) {
HandleScope scope(isolate);
TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase");
RETURN_RESULT_OR_FAILURE(
isolate, Intl::StringLocaleConvertCase(isolate, string, false,
args.atOrUndefined(isolate, 1)));
}
BUILTIN(StringPrototypeToLocaleUpperCase) {
HandleScope scope(isolate);
TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase");
RETURN_RESULT_OR_FAILURE(
isolate, Intl::StringLocaleConvertCase(isolate, string, true,
args.atOrUndefined(isolate, 1)));
}
} // namespace internal
} // namespace v8
......@@ -70,43 +70,44 @@ enum ContextLookupFlags {
V(ASYNC_GENERATOR_AWAIT_CAUGHT, JSFunction, async_generator_await_caught) \
V(ASYNC_GENERATOR_AWAIT_UNCAUGHT, JSFunction, async_generator_await_uncaught)
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
V(ARRAY_SHIFT_INDEX, JSFunction, array_shift) \
V(ARRAY_SPLICE_INDEX, JSFunction, array_splice) \
V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift) \
V(ARRAY_ENTRIES_ITERATOR_INDEX, JSFunction, array_entries_iterator) \
V(ARRAY_FOR_EACH_ITERATOR_INDEX, JSFunction, array_for_each_iterator) \
V(ARRAY_KEYS_ITERATOR_INDEX, JSFunction, array_keys_iterator) \
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
V(ERROR_TO_STRING, JSFunction, error_to_string) \
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
V(GLOBAL_PROXY_FUNCTION_INDEX, JSFunction, global_proxy_function) \
V(MAP_DELETE_INDEX, JSFunction, map_delete) \
V(MAP_GET_INDEX, JSFunction, map_get) \
V(MAP_HAS_INDEX, JSFunction, map_has) \
V(MAP_SET_INDEX, JSFunction, map_set) \
V(FUNCTION_HAS_INSTANCE_INDEX, JSFunction, function_has_instance) \
V(OBJECT_VALUE_OF, JSFunction, object_value_of) \
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
V(RESOLVE_LOCALE_FUNCTION_INDEX, JSFunction, resolve_locale) \
V(SET_ADD_INDEX, JSFunction, set_add) \
V(SET_DELETE_INDEX, JSFunction, set_delete) \
V(SET_HAS_INDEX, JSFunction, set_has) \
V(SYNTAX_ERROR_FUNCTION_INDEX, JSFunction, syntax_error_function) \
V(TYPE_ERROR_FUNCTION_INDEX, JSFunction, type_error_function) \
V(URI_ERROR_FUNCTION_INDEX, JSFunction, uri_error_function) \
V(WASM_COMPILE_ERROR_FUNCTION_INDEX, JSFunction, \
wasm_compile_error_function) \
V(WASM_LINK_ERROR_FUNCTION_INDEX, JSFunction, wasm_link_error_function) \
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, \
wasm_runtime_error_function) \
V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
V(ARRAY_SHIFT_INDEX, JSFunction, array_shift) \
V(ARRAY_SPLICE_INDEX, JSFunction, array_splice) \
V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift) \
V(ARRAY_ENTRIES_ITERATOR_INDEX, JSFunction, array_entries_iterator) \
V(ARRAY_FOR_EACH_ITERATOR_INDEX, JSFunction, array_for_each_iterator) \
V(ARRAY_KEYS_ITERATOR_INDEX, JSFunction, array_keys_iterator) \
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
V(ERROR_TO_STRING, JSFunction, error_to_string) \
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
V(GLOBAL_PROXY_FUNCTION_INDEX, JSFunction, global_proxy_function) \
V(MAP_DELETE_INDEX, JSFunction, map_delete) \
V(MAP_GET_INDEX, JSFunction, map_get) \
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) \
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
V(RESOLVE_LOCALE_FUNCTION_INDEX, JSFunction, resolve_locale) \
V(SET_ADD_INDEX, JSFunction, set_add) \
V(SET_DELETE_INDEX, JSFunction, set_delete) \
V(SET_HAS_INDEX, JSFunction, set_has) \
V(SYNTAX_ERROR_FUNCTION_INDEX, JSFunction, syntax_error_function) \
V(TYPE_ERROR_FUNCTION_INDEX, JSFunction, type_error_function) \
V(URI_ERROR_FUNCTION_INDEX, JSFunction, uri_error_function) \
V(WASM_COMPILE_ERROR_FUNCTION_INDEX, JSFunction, \
wasm_compile_error_function) \
V(WASM_LINK_ERROR_FUNCTION_INDEX, JSFunction, wasm_link_error_function) \
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, \
wasm_runtime_error_function) \
V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \
V(WEAKSET_ADD_INDEX, JSFunction, weakset_add)
#define NATIVE_CONTEXT_FIELDS(V) \
......
......@@ -807,7 +807,6 @@ function canonicalizeLanguageTag(localeID) {
return %CanonicalizeLanguageTag(localeString);
}
/**
* Returns an InternalArray where all locales are canonicalized and duplicates
* removed.
......@@ -846,6 +845,13 @@ 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
]);
/**
* Check the structural Validity of the language tag per ECMA 402 6.2.2:
* - Well-formed per RFC 5646 2.1
......@@ -2083,30 +2089,6 @@ function cachedOrNewService(service, locales, options, defaults) {
return new savedObjects[service](locales, useOptions);
}
function LocaleConvertCase(s, locales, isToUpper) {
// ECMA 402 section 13.1.2 steps 1 through 12.
var language;
// Optimize for the most common two cases. initializeLocaleList() can handle
// them as well, but it's rather slow accounting for over 60% of
// toLocale{U,L}Case() and about 40% of toLocale{U,L}Case("<locale>").
if (IS_UNDEFINED(locales)) {
language = GetDefaultICULocaleJS();
} else if (IS_STRING(locales)) {
language = canonicalizeLanguageTag(locales);
} else {
var locales = initializeLocaleList(locales);
language = locales.length > 0 ? locales[0] : GetDefaultICULocaleJS();
}
// StringSplit is slower than this.
var pos = %StringIndexOf(language, '-', 0);
if (pos !== -1) {
language = %_Call(StringSubstring, language, 0, pos);
}
return %StringLocaleConvertCase(s, isToUpper, language);
}
/**
* Compares this and that, and returns less than 0, 0 or greater than 0 value.
* Overrides the built-in method.
......@@ -2125,23 +2107,6 @@ DEFINE_METHOD(
}
);
DEFINE_METHODS_LEN(
GlobalString.prototype,
{
toLocaleLowerCase(locales) {
REQUIRE_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
return LocaleConvertCase(TO_STRING(this), locales, false);
}
toLocaleUpperCase(locales) {
REQUIRE_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
return LocaleConvertCase(TO_STRING(this), locales, true);
}
},
0 /* Set function length of both methods. */
);
/**
* Formats a Number object (this) using locale and options values.
* If locale or options are omitted, defaults are used.
......
......@@ -1763,6 +1763,22 @@ 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
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'));
}
......@@ -1789,5 +1805,92 @@ bool Intl::IsWellFormedCurrencyCode(Isolate* isolate, Handle<String> currency) {
}
}
// ecma402 #sup-string.prototype.tolocalelowercase
// ecma402 #sup-string.prototype.tolocaleuppercase
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);
}
}
int dash = String::IndexOf(isolate, requested_locale,
factory->NewStringFromStaticChars("-"), 0);
if (dash > 0) {
requested_locale = factory->NewSubString(requested_locale, 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);
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);
}
// 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(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);
}
} // namespace internal
} // namespace v8
......@@ -314,6 +314,11 @@ class Intl {
// ecma402/#sec-iswellformedcurrencycode
static bool IsWellFormedCurrencyCode(Isolate* isolate,
Handle<String> currency);
// For locale sensitive functions
V8_WARN_UNUSED_RESULT static MaybeHandle<String> StringLocaleConvertCase(
Isolate* isolate, Handle<String> s, bool is_upper,
Handle<Object> locales);
};
} // namespace internal
......
......@@ -547,49 +547,6 @@ RUNTIME_FUNCTION(Runtime_StringToUpperCaseIntl) {
return ConvertToUpper(s, isolate);
}
RUNTIME_FUNCTION(Runtime_StringLocaleConvertCase) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 3);
CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
CONVERT_BOOLEAN_ARG_CHECKED(is_upper, 1);
CONVERT_ARG_HANDLE_CHECKED(String, lang_arg, 2);
// Primary language tag can be up to 8 characters long in theory.
// https://tools.ietf.org/html/bcp47#section-2.2.1
DCHECK_LE(lang_arg->length(), 8);
lang_arg = String::Flatten(isolate, lang_arg);
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(lang_arg->length() != 2))
return ConvertCase(s, is_upper, isolate);
char c1, c2;
{
DisallowHeapAllocation no_gc;
String::FlatContent lang = lang_arg->GetFlatContent();
c1 = lang.Get(0);
c2 = lang.Get(1);
}
// 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'))
return LocaleConvertCase(s, isolate, is_upper, "tr");
if (V8_UNLIKELY(c1 == 'e' && c2 == 'l'))
return LocaleConvertCase(s, isolate, is_upper, "el");
if (V8_UNLIKELY(c1 == 'l' && c2 == 't'))
return LocaleConvertCase(s, isolate, is_upper, "lt");
if (V8_UNLIKELY(c1 == 'a' && c2 == 'z'))
return LocaleConvertCase(s, isolate, is_upper, "az");
return ConvertCase(s, is_upper, isolate);
}
RUNTIME_FUNCTION(Runtime_DateCacheVersion) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
......
......@@ -225,7 +225,6 @@ namespace internal {
F(IsWellFormedCurrencyCode, 1, 1) \
F(MarkAsInitializedIntlObjectOfType, 2, 1) \
F(PluralRulesSelect, 2, 1) \
F(StringLocaleConvertCase, 3, 1) \
F(StringToLowerCaseIntl, 1, 1) \
F(StringToUpperCaseIntl, 1, 1)
#else
......
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