5 #include "src/builtins/builtins-utils-inl.h" 6 #include "src/builtins/builtins.h" 7 #include "src/conversions.h" 8 #include "src/counters.h" 9 #include "src/objects-inl.h" 10 #ifdef V8_INTL_SUPPORT 11 #include "src/objects/intl-objects.h" 13 #include "src/regexp/regexp-utils.h" 14 #include "src/string-builder-inl.h" 15 #include "src/string-case.h" 16 #include "src/unicode-inl.h" 24 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
25 if (!value->IsNumber() &&
26 !Object::ToNumber(isolate, value).ToHandle(&value)) {
30 if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
35 if (value->Number() < 0 || value->Number() > 0x10FFFF) {
42 uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args,
int index) {
43 Handle<Object> value = args.at(1 + index);
44 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value,
45 Object::ToNumber(isolate, value), -1);
46 if (!IsValidCodePoint(isolate, value)) {
47 isolate->Throw(*isolate->factory()->NewRangeError(
48 MessageTemplate::kInvalidCodePoint, value));
51 return DoubleToUint32(value->Number());
57 BUILTIN(StringFromCodePoint) {
58 HandleScope scope(isolate);
59 int const length = args.length() - 1;
60 if (length == 0)
return ReadOnlyRoots(isolate).empty_string();
65 std::vector<uint8_t> one_byte_buffer;
66 one_byte_buffer.reserve(length);
69 for (index = 0; index < length; index++) {
70 code = NextCodePoint(isolate, args, index);
72 return ReadOnlyRoots(isolate).exception();
74 if (code > String::kMaxOneByteCharCode) {
77 one_byte_buffer.push_back(code);
80 if (index == length) {
81 RETURN_RESULT_OR_FAILURE(
82 isolate, isolate->factory()->NewStringFromOneByte(Vector<uint8_t>(
83 one_byte_buffer.data(), one_byte_buffer.size())));
86 std::vector<uc16> two_byte_buffer;
87 two_byte_buffer.reserve(length - index);
90 if (code <= static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
91 two_byte_buffer.push_back(code);
93 two_byte_buffer.push_back(unibrow::Utf16::LeadSurrogate(code));
94 two_byte_buffer.push_back(unibrow::Utf16::TrailSurrogate(code));
97 if (++index == length) {
100 code = NextCodePoint(isolate, args, index);
102 return ReadOnlyRoots(isolate).exception();
106 Handle<SeqTwoByteString> result;
107 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
109 isolate->factory()->NewRawTwoByteString(
110 static_cast<int>(one_byte_buffer.size() + two_byte_buffer.size())));
112 DisallowHeapAllocation no_gc;
113 CopyChars(result->GetChars(), one_byte_buffer.data(), one_byte_buffer.size());
114 CopyChars(result->GetChars() + one_byte_buffer.size(), two_byte_buffer.data(),
115 two_byte_buffer.size());
122 BUILTIN(StringPrototypeEndsWith) {
123 HandleScope handle_scope(isolate);
124 TO_THIS_STRING(str,
"String.prototype.endsWith");
127 Handle<Object> search = args.atOrUndefined(isolate, 1);
128 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
129 if (is_reg_exp.IsNothing()) {
130 DCHECK(isolate->has_pending_exception());
131 return ReadOnlyRoots(isolate).exception();
133 if (is_reg_exp.FromJust()) {
134 THROW_NEW_ERROR_RETURN_FAILURE(
135 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
136 isolate->factory()->NewStringFromStaticChars(
137 "String.prototype.endsWith")));
139 Handle<String> search_string;
140 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
141 Object::ToString(isolate, search));
143 Handle<Object> position = args.atOrUndefined(isolate, 2);
146 if (position->IsUndefined(isolate)) {
149 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
150 Object::ToInteger(isolate, position));
151 end = str->ToValidIndex(*position);
154 int start = end - search_string->length();
155 if (start < 0)
return ReadOnlyRoots(isolate).false_value();
157 str = String::Flatten(isolate, str);
158 search_string = String::Flatten(isolate, search_string);
160 DisallowHeapAllocation no_gc;
161 String::FlatContent str_content = str->GetFlatContent();
162 String::FlatContent search_content = search_string->GetFlatContent();
164 if (str_content.IsOneByte() && search_content.IsOneByte()) {
165 Vector<const uint8_t> str_vector = str_content.ToOneByteVector();
166 Vector<const uint8_t> search_vector = search_content.ToOneByteVector();
168 return isolate->heap()->ToBoolean(memcmp(str_vector.start() + start,
169 search_vector.start(),
170 search_string->length()) == 0);
173 FlatStringReader str_reader(isolate, str);
174 FlatStringReader search_reader(isolate, search_string);
176 for (
int i = 0;
i < search_string->length();
i++) {
177 if (str_reader.Get(start +
i) != search_reader.Get(
i)) {
178 return ReadOnlyRoots(isolate).false_value();
181 return ReadOnlyRoots(isolate).true_value();
186 BUILTIN(StringPrototypeLastIndexOf) {
187 HandleScope handle_scope(isolate);
188 return String::LastIndexOf(isolate, args.receiver(),
189 args.atOrUndefined(isolate, 1),
190 args.atOrUndefined(isolate, 2));
197 BUILTIN(StringPrototypeLocaleCompare) {
198 HandleScope handle_scope(isolate);
200 isolate->CountUsage(v8::Isolate::UseCounterFeature::kStringLocaleCompare);
202 #ifdef V8_INTL_SUPPORT 203 TO_THIS_STRING(str1,
"String.prototype.localeCompare");
205 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
206 isolate, str2, Object::ToString(isolate, args.atOrUndefined(isolate, 1)));
207 RETURN_RESULT_OR_FAILURE(
208 isolate, Intl::StringLocaleCompare(isolate, str1, str2,
209 args.atOrUndefined(isolate, 2),
210 args.atOrUndefined(isolate, 3)));
212 DCHECK_EQ(2, args.length());
214 TO_THIS_STRING(str1,
"String.prototype.localeCompare");
216 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str2,
217 Object::ToString(isolate, args.at(1)));
219 if (str1.is_identical_to(str2))
return Smi::kZero;
220 int str1_length = str1->length();
221 int str2_length = str2->length();
224 if (str1_length == 0) {
225 if (str2_length == 0)
return Smi::kZero;
226 return Smi::FromInt(-str2_length);
228 if (str2_length == 0)
return Smi::FromInt(str1_length);
231 int end = str1_length < str2_length ? str1_length : str2_length;
236 int d = str1->Get(0) - str2->Get(0);
237 if (d != 0)
return Smi::FromInt(d);
239 str1 = String::Flatten(isolate, str1);
240 str2 = String::Flatten(isolate, str2);
242 DisallowHeapAllocation no_gc;
243 String::FlatContent flat1 = str1->GetFlatContent();
244 String::FlatContent flat2 = str2->GetFlatContent();
246 for (
int i = 0;
i < end;
i++) {
247 if (flat1.Get(
i) != flat2.Get(
i)) {
248 return Smi::FromInt(flat1.Get(
i) - flat2.Get(
i));
252 return Smi::FromInt(str1_length - str2_length);
253 #endif // !V8_INTL_SUPPORT 256 #ifndef V8_INTL_SUPPORT 262 BUILTIN(StringPrototypeNormalize) {
263 HandleScope handle_scope(isolate);
264 TO_THIS_STRING(
string,
"String.prototype.normalize");
266 Handle<Object> form_input = args.atOrUndefined(isolate, 1);
267 if (form_input->IsUndefined(isolate))
return *
string;
270 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form,
271 Object::ToString(isolate, form_input));
273 if (!(String::Equals(isolate, form,
274 isolate->factory()->NewStringFromStaticChars(
"NFC")) ||
275 String::Equals(isolate, form,
276 isolate->factory()->NewStringFromStaticChars(
"NFD")) ||
277 String::Equals(isolate, form,
278 isolate->factory()->NewStringFromStaticChars(
"NFKC")) ||
279 String::Equals(isolate, form,
280 isolate->factory()->NewStringFromStaticChars(
"NFKD")))) {
281 Handle<String> valid_forms =
282 isolate->factory()->NewStringFromStaticChars(
"NFC, NFD, NFKC, NFKD");
283 THROW_NEW_ERROR_RETURN_FAILURE(
285 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
290 #endif // !V8_INTL_SUPPORT 292 BUILTIN(StringPrototypeStartsWith) {
293 HandleScope handle_scope(isolate);
294 TO_THIS_STRING(str,
"String.prototype.startsWith");
297 Handle<Object> search = args.atOrUndefined(isolate, 1);
298 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
299 if (is_reg_exp.IsNothing()) {
300 DCHECK(isolate->has_pending_exception());
301 return ReadOnlyRoots(isolate).exception();
303 if (is_reg_exp.FromJust()) {
304 THROW_NEW_ERROR_RETURN_FAILURE(
305 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
306 isolate->factory()->NewStringFromStaticChars(
307 "String.prototype.startsWith")));
309 Handle<String> search_string;
310 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
311 Object::ToString(isolate, search));
313 Handle<Object> position = args.atOrUndefined(isolate, 2);
316 if (position->IsUndefined(isolate)) {
319 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
320 Object::ToInteger(isolate, position));
321 start = str->ToValidIndex(*position);
324 if (start + search_string->length() > str->length()) {
325 return ReadOnlyRoots(isolate).false_value();
328 FlatStringReader str_reader(isolate, String::Flatten(isolate, str));
329 FlatStringReader search_reader(isolate,
330 String::Flatten(isolate, search_string));
332 for (
int i = 0;
i < search_string->length();
i++) {
333 if (str_reader.Get(start +
i) != search_reader.Get(
i)) {
334 return ReadOnlyRoots(isolate).false_value();
337 return ReadOnlyRoots(isolate).true_value();
340 #ifndef V8_INTL_SUPPORT 343 inline bool ToUpperOverflows(uc32 character) {
346 static const uc32 yuml_code = 0xFF;
347 static const uc32 micro_code = 0xB5;
348 return (character == yuml_code || character == micro_code);
351 template <
class Converter>
352 V8_WARN_UNUSED_RESULT
static Object* ConvertCaseHelper(
353 Isolate* isolate, String
string, SeqString result,
int result_length,
355 DisallowHeapAllocation no_gc;
365 bool has_changed_character =
false;
369 StringCharacterStream stream(
string);
370 unibrow::uchar chars[Converter::kMaxWidth];
372 uc32 current = stream.GetNext();
373 bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString();
374 for (
int i = 0;
i < result_length;) {
375 bool has_next = stream.HasMore();
376 uc32 next = has_next ? stream.GetNext() : 0;
377 int char_length = mapping->get(current, next, chars);
378 if (char_length == 0) {
380 result->Set(
i, current);
382 }
else if (char_length == 1 &&
383 (ignore_overflow || !ToUpperOverflows(current))) {
385 DCHECK(static_cast<uc32>(chars[0]) != current);
386 result->Set(
i, chars[0]);
387 has_changed_character =
true;
389 }
else if (result_length == string->length()) {
390 bool overflows = ToUpperOverflows(current);
404 next_length = mapping->get(next, 0, chars);
405 if (next_length == 0) next_length = 1;
407 int current_length =
i + char_length + next_length;
408 while (stream.HasMore()) {
409 current = stream.GetNext();
410 overflows |= ToUpperOverflows(current);
415 int char_length = mapping->get(current, 0, chars);
416 if (char_length == 0) char_length = 1;
417 current_length += char_length;
418 if (current_length > String::kMaxLength) {
419 AllowHeapAllocation allocate_error_and_return;
420 THROW_NEW_ERROR_RETURN_FAILURE(isolate,
421 NewInvalidStringLengthError());
426 return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
427 : Smi::FromInt(current_length);
429 for (
int j = 0; j < char_length; j++) {
430 result->Set(
i, chars[j]);
433 has_changed_character =
true;
437 if (has_changed_character) {
448 template <
class Converter>
449 V8_WARN_UNUSED_RESULT
static Object* ConvertCase(
450 Handle<String> s, Isolate* isolate,
452 s = String::Flatten(isolate, s);
453 int length = s->length();
455 if (length == 0)
return *s;
463 if (s->IsOneByteRepresentationUnderneath()) {
465 Handle<SeqOneByteString> result =
466 isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
467 DisallowHeapAllocation no_gc;
468 String::FlatContent flat_content = s->GetFlatContent();
469 DCHECK(flat_content.IsFlat());
470 bool has_changed_character =
false;
471 int index_to_first_unprocessed = FastAsciiConvert<Converter::kIsToLower>(
472 reinterpret_cast<char*
>(result->GetChars()),
473 reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()),
474 length, &has_changed_character);
476 if (index_to_first_unprocessed == length)
477 return has_changed_character ? *result : *s;
480 Handle<SeqString> result;
481 if (s->IsOneByteRepresentation()) {
482 result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
484 result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
487 Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping);
488 if (answer->IsException(isolate) || answer->IsString())
return answer;
490 DCHECK(answer->IsSmi());
491 length = Smi::ToInt(answer);
492 if (s->IsOneByteRepresentation() && length > 0) {
493 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
494 isolate, result, isolate->factory()->NewRawOneByteString(length));
496 if (length < 0) length = -length;
497 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
498 isolate, result, isolate->factory()->NewRawTwoByteString(length));
500 return ConvertCaseHelper(isolate, *s, *result, length, mapping);
505 BUILTIN(StringPrototypeToLocaleLowerCase) {
506 HandleScope scope(isolate);
507 TO_THIS_STRING(
string,
"String.prototype.toLocaleLowerCase");
508 return ConvertCase(
string, isolate,
509 isolate->runtime_state()->to_lower_mapping());
512 BUILTIN(StringPrototypeToLocaleUpperCase) {
513 HandleScope scope(isolate);
514 TO_THIS_STRING(
string,
"String.prototype.toLocaleUpperCase");
515 return ConvertCase(
string, isolate,
516 isolate->runtime_state()->to_upper_mapping());
519 BUILTIN(StringPrototypeToLowerCase) {
520 HandleScope scope(isolate);
521 TO_THIS_STRING(
string,
"String.prototype.toLowerCase");
522 return ConvertCase(
string, isolate,
523 isolate->runtime_state()->to_lower_mapping());
526 BUILTIN(StringPrototypeToUpperCase) {
527 HandleScope scope(isolate);
528 TO_THIS_STRING(
string,
"String.prototype.toUpperCase");
529 return ConvertCase(
string, isolate,
530 isolate->runtime_state()->to_upper_mapping());
532 #endif // !V8_INTL_SUPPORT 536 HandleScope scope(isolate);
537 Handle<Object> templ = args.atOrUndefined(isolate, 1);
538 const uint32_t argc = args.length();
539 Handle<String> raw_string =
540 isolate->factory()->NewStringFromAsciiChecked(
"raw");
542 Handle<Object> cooked;
543 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, cooked,
544 Object::ToObject(isolate, templ));
547 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
548 isolate, raw, Object::GetProperty(isolate, cooked, raw_string));
549 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, raw,
550 Object::ToObject(isolate, raw));
551 Handle<Object> raw_len;
552 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
554 Object::GetProperty(isolate, raw, isolate->factory()->length_string()));
556 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, raw_len,
557 Object::ToLength(isolate, raw_len));
559 IncrementalStringBuilder result_builder(isolate);
562 Handle<Object> first_element;
563 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, first_element,
564 Object::GetElement(isolate, raw, 0));
566 Handle<String> first_string;
567 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
568 isolate, first_string, Object::ToString(isolate, first_element));
569 result_builder.AppendString(first_string);
571 for (
uint32_t i = 1, arg_i = 2;
i < length;
i++, arg_i++) {
573 Handle<String> argument_string;
574 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
575 isolate, argument_string,
576 Object::ToString(isolate, args.at(arg_i)));
577 result_builder.AppendString(argument_string);
580 Handle<Object> element;
581 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element,
582 Object::GetElement(isolate, raw,
i));
584 Handle<String> element_string;
585 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_string,
586 Object::ToString(isolate, element));
587 result_builder.AppendString(element_string);
591 RETURN_RESULT_OR_FAILURE(isolate, result_builder.Finish());