Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(131)

Side by Side Diff: src/runtime/runtime-strings.cc

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

Powered by Google App Engine
This is Rietveld 408576698