#include <optional>
#include "src/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h"
#include "src/heap/heap-inl.h"
#include "src/logging/counters.h"
#include "src/numbers/conversions.h"
#include "src/objects/objects-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/intl-objects.h"
#endif
#include "src/base/strings.h"
#include "src/regexp/regexp-utils.h"
#include "src/strings/string-builder-inl.h"
#include "src/strings/string-case.h"
#include "src/strings/unicode-inl.h"
#include "src/strings/unicode.h"
namespace v8 {
namespace internal {
namespace {
bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) { … }
static constexpr base::uc32 kInvalidCodePoint = …;
base::uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) { … }
}
BUILTIN(StringFromCodePoint) { … }
BUILTIN(StringPrototypeLastIndexOf) { … }
BUILTIN(StringPrototypeLocaleCompare) { … }
#ifndef V8_INTL_SUPPORT
BUILTIN(StringPrototypeNormalize) {
HandleScope handle_scope(isolate);
TO_THIS_STRING(string, "String.prototype.normalize");
Handle<Object> form_input = args.atOrUndefined(isolate, 1);
if (IsUndefined(*form_input, isolate)) return *string;
Handle<String> form;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form,
Object::ToString(isolate, form_input));
if (!(String::Equals(isolate, form, isolate->factory()->NFC_string()) ||
String::Equals(isolate, form, isolate->factory()->NFD_string()) ||
String::Equals(isolate, form, isolate->factory()->NFKC_string()) ||
String::Equals(isolate, form, isolate->factory()->NFKD_string()))) {
Handle<String> valid_forms =
isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
}
return *string;
}
#endif
#ifndef V8_INTL_SUPPORT
namespace {
inline bool ToUpperOverflows(base::uc32 character) {
static const base::uc32 yuml_code = 0xFF;
static const base::uc32 micro_code = 0xB5;
return (character == yuml_code || character == micro_code);
}
template <class Converter>
V8_WARN_UNUSED_RESULT static Tagged<Object> ConvertCaseHelper(
Isolate* isolate, Tagged<String> string, Tagged<SeqString> result,
int result_length, unibrow::Mapping<Converter, 128>* mapping) {
DisallowGarbageCollection no_gc;
bool has_changed_character = false;
StringCharacterStream stream(string);
unibrow::uchar chars[Converter::kMaxWidth];
base::uc32 current = stream.GetNext();
bool ignore_overflow = Converter::kIsToLower || IsSeqTwoByteString(result);
for (int i = 0; i < result_length;) {
bool has_next = stream.HasMore();
base::uc32 next = has_next ? stream.GetNext() : 0;
int char_length = mapping->get(current, next, chars);
if (char_length == 0) {
result->Set(i, current);
i++;
} else if (char_length == 1 &&
(ignore_overflow || !ToUpperOverflows(current))) {
DCHECK(static_cast<base::uc32>(chars[0]) != current);
result->Set(i, chars[0]);
has_changed_character = true;
i++;
} else if (result_length == string->length()) {
bool overflows = ToUpperOverflows(current);
int next_length = 0;
if (has_next) {
next_length = mapping->get(next, 0, chars);
if (next_length == 0) next_length = 1;
}
int current_length = i + char_length + next_length;
while (stream.HasMore()) {
current = stream.GetNext();
overflows |= ToUpperOverflows(current);
int char_length = mapping->get(current, 0, chars);
if (char_length == 0) char_length = 1;
current_length += char_length;
if (current_length > String::kMaxLength) {
AllowGarbageCollection allocate_error_and_return;
THROW_NEW_ERROR_RETURN_FAILURE(isolate,
NewInvalidStringLengthError());
}
}
return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
: Smi::FromInt(current_length);
} else {
for (int j = 0; j < char_length; j++) {
result->Set(i, chars[j]);
i++;
}
has_changed_character = true;
}
current = next;
}
if (has_changed_character) {
return result;
} else {
return string;
}
}
template <class Converter>
V8_WARN_UNUSED_RESULT static Tagged<Object> ConvertCase(
Handle<String> s, Isolate* isolate,
unibrow::Mapping<Converter, 128>* mapping) {
s = String::Flatten(isolate, s);
int length = s->length();
if (length == 0) return *s;
if (String::IsOneByteRepresentationUnderneath(*s)) {
Handle<SeqOneByteString> result =
isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
DisallowGarbageCollection no_gc;
String::FlatContent flat_content = s->GetFlatContent(no_gc);
DCHECK(flat_content.IsFlat());
bool has_changed_character = false;
int index_to_first_unprocessed = FastAsciiConvert<Converter::kIsToLower>(
reinterpret_cast<char*>(result->GetChars(no_gc)),
reinterpret_cast<const char*>(flat_content.ToOneByteVector().begin()),
length, &has_changed_character);
if (index_to_first_unprocessed == length)
return has_changed_character ? *result : *s;
}
Handle<SeqString> result;
if (s->IsOneByteRepresentation()) {
result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
} else {
result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
}
Tagged<Object> answer =
ConvertCaseHelper(isolate, *s, *result, length, mapping);
if (IsException(answer, isolate) || IsString(answer)) return answer;
DCHECK(IsSmi(answer));
length = Smi::ToInt(answer);
if (s->IsOneByteRepresentation() && length > 0) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawOneByteString(length));
} else {
if (length < 0) length = -length;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawTwoByteString(length));
}
return ConvertCaseHelper(isolate, *s, *result, length, mapping);
}
}
BUILTIN(StringPrototypeToLocaleLowerCase) {
HandleScope scope(isolate);
TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase");
return ConvertCase(string, isolate,
isolate->runtime_state()->to_lower_mapping());
}
BUILTIN(StringPrototypeToLocaleUpperCase) {
HandleScope scope(isolate);
TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase");
return ConvertCase(string, isolate,
isolate->runtime_state()->to_upper_mapping());
}
BUILTIN(StringPrototypeToLowerCase) {
HandleScope scope(isolate);
TO_THIS_STRING(string, "String.prototype.toLowerCase");
return ConvertCase(string, isolate,
isolate->runtime_state()->to_lower_mapping());
}
BUILTIN(StringPrototypeToUpperCase) {
HandleScope scope(isolate);
TO_THIS_STRING(string, "String.prototype.toUpperCase");
return ConvertCase(string, isolate,
isolate->runtime_state()->to_upper_mapping());
}
#endif
BUILTIN(StringRaw) { … }
}
}