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