blob: 6786fa99fba0f92e1c41e5e228d9f6852349dd74 [file] [log] [blame]
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001// 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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00005#include "src/runtime/runtime-utils.h"
Emily Bernierd0a1eb72015-03-24 16:35:39 -04006
7#include "src/arguments.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00008#include "src/conversions-inl.h"
9#include "src/isolate-inl.h"
10#include "src/regexp/jsregexp-inl.h"
11#include "src/regexp/jsregexp.h"
Emily Bernierd0a1eb72015-03-24 16:35:39 -040012#include "src/string-builder.h"
13#include "src/string-search.h"
14
15namespace v8 {
16namespace internal {
17
18
19// Perform string match of pattern on subject, starting at start index.
20// Caller must ensure that 0 <= start_index <= sub->length(),
21// and should check that pat->length() + start_index <= sub->length().
22int StringMatch(Isolate* isolate, Handle<String> sub, Handle<String> pat,
23 int start_index) {
24 DCHECK(0 <= start_index);
25 DCHECK(start_index <= sub->length());
26
27 int pattern_length = pat->length();
28 if (pattern_length == 0) return start_index;
29
30 int subject_length = sub->length();
31 if (start_index + pattern_length > subject_length) return -1;
32
33 sub = String::Flatten(sub);
34 pat = String::Flatten(pat);
35
36 DisallowHeapAllocation no_gc; // ensure vectors stay valid
37 // Extract flattened substrings of cons strings before getting encoding.
38 String::FlatContent seq_sub = sub->GetFlatContent();
39 String::FlatContent seq_pat = pat->GetFlatContent();
40
41 // dispatch on type of strings
42 if (seq_pat.IsOneByte()) {
43 Vector<const uint8_t> pat_vector = seq_pat.ToOneByteVector();
44 if (seq_sub.IsOneByte()) {
45 return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector,
46 start_index);
47 }
48 return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector,
49 start_index);
50 }
51 Vector<const uc16> pat_vector = seq_pat.ToUC16Vector();
52 if (seq_sub.IsOneByte()) {
53 return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector,
54 start_index);
55 }
56 return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, start_index);
57}
58
59
60// This may return an empty MaybeHandle if an exception is thrown or
61// we abort due to reaching the recursion limit.
62MaybeHandle<String> StringReplaceOneCharWithString(
63 Isolate* isolate, Handle<String> subject, Handle<String> search,
64 Handle<String> replace, bool* found, int recursion_limit) {
65 StackLimitCheck stackLimitCheck(isolate);
66 if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) {
67 return MaybeHandle<String>();
68 }
69 recursion_limit--;
70 if (subject->IsConsString()) {
71 ConsString* cons = ConsString::cast(*subject);
72 Handle<String> first = Handle<String>(cons->first());
73 Handle<String> second = Handle<String>(cons->second());
74 Handle<String> new_first;
75 if (!StringReplaceOneCharWithString(isolate, first, search, replace, found,
76 recursion_limit).ToHandle(&new_first)) {
77 return MaybeHandle<String>();
78 }
79 if (*found) return isolate->factory()->NewConsString(new_first, second);
80
81 Handle<String> new_second;
82 if (!StringReplaceOneCharWithString(isolate, second, search, replace, found,
83 recursion_limit)
84 .ToHandle(&new_second)) {
85 return MaybeHandle<String>();
86 }
87 if (*found) return isolate->factory()->NewConsString(first, new_second);
88
89 return subject;
90 } else {
91 int index = StringMatch(isolate, subject, search, 0);
92 if (index == -1) return subject;
93 *found = true;
94 Handle<String> first = isolate->factory()->NewSubString(subject, 0, index);
95 Handle<String> cons1;
96 ASSIGN_RETURN_ON_EXCEPTION(
97 isolate, cons1, isolate->factory()->NewConsString(first, replace),
98 String);
99 Handle<String> second =
100 isolate->factory()->NewSubString(subject, index + 1, subject->length());
101 return isolate->factory()->NewConsString(cons1, second);
102 }
103}
104
105
106RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
107 HandleScope scope(isolate);
108 DCHECK(args.length() == 3);
109 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
110 CONVERT_ARG_HANDLE_CHECKED(String, search, 1);
111 CONVERT_ARG_HANDLE_CHECKED(String, replace, 2);
112
113 // If the cons string tree is too deep, we simply abort the recursion and
114 // retry with a flattened subject string.
115 const int kRecursionLimit = 0x1000;
116 bool found = false;
117 Handle<String> result;
118 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
119 kRecursionLimit).ToHandle(&result)) {
120 return *result;
121 }
122 if (isolate->has_pending_exception()) return isolate->heap()->exception();
123
124 subject = String::Flatten(subject);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000125 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found,
126 kRecursionLimit).ToHandle(&result)) {
127 return *result;
128 }
129 if (isolate->has_pending_exception()) return isolate->heap()->exception();
130 // In case of empty handle and no pending exception we have stack overflow.
131 return isolate->StackOverflow();
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400132}
133
134
135RUNTIME_FUNCTION(Runtime_StringIndexOf) {
136 HandleScope scope(isolate);
137 DCHECK(args.length() == 3);
138
139 CONVERT_ARG_HANDLE_CHECKED(String, sub, 0);
140 CONVERT_ARG_HANDLE_CHECKED(String, pat, 1);
141 CONVERT_ARG_HANDLE_CHECKED(Object, index, 2);
142
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000143 uint32_t start_index = 0;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400144 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
145
146 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
147 int position = StringMatch(isolate, sub, pat, start_index);
148 return Smi::FromInt(position);
149}
150
151
152template <typename schar, typename pchar>
153static int StringMatchBackwards(Vector<const schar> subject,
154 Vector<const pchar> pattern, int idx) {
155 int pattern_length = pattern.length();
156 DCHECK(pattern_length >= 1);
157 DCHECK(idx + pattern_length <= subject.length());
158
159 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
160 for (int i = 0; i < pattern_length; i++) {
161 uc16 c = pattern[i];
162 if (c > String::kMaxOneByteCharCode) {
163 return -1;
164 }
165 }
166 }
167
168 pchar pattern_first_char = pattern[0];
169 for (int i = idx; i >= 0; i--) {
170 if (subject[i] != pattern_first_char) continue;
171 int j = 1;
172 while (j < pattern_length) {
173 if (pattern[j] != subject[i + j]) {
174 break;
175 }
176 j++;
177 }
178 if (j == pattern_length) {
179 return i;
180 }
181 }
182 return -1;
183}
184
185
186RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
187 HandleScope scope(isolate);
188 DCHECK(args.length() == 3);
189
190 CONVERT_ARG_HANDLE_CHECKED(String, sub, 0);
191 CONVERT_ARG_HANDLE_CHECKED(String, pat, 1);
192 CONVERT_ARG_HANDLE_CHECKED(Object, index, 2);
193
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000194 uint32_t start_index = 0;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400195 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
196
197 uint32_t pat_length = pat->length();
198 uint32_t sub_length = sub->length();
199
200 if (start_index + pat_length > sub_length) {
201 start_index = sub_length - pat_length;
202 }
203
204 if (pat_length == 0) {
205 return Smi::FromInt(start_index);
206 }
207
208 sub = String::Flatten(sub);
209 pat = String::Flatten(pat);
210
211 int position = -1;
212 DisallowHeapAllocation no_gc; // ensure vectors stay valid
213
214 String::FlatContent sub_content = sub->GetFlatContent();
215 String::FlatContent pat_content = pat->GetFlatContent();
216
217 if (pat_content.IsOneByte()) {
218 Vector<const uint8_t> pat_vector = pat_content.ToOneByteVector();
219 if (sub_content.IsOneByte()) {
220 position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector,
221 start_index);
222 } else {
223 position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector,
224 start_index);
225 }
226 } else {
227 Vector<const uc16> pat_vector = pat_content.ToUC16Vector();
228 if (sub_content.IsOneByte()) {
229 position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector,
230 start_index);
231 } else {
232 position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector,
233 start_index);
234 }
235 }
236
237 return Smi::FromInt(position);
238}
239
240
241RUNTIME_FUNCTION(Runtime_StringLocaleCompare) {
242 HandleScope handle_scope(isolate);
243 DCHECK(args.length() == 2);
244
245 CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
246 CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
247
248 if (str1.is_identical_to(str2)) return Smi::FromInt(0); // Equal.
249 int str1_length = str1->length();
250 int str2_length = str2->length();
251
252 // Decide trivial cases without flattening.
253 if (str1_length == 0) {
254 if (str2_length == 0) return Smi::FromInt(0); // Equal.
255 return Smi::FromInt(-str2_length);
256 } else {
257 if (str2_length == 0) return Smi::FromInt(str1_length);
258 }
259
260 int end = str1_length < str2_length ? str1_length : str2_length;
261
262 // No need to flatten if we are going to find the answer on the first
263 // character. At this point we know there is at least one character
264 // in each string, due to the trivial case handling above.
265 int d = str1->Get(0) - str2->Get(0);
266 if (d != 0) return Smi::FromInt(d);
267
268 str1 = String::Flatten(str1);
269 str2 = String::Flatten(str2);
270
271 DisallowHeapAllocation no_gc;
272 String::FlatContent flat1 = str1->GetFlatContent();
273 String::FlatContent flat2 = str2->GetFlatContent();
274
275 for (int i = 0; i < end; i++) {
276 if (flat1.Get(i) != flat2.Get(i)) {
277 return Smi::FromInt(flat1.Get(i) - flat2.Get(i));
278 }
279 }
280
281 return Smi::FromInt(str1_length - str2_length);
282}
283
284
285RUNTIME_FUNCTION(Runtime_SubString) {
286 HandleScope scope(isolate);
287 DCHECK(args.length() == 3);
288
289 CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
290 int start, end;
291 // We have a fast integer-only case here to avoid a conversion to double in
292 // the common case where from and to are Smis.
293 if (args[1]->IsSmi() && args[2]->IsSmi()) {
294 CONVERT_SMI_ARG_CHECKED(from_number, 1);
295 CONVERT_SMI_ARG_CHECKED(to_number, 2);
296 start = from_number;
297 end = to_number;
298 } else {
299 CONVERT_DOUBLE_ARG_CHECKED(from_number, 1);
300 CONVERT_DOUBLE_ARG_CHECKED(to_number, 2);
301 start = FastD2IChecked(from_number);
302 end = FastD2IChecked(to_number);
303 }
304 RUNTIME_ASSERT(end >= start);
305 RUNTIME_ASSERT(start >= 0);
306 RUNTIME_ASSERT(end <= string->length());
307 isolate->counters()->sub_string_runtime()->Increment();
308
309 return *isolate->factory()->NewSubString(string, start, end);
310}
311
312
313RUNTIME_FUNCTION(Runtime_StringAdd) {
314 HandleScope scope(isolate);
315 DCHECK(args.length() == 2);
316 CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
317 CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
318 isolate->counters()->string_add_runtime()->Increment();
319 Handle<String> result;
320 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
321 isolate, result, isolate->factory()->NewConsString(str1, str2));
322 return *result;
323}
324
325
326RUNTIME_FUNCTION(Runtime_InternalizeString) {
327 HandleScope handles(isolate);
328 RUNTIME_ASSERT(args.length() == 1);
329 CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
330 return *isolate->factory()->InternalizeString(string);
331}
332
333
334RUNTIME_FUNCTION(Runtime_StringMatch) {
335 HandleScope handles(isolate);
336 DCHECK(args.length() == 3);
337
338 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
339 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
340 CONVERT_ARG_HANDLE_CHECKED(JSArray, regexp_info, 2);
341
342 RUNTIME_ASSERT(regexp_info->HasFastObjectElements());
343
Ben Murdoch097c5b22016-05-18 11:27:45 +0100344 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400345 if (global_cache.HasException()) return isolate->heap()->exception();
346
347 int capture_count = regexp->CaptureCount();
348
349 ZoneScope zone_scope(isolate->runtime_zone());
350 ZoneList<int> offsets(8, zone_scope.zone());
351
352 while (true) {
353 int32_t* match = global_cache.FetchNext();
354 if (match == NULL) break;
355 offsets.Add(match[0], zone_scope.zone()); // start
356 offsets.Add(match[1], zone_scope.zone()); // end
357 }
358
359 if (global_cache.HasException()) return isolate->heap()->exception();
360
361 if (offsets.length() == 0) {
362 // Not a single match.
363 return isolate->heap()->null_value();
364 }
365
366 RegExpImpl::SetLastMatchInfo(regexp_info, subject, capture_count,
367 global_cache.LastSuccessfulMatch());
368
369 int matches = offsets.length() / 2;
370 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(matches);
371 Handle<String> substring =
372 isolate->factory()->NewSubString(subject, offsets.at(0), offsets.at(1));
373 elements->set(0, *substring);
Ben Murdochda12d292016-06-02 14:46:10 +0100374 FOR_WITH_HANDLE_SCOPE(isolate, int, i = 1, i, i < matches, i++, {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400375 int from = offsets.at(i * 2);
376 int to = offsets.at(i * 2 + 1);
377 Handle<String> substring =
378 isolate->factory()->NewProperSubString(subject, from, to);
379 elements->set(i, *substring);
Ben Murdochda12d292016-06-02 14:46:10 +0100380 });
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400381 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(elements);
382 result->set_length(Smi::FromInt(matches));
383 return *result;
384}
385
386
387RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) {
388 HandleScope handle_scope(isolate);
389 DCHECK(args.length() == 2);
390
391 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
392 CONVERT_NUMBER_CHECKED(uint32_t, i, Uint32, args[1]);
393
394 // Flatten the string. If someone wants to get a char at an index
395 // in a cons string, it is likely that more indices will be
396 // accessed.
397 subject = String::Flatten(subject);
398
399 if (i >= static_cast<uint32_t>(subject->length())) {
400 return isolate->heap()->nan_value();
401 }
402
403 return Smi::FromInt(subject->Get(i));
404}
405
406
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400407RUNTIME_FUNCTION(Runtime_StringCompare) {
408 HandleScope handle_scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000409 DCHECK_EQ(2, args.length());
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400410 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
411 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400412 isolate->counters()->string_compare_runtime()->Increment();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000413 switch (String::Compare(x, y)) {
414 case ComparisonResult::kLessThan:
415 return Smi::FromInt(LESS);
416 case ComparisonResult::kEqual:
417 return Smi::FromInt(EQUAL);
418 case ComparisonResult::kGreaterThan:
419 return Smi::FromInt(GREATER);
420 case ComparisonResult::kUndefined:
421 break;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400422 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000423 UNREACHABLE();
424 return Smi::FromInt(0);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400425}
426
427
428RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
429 HandleScope scope(isolate);
430 DCHECK(args.length() == 3);
431 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
432 int32_t array_length;
433 if (!args[1]->ToInt32(&array_length)) {
434 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
435 }
436 CONVERT_ARG_HANDLE_CHECKED(String, special, 2);
437
438 size_t actual_array_length = 0;
439 RUNTIME_ASSERT(
440 TryNumberToSize(isolate, array->length(), &actual_array_length));
441 RUNTIME_ASSERT(array_length >= 0);
442 RUNTIME_ASSERT(static_cast<size_t>(array_length) <= actual_array_length);
443
444 // This assumption is used by the slice encoding in one or two smis.
445 DCHECK(Smi::kMaxValue >= String::kMaxLength);
446
447 RUNTIME_ASSERT(array->HasFastElements());
448 JSObject::EnsureCanContainHeapObjectElements(array);
449
450 int special_length = special->length();
451 if (!array->HasFastObjectElements()) {
452 return isolate->Throw(isolate->heap()->illegal_argument_string());
453 }
454
455 int length;
456 bool one_byte = special->HasOnlyOneByteChars();
457
458 {
459 DisallowHeapAllocation no_gc;
460 FixedArray* fixed_array = FixedArray::cast(array->elements());
461 if (fixed_array->length() < array_length) {
462 array_length = fixed_array->length();
463 }
464
465 if (array_length == 0) {
466 return isolate->heap()->empty_string();
467 } else if (array_length == 1) {
468 Object* first = fixed_array->get(0);
469 if (first->IsString()) return first;
470 }
471 length = StringBuilderConcatLength(special_length, fixed_array,
472 array_length, &one_byte);
473 }
474
475 if (length == -1) {
476 return isolate->Throw(isolate->heap()->illegal_argument_string());
477 }
478
479 if (one_byte) {
480 Handle<SeqOneByteString> answer;
481 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
482 isolate, answer, isolate->factory()->NewRawOneByteString(length));
483 StringBuilderConcatHelper(*special, answer->GetChars(),
484 FixedArray::cast(array->elements()),
485 array_length);
486 return *answer;
487 } else {
488 Handle<SeqTwoByteString> answer;
489 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
490 isolate, answer, isolate->factory()->NewRawTwoByteString(length));
491 StringBuilderConcatHelper(*special, answer->GetChars(),
492 FixedArray::cast(array->elements()),
493 array_length);
494 return *answer;
495 }
496}
497
498
499RUNTIME_FUNCTION(Runtime_StringBuilderJoin) {
500 HandleScope scope(isolate);
501 DCHECK(args.length() == 3);
502 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
503 int32_t array_length;
504 if (!args[1]->ToInt32(&array_length)) {
505 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
506 }
507 CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
508 RUNTIME_ASSERT(array->HasFastObjectElements());
509 RUNTIME_ASSERT(array_length >= 0);
510
511 Handle<FixedArray> fixed_array(FixedArray::cast(array->elements()));
512 if (fixed_array->length() < array_length) {
513 array_length = fixed_array->length();
514 }
515
516 if (array_length == 0) {
517 return isolate->heap()->empty_string();
518 } else if (array_length == 1) {
519 Object* first = fixed_array->get(0);
520 RUNTIME_ASSERT(first->IsString());
521 return first;
522 }
523
524 int separator_length = separator->length();
525 RUNTIME_ASSERT(separator_length > 0);
526 int max_nof_separators =
527 (String::kMaxLength + separator_length - 1) / separator_length;
528 if (max_nof_separators < (array_length - 1)) {
529 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
530 }
531 int length = (array_length - 1) * separator_length;
532 for (int i = 0; i < array_length; i++) {
533 Object* element_obj = fixed_array->get(i);
534 RUNTIME_ASSERT(element_obj->IsString());
535 String* element = String::cast(element_obj);
536 int increment = element->length();
537 if (increment > String::kMaxLength - length) {
538 STATIC_ASSERT(String::kMaxLength < kMaxInt);
539 length = kMaxInt; // Provoke exception;
540 break;
541 }
542 length += increment;
543 }
544
545 Handle<SeqTwoByteString> answer;
546 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
547 isolate, answer, isolate->factory()->NewRawTwoByteString(length));
548
549 DisallowHeapAllocation no_gc;
550
551 uc16* sink = answer->GetChars();
552#ifdef DEBUG
553 uc16* end = sink + length;
554#endif
555
556 RUNTIME_ASSERT(fixed_array->get(0)->IsString());
557 String* first = String::cast(fixed_array->get(0));
558 String* separator_raw = *separator;
Ben Murdochda12d292016-06-02 14:46:10 +0100559
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400560 int first_length = first->length();
561 String::WriteToFlat(first, sink, 0, first_length);
562 sink += first_length;
563
564 for (int i = 1; i < array_length; i++) {
565 DCHECK(sink + separator_length <= end);
566 String::WriteToFlat(separator_raw, sink, 0, separator_length);
567 sink += separator_length;
568
569 RUNTIME_ASSERT(fixed_array->get(i)->IsString());
570 String* element = String::cast(fixed_array->get(i));
571 int element_length = element->length();
572 DCHECK(sink + element_length <= end);
573 String::WriteToFlat(element, sink, 0, element_length);
574 sink += element_length;
575 }
576 DCHECK(sink == end);
577
578 // Use %_FastOneByteArrayJoin instead.
579 DCHECK(!answer->IsOneByteRepresentation());
580 return *answer;
581}
582
Ben Murdochda12d292016-06-02 14:46:10 +0100583template <typename sinkchar>
584static void WriteRepeatToFlat(String* src, Vector<sinkchar> buffer, int cursor,
585 int repeat, int length) {
586 if (repeat == 0) return;
587
588 sinkchar* start = &buffer[cursor];
589 String::WriteToFlat<sinkchar>(src, start, 0, length);
590
591 int done = 1;
592 sinkchar* next = start + length;
593
594 while (done < repeat) {
595 int block = Min(done, repeat - done);
596 int block_chars = block * length;
597 CopyChars(next, start, block_chars);
598 next += block_chars;
599 done += block;
600 }
601}
602
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400603template <typename Char>
604static void JoinSparseArrayWithSeparator(FixedArray* elements,
605 int elements_length,
606 uint32_t array_length,
607 String* separator,
608 Vector<Char> buffer) {
609 DisallowHeapAllocation no_gc;
610 int previous_separator_position = 0;
611 int separator_length = separator->length();
Ben Murdochda12d292016-06-02 14:46:10 +0100612 DCHECK_LT(0, separator_length);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400613 int cursor = 0;
614 for (int i = 0; i < elements_length; i += 2) {
615 int position = NumberToInt32(elements->get(i));
616 String* string = String::cast(elements->get(i + 1));
617 int string_length = string->length();
618 if (string->length() > 0) {
Ben Murdochda12d292016-06-02 14:46:10 +0100619 int repeat = position - previous_separator_position;
620 WriteRepeatToFlat<Char>(separator, buffer, cursor, repeat,
621 separator_length);
622 cursor += repeat * separator_length;
623 previous_separator_position = position;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400624 String::WriteToFlat<Char>(string, &buffer[cursor], 0, string_length);
625 cursor += string->length();
626 }
627 }
Ben Murdochda12d292016-06-02 14:46:10 +0100628
629 int last_array_index = static_cast<int>(array_length - 1);
630 // Array length must be representable as a signed 32-bit number,
631 // otherwise the total string length would have been too large.
632 DCHECK(array_length <= 0x7fffffff); // Is int32_t.
633 int repeat = last_array_index - previous_separator_position;
634 WriteRepeatToFlat<Char>(separator, buffer, cursor, repeat, separator_length);
635 cursor += repeat * separator_length;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400636 DCHECK(cursor <= buffer.length());
637}
638
639
640RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
641 HandleScope scope(isolate);
642 DCHECK(args.length() == 3);
643 CONVERT_ARG_HANDLE_CHECKED(JSArray, elements_array, 0);
644 CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]);
645 CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
646 // elements_array is fast-mode JSarray of alternating positions
647 // (increasing order) and strings.
648 RUNTIME_ASSERT(elements_array->HasFastSmiOrObjectElements());
649 // array_length is length of original array (used to add separators);
650 // separator is string to put between elements. Assumed to be non-empty.
651 RUNTIME_ASSERT(array_length > 0);
652
653 // Find total length of join result.
654 int string_length = 0;
655 bool is_one_byte = separator->IsOneByteRepresentation();
656 bool overflow = false;
657 CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length());
658 RUNTIME_ASSERT(elements_length <= elements_array->elements()->length());
659 RUNTIME_ASSERT((elements_length & 1) == 0); // Even length.
660 FixedArray* elements = FixedArray::cast(elements_array->elements());
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400661 {
662 DisallowHeapAllocation no_gc;
663 for (int i = 0; i < elements_length; i += 2) {
664 String* string = String::cast(elements->get(i + 1));
665 int length = string->length();
666 if (is_one_byte && !string->IsOneByteRepresentation()) {
667 is_one_byte = false;
668 }
669 if (length > String::kMaxLength ||
670 String::kMaxLength - length < string_length) {
671 overflow = true;
672 break;
673 }
674 string_length += length;
675 }
676 }
677
678 int separator_length = separator->length();
679 if (!overflow && separator_length > 0) {
680 if (array_length <= 0x7fffffffu) {
681 int separator_count = static_cast<int>(array_length) - 1;
682 int remaining_length = String::kMaxLength - string_length;
683 if ((remaining_length / separator_length) >= separator_count) {
684 string_length += separator_length * (array_length - 1);
685 } else {
686 // Not room for the separators within the maximal string length.
687 overflow = true;
688 }
689 } else {
690 // Nonempty separator and at least 2^31-1 separators necessary
691 // means that the string is too large to create.
692 STATIC_ASSERT(String::kMaxLength < 0x7fffffff);
693 overflow = true;
694 }
695 }
696 if (overflow) {
697 // Throw an exception if the resulting string is too large. See
698 // https://code.google.com/p/chromium/issues/detail?id=336820
699 // for details.
700 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
701 }
702
703 if (is_one_byte) {
704 Handle<SeqOneByteString> result = isolate->factory()
705 ->NewRawOneByteString(string_length)
706 .ToHandleChecked();
707 JoinSparseArrayWithSeparator<uint8_t>(
708 FixedArray::cast(elements_array->elements()), elements_length,
709 array_length, *separator,
710 Vector<uint8_t>(result->GetChars(), string_length));
711 return *result;
712 } else {
713 Handle<SeqTwoByteString> result = isolate->factory()
714 ->NewRawTwoByteString(string_length)
715 .ToHandleChecked();
716 JoinSparseArrayWithSeparator<uc16>(
717 FixedArray::cast(elements_array->elements()), elements_length,
718 array_length, *separator,
719 Vector<uc16>(result->GetChars(), string_length));
720 return *result;
721 }
722}
723
724
725// Copies Latin1 characters to the given fixed array looking up
726// one-char strings in the cache. Gives up on the first char that is
727// not in the cache and fills the remainder with smi zeros. Returns
728// the length of the successfully copied prefix.
729static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars,
730 FixedArray* elements, int length) {
731 DisallowHeapAllocation no_gc;
732 FixedArray* one_byte_cache = heap->single_character_string_cache();
733 Object* undefined = heap->undefined_value();
734 int i;
735 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
736 for (i = 0; i < length; ++i) {
737 Object* value = one_byte_cache->get(chars[i]);
738 if (value == undefined) break;
739 elements->set(i, value, mode);
740 }
741 if (i < length) {
742 DCHECK(Smi::FromInt(0) == 0);
743 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
744 }
745#ifdef DEBUG
746 for (int j = 0; j < length; ++j) {
747 Object* element = elements->get(j);
748 DCHECK(element == Smi::FromInt(0) ||
749 (element->IsString() && String::cast(element)->LooksValid()));
750 }
751#endif
752 return i;
753}
754
755
756// Converts a String to JSArray.
757// For example, "foo" => ["f", "o", "o"].
758RUNTIME_FUNCTION(Runtime_StringToArray) {
759 HandleScope scope(isolate);
760 DCHECK(args.length() == 2);
761 CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
762 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
763
764 s = String::Flatten(s);
765 const int length = static_cast<int>(Min<uint32_t>(s->length(), limit));
766
767 Handle<FixedArray> elements;
768 int position = 0;
769 if (s->IsFlat() && s->IsOneByteRepresentation()) {
770 // Try using cached chars where possible.
771 elements = isolate->factory()->NewUninitializedFixedArray(length);
772
773 DisallowHeapAllocation no_gc;
774 String::FlatContent content = s->GetFlatContent();
775 if (content.IsOneByte()) {
776 Vector<const uint8_t> chars = content.ToOneByteVector();
777 // Note, this will initialize all elements (not only the prefix)
778 // to prevent GC from seeing partially initialized array.
779 position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.start(),
780 *elements, length);
781 } else {
782 MemsetPointer(elements->data_start(), isolate->heap()->undefined_value(),
783 length);
784 }
785 } else {
786 elements = isolate->factory()->NewFixedArray(length);
787 }
788 for (int i = position; i < length; ++i) {
789 Handle<Object> str =
790 isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));
791 elements->set(i, *str);
792 }
793
794#ifdef DEBUG
795 for (int i = 0; i < length; ++i) {
796 DCHECK(String::cast(elements->get(i))->length() == 1);
797 }
798#endif
799
800 return *isolate->factory()->NewJSArrayWithElements(elements);
801}
802
803
804static inline bool ToUpperOverflows(uc32 character) {
805 // y with umlauts and the micro sign are the only characters that stop
806 // fitting into one-byte when converting to uppercase.
807 static const uc32 yuml_code = 0xff;
808 static const uc32 micro_code = 0xb5;
809 return (character == yuml_code || character == micro_code);
810}
811
812
813template <class Converter>
814MUST_USE_RESULT static Object* ConvertCaseHelper(
815 Isolate* isolate, String* string, SeqString* result, int result_length,
816 unibrow::Mapping<Converter, 128>* mapping) {
817 DisallowHeapAllocation no_gc;
818 // We try this twice, once with the assumption that the result is no longer
819 // than the input and, if that assumption breaks, again with the exact
820 // length. This may not be pretty, but it is nicer than what was here before
821 // and I hereby claim my vaffel-is.
822 //
823 // NOTE: This assumes that the upper/lower case of an ASCII
824 // character is also ASCII. This is currently the case, but it
825 // might break in the future if we implement more context and locale
826 // dependent upper/lower conversions.
827 bool has_changed_character = false;
828
829 // Convert all characters to upper case, assuming that they will fit
830 // in the buffer
831 StringCharacterStream stream(string);
832 unibrow::uchar chars[Converter::kMaxWidth];
833 // We can assume that the string is not empty
834 uc32 current = stream.GetNext();
835 bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString();
836 for (int i = 0; i < result_length;) {
837 bool has_next = stream.HasMore();
838 uc32 next = has_next ? stream.GetNext() : 0;
839 int char_length = mapping->get(current, next, chars);
840 if (char_length == 0) {
841 // The case conversion of this character is the character itself.
842 result->Set(i, current);
843 i++;
844 } else if (char_length == 1 &&
845 (ignore_overflow || !ToUpperOverflows(current))) {
846 // Common case: converting the letter resulted in one character.
847 DCHECK(static_cast<uc32>(chars[0]) != current);
848 result->Set(i, chars[0]);
849 has_changed_character = true;
850 i++;
851 } else if (result_length == string->length()) {
852 bool overflows = ToUpperOverflows(current);
853 // We've assumed that the result would be as long as the
854 // input but here is a character that converts to several
855 // characters. No matter, we calculate the exact length
856 // of the result and try the whole thing again.
857 //
858 // Note that this leaves room for optimization. We could just
859 // memcpy what we already have to the result string. Also,
860 // the result string is the last object allocated we could
861 // "realloc" it and probably, in the vast majority of cases,
862 // extend the existing string to be able to hold the full
863 // result.
864 int next_length = 0;
865 if (has_next) {
866 next_length = mapping->get(next, 0, chars);
867 if (next_length == 0) next_length = 1;
868 }
869 int current_length = i + char_length + next_length;
870 while (stream.HasMore()) {
871 current = stream.GetNext();
872 overflows |= ToUpperOverflows(current);
873 // NOTE: we use 0 as the next character here because, while
874 // the next character may affect what a character converts to,
875 // it does not in any case affect the length of what it convert
876 // to.
877 int char_length = mapping->get(current, 0, chars);
878 if (char_length == 0) char_length = 1;
879 current_length += char_length;
880 if (current_length > String::kMaxLength) {
881 AllowHeapAllocation allocate_error_and_return;
882 THROW_NEW_ERROR_RETURN_FAILURE(isolate,
883 NewInvalidStringLengthError());
884 }
885 }
886 // Try again with the real length. Return signed if we need
887 // to allocate a two-byte string for to uppercase.
888 return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
889 : Smi::FromInt(current_length);
890 } else {
891 for (int j = 0; j < char_length; j++) {
892 result->Set(i, chars[j]);
893 i++;
894 }
895 has_changed_character = true;
896 }
897 current = next;
898 }
899 if (has_changed_character) {
900 return result;
901 } else {
902 // If we didn't actually change anything in doing the conversion
903 // we simple return the result and let the converted string
904 // become garbage; there is no reason to keep two identical strings
905 // alive.
906 return string;
907 }
908}
909
910
911static const uintptr_t kOneInEveryByte = kUintptrAllBitsSet / 0xFF;
912static const uintptr_t kAsciiMask = kOneInEveryByte << 7;
913
914// Given a word and two range boundaries returns a word with high bit
915// set in every byte iff the corresponding input byte was strictly in
916// the range (m, n). All the other bits in the result are cleared.
917// This function is only useful when it can be inlined and the
918// boundaries are statically known.
919// Requires: all bytes in the input word and the boundaries must be
920// ASCII (less than 0x7F).
921static inline uintptr_t AsciiRangeMask(uintptr_t w, char m, char n) {
922 // Use strict inequalities since in edge cases the function could be
923 // further simplified.
924 DCHECK(0 < m && m < n);
925 // Has high bit set in every w byte less than n.
926 uintptr_t tmp1 = kOneInEveryByte * (0x7F + n) - w;
927 // Has high bit set in every w byte greater than m.
928 uintptr_t tmp2 = w + kOneInEveryByte * (0x7F - m);
929 return (tmp1 & tmp2 & (kOneInEveryByte * 0x80));
930}
931
932
933#ifdef DEBUG
934static bool CheckFastAsciiConvert(char* dst, const char* src, int length,
935 bool changed, bool is_to_lower) {
936 bool expected_changed = false;
937 for (int i = 0; i < length; i++) {
938 if (dst[i] == src[i]) continue;
939 expected_changed = true;
940 if (is_to_lower) {
941 DCHECK('A' <= src[i] && src[i] <= 'Z');
942 DCHECK(dst[i] == src[i] + ('a' - 'A'));
943 } else {
944 DCHECK('a' <= src[i] && src[i] <= 'z');
945 DCHECK(dst[i] == src[i] - ('a' - 'A'));
946 }
947 }
948 return (expected_changed == changed);
949}
950#endif
951
952
953template <class Converter>
954static bool FastAsciiConvert(char* dst, const char* src, int length,
955 bool* changed_out) {
956#ifdef DEBUG
957 char* saved_dst = dst;
958 const char* saved_src = src;
959#endif
960 DisallowHeapAllocation no_gc;
961 // We rely on the distance between upper and lower case letters
962 // being a known power of 2.
963 DCHECK('a' - 'A' == (1 << 5));
964 // Boundaries for the range of input characters than require conversion.
965 static const char lo = Converter::kIsToLower ? 'A' - 1 : 'a' - 1;
966 static const char hi = Converter::kIsToLower ? 'Z' + 1 : 'z' + 1;
967 bool changed = false;
968 uintptr_t or_acc = 0;
969 const char* const limit = src + length;
970
971 // dst is newly allocated and always aligned.
972 DCHECK(IsAligned(reinterpret_cast<intptr_t>(dst), sizeof(uintptr_t)));
973 // Only attempt processing one word at a time if src is also aligned.
974 if (IsAligned(reinterpret_cast<intptr_t>(src), sizeof(uintptr_t))) {
975 // Process the prefix of the input that requires no conversion one aligned
976 // (machine) word at a time.
977 while (src <= limit - sizeof(uintptr_t)) {
978 const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src);
979 or_acc |= w;
980 if (AsciiRangeMask(w, lo, hi) != 0) {
981 changed = true;
982 break;
983 }
984 *reinterpret_cast<uintptr_t*>(dst) = w;
985 src += sizeof(uintptr_t);
986 dst += sizeof(uintptr_t);
987 }
988 // Process the remainder of the input performing conversion when
989 // required one word at a time.
990 while (src <= limit - sizeof(uintptr_t)) {
991 const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src);
992 or_acc |= w;
993 uintptr_t m = AsciiRangeMask(w, lo, hi);
994 // The mask has high (7th) bit set in every byte that needs
995 // conversion and we know that the distance between cases is
996 // 1 << 5.
997 *reinterpret_cast<uintptr_t*>(dst) = w ^ (m >> 2);
998 src += sizeof(uintptr_t);
999 dst += sizeof(uintptr_t);
1000 }
1001 }
1002 // Process the last few bytes of the input (or the whole input if
1003 // unaligned access is not supported).
1004 while (src < limit) {
1005 char c = *src;
1006 or_acc |= c;
1007 if (lo < c && c < hi) {
1008 c ^= (1 << 5);
1009 changed = true;
1010 }
1011 *dst = c;
1012 ++src;
1013 ++dst;
1014 }
1015
1016 if ((or_acc & kAsciiMask) != 0) return false;
1017
1018 DCHECK(CheckFastAsciiConvert(saved_dst, saved_src, length, changed,
1019 Converter::kIsToLower));
1020
1021 *changed_out = changed;
1022 return true;
1023}
1024
1025
1026template <class Converter>
1027MUST_USE_RESULT static Object* ConvertCase(
1028 Handle<String> s, Isolate* isolate,
1029 unibrow::Mapping<Converter, 128>* mapping) {
1030 s = String::Flatten(s);
1031 int length = s->length();
1032 // Assume that the string is not empty; we need this assumption later
1033 if (length == 0) return *s;
1034
1035 // Simpler handling of ASCII strings.
1036 //
1037 // NOTE: This assumes that the upper/lower case of an ASCII
1038 // character is also ASCII. This is currently the case, but it
1039 // might break in the future if we implement more context and locale
1040 // dependent upper/lower conversions.
1041 if (s->IsOneByteRepresentationUnderneath()) {
1042 // Same length as input.
1043 Handle<SeqOneByteString> result =
1044 isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
1045 DisallowHeapAllocation no_gc;
1046 String::FlatContent flat_content = s->GetFlatContent();
1047 DCHECK(flat_content.IsFlat());
1048 bool has_changed_character = false;
1049 bool is_ascii = FastAsciiConvert<Converter>(
1050 reinterpret_cast<char*>(result->GetChars()),
1051 reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()),
1052 length, &has_changed_character);
1053 // If not ASCII, we discard the result and take the 2 byte path.
1054 if (is_ascii) return has_changed_character ? *result : *s;
1055 }
1056
1057 Handle<SeqString> result; // Same length as input.
1058 if (s->IsOneByteRepresentation()) {
1059 result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
1060 } else {
1061 result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
1062 }
1063
1064 Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping);
1065 if (answer->IsException() || answer->IsString()) return answer;
1066
1067 DCHECK(answer->IsSmi());
1068 length = Smi::cast(answer)->value();
1069 if (s->IsOneByteRepresentation() && length > 0) {
1070 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1071 isolate, result, isolate->factory()->NewRawOneByteString(length));
1072 } else {
1073 if (length < 0) length = -length;
1074 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1075 isolate, result, isolate->factory()->NewRawTwoByteString(length));
1076 }
1077 return ConvertCaseHelper(isolate, *s, *result, length, mapping);
1078}
1079
1080
1081RUNTIME_FUNCTION(Runtime_StringToLowerCase) {
1082 HandleScope scope(isolate);
1083 DCHECK(args.length() == 1);
1084 CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
1085 return ConvertCase(s, isolate, isolate->runtime_state()->to_lower_mapping());
1086}
1087
1088
1089RUNTIME_FUNCTION(Runtime_StringToUpperCase) {
1090 HandleScope scope(isolate);
1091 DCHECK(args.length() == 1);
1092 CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
1093 return ConvertCase(s, isolate, isolate->runtime_state()->to_upper_mapping());
1094}
1095
1096
1097RUNTIME_FUNCTION(Runtime_StringTrim) {
1098 HandleScope scope(isolate);
1099 DCHECK(args.length() == 3);
1100
1101 CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
1102 CONVERT_BOOLEAN_ARG_CHECKED(trimLeft, 1);
1103 CONVERT_BOOLEAN_ARG_CHECKED(trimRight, 2);
1104
1105 string = String::Flatten(string);
1106 int length = string->length();
1107
1108 int left = 0;
1109 UnicodeCache* unicode_cache = isolate->unicode_cache();
1110 if (trimLeft) {
1111 while (left < length &&
1112 unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(left))) {
1113 left++;
1114 }
1115 }
1116
1117 int right = length;
1118 if (trimRight) {
1119 while (
1120 right > left &&
1121 unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(right - 1))) {
1122 right--;
1123 }
1124 }
1125
1126 return *isolate->factory()->NewSubString(string, left, right);
1127}
1128
1129
1130RUNTIME_FUNCTION(Runtime_TruncateString) {
1131 HandleScope scope(isolate);
1132 DCHECK(args.length() == 2);
1133 CONVERT_ARG_HANDLE_CHECKED(SeqString, string, 0);
1134 CONVERT_INT32_ARG_CHECKED(new_length, 1);
1135 RUNTIME_ASSERT(new_length >= 0);
1136 return *SeqString::Truncate(string, new_length);
1137}
1138
1139
1140RUNTIME_FUNCTION(Runtime_NewString) {
1141 HandleScope scope(isolate);
1142 DCHECK(args.length() == 2);
1143 CONVERT_INT32_ARG_CHECKED(length, 0);
1144 CONVERT_BOOLEAN_ARG_CHECKED(is_one_byte, 1);
1145 if (length == 0) return isolate->heap()->empty_string();
1146 Handle<String> result;
1147 if (is_one_byte) {
1148 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1149 isolate, result, isolate->factory()->NewRawOneByteString(length));
1150 } else {
1151 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1152 isolate, result, isolate->factory()->NewRawTwoByteString(length));
1153 }
1154 return *result;
1155}
1156
Ben Murdochda12d292016-06-02 14:46:10 +01001157RUNTIME_FUNCTION(Runtime_StringLessThan) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001158 HandleScope handle_scope(isolate);
Ben Murdochda12d292016-06-02 14:46:10 +01001159 DCHECK_EQ(2, args.length());
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001160 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
1161 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
Ben Murdochda12d292016-06-02 14:46:10 +01001162 switch (String::Compare(x, y)) {
1163 case ComparisonResult::kLessThan:
1164 return isolate->heap()->true_value();
1165 case ComparisonResult::kEqual:
1166 case ComparisonResult::kGreaterThan:
1167 return isolate->heap()->false_value();
1168 case ComparisonResult::kUndefined:
1169 break;
1170 }
1171 UNREACHABLE();
1172 return Smi::FromInt(0);
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001173}
1174
Ben Murdochda12d292016-06-02 14:46:10 +01001175RUNTIME_FUNCTION(Runtime_StringLessThanOrEqual) {
1176 HandleScope handle_scope(isolate);
1177 DCHECK_EQ(2, args.length());
1178 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
1179 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
1180 switch (String::Compare(x, y)) {
1181 case ComparisonResult::kEqual:
1182 case ComparisonResult::kLessThan:
1183 return isolate->heap()->true_value();
1184 case ComparisonResult::kGreaterThan:
1185 return isolate->heap()->false_value();
1186 case ComparisonResult::kUndefined:
1187 break;
1188 }
1189 UNREACHABLE();
1190 return Smi::FromInt(0);
1191}
1192
1193RUNTIME_FUNCTION(Runtime_StringGreaterThan) {
1194 HandleScope handle_scope(isolate);
1195 DCHECK_EQ(2, args.length());
1196 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
1197 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
1198 switch (String::Compare(x, y)) {
1199 case ComparisonResult::kGreaterThan:
1200 return isolate->heap()->true_value();
1201 case ComparisonResult::kEqual:
1202 case ComparisonResult::kLessThan:
1203 return isolate->heap()->false_value();
1204 case ComparisonResult::kUndefined:
1205 break;
1206 }
1207 UNREACHABLE();
1208 return Smi::FromInt(0);
1209}
1210
1211RUNTIME_FUNCTION(Runtime_StringGreaterThanOrEqual) {
1212 HandleScope handle_scope(isolate);
1213 DCHECK_EQ(2, args.length());
1214 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
1215 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
1216 switch (String::Compare(x, y)) {
1217 case ComparisonResult::kEqual:
1218 case ComparisonResult::kGreaterThan:
1219 return isolate->heap()->true_value();
1220 case ComparisonResult::kLessThan:
1221 return isolate->heap()->false_value();
1222 case ComparisonResult::kUndefined:
1223 break;
1224 }
1225 UNREACHABLE();
1226 return Smi::FromInt(0);
1227}
1228
1229RUNTIME_FUNCTION(Runtime_StringEqual) {
1230 HandleScope handle_scope(isolate);
1231 DCHECK_EQ(2, args.length());
1232 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
1233 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
1234 return isolate->heap()->ToBoolean(String::Equals(x, y));
1235}
1236
1237RUNTIME_FUNCTION(Runtime_StringNotEqual) {
1238 HandleScope handle_scope(isolate);
1239 DCHECK_EQ(2, args.length());
1240 CONVERT_ARG_HANDLE_CHECKED(String, x, 0);
1241 CONVERT_ARG_HANDLE_CHECKED(String, y, 1);
1242 return isolate->heap()->ToBoolean(!String::Equals(x, y));
1243}
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001244
1245RUNTIME_FUNCTION(Runtime_FlattenString) {
1246 HandleScope scope(isolate);
1247 DCHECK(args.length() == 1);
1248 CONVERT_ARG_HANDLE_CHECKED(String, str, 0);
1249 return *String::Flatten(str);
1250}
1251
1252
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001253RUNTIME_FUNCTION(Runtime_StringCharFromCode) {
1254 HandleScope handlescope(isolate);
1255 DCHECK_EQ(1, args.length());
1256 if (args[0]->IsNumber()) {
1257 CONVERT_NUMBER_CHECKED(uint32_t, code, Uint32, args[0]);
1258 code &= 0xffff;
1259 return *isolate->factory()->LookupSingleCharacterStringFromCode(code);
1260 }
1261 return isolate->heap()->empty_string();
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001262}
1263
1264
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001265RUNTIME_FUNCTION(Runtime_StringCharAt) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001266 SealHandleScope shs(isolate);
1267 DCHECK(args.length() == 2);
1268 if (!args[0]->IsString()) return Smi::FromInt(0);
1269 if (!args[1]->IsNumber()) return Smi::FromInt(0);
1270 if (std::isinf(args.number_at(1))) return isolate->heap()->empty_string();
1271 Object* code = __RT_impl_Runtime_StringCharCodeAtRT(args, isolate);
1272 if (code->IsNaN()) return isolate->heap()->empty_string();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001273 return __RT_impl_Runtime_StringCharFromCode(Arguments(1, &code), isolate);
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001274}
1275
1276
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001277RUNTIME_FUNCTION(Runtime_OneByteSeqStringGetChar) {
1278 SealHandleScope shs(isolate);
1279 DCHECK(args.length() == 2);
1280 CONVERT_ARG_CHECKED(SeqOneByteString, string, 0);
1281 CONVERT_INT32_ARG_CHECKED(index, 1);
1282 return Smi::FromInt(string->SeqOneByteStringGet(index));
1283}
1284
1285
1286RUNTIME_FUNCTION(Runtime_OneByteSeqStringSetChar) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001287 SealHandleScope shs(isolate);
1288 DCHECK(args.length() == 3);
1289 CONVERT_INT32_ARG_CHECKED(index, 0);
1290 CONVERT_INT32_ARG_CHECKED(value, 1);
1291 CONVERT_ARG_CHECKED(SeqOneByteString, string, 2);
1292 string->SeqOneByteStringSet(index, value);
1293 return string;
1294}
1295
1296
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001297RUNTIME_FUNCTION(Runtime_TwoByteSeqStringGetChar) {
1298 SealHandleScope shs(isolate);
1299 DCHECK(args.length() == 2);
1300 CONVERT_ARG_CHECKED(SeqTwoByteString, string, 0);
1301 CONVERT_INT32_ARG_CHECKED(index, 1);
1302 return Smi::FromInt(string->SeqTwoByteStringGet(index));
1303}
1304
1305
1306RUNTIME_FUNCTION(Runtime_TwoByteSeqStringSetChar) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001307 SealHandleScope shs(isolate);
1308 DCHECK(args.length() == 3);
1309 CONVERT_INT32_ARG_CHECKED(index, 0);
1310 CONVERT_INT32_ARG_CHECKED(value, 1);
1311 CONVERT_ARG_CHECKED(SeqTwoByteString, string, 2);
1312 string->SeqTwoByteStringSet(index, value);
1313 return string;
1314}
1315
1316
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001317RUNTIME_FUNCTION(Runtime_StringCharCodeAt) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001318 SealHandleScope shs(isolate);
1319 DCHECK(args.length() == 2);
1320 if (!args[0]->IsString()) return isolate->heap()->undefined_value();
1321 if (!args[1]->IsNumber()) return isolate->heap()->undefined_value();
1322 if (std::isinf(args.number_at(1))) return isolate->heap()->nan_value();
1323 return __RT_impl_Runtime_StringCharCodeAtRT(args, isolate);
1324}
1325
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001326} // namespace internal
1327} // namespace v8