Index: src/runtime/runtime-strings.cc |
diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc |
index bd4dd699b4576363f7232380b75653e20863fce1..09f9cafb59f0df3299020d29020ea4643dbeccb8 100644 |
--- a/src/runtime/runtime-strings.cc |
+++ b/src/runtime/runtime-strings.cc |
@@ -12,6 +12,11 @@ |
#include "src/string-builder.h" |
#include "src/string-search.h" |
+#ifdef V8_I18N_SUPPORT |
+#include "unicode/uchar.h" |
+#include "unicode/ustring.h" |
+#endif |
+ |
namespace v8 { |
namespace internal { |
@@ -1069,19 +1074,242 @@ MUST_USE_RESULT static Object* ConvertCase( |
} |
+#ifdef V8_I18N_SUPPORT |
+namespace { |
Yang
2016/01/07 09:47:38
Can we move all of that into its own file?
|
+ |
+typedef int32_t (*case_conversion_fn)(UChar* dest, int32_t destCapacity, |
+ const UChar* src, int32_t srcLength, |
+ const char* locale, |
+ UErrorCode* pErrorCode); |
+ |
+MUST_USE_RESULT static Handle<String> ConvertCaseICU(Handle<String> s, |
+ Isolate* isolate, |
+ case_conversion_fn fn) { |
+ int32_t length = s->length(); |
+ Handle<SeqTwoByteString> result = |
+ isolate->factory()->NewRawTwoByteString(length).ToHandleChecked(); |
+ |
+ // If we already have a UTF-16 string, use that, otherwise build it |
+ base::SmartArrayPointer<uc16> sap; |
+ const UChar* src; |
+ if (StringShape(*s).IsSequentialTwoByte()) { |
+ src = |
+ reinterpret_cast<const UChar*>(SeqTwoByteString::cast(*s)->GetChars()); |
Yang
2016/01/07 09:47:38
Please use String::FlatContent to access the flat
|
+ } else { |
+ sap = s->ToWideCString(ROBUST_STRING_TRAVERSAL); |
+ src = reinterpret_cast<const UChar*>(sap.get()); |
+ } |
+ |
+ UErrorCode error = U_ZERO_ERROR; |
+ int32_t real_length = fn(reinterpret_cast<UChar*>(result->GetChars()), length, |
+ src, length, "", &error); |
+ // If the lengths are equal, ICU will be unable to put the terminating \0 |
+ // but that's to be expected, as V8 strings are not null-terminated. |
+ if (error == U_STRING_NOT_TERMINATED_WARNING) { |
+ DCHECK_EQ(real_length, length); |
+ return Handle<String>(*result); |
+ } |
+ // These are the two expected error types from an oversized converted string |
+ // "buffer overflow" when needs more space; "success" when too long |
+ DCHECK(error == U_BUFFER_OVERFLOW_ERROR || error == U_ZERO_ERROR); |
+ |
+ error = U_ZERO_ERROR; |
+ result = |
+ isolate->factory()->NewRawTwoByteString(real_length).ToHandleChecked(); |
+ int32_t real_length_again = fn(reinterpret_cast<UChar*>(result->GetChars()), |
Yang
2016/01/07 09:47:38
Instead of running the whole thing again, you coul
|
+ real_length, src, length, "", &error); |
Yang
2016/01/07 09:47:38
src can have moved already after the allocation of
|
+ USE(real_length_again); // Shouldn't that be part of DCHECK_EQ? |
+ DCHECK_EQ(real_length, real_length_again); |
+ DCHECK_EQ(error, U_STRING_NOT_TERMINATED_WARNING); |
+ if (error != U_STRING_NOT_TERMINATED_WARNING) return s; |
+ return Handle<String>(*result); |
+} |
+ |
+ |
+inline bool IsASCIIUpper(uint16_t ch) { return ch >= 'A' && ch <= 'Z'; } |
+ |
+ |
+inline uint16_t ToASCIILower(uint16_t ch) { |
+ return ch | ((ch >= 'A' && ch <= 'Z') << 5); |
+} |
+ |
+ |
+inline uint16_t ToASCIIUpper(uint16_t ch) { |
+ return ch & ~((ch >= 'a' && ch <= 'z') << 5); |
+} |
+ |
+ |
+MUST_USE_RESULT Handle<String> StringToLowerCase(Handle<String> s, |
+ Isolate* isolate) { |
+ // Note: This is a hot function in the Dromaeo benchmark, specifically the |
+ // no-op code path up through the first 'return' statement. |
+ |
+ int length = s->length(); |
+ // First scan the string for uppercase and non-ASCII characters: |
+ if (s->HasOnlyOneByteChars()) { |
+ unsigned first_index_to_lower = length; |
+ for (int index = 0; index < length; ++index) { |
+ // Blink specializes this path for one-byte strings, so it |
+ // does not need to do a generic get, but can do the equivalent |
+ // of SeqOneByteStringGet. |
+ uint16_t ch = s->Get(index); |
Yang
2016/01/07 09:47:38
This can get pretty expensive if the string is a d
|
+ if (V8_UNLIKELY(IsASCIIUpper(ch) || ch & ~0x7F)) { |
+ first_index_to_lower = index; |
+ break; |
+ } |
+ } |
+ |
+ // Nothing to do if the string is all ASCII with no uppercase. |
+ if (first_index_to_lower == length) return s; |
+ |
+ // We depend here on the invariant that the length of a Latin1 |
+ // string is invariant under ToLowerCase, and the result always |
+ // fits in the Latin1 range (untrue for ToUpperCase, and might |
+ // be untrue in some locales, but this is the root locale) |
+ Handle<SeqOneByteString> result = |
+ isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); |
+ // In Blink, this is a simple memcpy, but in V8, the path applies in |
+ // more cases. The optimization here is conditional on whether the |
+ // source is actually a simple 8-bit string (always true in Blink). |
+ // The broader condition lets us eliminate a bunch of duplicate code |
+ // which Blink had in a separate section below. |
+ if (StringShape(*s).IsSequentialOneByte()) { |
Yang
2016/01/07 09:47:38
Let's not use StringShape. Just check for s->IsSeq
|
+ // In this path, we can use the one-byte-specific Get, and |
+ // memcpy until the first_index_to_lower. |
+ SeqOneByteString* source = SeqOneByteString::cast(*s); |
+ memcpy(result->GetChars(), source->GetChars(), first_index_to_lower); |
Yang
2016/01/07 09:47:38
We should just use CopyChars for uniformity here.
|
+ for (int index = first_index_to_lower; index < length; ++index) { |
+ uint16_t ch = source->SeqOneByteStringGet(index); |
+ result->SeqOneByteStringSet(index, |
+ V8_UNLIKELY(ch & ~0x7F) |
+ ? static_cast<uint16_t>(u_tolower(ch)) |
+ : ToASCIILower(ch)); |
+ } |
+ } else { |
+ // In this path, we start from the beginning of the string, |
+ // since there is nothing to memcpy from, and we have to |
+ // use the generic Get. Another option here would be to create |
+ // a two-byte string as output, and do a memcpy from that, |
+ // as Blink does, but there's also the ConsString case. |
Yang
2016/01/07 09:47:38
As explained above, we should flatten upfront, so
|
+ for (int index = 0; index < length; ++index) { |
+ uint16_t ch = s->Get(index); |
+ result->SeqOneByteStringSet(index, |
+ V8_UNLIKELY(ch & ~0x7F) |
+ ? static_cast<uint16_t>(u_tolower(ch)) |
+ : ToASCIILower(ch)); |
+ } |
+ } |
+ |
+ return Handle<String>(*result); |
+ } |
+ |
+ // Blink had an additional case here for ASCII 2-byte strings, but |
+ // that is subsumed by the above code (assuming there isn't a false |
+ // negative for HasOnlyOneByteChars). |
+ |
+ // Do a slower implementation for cases that include non-ASCII characters. |
+ return ConvertCaseICU(s, isolate, u_strToLower); |
+} |
+ |
+ |
+const uint16_t sharp_s = L'\u00DF'; |
Yang
2016/01/07 09:47:38
0xDF should do the trick to, right?
|
+ |
+MUST_USE_RESULT Handle<String> StringToUpperCase(Handle<String> s, |
+ Isolate* isolate) { |
+ // This function could be optimized for no-op cases the way lower() is, |
+ // but in empirical testing, few actual calls to upper() are no-ops, so |
+ // it wouldn't be worth the extra time for pre-scanning. |
+ |
+ int32_t length = s->length(); |
+ |
+ if (s->HasOnlyOneByteChars()) { |
+ Handle<SeqOneByteString> result = |
+ isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); |
+ |
+ // Do a faster loop for the case where all the characters are ASCII. |
+ uint16_t ored = 0; |
+ for (int index = 0; index < length; ++index) { |
+ uint16_t ch = s->Get(index); |
Yang
2016/01/07 09:47:38
Again, please flatten upfront and use String::Flat
|
+ ored |= ch; |
+ result->SeqOneByteStringSet(index, ToASCIIUpper(ch)); |
+ } |
+ if (!(ored & ~0x7F)) return Handle<String>(*result); |
+ |
+ // Do a slower implementation for cases that include non-ASCII Latin-1 |
+ // characters. |
+ int sharp_s_count = 0; |
+ |
+ // There are two special cases. |
+ // 1. latin-1 characters when converted to upper case are 16 bit |
+ // characters. |
+ // 2. Lower case sharp-S converts to "SS" (two characters) |
+ for (int32_t index = 0; index < length; ++index) { |
+ uint16_t ch = s->Get(index); |
+ if (V8_UNLIKELY(ch == sharp_s)) ++sharp_s_count; |
+ uint16_t upper = static_cast<uint16_t>(u_toupper(static_cast<UChar>(ch))); |
+ if (V8_UNLIKELY(upper > 0xff)) { |
+ // Since this upper-cased character does not fit in an 8-bit string, we |
+ // need to take the 16-bit path. |
+ goto upconvert; |
Yang
2016/01/07 09:47:38
Can't we simply "return ConvertCaseICU(s, isolate,
|
+ } |
+ result->SeqOneByteStringSet(index, upper); |
+ } |
+ |
+ if (sharp_s_count == 0) return Handle<String>(*result); |
+ |
+ // We have sharp_s_count sharp-s characters, but none of the other special |
+ // characters. |
+ result = isolate->factory() |
+ ->NewRawOneByteString(length + sharp_s_count) |
+ .ToHandleChecked(); |
+ for (int32_t index = 0, dest_index = 0; index < length; ++index) { |
+ uint16_t ch = s->Get(index); |
+ if (ch == sharp_s) { |
+ result->SeqOneByteStringSet(dest_index++, 'S'); |
+ result->SeqOneByteStringSet(dest_index++, 'S'); |
+ } else { |
+ uint16_t upper = |
+ static_cast<uint16_t>(u_toupper(static_cast<UChar>(ch))); |
+ result->SeqOneByteStringSet(dest_index++, upper); |
+ } |
+ } |
+ |
+ return Handle<String>(*result); |
+ } |
+ |
+upconvert: |
+ return ConvertCaseICU(s, isolate, u_strToUpper); |
+} |
+ |
+} // namespace |
+#endif |
+ |
+ |
RUNTIME_FUNCTION(Runtime_StringToLowerCase) { |
HandleScope scope(isolate); |
- DCHECK(args.length() == 1); |
+ DCHECK_EQ(args.length(), 1); |
CONVERT_ARG_HANDLE_CHECKED(String, s, 0); |
- return ConvertCase(s, isolate, isolate->runtime_state()->to_lower_mapping()); |
+#ifdef V8_I18N_SUPPORT |
+ if (FLAG_icu_case_mapping) |
Yang
2016/01/07 09:47:38
the convention with multiline conditionals is to u
|
+ return *StringToLowerCase(s, isolate); |
+ else |
+#endif |
+ return ConvertCase(s, isolate, |
+ isolate->runtime_state()->to_lower_mapping()); |
} |
RUNTIME_FUNCTION(Runtime_StringToUpperCase) { |
HandleScope scope(isolate); |
- DCHECK(args.length() == 1); |
+ DCHECK_EQ(args.length(), 1); |
CONVERT_ARG_HANDLE_CHECKED(String, s, 0); |
- return ConvertCase(s, isolate, isolate->runtime_state()->to_upper_mapping()); |
+#ifdef V8_I18N_SUPPORT |
+ if (FLAG_icu_case_mapping) |
+ return *StringToUpperCase(s, isolate); |
+ else |
+#endif |
+ return ConvertCase(s, isolate, |
+ isolate->runtime_state()->to_upper_mapping()); |
} |