Line data Source code
1 : // Copyright 2016 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/builtins/builtins-utils-inl.h"
6 : #include "src/builtins/builtins.h"
7 : #include "src/conversions.h"
8 : #include "src/counters.h"
9 : #include "src/objects-inl.h"
10 : #ifdef V8_INTL_SUPPORT
11 : #include "src/objects/intl-objects.h"
12 : #endif
13 : #include "src/regexp/regexp-utils.h"
14 : #include "src/string-builder-inl.h"
15 : #include "src/string-case.h"
16 : #include "src/unicode-inl.h"
17 : #include "src/unicode.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 :
22 : namespace { // for String.fromCodePoint
23 :
24 3609476 : bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
25 10828428 : if (!value->IsNumber() &&
26 3609476 : !Object::ToNumber(isolate, value).ToHandle(&value)) {
27 : return false;
28 : }
29 :
30 10828428 : if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
31 3609476 : value->Number()) {
32 : return false;
33 : }
34 :
35 7218780 : if (value->Number() < 0 || value->Number() > 0x10FFFF) {
36 : return false;
37 : }
38 :
39 3609331 : return true;
40 : }
41 :
42 3609494 : uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) {
43 3609494 : Handle<Object> value = args.at(1 + index);
44 7218988 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value,
45 : Object::ToNumber(isolate, value), -1);
46 3609476 : if (!IsValidCodePoint(isolate, value)) {
47 : isolate->Throw(*isolate->factory()->NewRangeError(
48 290 : MessageTemplate::kInvalidCodePoint, value));
49 : return -1;
50 : }
51 7218662 : return DoubleToUint32(value->Number());
52 : }
53 :
54 : } // namespace
55 :
56 : // ES6 section 21.1.2.2 String.fromCodePoint ( ...codePoints )
57 10898576 : BUILTIN(StringFromCodePoint) {
58 : HandleScope scope(isolate);
59 2724644 : int const length = args.length() - 1;
60 2724688 : if (length == 0) return ReadOnlyRoots(isolate).empty_string();
61 : DCHECK_LT(0, length);
62 :
63 : // Optimistically assume that the resulting String contains only one byte
64 : // characters.
65 : std::vector<uint8_t> one_byte_buffer;
66 2724600 : one_byte_buffer.reserve(length);
67 : uc32 code = 0;
68 : int index;
69 3169586 : for (index = 0; index < length; index++) {
70 3167047 : code = NextCodePoint(isolate, args, index);
71 3167047 : if (code < 0) {
72 163 : return ReadOnlyRoots(isolate).exception();
73 : }
74 3166884 : if (code > String::kMaxOneByteCharCode) {
75 : break;
76 : }
77 889972 : one_byte_buffer.push_back(code);
78 : }
79 :
80 2724437 : if (index == length) {
81 7617 : RETURN_RESULT_OR_FAILURE(
82 : isolate, isolate->factory()->NewStringFromOneByte(Vector<uint8_t>(
83 : one_byte_buffer.data(), one_byte_buffer.size())));
84 : }
85 :
86 : std::vector<uc16> two_byte_buffer;
87 : two_byte_buffer.reserve(length - index);
88 :
89 : while (true) {
90 3164345 : if (code <= static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
91 357150 : two_byte_buffer.push_back(code);
92 : } else {
93 8957310 : two_byte_buffer.push_back(unibrow::Utf16::LeadSurrogate(code));
94 5971540 : two_byte_buffer.push_back(unibrow::Utf16::TrailSurrogate(code));
95 : }
96 :
97 3164345 : if (++index == length) {
98 : break;
99 : }
100 442447 : code = NextCodePoint(isolate, args, index);
101 442447 : if (code < 0) {
102 0 : return ReadOnlyRoots(isolate).exception();
103 : }
104 : }
105 :
106 : Handle<SeqTwoByteString> result;
107 10887592 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
108 : isolate, result,
109 : isolate->factory()->NewRawTwoByteString(
110 : static_cast<int>(one_byte_buffer.size() + two_byte_buffer.size())));
111 :
112 : DisallowHeapAllocation no_gc;
113 : CopyChars(result->GetChars(no_gc), one_byte_buffer.data(),
114 2721898 : one_byte_buffer.size());
115 2721898 : CopyChars(result->GetChars(no_gc) + one_byte_buffer.size(),
116 5443796 : two_byte_buffer.data(), two_byte_buffer.size());
117 :
118 2721898 : return *result;
119 : }
120 :
121 : // ES6 section 21.1.3.6
122 : // String.prototype.endsWith ( searchString [ , endPosition ] )
123 100292 : BUILTIN(StringPrototypeEndsWith) {
124 : HandleScope handle_scope(isolate);
125 75561 : TO_THIS_STRING(str, "String.prototype.endsWith");
126 :
127 : // Check if the search string is a regExp and fail if it is.
128 24893 : Handle<Object> search = args.atOrUndefined(isolate, 1);
129 24893 : Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
130 24893 : if (is_reg_exp.IsNothing()) {
131 : DCHECK(isolate->has_pending_exception());
132 0 : return ReadOnlyRoots(isolate).exception();
133 : }
134 24893 : if (is_reg_exp.FromJust()) {
135 135 : THROW_NEW_ERROR_RETURN_FAILURE(
136 : isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
137 : isolate->factory()->NewStringFromStaticChars(
138 : "String.prototype.endsWith")));
139 : }
140 : Handle<String> search_string;
141 49696 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
142 : Object::ToString(isolate, search));
143 :
144 : Handle<Object> position = args.atOrUndefined(isolate, 2);
145 : int end;
146 :
147 49696 : if (position->IsUndefined(isolate)) {
148 : end = str->length();
149 : } else {
150 2754 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
151 : Object::ToInteger(isolate, position));
152 1377 : end = str->ToValidIndex(*position);
153 : }
154 :
155 24848 : int start = end - search_string->length();
156 25802 : if (start < 0) return ReadOnlyRoots(isolate).false_value();
157 :
158 23894 : str = String::Flatten(isolate, str);
159 23894 : search_string = String::Flatten(isolate, search_string);
160 :
161 : DisallowHeapAllocation no_gc; // ensure vectors stay valid
162 23894 : String::FlatContent str_content = str->GetFlatContent(no_gc);
163 23894 : String::FlatContent search_content = search_string->GetFlatContent(no_gc);
164 :
165 23894 : if (str_content.IsOneByte() && search_content.IsOneByte()) {
166 23768 : Vector<const uint8_t> str_vector = str_content.ToOneByteVector();
167 23768 : Vector<const uint8_t> search_vector = search_content.ToOneByteVector();
168 :
169 23768 : return isolate->heap()->ToBoolean(memcmp(str_vector.start() + start,
170 : search_vector.start(),
171 47536 : search_string->length()) == 0);
172 : }
173 :
174 126 : FlatStringReader str_reader(isolate, str);
175 126 : FlatStringReader search_reader(isolate, search_string);
176 :
177 900 : for (int i = 0; i < search_string->length(); i++) {
178 720 : if (str_reader.Get(start + i) != search_reader.Get(i)) {
179 36 : return ReadOnlyRoots(isolate).false_value();
180 : }
181 : }
182 90 : return ReadOnlyRoots(isolate).true_value();
183 : }
184 :
185 : // ES6 section 21.1.3.9
186 : // String.prototype.lastIndexOf ( searchString [ , position ] )
187 6888 : BUILTIN(StringPrototypeLastIndexOf) {
188 : HandleScope handle_scope(isolate);
189 : return String::LastIndexOf(isolate, args.receiver(),
190 : args.atOrUndefined(isolate, 1),
191 3444 : args.atOrUndefined(isolate, 2));
192 : }
193 :
194 : // ES6 section 21.1.3.10 String.prototype.localeCompare ( that )
195 : //
196 : // This function is implementation specific. For now, we do not
197 : // do anything locale specific.
198 253828 : BUILTIN(StringPrototypeLocaleCompare) {
199 : HandleScope handle_scope(isolate);
200 :
201 63457 : isolate->CountUsage(v8::Isolate::UseCounterFeature::kStringLocaleCompare);
202 :
203 : #ifdef V8_INTL_SUPPORT
204 190713 : TO_THIS_STRING(str1, "String.prototype.localeCompare");
205 : Handle<String> str2;
206 126572 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
207 : isolate, str2, Object::ToString(isolate, args.atOrUndefined(isolate, 1)));
208 126572 : RETURN_RESULT_OR_FAILURE(
209 : isolate, Intl::StringLocaleCompare(isolate, str1, str2,
210 : args.atOrUndefined(isolate, 2),
211 : args.atOrUndefined(isolate, 3)));
212 : #else
213 : DCHECK_EQ(2, args.length());
214 :
215 : TO_THIS_STRING(str1, "String.prototype.localeCompare");
216 : Handle<String> str2;
217 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str2,
218 : Object::ToString(isolate, args.at(1)));
219 :
220 : if (str1.is_identical_to(str2)) return Smi::kZero; // Equal.
221 : int str1_length = str1->length();
222 : int str2_length = str2->length();
223 :
224 : // Decide trivial cases without flattening.
225 : if (str1_length == 0) {
226 : if (str2_length == 0) return Smi::kZero; // Equal.
227 : return Smi::FromInt(-str2_length);
228 : } else {
229 : if (str2_length == 0) return Smi::FromInt(str1_length);
230 : }
231 :
232 : int end = str1_length < str2_length ? str1_length : str2_length;
233 :
234 : // No need to flatten if we are going to find the answer on the first
235 : // character. At this point we know there is at least one character
236 : // in each string, due to the trivial case handling above.
237 : int d = str1->Get(0) - str2->Get(0);
238 : if (d != 0) return Smi::FromInt(d);
239 :
240 : str1 = String::Flatten(isolate, str1);
241 : str2 = String::Flatten(isolate, str2);
242 :
243 : DisallowHeapAllocation no_gc;
244 : String::FlatContent flat1 = str1->GetFlatContent(no_gc);
245 : String::FlatContent flat2 = str2->GetFlatContent(no_gc);
246 :
247 : for (int i = 0; i < end; i++) {
248 : if (flat1.Get(i) != flat2.Get(i)) {
249 : return Smi::FromInt(flat1.Get(i) - flat2.Get(i));
250 : }
251 : }
252 :
253 : return Smi::FromInt(str1_length - str2_length);
254 : #endif // !V8_INTL_SUPPORT
255 : }
256 :
257 : #ifndef V8_INTL_SUPPORT
258 : // ES6 section 21.1.3.12 String.prototype.normalize ( [form] )
259 : //
260 : // Simply checks the argument is valid and returns the string itself.
261 : // If internationalization is enabled, then intl.js will override this function
262 : // and provide the proper functionality, so this is just a fallback.
263 : BUILTIN(StringPrototypeNormalize) {
264 : HandleScope handle_scope(isolate);
265 : TO_THIS_STRING(string, "String.prototype.normalize");
266 :
267 : Handle<Object> form_input = args.atOrUndefined(isolate, 1);
268 : if (form_input->IsUndefined(isolate)) return *string;
269 :
270 : Handle<String> form;
271 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form,
272 : Object::ToString(isolate, form_input));
273 :
274 : if (!(String::Equals(isolate, form,
275 : isolate->factory()->NewStringFromStaticChars("NFC")) ||
276 : String::Equals(isolate, form,
277 : isolate->factory()->NewStringFromStaticChars("NFD")) ||
278 : String::Equals(isolate, form,
279 : isolate->factory()->NewStringFromStaticChars("NFKC")) ||
280 : String::Equals(isolate, form,
281 : isolate->factory()->NewStringFromStaticChars("NFKD")))) {
282 : Handle<String> valid_forms =
283 : isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
284 : THROW_NEW_ERROR_RETURN_FAILURE(
285 : isolate,
286 : NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
287 : }
288 :
289 : return *string;
290 : }
291 : #endif // !V8_INTL_SUPPORT
292 :
293 51680 : BUILTIN(StringPrototypeStartsWith) {
294 : HandleScope handle_scope(isolate);
295 39102 : TO_THIS_STRING(str, "String.prototype.startsWith");
296 :
297 : // Check if the search string is a regExp and fail if it is.
298 12740 : Handle<Object> search = args.atOrUndefined(isolate, 1);
299 12740 : Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
300 12740 : if (is_reg_exp.IsNothing()) {
301 : DCHECK(isolate->has_pending_exception());
302 0 : return ReadOnlyRoots(isolate).exception();
303 : }
304 12740 : if (is_reg_exp.FromJust()) {
305 216 : THROW_NEW_ERROR_RETURN_FAILURE(
306 : isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
307 : isolate->factory()->NewStringFromStaticChars(
308 : "String.prototype.startsWith")));
309 : }
310 : Handle<String> search_string;
311 25336 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
312 : Object::ToString(isolate, search));
313 :
314 : Handle<Object> position = args.atOrUndefined(isolate, 2);
315 : int start;
316 :
317 25336 : if (position->IsUndefined(isolate)) {
318 : start = 0;
319 : } else {
320 2610 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
321 : Object::ToInteger(isolate, position));
322 1305 : start = str->ToValidIndex(*position);
323 : }
324 :
325 25336 : if (start + search_string->length() > str->length()) {
326 565 : return ReadOnlyRoots(isolate).false_value();
327 : }
328 :
329 12103 : FlatStringReader str_reader(isolate, String::Flatten(isolate, str));
330 : FlatStringReader search_reader(isolate,
331 12103 : String::Flatten(isolate, search_string));
332 :
333 127116 : for (int i = 0; i < search_string->length(); i++) {
334 120746 : if (str_reader.Get(start + i) != search_reader.Get(i)) {
335 8918 : return ReadOnlyRoots(isolate).false_value();
336 : }
337 : }
338 3185 : return ReadOnlyRoots(isolate).true_value();
339 : }
340 :
341 : #ifndef V8_INTL_SUPPORT
342 : namespace {
343 :
344 : inline bool ToUpperOverflows(uc32 character) {
345 : // y with umlauts and the micro sign are the only characters that stop
346 : // fitting into one-byte when converting to uppercase.
347 : static const uc32 yuml_code = 0xFF;
348 : static const uc32 micro_code = 0xB5;
349 : return (character == yuml_code || character == micro_code);
350 : }
351 :
352 : template <class Converter>
353 : V8_WARN_UNUSED_RESULT static Object ConvertCaseHelper(
354 : Isolate* isolate, String string, SeqString result, int result_length,
355 : unibrow::Mapping<Converter, 128>* mapping) {
356 : DisallowHeapAllocation no_gc;
357 : // We try this twice, once with the assumption that the result is no longer
358 : // than the input and, if that assumption breaks, again with the exact
359 : // length. This may not be pretty, but it is nicer than what was here before
360 : // and I hereby claim my vaffel-is.
361 : //
362 : // NOTE: This assumes that the upper/lower case of an ASCII
363 : // character is also ASCII. This is currently the case, but it
364 : // might break in the future if we implement more context and locale
365 : // dependent upper/lower conversions.
366 : bool has_changed_character = false;
367 :
368 : // Convert all characters to upper case, assuming that they will fit
369 : // in the buffer
370 : StringCharacterStream stream(string);
371 : unibrow::uchar chars[Converter::kMaxWidth];
372 : // We can assume that the string is not empty
373 : uc32 current = stream.GetNext();
374 : bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString();
375 : for (int i = 0; i < result_length;) {
376 : bool has_next = stream.HasMore();
377 : uc32 next = has_next ? stream.GetNext() : 0;
378 : int char_length = mapping->get(current, next, chars);
379 : if (char_length == 0) {
380 : // The case conversion of this character is the character itself.
381 : result->Set(i, current);
382 : i++;
383 : } else if (char_length == 1 &&
384 : (ignore_overflow || !ToUpperOverflows(current))) {
385 : // Common case: converting the letter resulted in one character.
386 : DCHECK(static_cast<uc32>(chars[0]) != current);
387 : result->Set(i, chars[0]);
388 : has_changed_character = true;
389 : i++;
390 : } else if (result_length == string->length()) {
391 : bool overflows = ToUpperOverflows(current);
392 : // We've assumed that the result would be as long as the
393 : // input but here is a character that converts to several
394 : // characters. No matter, we calculate the exact length
395 : // of the result and try the whole thing again.
396 : //
397 : // Note that this leaves room for optimization. We could just
398 : // memcpy what we already have to the result string. Also,
399 : // the result string is the last object allocated we could
400 : // "realloc" it and probably, in the vast majority of cases,
401 : // extend the existing string to be able to hold the full
402 : // result.
403 : int next_length = 0;
404 : if (has_next) {
405 : next_length = mapping->get(next, 0, chars);
406 : if (next_length == 0) next_length = 1;
407 : }
408 : int current_length = i + char_length + next_length;
409 : while (stream.HasMore()) {
410 : current = stream.GetNext();
411 : overflows |= ToUpperOverflows(current);
412 : // NOTE: we use 0 as the next character here because, while
413 : // the next character may affect what a character converts to,
414 : // it does not in any case affect the length of what it convert
415 : // to.
416 : int char_length = mapping->get(current, 0, chars);
417 : if (char_length == 0) char_length = 1;
418 : current_length += char_length;
419 : if (current_length > String::kMaxLength) {
420 : AllowHeapAllocation allocate_error_and_return;
421 : THROW_NEW_ERROR_RETURN_FAILURE(isolate,
422 : NewInvalidStringLengthError());
423 : }
424 : }
425 : // Try again with the real length. Return signed if we need
426 : // to allocate a two-byte string for to uppercase.
427 : return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length)
428 : : Smi::FromInt(current_length);
429 : } else {
430 : for (int j = 0; j < char_length; j++) {
431 : result->Set(i, chars[j]);
432 : i++;
433 : }
434 : has_changed_character = true;
435 : }
436 : current = next;
437 : }
438 : if (has_changed_character) {
439 : return result;
440 : } else {
441 : // If we didn't actually change anything in doing the conversion
442 : // we simple return the result and let the converted string
443 : // become garbage; there is no reason to keep two identical strings
444 : // alive.
445 : return string;
446 : }
447 : }
448 :
449 : template <class Converter>
450 : V8_WARN_UNUSED_RESULT static Object ConvertCase(
451 : Handle<String> s, Isolate* isolate,
452 : unibrow::Mapping<Converter, 128>* mapping) {
453 : s = String::Flatten(isolate, s);
454 : int length = s->length();
455 : // Assume that the string is not empty; we need this assumption later
456 : if (length == 0) return *s;
457 :
458 : // Simpler handling of ASCII strings.
459 : //
460 : // NOTE: This assumes that the upper/lower case of an ASCII
461 : // character is also ASCII. This is currently the case, but it
462 : // might break in the future if we implement more context and locale
463 : // dependent upper/lower conversions.
464 : if (String::IsOneByteRepresentationUnderneath(*s)) {
465 : // Same length as input.
466 : Handle<SeqOneByteString> result =
467 : isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
468 : DisallowHeapAllocation no_gc;
469 : String::FlatContent flat_content = s->GetFlatContent(no_gc);
470 : DCHECK(flat_content.IsFlat());
471 : bool has_changed_character = false;
472 : int index_to_first_unprocessed = FastAsciiConvert<Converter::kIsToLower>(
473 : reinterpret_cast<char*>(result->GetChars(no_gc)),
474 : reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()),
475 : length, &has_changed_character);
476 : // If not ASCII, we discard the result and take the 2 byte path.
477 : if (index_to_first_unprocessed == length)
478 : return has_changed_character ? *result : *s;
479 : }
480 :
481 : Handle<SeqString> result; // Same length as input.
482 : if (s->IsOneByteRepresentation()) {
483 : result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
484 : } else {
485 : result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked();
486 : }
487 :
488 : Object answer = ConvertCaseHelper(isolate, *s, *result, length, mapping);
489 : if (answer->IsException(isolate) || answer->IsString()) return answer;
490 :
491 : DCHECK(answer->IsSmi());
492 : length = Smi::ToInt(answer);
493 : if (s->IsOneByteRepresentation() && length > 0) {
494 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
495 : isolate, result, isolate->factory()->NewRawOneByteString(length));
496 : } else {
497 : if (length < 0) length = -length;
498 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
499 : isolate, result, isolate->factory()->NewRawTwoByteString(length));
500 : }
501 : return ConvertCaseHelper(isolate, *s, *result, length, mapping);
502 : }
503 :
504 : } // namespace
505 :
506 : BUILTIN(StringPrototypeToLocaleLowerCase) {
507 : HandleScope scope(isolate);
508 : TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase");
509 : return ConvertCase(string, isolate,
510 : isolate->runtime_state()->to_lower_mapping());
511 : }
512 :
513 : BUILTIN(StringPrototypeToLocaleUpperCase) {
514 : HandleScope scope(isolate);
515 : TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase");
516 : return ConvertCase(string, isolate,
517 : isolate->runtime_state()->to_upper_mapping());
518 : }
519 :
520 : BUILTIN(StringPrototypeToLowerCase) {
521 : HandleScope scope(isolate);
522 : TO_THIS_STRING(string, "String.prototype.toLowerCase");
523 : return ConvertCase(string, isolate,
524 : isolate->runtime_state()->to_lower_mapping());
525 : }
526 :
527 : BUILTIN(StringPrototypeToUpperCase) {
528 : HandleScope scope(isolate);
529 : TO_THIS_STRING(string, "String.prototype.toUpperCase");
530 : return ConvertCase(string, isolate,
531 : isolate->runtime_state()->to_upper_mapping());
532 : }
533 : #endif // !V8_INTL_SUPPORT
534 :
535 : // ES6 #sec-string.prototype.raw
536 1692 : BUILTIN(StringRaw) {
537 : HandleScope scope(isolate);
538 423 : Handle<Object> templ = args.atOrUndefined(isolate, 1);
539 423 : const uint32_t argc = args.length();
540 : Handle<String> raw_string =
541 423 : isolate->factory()->NewStringFromAsciiChecked("raw");
542 :
543 : Handle<Object> cooked;
544 855 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, cooked,
545 : Object::ToObject(isolate, templ));
546 :
547 : Handle<Object> raw;
548 828 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
549 : isolate, raw, Object::GetProperty(isolate, cooked, raw_string));
550 837 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, raw,
551 : Object::ToObject(isolate, raw));
552 : Handle<Object> raw_len;
553 810 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
554 : isolate, raw_len,
555 : Object::GetProperty(isolate, raw, isolate->factory()->length_string()));
556 :
557 810 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, raw_len,
558 : Object::ToLength(isolate, raw_len));
559 :
560 405 : IncrementalStringBuilder result_builder(isolate);
561 405 : const uint32_t length = static_cast<uint32_t>(raw_len->Number());
562 405 : if (length > 0) {
563 : Handle<Object> first_element;
564 567 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, first_element,
565 : Object::GetElement(isolate, raw, 0));
566 :
567 : Handle<String> first_string;
568 567 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
569 : isolate, first_string, Object::ToString(isolate, first_element));
570 243 : result_builder.AppendString(first_string);
571 :
572 387 : for (uint32_t i = 1, arg_i = 2; i < length; i++, arg_i++) {
573 414 : if (arg_i < argc) {
574 : Handle<String> argument_string;
575 864 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
576 : isolate, argument_string,
577 : Object::ToString(isolate, args.at(arg_i)));
578 252 : result_builder.AppendString(argument_string);
579 : }
580 :
581 : Handle<Object> element;
582 774 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element,
583 : Object::GetElement(isolate, raw, i));
584 :
585 : Handle<String> element_string;
586 774 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_string,
587 : Object::ToString(isolate, element));
588 387 : result_builder.AppendString(element_string);
589 : }
590 : }
591 :
592 684 : RETURN_RESULT_OR_FAILURE(isolate, result_builder.Finish());
593 : }
594 :
595 : } // namespace internal
596 183867 : } // namespace v8
|