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