1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/arguments-inl.h"
6 #include "src/conversions.h"
7 #include "src/counters.h"
8 #include "src/objects-inl.h"
9 #include "src/objects/js-array-inl.h"
10 #include "src/regexp/jsregexp-inl.h"
11 #include "src/regexp/regexp-utils.h"
12 #include "src/runtime/runtime-utils.h"
13 #include "src/string-builder-inl.h"
14 #include "src/string-search.h"
15
16 namespace v8 {
17 namespace internal {
18
RUNTIME_FUNCTION(Runtime_GetSubstitution)19 RUNTIME_FUNCTION(Runtime_GetSubstitution) {
20 HandleScope scope(isolate);
21 DCHECK_EQ(5, args.length());
22 CONVERT_ARG_HANDLE_CHECKED(String, matched, 0);
23 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
24 CONVERT_SMI_ARG_CHECKED(position, 2);
25 CONVERT_ARG_HANDLE_CHECKED(String, replacement, 3);
26 CONVERT_SMI_ARG_CHECKED(start_index, 4);
27
28 // A simple match without captures.
29 class SimpleMatch : public String::Match {
30 public:
31 SimpleMatch(Handle<String> match, Handle<String> prefix,
32 Handle<String> suffix)
33 : match_(match), prefix_(prefix), suffix_(suffix) {}
34
35 Handle<String> GetMatch() override { return match_; }
36 Handle<String> GetPrefix() override { return prefix_; }
37 Handle<String> GetSuffix() override { return suffix_; }
38
39 int CaptureCount() override { return 0; }
40 bool HasNamedCaptures() override { return false; }
41 MaybeHandle<String> GetCapture(int i, bool* capture_exists) override {
42 *capture_exists = false;
43 return match_; // Return arbitrary string handle.
44 }
45 MaybeHandle<String> GetNamedCapture(Handle<String> name,
46 CaptureState* state) override {
47 UNREACHABLE();
48 }
49
50 private:
51 Handle<String> match_, prefix_, suffix_;
52 };
53
54 Handle<String> prefix =
55 isolate->factory()->NewSubString(subject, 0, position);
56 Handle<String> suffix = isolate->factory()->NewSubString(
57 subject, position + matched->length(), subject->length());
58 SimpleMatch match(matched, prefix, suffix);
59
60 RETURN_RESULT_OR_FAILURE(
61 isolate,
62 String::GetSubstitution(isolate, &match, replacement, start_index));
63 }
64
65 // This may return an empty MaybeHandle if an exception is thrown or
66 // we abort due to reaching the recursion limit.
StringReplaceOneCharWithString(Isolate * isolate,Handle<String> subject,Handle<String> search,Handle<String> replace,bool * found,int recursion_limit)67 MaybeHandle<String> StringReplaceOneCharWithString(
68 Isolate* isolate, Handle<String> subject, Handle<String> search,
69 Handle<String> replace, bool* found, int recursion_limit) {
70 StackLimitCheck stackLimitCheck(isolate);
71 if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) {
72 return MaybeHandle<String>();
73 }
74 recursion_limit--;
75 if (subject->IsConsString()) {
76 ConsString* cons = ConsString::cast(*subject);
77 Handle<String> first = handle(cons->first(), isolate);
78 Handle<String> second = handle(cons->second(), isolate);
79 Handle<String> new_first;
80 if (!StringReplaceOneCharWithString(isolate, first, search, replace, found,
81 recursion_limit).ToHandle(&new_first)) {
82 return MaybeHandle<String>();
83 }
84 if (*found) return isolate->factory()->NewConsString(new_first, second);
85
86 Handle<String> new_second;
87 if (!StringReplaceOneCharWithString(isolate, second, search, replace, found,
88 recursion_limit)
89 .ToHandle(&new_second)) {
90 return MaybeHandle<String>();
91 }
92 if (*found) return isolate->factory()->NewConsString(first, new_second);
93
94 return subject;
95 } else {
96 int index = String::IndexOf(isolate, subject, search, 0);
97 if (index == -1) return subject;
98 *found = true;
99 Handle<String> first = isolate->factory()->NewSubString(subject, 0, index);
100 Handle<String> cons1;
101 ASSIGN_RETURN_ON_EXCEPTION(
102 isolate, cons1, isolate->factory()->NewConsString(first, replace),
103 String);
104 Handle<String> second =
105 isolate->factory()->NewSubString(subject, index + 1, subject->length());
106 return isolate->factory()->NewConsString(cons1, second);
107 }
108 }
109
RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString)110 RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
111 HandleScope scope(isolate);
112 DCHECK_EQ(3, args.length());
113 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
114 CONVERT_ARG_HANDLE_CHECKED(String, search, 1);
115 CONVERT_ARG_HANDLE_CHECKED(String, replace, 2);
116
117 // If the cons string tree is too deep, we simply abort the recursion and
118 // retry with a flattened subject string.
119 const int kRecursionLimit = 0x1000;
120 bool found = false;
121 Handle<String> result;
122 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
123 kRecursionLimit).ToHandle(&result)) {
124 return *result;
125 }
126 if (isolate->has_pending_exception())
127 return ReadOnlyRoots(isolate).exception();
128
129 subject = String::Flatten(isolate, subject);
130 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
131 kRecursionLimit).ToHandle(&result)) {
132 return *result;
133 }
134 if (isolate->has_pending_exception())
135 return ReadOnlyRoots(isolate).exception();
136 // In case of empty handle and no pending exception we have stack overflow.
137 return isolate->StackOverflow();
138 }
139
RUNTIME_FUNCTION(Runtime_StringTrim)140 RUNTIME_FUNCTION(Runtime_StringTrim) {
141 HandleScope scope(isolate);
142 DCHECK_EQ(2, args.length());
143 Handle<String> string = args.at<String>(0);
144 CONVERT_SMI_ARG_CHECKED(mode, 1);
145 String::TrimMode trim_mode = static_cast<String::TrimMode>(mode);
146 return *String::Trim(isolate, string, trim_mode);
147 }
148
149 // ES6 #sec-string.prototype.includes
150 // String.prototype.includes(searchString [, position])
RUNTIME_FUNCTION(Runtime_StringIncludes)151 RUNTIME_FUNCTION(Runtime_StringIncludes) {
152 HandleScope scope(isolate);
153 DCHECK_EQ(3, args.length());
154
155 Handle<Object> receiver = args.at(0);
156 if (receiver->IsNullOrUndefined(isolate)) {
157 THROW_NEW_ERROR_RETURN_FAILURE(
158 isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
159 isolate->factory()->NewStringFromAsciiChecked(
160 "String.prototype.includes")));
161 }
162 Handle<String> receiver_string;
163 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver_string,
164 Object::ToString(isolate, receiver));
165
166 // Check if the search string is a regExp and fail if it is.
167 Handle<Object> search = args.at(1);
168 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
169 if (is_reg_exp.IsNothing()) {
170 DCHECK(isolate->has_pending_exception());
171 return ReadOnlyRoots(isolate).exception();
172 }
173 if (is_reg_exp.FromJust()) {
174 THROW_NEW_ERROR_RETURN_FAILURE(
175 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
176 isolate->factory()->NewStringFromStaticChars(
177 "String.prototype.includes")));
178 }
179 Handle<String> search_string;
180 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
181 Object::ToString(isolate, args.at(1)));
182 Handle<Object> position;
183 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
184 Object::ToInteger(isolate, args.at(2)));
185
186 uint32_t index = receiver_string->ToValidIndex(*position);
187 int index_in_str =
188 String::IndexOf(isolate, receiver_string, search_string, index);
189 return *isolate->factory()->ToBoolean(index_in_str != -1);
190 }
191
192 // ES6 #sec-string.prototype.indexof
193 // String.prototype.indexOf(searchString [, position])
RUNTIME_FUNCTION(Runtime_StringIndexOf)194 RUNTIME_FUNCTION(Runtime_StringIndexOf) {
195 HandleScope scope(isolate);
196 DCHECK_EQ(3, args.length());
197 return String::IndexOf(isolate, args.at(0), args.at(1), args.at(2));
198 }
199
200 // ES6 #sec-string.prototype.indexof
201 // String.prototype.indexOf(searchString, position)
202 // Fast version that assumes that does not perform conversions of the incoming
203 // arguments.
RUNTIME_FUNCTION(Runtime_StringIndexOfUnchecked)204 RUNTIME_FUNCTION(Runtime_StringIndexOfUnchecked) {
205 HandleScope scope(isolate);
206 DCHECK_EQ(3, args.length());
207 Handle<String> receiver_string = args.at<String>(0);
208 Handle<String> search_string = args.at<String>(1);
209 int index = std::min(std::max(args.smi_at(2), 0), receiver_string->length());
210
211 return Smi::FromInt(String::IndexOf(isolate, receiver_string, search_string,
212 static_cast<uint32_t>(index)));
213 }
214
RUNTIME_FUNCTION(Runtime_StringLastIndexOf)215 RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
216 HandleScope handle_scope(isolate);
217 return String::LastIndexOf(isolate, args.at(0), args.at(1),
218 isolate->factory()->undefined_value());
219 }
220
RUNTIME_FUNCTION(Runtime_StringSubstring)221 RUNTIME_FUNCTION(Runtime_StringSubstring) {
222 HandleScope scope(isolate);
223 DCHECK_EQ(3, args.length());
224 CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
225 CONVERT_INT32_ARG_CHECKED(start, 1);
226 CONVERT_INT32_ARG_CHECKED(end, 2);
227 DCHECK_LE(0, start);
228 DCHECK_LE(start, end);
229 DCHECK_LE(end, string->length());
230 isolate->counters()->sub_string_runtime()->Increment();
231 return *isolate->factory()->NewSubString(string, start, end);
232 }
233
RUNTIME_FUNCTION(Runtime_StringAdd)234 RUNTIME_FUNCTION(Runtime_StringAdd) {
235 HandleScope scope(isolate);
236 DCHECK_EQ(2, args.length());
237 CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
238 CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
239 isolate->counters()->string_add_runtime()->Increment();
240 RETURN_RESULT_OR_FAILURE(isolate,
241 isolate->factory()->NewConsString(str1, str2));
242 }
243
244
RUNTIME_FUNCTION(Runtime_InternalizeString)245 RUNTIME_FUNCTION(Runtime_InternalizeString) {
246 HandleScope handles(isolate);
247 DCHECK_EQ(1, args.length());
248 CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
249 return *isolate->factory()->InternalizeString(string);
250 }
251
RUNTIME_FUNCTION(Runtime_StringCharCodeAt)252 RUNTIME_FUNCTION(Runtime_StringCharCodeAt) {
253 HandleScope handle_scope(isolate);
254 DCHECK_EQ(2, args.length());
255
256 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
257 CONVERT_NUMBER_CHECKED(uint32_t, i, Uint32, args[1]);
258
259 // Flatten the string. If someone wants to get a char at an index
260 // in a cons string, it is likely that more indices will be
261 // accessed.
262 subject = String::Flatten(isolate, subject);
263
264 if (i >= static_cast<uint32_t>(subject->length())) {
265 return ReadOnlyRoots(isolate).nan_value();
266 }
267
268 return Smi::FromInt(subject->Get(i));
269 }
270
RUNTIME_FUNCTION(Runtime_StringBuilderConcat)271 RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
272 HandleScope scope(isolate);
273 DCHECK_EQ(3, args.length());
274 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
275 int32_t array_length;
276 if (!args[1]->ToInt32(&array_length)) {
277 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
278 }
279 CONVERT_ARG_HANDLE_CHECKED(String, special, 2);
280
281 size_t actual_array_length = 0;
282 CHECK(TryNumberToSize(array->length(), &actual_array_length));
283 CHECK_GE(array_length, 0);
284 CHECK(static_cast<size_t>(array_length) <= actual_array_length);
285
286 // This assumption is used by the slice encoding in one or two smis.
287 DCHECK_GE(Smi::kMaxValue, String::kMaxLength);
288
289 CHECK(array->HasFastElements());
290 JSObject::EnsureCanContainHeapObjectElements(array);
291
292 int special_length = special->length();
293 if (!array->HasObjectElements()) {
294 return isolate->Throw(ReadOnlyRoots(isolate).illegal_argument_string());
295 }
296
297 int length;
298 bool one_byte = special->HasOnlyOneByteChars();
299
300 {
301 DisallowHeapAllocation no_gc;
302 FixedArray* fixed_array = FixedArray::cast(array->elements());
303 if (fixed_array->length() < array_length) {
304 array_length = fixed_array->length();
305 }
306
307 if (array_length == 0) {
308 return ReadOnlyRoots(isolate).empty_string();
309 } else if (array_length == 1) {
310 Object* first = fixed_array->get(0);
311 if (first->IsString()) return first;
312 }
313 length = StringBuilderConcatLength(special_length, fixed_array,
314 array_length, &one_byte);
315 }
316
317 if (length == -1) {
318 return isolate->Throw(ReadOnlyRoots(isolate).illegal_argument_string());
319 }
320 if (length == 0) {
321 return ReadOnlyRoots(isolate).empty_string();
322 }
323
324 if (one_byte) {
325 Handle<SeqOneByteString> answer;
326 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
327 isolate, answer, isolate->factory()->NewRawOneByteString(length));
328 StringBuilderConcatHelper(*special, answer->GetChars(),
329 FixedArray::cast(array->elements()),
330 array_length);
331 return *answer;
332 } else {
333 Handle<SeqTwoByteString> answer;
334 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
335 isolate, answer, isolate->factory()->NewRawTwoByteString(length));
336 StringBuilderConcatHelper(*special, answer->GetChars(),
337 FixedArray::cast(array->elements()),
338 array_length);
339 return *answer;
340 }
341 }
342
RUNTIME_FUNCTION(Runtime_StringBuilderJoin)343 RUNTIME_FUNCTION(Runtime_StringBuilderJoin) {
344 HandleScope scope(isolate);
345 DCHECK_EQ(3, args.length());
346 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
347 int32_t array_length;
348 if (!args[1]->ToInt32(&array_length)) {
349 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
350 }
351 CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
352 CHECK(array->HasObjectElements());
353 CHECK_GE(array_length, 0);
354
355 Handle<FixedArray> fixed_array(FixedArray::cast(array->elements()), isolate);
356 if (fixed_array->length() < array_length) {
357 array_length = fixed_array->length();
358 }
359
360 if (array_length == 0) {
361 return ReadOnlyRoots(isolate).empty_string();
362 } else if (array_length == 1) {
363 Object* first = fixed_array->get(0);
364 CHECK(first->IsString());
365 return first;
366 }
367
368 int separator_length = separator->length();
369 CHECK_GT(separator_length, 0);
370 int max_nof_separators =
371 (String::kMaxLength + separator_length - 1) / separator_length;
372 if (max_nof_separators < (array_length - 1)) {
373 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
374 }
375 int length = (array_length - 1) * separator_length;
376 for (int i = 0; i < array_length; i++) {
377 Object* element_obj = fixed_array->get(i);
378 CHECK(element_obj->IsString());
379 String* element = String::cast(element_obj);
380 int increment = element->length();
381 if (increment > String::kMaxLength - length) {
382 STATIC_ASSERT(String::kMaxLength < kMaxInt);
383 length = kMaxInt; // Provoke exception;
384 break;
385 }
386 length += increment;
387 }
388
389 Handle<SeqTwoByteString> answer;
390 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
391 isolate, answer, isolate->factory()->NewRawTwoByteString(length));
392
393 DisallowHeapAllocation no_gc;
394
395 uc16* sink = answer->GetChars();
396 #ifdef DEBUG
397 uc16* end = sink + length;
398 #endif
399
400 CHECK(fixed_array->get(0)->IsString());
401 String* first = String::cast(fixed_array->get(0));
402 String* separator_raw = *separator;
403
404 int first_length = first->length();
405 String::WriteToFlat(first, sink, 0, first_length);
406 sink += first_length;
407
408 for (int i = 1; i < array_length; i++) {
409 DCHECK(sink + separator_length <= end);
410 String::WriteToFlat(separator_raw, sink, 0, separator_length);
411 sink += separator_length;
412
413 CHECK(fixed_array->get(i)->IsString());
414 String* element = String::cast(fixed_array->get(i));
415 int element_length = element->length();
416 DCHECK(sink + element_length <= end);
417 String::WriteToFlat(element, sink, 0, element_length);
418 sink += element_length;
419 }
420 DCHECK(sink == end);
421
422 // Use %_FastOneByteArrayJoin instead.
423 DCHECK(!answer->IsOneByteRepresentation());
424 return *answer;
425 }
426
427 template <typename sinkchar>
WriteRepeatToFlat(String * src,Vector<sinkchar> buffer,int cursor,int repeat,int length)428 static void WriteRepeatToFlat(String* src, Vector<sinkchar> buffer, int cursor,
429 int repeat, int length) {
430 if (repeat == 0) return;
431
432 sinkchar* start = &buffer[cursor];
433 String::WriteToFlat<sinkchar>(src, start, 0, length);
434
435 int done = 1;
436 sinkchar* next = start + length;
437
438 while (done < repeat) {
439 int block = Min(done, repeat - done);
440 int block_chars = block * length;
441 CopyChars(next, start, block_chars);
442 next += block_chars;
443 done += block;
444 }
445 }
446
447 template <typename Char>
JoinSparseArrayWithSeparator(FixedArray * elements,int elements_length,uint32_t array_length,String * separator,Vector<Char> buffer)448 static void JoinSparseArrayWithSeparator(FixedArray* elements,
449 int elements_length,
450 uint32_t array_length,
451 String* separator,
452 Vector<Char> buffer) {
453 DisallowHeapAllocation no_gc;
454 int previous_separator_position = 0;
455 int separator_length = separator->length();
456 DCHECK_LT(0, separator_length);
457 int cursor = 0;
458 for (int i = 0; i < elements_length; i += 2) {
459 int position = NumberToInt32(elements->get(i));
460 String* string = String::cast(elements->get(i + 1));
461 int string_length = string->length();
462 if (string->length() > 0) {
463 int repeat = position - previous_separator_position;
464 WriteRepeatToFlat<Char>(separator, buffer, cursor, repeat,
465 separator_length);
466 cursor += repeat * separator_length;
467 previous_separator_position = position;
468 String::WriteToFlat<Char>(string, &buffer[cursor], 0, string_length);
469 cursor += string->length();
470 }
471 }
472
473 int last_array_index = static_cast<int>(array_length - 1);
474 // Array length must be representable as a signed 32-bit number,
475 // otherwise the total string length would have been too large.
476 DCHECK_LE(array_length, 0x7FFFFFFF); // Is int32_t.
477 int repeat = last_array_index - previous_separator_position;
478 WriteRepeatToFlat<Char>(separator, buffer, cursor, repeat, separator_length);
479 cursor += repeat * separator_length;
480 DCHECK(cursor <= buffer.length());
481 }
482
RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator)483 RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
484 HandleScope scope(isolate);
485 DCHECK_EQ(3, args.length());
486 CONVERT_ARG_HANDLE_CHECKED(JSArray, elements_array, 0);
487 CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]);
488 CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
489 // elements_array is fast-mode JSarray of alternating positions
490 // (increasing order) and strings.
491 CHECK(elements_array->HasSmiOrObjectElements());
492 // array_length is length of original array (used to add separators);
493 // separator is string to put between elements. Assumed to be non-empty.
494 CHECK_GT(array_length, 0);
495
496 // Find total length of join result.
497 int string_length = 0;
498 bool is_one_byte = separator->IsOneByteRepresentation();
499 bool overflow = false;
500 CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length());
501 CHECK(elements_length <= elements_array->elements()->length());
502 CHECK_EQ(elements_length & 1, 0); // Even length.
503 FixedArray* elements = FixedArray::cast(elements_array->elements());
504 {
505 DisallowHeapAllocation no_gc;
506 for (int i = 0; i < elements_length; i += 2) {
507 String* string = String::cast(elements->get(i + 1));
508 int length = string->length();
509 if (is_one_byte && !string->IsOneByteRepresentation()) {
510 is_one_byte = false;
511 }
512 if (length > String::kMaxLength ||
513 String::kMaxLength - length < string_length) {
514 overflow = true;
515 break;
516 }
517 string_length += length;
518 }
519 }
520
521 int separator_length = separator->length();
522 if (!overflow && separator_length > 0) {
523 if (array_length <= 0x7FFFFFFFu) {
524 int separator_count = static_cast<int>(array_length) - 1;
525 int remaining_length = String::kMaxLength - string_length;
526 if ((remaining_length / separator_length) >= separator_count) {
527 string_length += separator_length * (array_length - 1);
528 } else {
529 // Not room for the separators within the maximal string length.
530 overflow = true;
531 }
532 } else {
533 // Nonempty separator and at least 2^31-1 separators necessary
534 // means that the string is too large to create.
535 STATIC_ASSERT(String::kMaxLength < 0x7FFFFFFF);
536 overflow = true;
537 }
538 }
539 if (overflow) {
540 // Throw an exception if the resulting string is too large. See
541 // https://code.google.com/p/chromium/issues/detail?id=336820
542 // for details.
543 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
544 }
545
546 if (is_one_byte) {
547 Handle<SeqOneByteString> result = isolate->factory()
548 ->NewRawOneByteString(string_length)
549 .ToHandleChecked();
550 JoinSparseArrayWithSeparator<uint8_t>(
551 FixedArray::cast(elements_array->elements()), elements_length,
552 array_length, *separator,
553 Vector<uint8_t>(result->GetChars(), string_length));
554 return *result;
555 } else {
556 Handle<SeqTwoByteString> result = isolate->factory()
557 ->NewRawTwoByteString(string_length)
558 .ToHandleChecked();
559 JoinSparseArrayWithSeparator<uc16>(
560 FixedArray::cast(elements_array->elements()), elements_length,
561 array_length, *separator,
562 Vector<uc16>(result->GetChars(), string_length));
563 return *result;
564 }
565 }
566
567 // Copies Latin1 characters to the given fixed array looking up
568 // one-char strings in the cache. Gives up on the first char that is
569 // not in the cache and fills the remainder with smi zeros. Returns
570 // the length of the successfully copied prefix.
CopyCachedOneByteCharsToArray(Heap * heap,const uint8_t * chars,FixedArray * elements,int length)571 static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars,
572 FixedArray* elements, int length) {
573 DisallowHeapAllocation no_gc;
574 FixedArray* one_byte_cache = heap->single_character_string_cache();
575 Object* undefined = ReadOnlyRoots(heap).undefined_value();
576 int i;
577 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
578 for (i = 0; i < length; ++i) {
579 Object* value = one_byte_cache->get(chars[i]);
580 if (value == undefined) break;
581 elements->set(i, value, mode);
582 }
583 if (i < length) {
584 static_assert(Smi::kZero == 0, "Can use memset since Smi::kZero is 0");
585 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
586 }
587 #ifdef DEBUG
588 for (int j = 0; j < length; ++j) {
589 Object* element = elements->get(j);
590 DCHECK(element == Smi::kZero ||
591 (element->IsString() && String::cast(element)->LooksValid()));
592 }
593 #endif
594 return i;
595 }
596
597 // Converts a String to JSArray.
598 // For example, "foo" => ["f", "o", "o"].
RUNTIME_FUNCTION(Runtime_StringToArray)599 RUNTIME_FUNCTION(Runtime_StringToArray) {
600 HandleScope scope(isolate);
601 DCHECK_EQ(2, args.length());
602 CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
603 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
604
605 s = String::Flatten(isolate, s);
606 const int length = static_cast<int>(Min<uint32_t>(s->length(), limit));
607
608 Handle<FixedArray> elements;
609 int position = 0;
610 if (s->IsFlat() && s->IsOneByteRepresentation()) {
611 // Try using cached chars where possible.
612 elements = isolate->factory()->NewUninitializedFixedArray(length);
613
614 DisallowHeapAllocation no_gc;
615 String::FlatContent content = s->GetFlatContent();
616 if (content.IsOneByte()) {
617 Vector<const uint8_t> chars = content.ToOneByteVector();
618 // Note, this will initialize all elements (not only the prefix)
619 // to prevent GC from seeing partially initialized array.
620 position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.start(),
621 *elements, length);
622 } else {
623 MemsetPointer(elements->data_start(),
624 ReadOnlyRoots(isolate).undefined_value(), length);
625 }
626 } else {
627 elements = isolate->factory()->NewFixedArray(length);
628 }
629 for (int i = position; i < length; ++i) {
630 Handle<Object> str =
631 isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));
632 elements->set(i, *str);
633 }
634
635 #ifdef DEBUG
636 for (int i = 0; i < length; ++i) {
637 DCHECK_EQ(String::cast(elements->get(i))->length(), 1);
638 }
639 #endif
640
641 return *isolate->factory()->NewJSArrayWithElements(elements);
642 }
643
RUNTIME_FUNCTION(Runtime_StringLessThan)644 RUNTIME_FUNCTION(Runtime_StringLessThan) {
645 HandleScope handle_scope(isolate);
646 DCHECK_EQ(2, args.length());
647 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
648 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
649 ComparisonResult result = String::Compare(isolate, x, y);
650 DCHECK_NE(result, ComparisonResult::kUndefined);
651 return isolate->heap()->ToBoolean(
652 ComparisonResultToBool(Operation::kLessThan, result));
653 }
654
RUNTIME_FUNCTION(Runtime_StringLessThanOrEqual)655 RUNTIME_FUNCTION(Runtime_StringLessThanOrEqual) {
656 HandleScope handle_scope(isolate);
657 DCHECK_EQ(2, args.length());
658 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
659 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
660 ComparisonResult result = String::Compare(isolate, x, y);
661 DCHECK_NE(result, ComparisonResult::kUndefined);
662 return isolate->heap()->ToBoolean(
663 ComparisonResultToBool(Operation::kLessThanOrEqual, result));
664 }
665
RUNTIME_FUNCTION(Runtime_StringGreaterThan)666 RUNTIME_FUNCTION(Runtime_StringGreaterThan) {
667 HandleScope handle_scope(isolate);
668 DCHECK_EQ(2, args.length());
669 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
670 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
671 ComparisonResult result = String::Compare(isolate, x, y);
672 DCHECK_NE(result, ComparisonResult::kUndefined);
673 return isolate->heap()->ToBoolean(
674 ComparisonResultToBool(Operation::kGreaterThan, result));
675 }
676
RUNTIME_FUNCTION(Runtime_StringGreaterThanOrEqual)677 RUNTIME_FUNCTION(Runtime_StringGreaterThanOrEqual) {
678 HandleScope handle_scope(isolate);
679 DCHECK_EQ(2, args.length());
680 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
681 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
682 ComparisonResult result = String::Compare(isolate, x, y);
683 DCHECK_NE(result, ComparisonResult::kUndefined);
684 return isolate->heap()->ToBoolean(
685 ComparisonResultToBool(Operation::kGreaterThanOrEqual, result));
686 }
687
RUNTIME_FUNCTION(Runtime_StringEqual)688 RUNTIME_FUNCTION(Runtime_StringEqual) {
689 HandleScope handle_scope(isolate);
690 DCHECK_EQ(2, args.length());
691 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
692 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
693 return isolate->heap()->ToBoolean(String::Equals(isolate, x, y));
694 }
695
RUNTIME_FUNCTION(Runtime_StringNotEqual)696 RUNTIME_FUNCTION(Runtime_StringNotEqual) {
697 HandleScope handle_scope(isolate);
698 DCHECK_EQ(2, args.length());
699 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
700 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
701 return isolate->heap()->ToBoolean(!String::Equals(isolate, x, y));
702 }
703
RUNTIME_FUNCTION(Runtime_FlattenString)704 RUNTIME_FUNCTION(Runtime_FlattenString) {
705 HandleScope scope(isolate);
706 DCHECK_EQ(1, args.length());
707 CONVERT_ARG_HANDLE_CHECKED(String, str, 0);
708 return *String::Flatten(isolate, str);
709 }
710
RUNTIME_FUNCTION(Runtime_StringCharFromCode)711 RUNTIME_FUNCTION(Runtime_StringCharFromCode) {
712 HandleScope handlescope(isolate);
713 DCHECK_EQ(1, args.length());
714 if (args[0]->IsNumber()) {
715 CONVERT_NUMBER_CHECKED(uint32_t, code, Uint32, args[0]);
716 code &= 0xFFFF;
717 return *isolate->factory()->LookupSingleCharacterStringFromCode(code);
718 }
719 return ReadOnlyRoots(isolate).empty_string();
720 }
721
RUNTIME_FUNCTION(Runtime_StringMaxLength)722 RUNTIME_FUNCTION(Runtime_StringMaxLength) {
723 SealHandleScope shs(isolate);
724 return Smi::FromInt(String::kMaxLength);
725 }
726
727 } // namespace internal
728 } // namespace v8
729