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