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