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