| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifdef V8_I18N_SUPPORT |
| |
| #include "src/builtins/builtins-utils.h" |
| #include "src/builtins/builtins.h" |
| #include "src/i18n.h" |
| #include "src/objects-inl.h" |
| |
| #include "unicode/normalizer2.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| BUILTIN(StringPrototypeToLowerCaseI18N) { |
| HandleScope scope(isolate); |
| TO_THIS_STRING(string, "String.prototype.toLowerCase"); |
| string = String::Flatten(string); |
| return ConvertCase(string, false, isolate); |
| } |
| |
| BUILTIN(StringPrototypeToUpperCaseI18N) { |
| HandleScope scope(isolate); |
| TO_THIS_STRING(string, "String.prototype.toUpperCase"); |
| string = String::Flatten(string); |
| return ConvertCase(string, true, isolate); |
| } |
| |
| BUILTIN(StringPrototypeNormalizeI18N) { |
| HandleScope handle_scope(isolate); |
| TO_THIS_STRING(string, "String.prototype.normalize"); |
| |
| Handle<Object> form_input = args.atOrUndefined(isolate, 1); |
| const char* form_name; |
| UNormalization2Mode form_mode; |
| if (form_input->IsUndefined(isolate)) { |
| // default is FNC |
| form_name = "nfc"; |
| form_mode = UNORM2_COMPOSE; |
| } else { |
| Handle<String> form; |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form, |
| Object::ToString(isolate, form_input)); |
| |
| if (String::Equals(form, isolate->factory()->NFC_string())) { |
| form_name = "nfc"; |
| form_mode = UNORM2_COMPOSE; |
| } else if (String::Equals(form, isolate->factory()->NFD_string())) { |
| form_name = "nfc"; |
| form_mode = UNORM2_DECOMPOSE; |
| } else if (String::Equals(form, isolate->factory()->NFKC_string())) { |
| form_name = "nfkc"; |
| form_mode = UNORM2_COMPOSE; |
| } else if (String::Equals(form, isolate->factory()->NFKD_string())) { |
| form_name = "nfkc"; |
| form_mode = UNORM2_DECOMPOSE; |
| } else { |
| Handle<String> valid_forms = |
| isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD"); |
| THROW_NEW_ERROR_RETURN_FAILURE( |
| isolate, |
| NewRangeError(MessageTemplate::kNormalizationForm, valid_forms)); |
| } |
| } |
| |
| int length = string->length(); |
| string = String::Flatten(string); |
| icu::UnicodeString result; |
| std::unique_ptr<uc16[]> sap; |
| UErrorCode status = U_ZERO_ERROR; |
| { |
| DisallowHeapAllocation no_gc; |
| String::FlatContent flat = string->GetFlatContent(); |
| const UChar* src = GetUCharBufferFromFlat(flat, &sap, length); |
| icu::UnicodeString input(false, src, length); |
| // Getting a singleton. Should not free it. |
| const icu::Normalizer2* normalizer = |
| icu::Normalizer2::getInstance(nullptr, form_name, form_mode, status); |
| DCHECK(U_SUCCESS(status)); |
| CHECK(normalizer != nullptr); |
| int32_t normalized_prefix_length = |
| normalizer->spanQuickCheckYes(input, status); |
| // Quick return if the input is already normalized. |
| if (length == normalized_prefix_length) return *string; |
| icu::UnicodeString unnormalized = |
| input.tempSubString(normalized_prefix_length); |
| // Read-only alias of the normalized prefix. |
| result.setTo(false, input.getBuffer(), normalized_prefix_length); |
| // copy-on-write; normalize the suffix and append to |result|. |
| normalizer->normalizeSecondAndAppend(result, unnormalized, status); |
| } |
| |
| if (U_FAILURE(status)) { |
| return isolate->heap()->undefined_value(); |
| } |
| |
| RETURN_RESULT_OR_FAILURE( |
| isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>( |
| reinterpret_cast<const uint16_t*>(result.getBuffer()), |
| result.length()))); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_I18N_SUPPORT |