Index: src/runtime/runtime-strings.cc |
diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc |
index 6786fa99fba0f92e1c41e5e228d9f6852349dd74..38be231f27e5ad0aaee57e0257d23b4835f6f0bd 100644 |
--- a/src/runtime/runtime-strings.cc |
+++ b/src/runtime/runtime-strings.cc |
@@ -12,6 +12,12 @@ |
#include "src/string-builder.h" |
#include "src/string-search.h" |
+#ifdef V8_I18N_SUPPORT |
+#include "unicode/locid.h" |
+#include "unicode/uchar.h" |
+#include "unicode/unistr.h" |
+#endif |
+ |
namespace v8 { |
namespace internal { |
@@ -1077,20 +1083,210 @@ MUST_USE_RESULT static Object* ConvertCase( |
return ConvertCaseHelper(isolate, *s, *result, length, mapping); |
} |
+#ifdef V8_I18N_SUPPORT |
+namespace { |
+ |
+MUST_USE_RESULT static Handle<String> ConvertCaseICU(Handle<String> s, |
+ Isolate* isolate, |
+ bool is_to_upper) { |
+ DCHECK(s->IsFlat()); |
+ // Handle<String> flattened = String::Flatten(s); |
+ String::FlatContent flat = s->GetFlatContent(); |
adamk
2016/04/07 20:50:55
You'll want to add a DisallowHeapAllocation object
|
+ |
+ const UChar* src; |
+ if (flat.IsOneByte()) { |
+ base::SmartArrayPointer<uc16> sap = s->ToWideCString(); |
+ src = reinterpret_cast<const UChar*>(sap.get()); |
+ } else { |
+ src = reinterpret_cast<const UChar*>(flat.ToUC16Vector().start()); |
+ } |
+ |
+ int32_t length = s->length(); |
+ |
+ // This UnicodeString ctor has copy-on-write semantics. It starts as a |
adamk
2016/04/07 20:50:55
...this comment worries me. Does this mean that co
|
+ // read-only alias but the buffer is copied when it's written to. |
+ icu::UnicodeString converted(0, src, length); |
+ const icu::Locale& root_locale = icu::Locale::getRoot(); |
+ if (is_to_upper) |
+ converted.toUpper(root_locale); |
+ else |
+ converted.toLower(root_locale); |
+ |
+ return isolate->factory() |
+ ->NewStringFromTwoByte(Vector<const uint16_t>( |
+ reinterpret_cast<const uint16_t*>(converted.getBuffer()), |
+ converted.length())) |
+ .ToHandleChecked(); |
+} |
+ |
+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(); |
+ s = String::Flatten(s); |
+ // 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); |
+ 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(); |
+ if (s->IsSeqOneByteString()) { |
+ SeqOneByteString* source = SeqOneByteString::cast(*s); |
+ CopyChars(result->GetChars(), source->GetChars(), first_index_to_lower); |
+ } else { |
+ // Do we have to worry about External{One,Two}ByteString? |
+ DCHECK(s->IsSeqTwoByteString()); |
+ SeqTwoByteString* source = SeqTwoByteString::cast(*s); |
+ CopyChars(result->GetChars(), source->GetChars(), first_index_to_lower); |
+ } |
+ |
+ for (int index = first_index_to_lower; 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, false); |
+} |
+ |
+const uint16_t sharp_s = 0x00DFu; |
+ |
+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(); |
+ s = String::Flatten(s); |
+ |
+ 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); |
+ 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; |
+ continue; |
+ } |
+ 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. |
+ return ConvertCaseICU(s, isolate, true); |
+ } |
+ 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); |
+ } |
+ |
+ return ConvertCaseICU(s, isolate, true); |
+} |
+ |
+} // 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) |
+ 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()); |
} |