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.h"
6 #include "src/builtins/builtins-utils.h"
7
8 #include "src/code-factory.h"
9 #include "src/regexp/regexp-utils.h"
10
11 namespace v8 {
12 namespace internal {
13
14 typedef CodeStubAssembler::ResultMode ResultMode;
15 typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode;
16
17 namespace {
18
GenerateStringEqual(CodeStubAssembler * assembler,ResultMode mode)19 void GenerateStringEqual(CodeStubAssembler* assembler, ResultMode mode) {
20 // Here's pseudo-code for the algorithm below in case of kDontNegateResult
21 // mode; for kNegateResult mode we properly negate the result.
22 //
23 // if (lhs == rhs) return true;
24 // if (lhs->length() != rhs->length()) return false;
25 // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) {
26 // return false;
27 // }
28 // if (lhs->IsSeqOneByteString() && rhs->IsSeqOneByteString()) {
29 // for (i = 0; i != lhs->length(); ++i) {
30 // if (lhs[i] != rhs[i]) return false;
31 // }
32 // return true;
33 // }
34 // return %StringEqual(lhs, rhs);
35
36 typedef CodeStubAssembler::Label Label;
37 typedef compiler::Node Node;
38 typedef CodeStubAssembler::Variable Variable;
39
40 Node* lhs = assembler->Parameter(0);
41 Node* rhs = assembler->Parameter(1);
42 Node* context = assembler->Parameter(2);
43
44 Label if_equal(assembler), if_notequal(assembler);
45
46 // Fast check to see if {lhs} and {rhs} refer to the same String object.
47 Label if_same(assembler), if_notsame(assembler);
48 assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame);
49
50 assembler->Bind(&if_same);
51 assembler->Goto(&if_equal);
52
53 assembler->Bind(&if_notsame);
54 {
55 // The {lhs} and {rhs} don't refer to the exact same String object.
56
57 // Load the length of {lhs} and {rhs}.
58 Node* lhs_length = assembler->LoadStringLength(lhs);
59 Node* rhs_length = assembler->LoadStringLength(rhs);
60
61 // Check if the lengths of {lhs} and {rhs} are equal.
62 Label if_lengthisequal(assembler), if_lengthisnotequal(assembler);
63 assembler->Branch(assembler->WordEqual(lhs_length, rhs_length),
64 &if_lengthisequal, &if_lengthisnotequal);
65
66 assembler->Bind(&if_lengthisequal);
67 {
68 // Load instance types of {lhs} and {rhs}.
69 Node* lhs_instance_type = assembler->LoadInstanceType(lhs);
70 Node* rhs_instance_type = assembler->LoadInstanceType(rhs);
71
72 // Combine the instance types into a single 16-bit value, so we can check
73 // both of them at once.
74 Node* both_instance_types = assembler->Word32Or(
75 lhs_instance_type,
76 assembler->Word32Shl(rhs_instance_type, assembler->Int32Constant(8)));
77
78 // Check if both {lhs} and {rhs} are internalized.
79 int const kBothInternalizedMask =
80 kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
81 int const kBothInternalizedTag =
82 kInternalizedTag | (kInternalizedTag << 8);
83 Label if_bothinternalized(assembler), if_notbothinternalized(assembler);
84 assembler->Branch(assembler->Word32Equal(
85 assembler->Word32And(both_instance_types,
86 assembler->Int32Constant(
87 kBothInternalizedMask)),
88 assembler->Int32Constant(kBothInternalizedTag)),
89 &if_bothinternalized, &if_notbothinternalized);
90
91 assembler->Bind(&if_bothinternalized);
92 {
93 // Fast negative check for internalized-to-internalized equality.
94 assembler->Goto(&if_notequal);
95 }
96
97 assembler->Bind(&if_notbothinternalized);
98 {
99 // Check that both {lhs} and {rhs} are flat one-byte strings.
100 int const kBothSeqOneByteStringMask =
101 kStringEncodingMask | kStringRepresentationMask |
102 ((kStringEncodingMask | kStringRepresentationMask) << 8);
103 int const kBothSeqOneByteStringTag =
104 kOneByteStringTag | kSeqStringTag |
105 ((kOneByteStringTag | kSeqStringTag) << 8);
106 Label if_bothonebyteseqstrings(assembler),
107 if_notbothonebyteseqstrings(assembler);
108 assembler->Branch(
109 assembler->Word32Equal(
110 assembler->Word32And(
111 both_instance_types,
112 assembler->Int32Constant(kBothSeqOneByteStringMask)),
113 assembler->Int32Constant(kBothSeqOneByteStringTag)),
114 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
115
116 assembler->Bind(&if_bothonebyteseqstrings);
117 {
118 // Compute the effective offset of the first character.
119 Node* begin = assembler->IntPtrConstant(
120 SeqOneByteString::kHeaderSize - kHeapObjectTag);
121
122 // Compute the first offset after the string from the length.
123 Node* end =
124 assembler->IntPtrAdd(begin, assembler->SmiUntag(lhs_length));
125
126 // Loop over the {lhs} and {rhs} strings to see if they are equal.
127 Variable var_offset(assembler, MachineType::PointerRepresentation());
128 Label loop(assembler, &var_offset);
129 var_offset.Bind(begin);
130 assembler->Goto(&loop);
131 assembler->Bind(&loop);
132 {
133 // Check if {offset} equals {end}.
134 Node* offset = var_offset.value();
135 Label if_done(assembler), if_notdone(assembler);
136 assembler->Branch(assembler->WordEqual(offset, end), &if_done,
137 &if_notdone);
138
139 assembler->Bind(&if_notdone);
140 {
141 // Load the next characters from {lhs} and {rhs}.
142 Node* lhs_value =
143 assembler->Load(MachineType::Uint8(), lhs, offset);
144 Node* rhs_value =
145 assembler->Load(MachineType::Uint8(), rhs, offset);
146
147 // Check if the characters match.
148 Label if_valueissame(assembler), if_valueisnotsame(assembler);
149 assembler->Branch(assembler->Word32Equal(lhs_value, rhs_value),
150 &if_valueissame, &if_valueisnotsame);
151
152 assembler->Bind(&if_valueissame);
153 {
154 // Advance to next character.
155 var_offset.Bind(
156 assembler->IntPtrAdd(offset, assembler->IntPtrConstant(1)));
157 }
158 assembler->Goto(&loop);
159
160 assembler->Bind(&if_valueisnotsame);
161 assembler->Goto(&if_notequal);
162 }
163
164 assembler->Bind(&if_done);
165 assembler->Goto(&if_equal);
166 }
167 }
168
169 assembler->Bind(&if_notbothonebyteseqstrings);
170 {
171 // TODO(bmeurer): Add fast case support for flattened cons strings;
172 // also add support for two byte string equality checks.
173 Runtime::FunctionId function_id =
174 (mode == ResultMode::kDontNegateResult)
175 ? Runtime::kStringEqual
176 : Runtime::kStringNotEqual;
177 assembler->TailCallRuntime(function_id, context, lhs, rhs);
178 }
179 }
180 }
181
182 assembler->Bind(&if_lengthisnotequal);
183 {
184 // Mismatch in length of {lhs} and {rhs}, cannot be equal.
185 assembler->Goto(&if_notequal);
186 }
187 }
188
189 assembler->Bind(&if_equal);
190 assembler->Return(
191 assembler->BooleanConstant(mode == ResultMode::kDontNegateResult));
192
193 assembler->Bind(&if_notequal);
194 assembler->Return(
195 assembler->BooleanConstant(mode == ResultMode::kNegateResult));
196 }
197
198
GenerateStringRelationalComparison(CodeStubAssembler * assembler,RelationalComparisonMode mode)199 void GenerateStringRelationalComparison(CodeStubAssembler* assembler,
200 RelationalComparisonMode mode) {
201 typedef CodeStubAssembler::Label Label;
202 typedef compiler::Node Node;
203 typedef CodeStubAssembler::Variable Variable;
204
205 Node* lhs = assembler->Parameter(0);
206 Node* rhs = assembler->Parameter(1);
207 Node* context = assembler->Parameter(2);
208
209 Label if_less(assembler), if_equal(assembler), if_greater(assembler);
210
211 // Fast check to see if {lhs} and {rhs} refer to the same String object.
212 Label if_same(assembler), if_notsame(assembler);
213 assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame);
214
215 assembler->Bind(&if_same);
216 assembler->Goto(&if_equal);
217
218 assembler->Bind(&if_notsame);
219 {
220 // Load instance types of {lhs} and {rhs}.
221 Node* lhs_instance_type = assembler->LoadInstanceType(lhs);
222 Node* rhs_instance_type = assembler->LoadInstanceType(rhs);
223
224 // Combine the instance types into a single 16-bit value, so we can check
225 // both of them at once.
226 Node* both_instance_types = assembler->Word32Or(
227 lhs_instance_type,
228 assembler->Word32Shl(rhs_instance_type, assembler->Int32Constant(8)));
229
230 // Check that both {lhs} and {rhs} are flat one-byte strings.
231 int const kBothSeqOneByteStringMask =
232 kStringEncodingMask | kStringRepresentationMask |
233 ((kStringEncodingMask | kStringRepresentationMask) << 8);
234 int const kBothSeqOneByteStringTag =
235 kOneByteStringTag | kSeqStringTag |
236 ((kOneByteStringTag | kSeqStringTag) << 8);
237 Label if_bothonebyteseqstrings(assembler),
238 if_notbothonebyteseqstrings(assembler);
239 assembler->Branch(assembler->Word32Equal(
240 assembler->Word32And(both_instance_types,
241 assembler->Int32Constant(
242 kBothSeqOneByteStringMask)),
243 assembler->Int32Constant(kBothSeqOneByteStringTag)),
244 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
245
246 assembler->Bind(&if_bothonebyteseqstrings);
247 {
248 // Load the length of {lhs} and {rhs}.
249 Node* lhs_length = assembler->LoadStringLength(lhs);
250 Node* rhs_length = assembler->LoadStringLength(rhs);
251
252 // Determine the minimum length.
253 Node* length = assembler->SmiMin(lhs_length, rhs_length);
254
255 // Compute the effective offset of the first character.
256 Node* begin = assembler->IntPtrConstant(SeqOneByteString::kHeaderSize -
257 kHeapObjectTag);
258
259 // Compute the first offset after the string from the length.
260 Node* end = assembler->IntPtrAdd(begin, assembler->SmiUntag(length));
261
262 // Loop over the {lhs} and {rhs} strings to see if they are equal.
263 Variable var_offset(assembler, MachineType::PointerRepresentation());
264 Label loop(assembler, &var_offset);
265 var_offset.Bind(begin);
266 assembler->Goto(&loop);
267 assembler->Bind(&loop);
268 {
269 // Check if {offset} equals {end}.
270 Node* offset = var_offset.value();
271 Label if_done(assembler), if_notdone(assembler);
272 assembler->Branch(assembler->WordEqual(offset, end), &if_done,
273 &if_notdone);
274
275 assembler->Bind(&if_notdone);
276 {
277 // Load the next characters from {lhs} and {rhs}.
278 Node* lhs_value = assembler->Load(MachineType::Uint8(), lhs, offset);
279 Node* rhs_value = assembler->Load(MachineType::Uint8(), rhs, offset);
280
281 // Check if the characters match.
282 Label if_valueissame(assembler), if_valueisnotsame(assembler);
283 assembler->Branch(assembler->Word32Equal(lhs_value, rhs_value),
284 &if_valueissame, &if_valueisnotsame);
285
286 assembler->Bind(&if_valueissame);
287 {
288 // Advance to next character.
289 var_offset.Bind(
290 assembler->IntPtrAdd(offset, assembler->IntPtrConstant(1)));
291 }
292 assembler->Goto(&loop);
293
294 assembler->Bind(&if_valueisnotsame);
295 assembler->Branch(assembler->Uint32LessThan(lhs_value, rhs_value),
296 &if_less, &if_greater);
297 }
298
299 assembler->Bind(&if_done);
300 {
301 // All characters up to the min length are equal, decide based on
302 // string length.
303 Label if_lengthisequal(assembler), if_lengthisnotequal(assembler);
304 assembler->Branch(assembler->SmiEqual(lhs_length, rhs_length),
305 &if_lengthisequal, &if_lengthisnotequal);
306
307 assembler->Bind(&if_lengthisequal);
308 assembler->Goto(&if_equal);
309
310 assembler->Bind(&if_lengthisnotequal);
311 assembler->BranchIfSmiLessThan(lhs_length, rhs_length, &if_less,
312 &if_greater);
313 }
314 }
315 }
316
317 assembler->Bind(&if_notbothonebyteseqstrings);
318 {
319 // TODO(bmeurer): Add fast case support for flattened cons strings;
320 // also add support for two byte string relational comparisons.
321 switch (mode) {
322 case RelationalComparisonMode::kLessThan:
323 assembler->TailCallRuntime(Runtime::kStringLessThan, context, lhs,
324 rhs);
325 break;
326 case RelationalComparisonMode::kLessThanOrEqual:
327 assembler->TailCallRuntime(Runtime::kStringLessThanOrEqual, context,
328 lhs, rhs);
329 break;
330 case RelationalComparisonMode::kGreaterThan:
331 assembler->TailCallRuntime(Runtime::kStringGreaterThan, context, lhs,
332 rhs);
333 break;
334 case RelationalComparisonMode::kGreaterThanOrEqual:
335 assembler->TailCallRuntime(Runtime::kStringGreaterThanOrEqual,
336 context, lhs, rhs);
337 break;
338 }
339 }
340 }
341
342 assembler->Bind(&if_less);
343 switch (mode) {
344 case RelationalComparisonMode::kLessThan:
345 case RelationalComparisonMode::kLessThanOrEqual:
346 assembler->Return(assembler->BooleanConstant(true));
347 break;
348
349 case RelationalComparisonMode::kGreaterThan:
350 case RelationalComparisonMode::kGreaterThanOrEqual:
351 assembler->Return(assembler->BooleanConstant(false));
352 break;
353 }
354
355 assembler->Bind(&if_equal);
356 switch (mode) {
357 case RelationalComparisonMode::kLessThan:
358 case RelationalComparisonMode::kGreaterThan:
359 assembler->Return(assembler->BooleanConstant(false));
360 break;
361
362 case RelationalComparisonMode::kLessThanOrEqual:
363 case RelationalComparisonMode::kGreaterThanOrEqual:
364 assembler->Return(assembler->BooleanConstant(true));
365 break;
366 }
367
368 assembler->Bind(&if_greater);
369 switch (mode) {
370 case RelationalComparisonMode::kLessThan:
371 case RelationalComparisonMode::kLessThanOrEqual:
372 assembler->Return(assembler->BooleanConstant(false));
373 break;
374
375 case RelationalComparisonMode::kGreaterThan:
376 case RelationalComparisonMode::kGreaterThanOrEqual:
377 assembler->Return(assembler->BooleanConstant(true));
378 break;
379 }
380 }
381
382 } // namespace
383
384 // static
Generate_StringEqual(CodeStubAssembler * assembler)385 void Builtins::Generate_StringEqual(CodeStubAssembler* assembler) {
386 GenerateStringEqual(assembler, ResultMode::kDontNegateResult);
387 }
388
389 // static
Generate_StringNotEqual(CodeStubAssembler * assembler)390 void Builtins::Generate_StringNotEqual(CodeStubAssembler* assembler) {
391 GenerateStringEqual(assembler, ResultMode::kNegateResult);
392 }
393
394 // static
Generate_StringLessThan(CodeStubAssembler * assembler)395 void Builtins::Generate_StringLessThan(CodeStubAssembler* assembler) {
396 GenerateStringRelationalComparison(assembler,
397 RelationalComparisonMode::kLessThan);
398 }
399
400 // static
Generate_StringLessThanOrEqual(CodeStubAssembler * assembler)401 void Builtins::Generate_StringLessThanOrEqual(CodeStubAssembler* assembler) {
402 GenerateStringRelationalComparison(
403 assembler, RelationalComparisonMode::kLessThanOrEqual);
404 }
405
406 // static
Generate_StringGreaterThan(CodeStubAssembler * assembler)407 void Builtins::Generate_StringGreaterThan(CodeStubAssembler* assembler) {
408 GenerateStringRelationalComparison(assembler,
409 RelationalComparisonMode::kGreaterThan);
410 }
411
412 // static
Generate_StringGreaterThanOrEqual(CodeStubAssembler * assembler)413 void Builtins::Generate_StringGreaterThanOrEqual(CodeStubAssembler* assembler) {
414 GenerateStringRelationalComparison(
415 assembler, RelationalComparisonMode::kGreaterThanOrEqual);
416 }
417
418 // -----------------------------------------------------------------------------
419 // ES6 section 21.1 String Objects
420
421 // ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
Generate_StringFromCharCode(CodeStubAssembler * assembler)422 void Builtins::Generate_StringFromCharCode(CodeStubAssembler* assembler) {
423 typedef CodeStubAssembler::Label Label;
424 typedef compiler::Node Node;
425 typedef CodeStubAssembler::Variable Variable;
426
427 Node* argc = assembler->ChangeInt32ToIntPtr(
428 assembler->Parameter(BuiltinDescriptor::kArgumentsCount));
429 Node* context = assembler->Parameter(BuiltinDescriptor::kContext);
430
431 CodeStubArguments arguments(assembler, argc);
432
433 // Check if we have exactly one argument (plus the implicit receiver), i.e.
434 // if the parent frame is not an arguments adaptor frame.
435 Label if_oneargument(assembler), if_notoneargument(assembler);
436 assembler->Branch(assembler->WordEqual(argc, assembler->IntPtrConstant(1)),
437 &if_oneargument, &if_notoneargument);
438
439 assembler->Bind(&if_oneargument);
440 {
441 // Single argument case, perform fast single character string cache lookup
442 // for one-byte code units, or fall back to creating a single character
443 // string on the fly otherwise.
444 Node* code = arguments.AtIndex(0);
445 Node* code32 = assembler->TruncateTaggedToWord32(context, code);
446 Node* code16 = assembler->Word32And(
447 code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit));
448 Node* result = assembler->StringFromCharCode(code16);
449 arguments.PopAndReturn(result);
450 }
451
452 Node* code16 = nullptr;
453 assembler->Bind(&if_notoneargument);
454 {
455 Label two_byte(assembler);
456 // Assume that the resulting string contains only one-byte characters.
457 Node* one_byte_result = assembler->AllocateSeqOneByteString(context, argc);
458
459 Variable max_index(assembler, MachineType::PointerRepresentation());
460 max_index.Bind(assembler->IntPtrConstant(0));
461
462 // Iterate over the incoming arguments, converting them to 8-bit character
463 // codes. Stop if any of the conversions generates a code that doesn't fit
464 // in 8 bits.
465 CodeStubAssembler::VariableList vars({&max_index}, assembler->zone());
466 arguments.ForEach(vars, [context, &two_byte, &max_index, &code16,
467 one_byte_result](CodeStubAssembler* assembler,
468 Node* arg) {
469 Node* code32 = assembler->TruncateTaggedToWord32(context, arg);
470 code16 = assembler->Word32And(
471 code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit));
472
473 assembler->GotoIf(
474 assembler->Int32GreaterThan(
475 code16, assembler->Int32Constant(String::kMaxOneByteCharCode)),
476 &two_byte);
477
478 // The {code16} fits into the SeqOneByteString {one_byte_result}.
479 Node* offset = assembler->ElementOffsetFromIndex(
480 max_index.value(), UINT8_ELEMENTS,
481 CodeStubAssembler::INTPTR_PARAMETERS,
482 SeqOneByteString::kHeaderSize - kHeapObjectTag);
483 assembler->StoreNoWriteBarrier(MachineRepresentation::kWord8,
484 one_byte_result, offset, code16);
485 max_index.Bind(assembler->IntPtrAdd(max_index.value(),
486 assembler->IntPtrConstant(1)));
487 });
488 arguments.PopAndReturn(one_byte_result);
489
490 assembler->Bind(&two_byte);
491
492 // At least one of the characters in the string requires a 16-bit
493 // representation. Allocate a SeqTwoByteString to hold the resulting
494 // string.
495 Node* two_byte_result = assembler->AllocateSeqTwoByteString(context, argc);
496
497 // Copy the characters that have already been put in the 8-bit string into
498 // their corresponding positions in the new 16-bit string.
499 Node* zero = assembler->IntPtrConstant(0);
500 assembler->CopyStringCharacters(
501 one_byte_result, two_byte_result, zero, zero, max_index.value(),
502 String::ONE_BYTE_ENCODING, String::TWO_BYTE_ENCODING,
503 CodeStubAssembler::INTPTR_PARAMETERS);
504
505 // Write the character that caused the 8-bit to 16-bit fault.
506 Node* max_index_offset = assembler->ElementOffsetFromIndex(
507 max_index.value(), UINT16_ELEMENTS,
508 CodeStubAssembler::INTPTR_PARAMETERS,
509 SeqTwoByteString::kHeaderSize - kHeapObjectTag);
510 assembler->StoreNoWriteBarrier(MachineRepresentation::kWord16,
511 two_byte_result, max_index_offset, code16);
512 max_index.Bind(
513 assembler->IntPtrAdd(max_index.value(), assembler->IntPtrConstant(1)));
514
515 // Resume copying the passed-in arguments from the same place where the
516 // 8-bit copy stopped, but this time copying over all of the characters
517 // using a 16-bit representation.
518 arguments.ForEach(
519 vars,
520 [context, two_byte_result, &max_index](CodeStubAssembler* assembler,
521 Node* arg) {
522 Node* code32 = assembler->TruncateTaggedToWord32(context, arg);
523 Node* code16 = assembler->Word32And(
524 code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit));
525
526 Node* offset = assembler->ElementOffsetFromIndex(
527 max_index.value(), UINT16_ELEMENTS,
528 CodeStubAssembler::INTPTR_PARAMETERS,
529 SeqTwoByteString::kHeaderSize - kHeapObjectTag);
530 assembler->StoreNoWriteBarrier(MachineRepresentation::kWord16,
531 two_byte_result, offset, code16);
532 max_index.Bind(assembler->IntPtrAdd(max_index.value(),
533 assembler->IntPtrConstant(1)));
534 },
535 max_index.value());
536
537 arguments.PopAndReturn(two_byte_result);
538 }
539 }
540
541 namespace { // for String.fromCodePoint
542
IsValidCodePoint(Isolate * isolate,Handle<Object> value)543 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
544 if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) {
545 return false;
546 }
547
548 if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
549 value->Number()) {
550 return false;
551 }
552
553 if (value->Number() < 0 || value->Number() > 0x10FFFF) {
554 return false;
555 }
556
557 return true;
558 }
559
NextCodePoint(Isolate * isolate,BuiltinArguments args,int index)560 uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) {
561 Handle<Object> value = args.at<Object>(1 + index);
562 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1);
563 if (!IsValidCodePoint(isolate, value)) {
564 isolate->Throw(*isolate->factory()->NewRangeError(
565 MessageTemplate::kInvalidCodePoint, value));
566 return -1;
567 }
568 return DoubleToUint32(value->Number());
569 }
570
571 } // namespace
572
573 // ES6 section 21.1.2.2 String.fromCodePoint ( ...codePoints )
BUILTIN(StringFromCodePoint)574 BUILTIN(StringFromCodePoint) {
575 HandleScope scope(isolate);
576 int const length = args.length() - 1;
577 if (length == 0) return isolate->heap()->empty_string();
578 DCHECK_LT(0, length);
579
580 // Optimistically assume that the resulting String contains only one byte
581 // characters.
582 List<uint8_t> one_byte_buffer(length);
583 uc32 code = 0;
584 int index;
585 for (index = 0; index < length; index++) {
586 code = NextCodePoint(isolate, args, index);
587 if (code < 0) {
588 return isolate->heap()->exception();
589 }
590 if (code > String::kMaxOneByteCharCode) {
591 break;
592 }
593 one_byte_buffer.Add(code);
594 }
595
596 if (index == length) {
597 RETURN_RESULT_OR_FAILURE(isolate, isolate->factory()->NewStringFromOneByte(
598 one_byte_buffer.ToConstVector()));
599 }
600
601 List<uc16> two_byte_buffer(length - index);
602
603 while (true) {
604 if (code <= static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
605 two_byte_buffer.Add(code);
606 } else {
607 two_byte_buffer.Add(unibrow::Utf16::LeadSurrogate(code));
608 two_byte_buffer.Add(unibrow::Utf16::TrailSurrogate(code));
609 }
610
611 if (++index == length) {
612 break;
613 }
614 code = NextCodePoint(isolate, args, index);
615 if (code < 0) {
616 return isolate->heap()->exception();
617 }
618 }
619
620 Handle<SeqTwoByteString> result;
621 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
622 isolate, result,
623 isolate->factory()->NewRawTwoByteString(one_byte_buffer.length() +
624 two_byte_buffer.length()));
625
626 CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(),
627 one_byte_buffer.length());
628 CopyChars(result->GetChars() + one_byte_buffer.length(),
629 two_byte_buffer.ToConstVector().start(), two_byte_buffer.length());
630
631 return *result;
632 }
633
634 // ES6 section 21.1.3.1 String.prototype.charAt ( pos )
Generate_StringPrototypeCharAt(CodeStubAssembler * assembler)635 void Builtins::Generate_StringPrototypeCharAt(CodeStubAssembler* assembler) {
636 typedef CodeStubAssembler::Label Label;
637 typedef compiler::Node Node;
638
639 Node* receiver = assembler->Parameter(0);
640 Node* position = assembler->Parameter(1);
641 Node* context = assembler->Parameter(4);
642
643 // Check that {receiver} is coercible to Object and convert it to a String.
644 receiver =
645 assembler->ToThisString(context, receiver, "String.prototype.charAt");
646
647 // Convert the {position} to a Smi and check that it's in bounds of the
648 // {receiver}.
649 {
650 Label return_emptystring(assembler, Label::kDeferred);
651 position = assembler->ToInteger(context, position,
652 CodeStubAssembler::kTruncateMinusZero);
653 assembler->GotoUnless(assembler->TaggedIsSmi(position),
654 &return_emptystring);
655
656 // Determine the actual length of the {receiver} String.
657 Node* receiver_length =
658 assembler->LoadObjectField(receiver, String::kLengthOffset);
659
660 // Return "" if the Smi {position} is outside the bounds of the {receiver}.
661 Label if_positioninbounds(assembler);
662 assembler->Branch(assembler->SmiAboveOrEqual(position, receiver_length),
663 &return_emptystring, &if_positioninbounds);
664
665 assembler->Bind(&return_emptystring);
666 assembler->Return(assembler->EmptyStringConstant());
667
668 assembler->Bind(&if_positioninbounds);
669 }
670
671 // Load the character code at the {position} from the {receiver}.
672 Node* code = assembler->StringCharCodeAt(receiver, position);
673
674 // And return the single character string with only that {code}.
675 Node* result = assembler->StringFromCharCode(code);
676 assembler->Return(result);
677 }
678
679 // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
Generate_StringPrototypeCharCodeAt(CodeStubAssembler * assembler)680 void Builtins::Generate_StringPrototypeCharCodeAt(
681 CodeStubAssembler* assembler) {
682 typedef CodeStubAssembler::Label Label;
683 typedef compiler::Node Node;
684
685 Node* receiver = assembler->Parameter(0);
686 Node* position = assembler->Parameter(1);
687 Node* context = assembler->Parameter(4);
688
689 // Check that {receiver} is coercible to Object and convert it to a String.
690 receiver =
691 assembler->ToThisString(context, receiver, "String.prototype.charCodeAt");
692
693 // Convert the {position} to a Smi and check that it's in bounds of the
694 // {receiver}.
695 {
696 Label return_nan(assembler, Label::kDeferred);
697 position = assembler->ToInteger(context, position,
698 CodeStubAssembler::kTruncateMinusZero);
699 assembler->GotoUnless(assembler->TaggedIsSmi(position), &return_nan);
700
701 // Determine the actual length of the {receiver} String.
702 Node* receiver_length =
703 assembler->LoadObjectField(receiver, String::kLengthOffset);
704
705 // Return NaN if the Smi {position} is outside the bounds of the {receiver}.
706 Label if_positioninbounds(assembler);
707 assembler->Branch(assembler->SmiAboveOrEqual(position, receiver_length),
708 &return_nan, &if_positioninbounds);
709
710 assembler->Bind(&return_nan);
711 assembler->Return(assembler->NaNConstant());
712
713 assembler->Bind(&if_positioninbounds);
714 }
715
716 // Load the character at the {position} from the {receiver}.
717 Node* value = assembler->StringCharCodeAt(receiver, position);
718 Node* result = assembler->SmiFromWord32(value);
719 assembler->Return(result);
720 }
721
722 // ES6 section 21.1.3.6
723 // String.prototype.endsWith ( searchString [ , endPosition ] )
BUILTIN(StringPrototypeEndsWith)724 BUILTIN(StringPrototypeEndsWith) {
725 HandleScope handle_scope(isolate);
726 TO_THIS_STRING(str, "String.prototype.endsWith");
727
728 // Check if the search string is a regExp and fail if it is.
729 Handle<Object> search = args.atOrUndefined(isolate, 1);
730 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
731 if (is_reg_exp.IsNothing()) {
732 DCHECK(isolate->has_pending_exception());
733 return isolate->heap()->exception();
734 }
735 if (is_reg_exp.FromJust()) {
736 THROW_NEW_ERROR_RETURN_FAILURE(
737 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
738 isolate->factory()->NewStringFromStaticChars(
739 "String.prototype.endsWith")));
740 }
741 Handle<String> search_string;
742 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
743 Object::ToString(isolate, search));
744
745 Handle<Object> position = args.atOrUndefined(isolate, 2);
746 int end;
747
748 if (position->IsUndefined(isolate)) {
749 end = str->length();
750 } else {
751 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
752 Object::ToInteger(isolate, position));
753 double index = std::max(position->Number(), 0.0);
754 index = std::min(index, static_cast<double>(str->length()));
755 end = static_cast<uint32_t>(index);
756 }
757
758 int start = end - search_string->length();
759 if (start < 0) return isolate->heap()->false_value();
760
761 FlatStringReader str_reader(isolate, String::Flatten(str));
762 FlatStringReader search_reader(isolate, String::Flatten(search_string));
763
764 for (int i = 0; i < search_string->length(); i++) {
765 if (str_reader.Get(start + i) != search_reader.Get(i)) {
766 return isolate->heap()->false_value();
767 }
768 }
769 return isolate->heap()->true_value();
770 }
771
772 // ES6 section 21.1.3.7
773 // String.prototype.includes ( searchString [ , position ] )
BUILTIN(StringPrototypeIncludes)774 BUILTIN(StringPrototypeIncludes) {
775 HandleScope handle_scope(isolate);
776 TO_THIS_STRING(str, "String.prototype.includes");
777
778 // Check if the search string is a regExp and fail if it is.
779 Handle<Object> search = args.atOrUndefined(isolate, 1);
780 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
781 if (is_reg_exp.IsNothing()) {
782 DCHECK(isolate->has_pending_exception());
783 return isolate->heap()->exception();
784 }
785 if (is_reg_exp.FromJust()) {
786 THROW_NEW_ERROR_RETURN_FAILURE(
787 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
788 isolate->factory()->NewStringFromStaticChars(
789 "String.prototype.includes")));
790 }
791 Handle<String> search_string;
792 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
793 Object::ToString(isolate, search));
794 Handle<Object> position;
795 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
796 isolate, position,
797 Object::ToInteger(isolate, args.atOrUndefined(isolate, 2)));
798
799 double index = std::max(position->Number(), 0.0);
800 index = std::min(index, static_cast<double>(str->length()));
801
802 int index_in_str = String::IndexOf(isolate, str, search_string,
803 static_cast<uint32_t>(index));
804 return *isolate->factory()->ToBoolean(index_in_str != -1);
805 }
806
807 // ES6 section 21.1.3.8 String.prototype.indexOf ( searchString [ , position ] )
BUILTIN(StringPrototypeIndexOf)808 BUILTIN(StringPrototypeIndexOf) {
809 HandleScope handle_scope(isolate);
810
811 return String::IndexOf(isolate, args.receiver(),
812 args.atOrUndefined(isolate, 1),
813 args.atOrUndefined(isolate, 2));
814 }
815
816 // ES6 section 21.1.3.9
817 // String.prototype.lastIndexOf ( searchString [ , position ] )
BUILTIN(StringPrototypeLastIndexOf)818 BUILTIN(StringPrototypeLastIndexOf) {
819 HandleScope handle_scope(isolate);
820 return String::LastIndexOf(isolate, args.receiver(),
821 args.atOrUndefined(isolate, 1),
822 args.atOrUndefined(isolate, 2));
823 }
824
825 // ES6 section 21.1.3.10 String.prototype.localeCompare ( that )
826 //
827 // This function is implementation specific. For now, we do not
828 // do anything locale specific.
829 // If internationalization is enabled, then i18n.js will override this function
830 // and provide the proper functionality, so this is just a fallback.
BUILTIN(StringPrototypeLocaleCompare)831 BUILTIN(StringPrototypeLocaleCompare) {
832 HandleScope handle_scope(isolate);
833 DCHECK_EQ(2, args.length());
834
835 TO_THIS_STRING(str1, "String.prototype.localeCompare");
836 Handle<String> str2;
837 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
838 isolate, str2, Object::ToString(isolate, args.at<Object>(1)));
839
840 if (str1.is_identical_to(str2)) return Smi::kZero; // Equal.
841 int str1_length = str1->length();
842 int str2_length = str2->length();
843
844 // Decide trivial cases without flattening.
845 if (str1_length == 0) {
846 if (str2_length == 0) return Smi::kZero; // Equal.
847 return Smi::FromInt(-str2_length);
848 } else {
849 if (str2_length == 0) return Smi::FromInt(str1_length);
850 }
851
852 int end = str1_length < str2_length ? str1_length : str2_length;
853
854 // No need to flatten if we are going to find the answer on the first
855 // character. At this point we know there is at least one character
856 // in each string, due to the trivial case handling above.
857 int d = str1->Get(0) - str2->Get(0);
858 if (d != 0) return Smi::FromInt(d);
859
860 str1 = String::Flatten(str1);
861 str2 = String::Flatten(str2);
862
863 DisallowHeapAllocation no_gc;
864 String::FlatContent flat1 = str1->GetFlatContent();
865 String::FlatContent flat2 = str2->GetFlatContent();
866
867 for (int i = 0; i < end; i++) {
868 if (flat1.Get(i) != flat2.Get(i)) {
869 return Smi::FromInt(flat1.Get(i) - flat2.Get(i));
870 }
871 }
872
873 return Smi::FromInt(str1_length - str2_length);
874 }
875
876 // ES6 section 21.1.3.12 String.prototype.normalize ( [form] )
877 //
878 // Simply checks the argument is valid and returns the string itself.
879 // If internationalization is enabled, then i18n.js will override this function
880 // and provide the proper functionality, so this is just a fallback.
BUILTIN(StringPrototypeNormalize)881 BUILTIN(StringPrototypeNormalize) {
882 HandleScope handle_scope(isolate);
883 TO_THIS_STRING(string, "String.prototype.normalize");
884
885 Handle<Object> form_input = args.atOrUndefined(isolate, 1);
886 if (form_input->IsUndefined(isolate)) return *string;
887
888 Handle<String> form;
889 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form,
890 Object::ToString(isolate, form_input));
891
892 if (!(String::Equals(form,
893 isolate->factory()->NewStringFromStaticChars("NFC")) ||
894 String::Equals(form,
895 isolate->factory()->NewStringFromStaticChars("NFD")) ||
896 String::Equals(form,
897 isolate->factory()->NewStringFromStaticChars("NFKC")) ||
898 String::Equals(form,
899 isolate->factory()->NewStringFromStaticChars("NFKD")))) {
900 Handle<String> valid_forms =
901 isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
902 THROW_NEW_ERROR_RETURN_FAILURE(
903 isolate,
904 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
905 }
906
907 return *string;
908 }
909
910 // ES6 section B.2.3.1 String.prototype.substr ( start, length )
Generate_StringPrototypeSubstr(CodeStubAssembler * a)911 void Builtins::Generate_StringPrototypeSubstr(CodeStubAssembler* a) {
912 typedef CodeStubAssembler::Label Label;
913 typedef compiler::Node Node;
914 typedef CodeStubAssembler::Variable Variable;
915
916 Label out(a), handle_length(a);
917
918 Variable var_start(a, MachineRepresentation::kTagged);
919 Variable var_length(a, MachineRepresentation::kTagged);
920
921 Node* const receiver = a->Parameter(0);
922 Node* const start = a->Parameter(1);
923 Node* const length = a->Parameter(2);
924 Node* const context = a->Parameter(5);
925
926 Node* const zero = a->SmiConstant(Smi::kZero);
927
928 // Check that {receiver} is coercible to Object and convert it to a String.
929 Node* const string =
930 a->ToThisString(context, receiver, "String.prototype.substr");
931
932 Node* const string_length = a->LoadStringLength(string);
933
934 // Conversions and bounds-checks for {start}.
935 {
936 Node* const start_int =
937 a->ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
938
939 Label if_issmi(a), if_isheapnumber(a, Label::kDeferred);
940 a->Branch(a->TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
941
942 a->Bind(&if_issmi);
943 {
944 Node* const length_plus_start = a->SmiAdd(string_length, start_int);
945 var_start.Bind(a->Select(a->SmiLessThan(start_int, zero),
946 a->SmiMax(length_plus_start, zero), start_int));
947 a->Goto(&handle_length);
948 }
949
950 a->Bind(&if_isheapnumber);
951 {
952 // If {start} is a heap number, it is definitely out of bounds. If it is
953 // negative, {start} = max({string_length} + {start}),0) = 0'. If it is
954 // positive, set {start} to {string_length} which ultimately results in
955 // returning an empty string.
956 Node* const float_zero = a->Float64Constant(0.);
957 Node* const start_float = a->LoadHeapNumberValue(start_int);
958 var_start.Bind(a->Select(a->Float64LessThan(start_float, float_zero),
959 zero, string_length));
960 a->Goto(&handle_length);
961 }
962 }
963
964 // Conversions and bounds-checks for {length}.
965 a->Bind(&handle_length);
966 {
967 Label if_issmi(a), if_isheapnumber(a, Label::kDeferred);
968
969 // Default to {string_length} if {length} is undefined.
970 {
971 Label if_isundefined(a, Label::kDeferred), if_isnotundefined(a);
972 a->Branch(a->WordEqual(length, a->UndefinedConstant()), &if_isundefined,
973 &if_isnotundefined);
974
975 a->Bind(&if_isundefined);
976 var_length.Bind(string_length);
977 a->Goto(&if_issmi);
978
979 a->Bind(&if_isnotundefined);
980 var_length.Bind(
981 a->ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
982 }
983
984 a->Branch(a->TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
985
986 // Set {length} to min(max({length}, 0), {string_length} - {start}
987 a->Bind(&if_issmi);
988 {
989 Node* const positive_length = a->SmiMax(var_length.value(), zero);
990
991 Node* const minimal_length = a->SmiSub(string_length, var_start.value());
992 var_length.Bind(a->SmiMin(positive_length, minimal_length));
993
994 a->GotoUnless(a->SmiLessThanOrEqual(var_length.value(), zero), &out);
995 a->Return(a->EmptyStringConstant());
996 }
997
998 a->Bind(&if_isheapnumber);
999 {
1000 // If {length} is a heap number, it is definitely out of bounds. There are
1001 // two cases according to the spec: if it is negative, "" is returned; if
1002 // it is positive, then length is set to {string_length} - {start}.
1003
1004 CSA_ASSERT(a, a->WordEqual(a->LoadMap(var_length.value()),
1005 a->HeapNumberMapConstant()));
1006
1007 Label if_isnegative(a), if_ispositive(a);
1008 Node* const float_zero = a->Float64Constant(0.);
1009 Node* const length_float = a->LoadHeapNumberValue(var_length.value());
1010 a->Branch(a->Float64LessThan(length_float, float_zero), &if_isnegative,
1011 &if_ispositive);
1012
1013 a->Bind(&if_isnegative);
1014 a->Return(a->EmptyStringConstant());
1015
1016 a->Bind(&if_ispositive);
1017 {
1018 var_length.Bind(a->SmiSub(string_length, var_start.value()));
1019 a->GotoUnless(a->SmiLessThanOrEqual(var_length.value(), zero), &out);
1020 a->Return(a->EmptyStringConstant());
1021 }
1022 }
1023 }
1024
1025 a->Bind(&out);
1026 {
1027 Node* const end = a->SmiAdd(var_start.value(), var_length.value());
1028 Node* const result = a->SubString(context, string, var_start.value(), end);
1029 a->Return(result);
1030 }
1031 }
1032
1033 namespace {
1034
ToSmiBetweenZeroAnd(CodeStubAssembler * a,compiler::Node * context,compiler::Node * value,compiler::Node * limit)1035 compiler::Node* ToSmiBetweenZeroAnd(CodeStubAssembler* a,
1036 compiler::Node* context,
1037 compiler::Node* value,
1038 compiler::Node* limit) {
1039 typedef CodeStubAssembler::Label Label;
1040 typedef compiler::Node Node;
1041 typedef CodeStubAssembler::Variable Variable;
1042
1043 Label out(a);
1044 Variable var_result(a, MachineRepresentation::kTagged);
1045
1046 Node* const value_int =
1047 a->ToInteger(context, value, CodeStubAssembler::kTruncateMinusZero);
1048
1049 Label if_issmi(a), if_isnotsmi(a, Label::kDeferred);
1050 a->Branch(a->TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
1051
1052 a->Bind(&if_issmi);
1053 {
1054 Label if_isinbounds(a), if_isoutofbounds(a, Label::kDeferred);
1055 a->Branch(a->SmiAbove(value_int, limit), &if_isoutofbounds, &if_isinbounds);
1056
1057 a->Bind(&if_isinbounds);
1058 {
1059 var_result.Bind(value_int);
1060 a->Goto(&out);
1061 }
1062
1063 a->Bind(&if_isoutofbounds);
1064 {
1065 Node* const zero = a->SmiConstant(Smi::kZero);
1066 var_result.Bind(a->Select(a->SmiLessThan(value_int, zero), zero, limit));
1067 a->Goto(&out);
1068 }
1069 }
1070
1071 a->Bind(&if_isnotsmi);
1072 {
1073 // {value} is a heap number - in this case, it is definitely out of bounds.
1074 CSA_ASSERT(a,
1075 a->WordEqual(a->LoadMap(value_int), a->HeapNumberMapConstant()));
1076
1077 Node* const float_zero = a->Float64Constant(0.);
1078 Node* const smi_zero = a->SmiConstant(Smi::kZero);
1079 Node* const value_float = a->LoadHeapNumberValue(value_int);
1080 var_result.Bind(a->Select(a->Float64LessThan(value_float, float_zero),
1081 smi_zero, limit));
1082 a->Goto(&out);
1083 }
1084
1085 a->Bind(&out);
1086 return var_result.value();
1087 }
1088
1089 } // namespace
1090
1091 // ES6 section 21.1.3.19 String.prototype.substring ( start, end )
Generate_StringPrototypeSubstring(CodeStubAssembler * a)1092 void Builtins::Generate_StringPrototypeSubstring(CodeStubAssembler* a) {
1093 typedef CodeStubAssembler::Label Label;
1094 typedef compiler::Node Node;
1095 typedef CodeStubAssembler::Variable Variable;
1096
1097 Label out(a);
1098
1099 Variable var_start(a, MachineRepresentation::kTagged);
1100 Variable var_end(a, MachineRepresentation::kTagged);
1101
1102 Node* const receiver = a->Parameter(0);
1103 Node* const start = a->Parameter(1);
1104 Node* const end = a->Parameter(2);
1105 Node* const context = a->Parameter(5);
1106
1107 // Check that {receiver} is coercible to Object and convert it to a String.
1108 Node* const string =
1109 a->ToThisString(context, receiver, "String.prototype.substring");
1110
1111 Node* const length = a->LoadStringLength(string);
1112
1113 // Conversion and bounds-checks for {start}.
1114 var_start.Bind(ToSmiBetweenZeroAnd(a, context, start, length));
1115
1116 // Conversion and bounds-checks for {end}.
1117 {
1118 var_end.Bind(length);
1119 a->GotoIf(a->WordEqual(end, a->UndefinedConstant()), &out);
1120
1121 var_end.Bind(ToSmiBetweenZeroAnd(a, context, end, length));
1122
1123 Label if_endislessthanstart(a);
1124 a->Branch(a->SmiLessThan(var_end.value(), var_start.value()),
1125 &if_endislessthanstart, &out);
1126
1127 a->Bind(&if_endislessthanstart);
1128 {
1129 Node* const tmp = var_end.value();
1130 var_end.Bind(var_start.value());
1131 var_start.Bind(tmp);
1132 a->Goto(&out);
1133 }
1134 }
1135
1136 a->Bind(&out);
1137 {
1138 Node* result =
1139 a->SubString(context, string, var_start.value(), var_end.value());
1140 a->Return(result);
1141 }
1142 }
1143
BUILTIN(StringPrototypeStartsWith)1144 BUILTIN(StringPrototypeStartsWith) {
1145 HandleScope handle_scope(isolate);
1146 TO_THIS_STRING(str, "String.prototype.startsWith");
1147
1148 // Check if the search string is a regExp and fail if it is.
1149 Handle<Object> search = args.atOrUndefined(isolate, 1);
1150 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
1151 if (is_reg_exp.IsNothing()) {
1152 DCHECK(isolate->has_pending_exception());
1153 return isolate->heap()->exception();
1154 }
1155 if (is_reg_exp.FromJust()) {
1156 THROW_NEW_ERROR_RETURN_FAILURE(
1157 isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
1158 isolate->factory()->NewStringFromStaticChars(
1159 "String.prototype.startsWith")));
1160 }
1161 Handle<String> search_string;
1162 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
1163 Object::ToString(isolate, search));
1164
1165 Handle<Object> position = args.atOrUndefined(isolate, 2);
1166 int start;
1167
1168 if (position->IsUndefined(isolate)) {
1169 start = 0;
1170 } else {
1171 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
1172 Object::ToInteger(isolate, position));
1173 double index = std::max(position->Number(), 0.0);
1174 index = std::min(index, static_cast<double>(str->length()));
1175 start = static_cast<uint32_t>(index);
1176 }
1177
1178 if (start + search_string->length() > str->length()) {
1179 return isolate->heap()->false_value();
1180 }
1181
1182 FlatStringReader str_reader(isolate, String::Flatten(str));
1183 FlatStringReader search_reader(isolate, String::Flatten(search_string));
1184
1185 for (int i = 0; i < search_string->length(); i++) {
1186 if (str_reader.Get(start + i) != search_reader.Get(i)) {
1187 return isolate->heap()->false_value();
1188 }
1189 }
1190 return isolate->heap()->true_value();
1191 }
1192
1193 // ES6 section 21.1.3.25 String.prototype.toString ()
Generate_StringPrototypeToString(CodeStubAssembler * assembler)1194 void Builtins::Generate_StringPrototypeToString(CodeStubAssembler* assembler) {
1195 typedef compiler::Node Node;
1196
1197 Node* receiver = assembler->Parameter(0);
1198 Node* context = assembler->Parameter(3);
1199
1200 Node* result = assembler->ToThisValue(
1201 context, receiver, PrimitiveType::kString, "String.prototype.toString");
1202 assembler->Return(result);
1203 }
1204
1205 // ES6 section 21.1.3.27 String.prototype.trim ()
BUILTIN(StringPrototypeTrim)1206 BUILTIN(StringPrototypeTrim) {
1207 HandleScope scope(isolate);
1208 TO_THIS_STRING(string, "String.prototype.trim");
1209 return *String::Trim(string, String::kTrim);
1210 }
1211
1212 // Non-standard WebKit extension
BUILTIN(StringPrototypeTrimLeft)1213 BUILTIN(StringPrototypeTrimLeft) {
1214 HandleScope scope(isolate);
1215 TO_THIS_STRING(string, "String.prototype.trimLeft");
1216 return *String::Trim(string, String::kTrimLeft);
1217 }
1218
1219 // Non-standard WebKit extension
BUILTIN(StringPrototypeTrimRight)1220 BUILTIN(StringPrototypeTrimRight) {
1221 HandleScope scope(isolate);
1222 TO_THIS_STRING(string, "String.prototype.trimRight");
1223 return *String::Trim(string, String::kTrimRight);
1224 }
1225
1226 // ES6 section 21.1.3.28 String.prototype.valueOf ( )
Generate_StringPrototypeValueOf(CodeStubAssembler * assembler)1227 void Builtins::Generate_StringPrototypeValueOf(CodeStubAssembler* assembler) {
1228 typedef compiler::Node Node;
1229
1230 Node* receiver = assembler->Parameter(0);
1231 Node* context = assembler->Parameter(3);
1232
1233 Node* result = assembler->ToThisValue(
1234 context, receiver, PrimitiveType::kString, "String.prototype.valueOf");
1235 assembler->Return(result);
1236 }
1237
Generate_StringPrototypeIterator(CodeStubAssembler * assembler)1238 void Builtins::Generate_StringPrototypeIterator(CodeStubAssembler* assembler) {
1239 typedef compiler::Node Node;
1240
1241 Node* receiver = assembler->Parameter(0);
1242 Node* context = assembler->Parameter(3);
1243
1244 Node* string = assembler->ToThisString(context, receiver,
1245 "String.prototype[Symbol.iterator]");
1246
1247 Node* native_context = assembler->LoadNativeContext(context);
1248 Node* map = assembler->LoadFixedArrayElement(
1249 native_context,
1250 assembler->IntPtrConstant(Context::STRING_ITERATOR_MAP_INDEX), 0,
1251 CodeStubAssembler::INTPTR_PARAMETERS);
1252 Node* iterator = assembler->Allocate(JSStringIterator::kSize);
1253 assembler->StoreMapNoWriteBarrier(iterator, map);
1254 assembler->StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset,
1255 Heap::kEmptyFixedArrayRootIndex);
1256 assembler->StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
1257 Heap::kEmptyFixedArrayRootIndex);
1258 assembler->StoreObjectFieldNoWriteBarrier(
1259 iterator, JSStringIterator::kStringOffset, string);
1260 Node* index = assembler->SmiConstant(Smi::kZero);
1261 assembler->StoreObjectFieldNoWriteBarrier(
1262 iterator, JSStringIterator::kNextIndexOffset, index);
1263 assembler->Return(iterator);
1264 }
1265
1266 namespace {
1267
1268 // Return the |word32| codepoint at {index}. Supports SeqStrings and
1269 // ExternalStrings.
LoadSurrogatePairInternal(CodeStubAssembler * assembler,compiler::Node * string,compiler::Node * length,compiler::Node * index,UnicodeEncoding encoding)1270 compiler::Node* LoadSurrogatePairInternal(CodeStubAssembler* assembler,
1271 compiler::Node* string,
1272 compiler::Node* length,
1273 compiler::Node* index,
1274 UnicodeEncoding encoding) {
1275 typedef CodeStubAssembler::Label Label;
1276 typedef compiler::Node Node;
1277 typedef CodeStubAssembler::Variable Variable;
1278 Label handle_surrogate_pair(assembler), return_result(assembler);
1279 Variable var_result(assembler, MachineRepresentation::kWord32);
1280 Variable var_trail(assembler, MachineRepresentation::kWord16);
1281 var_result.Bind(assembler->StringCharCodeAt(string, index));
1282 var_trail.Bind(assembler->Int32Constant(0));
1283
1284 assembler->GotoIf(assembler->Word32NotEqual(
1285 assembler->Word32And(var_result.value(),
1286 assembler->Int32Constant(0xFC00)),
1287 assembler->Int32Constant(0xD800)),
1288 &return_result);
1289 Node* next_index =
1290 assembler->SmiAdd(index, assembler->SmiConstant(Smi::FromInt(1)));
1291
1292 assembler->GotoUnless(assembler->SmiLessThan(next_index, length),
1293 &return_result);
1294 var_trail.Bind(assembler->StringCharCodeAt(string, next_index));
1295 assembler->Branch(assembler->Word32Equal(
1296 assembler->Word32And(var_trail.value(),
1297 assembler->Int32Constant(0xFC00)),
1298 assembler->Int32Constant(0xDC00)),
1299 &handle_surrogate_pair, &return_result);
1300
1301 assembler->Bind(&handle_surrogate_pair);
1302 {
1303 Node* lead = var_result.value();
1304 Node* trail = var_trail.value();
1305
1306 // Check that this path is only taken if a surrogate pair is found
1307 CSA_SLOW_ASSERT(assembler, assembler->Uint32GreaterThanOrEqual(
1308 lead, assembler->Int32Constant(0xD800)));
1309 CSA_SLOW_ASSERT(assembler, assembler->Uint32LessThan(
1310 lead, assembler->Int32Constant(0xDC00)));
1311 CSA_SLOW_ASSERT(assembler, assembler->Uint32GreaterThanOrEqual(
1312 trail, assembler->Int32Constant(0xDC00)));
1313 CSA_SLOW_ASSERT(assembler, assembler->Uint32LessThan(
1314 trail, assembler->Int32Constant(0xE000)));
1315
1316 switch (encoding) {
1317 case UnicodeEncoding::UTF16:
1318 var_result.Bind(assembler->WordOr(
1319 // Need to swap the order for big-endian platforms
1320 #if V8_TARGET_BIG_ENDIAN
1321 assembler->WordShl(lead, assembler->Int32Constant(16)), trail));
1322 #else
1323 assembler->WordShl(trail, assembler->Int32Constant(16)), lead));
1324 #endif
1325 break;
1326
1327 case UnicodeEncoding::UTF32: {
1328 // Convert UTF16 surrogate pair into |word32| code point, encoded as
1329 // UTF32.
1330 Node* surrogate_offset =
1331 assembler->Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
1332
1333 // (lead << 10) + trail + SURROGATE_OFFSET
1334 var_result.Bind(assembler->Int32Add(
1335 assembler->WordShl(lead, assembler->Int32Constant(10)),
1336 assembler->Int32Add(trail, surrogate_offset)));
1337 break;
1338 }
1339 }
1340 assembler->Goto(&return_result);
1341 }
1342
1343 assembler->Bind(&return_result);
1344 return var_result.value();
1345 }
1346
LoadSurrogatePairAt(CodeStubAssembler * assembler,compiler::Node * string,compiler::Node * length,compiler::Node * index)1347 compiler::Node* LoadSurrogatePairAt(CodeStubAssembler* assembler,
1348 compiler::Node* string,
1349 compiler::Node* length,
1350 compiler::Node* index) {
1351 return LoadSurrogatePairInternal(assembler, string, length, index,
1352 UnicodeEncoding::UTF16);
1353 }
1354
1355 } // namespace
1356
Generate_StringIteratorPrototypeNext(CodeStubAssembler * assembler)1357 void Builtins::Generate_StringIteratorPrototypeNext(
1358 CodeStubAssembler* assembler) {
1359 typedef CodeStubAssembler::Label Label;
1360 typedef compiler::Node Node;
1361 typedef CodeStubAssembler::Variable Variable;
1362
1363 Variable var_value(assembler, MachineRepresentation::kTagged);
1364 Variable var_done(assembler, MachineRepresentation::kTagged);
1365
1366 var_value.Bind(assembler->UndefinedConstant());
1367 var_done.Bind(assembler->BooleanConstant(true));
1368
1369 Label throw_bad_receiver(assembler), next_codepoint(assembler),
1370 return_result(assembler);
1371
1372 Node* iterator = assembler->Parameter(0);
1373 Node* context = assembler->Parameter(3);
1374
1375 assembler->GotoIf(assembler->TaggedIsSmi(iterator), &throw_bad_receiver);
1376 assembler->GotoUnless(
1377 assembler->WordEqual(assembler->LoadInstanceType(iterator),
1378 assembler->Int32Constant(JS_STRING_ITERATOR_TYPE)),
1379 &throw_bad_receiver);
1380
1381 Node* string =
1382 assembler->LoadObjectField(iterator, JSStringIterator::kStringOffset);
1383 Node* position =
1384 assembler->LoadObjectField(iterator, JSStringIterator::kNextIndexOffset);
1385 Node* length = assembler->LoadObjectField(string, String::kLengthOffset);
1386
1387 assembler->Branch(assembler->SmiLessThan(position, length), &next_codepoint,
1388 &return_result);
1389
1390 assembler->Bind(&next_codepoint);
1391 {
1392 Node* ch = LoadSurrogatePairAt(assembler, string, length, position);
1393 Node* value = assembler->StringFromCodePoint(ch, UnicodeEncoding::UTF16);
1394 var_value.Bind(value);
1395 Node* length = assembler->LoadObjectField(value, String::kLengthOffset);
1396 assembler->StoreObjectFieldNoWriteBarrier(
1397 iterator, JSStringIterator::kNextIndexOffset,
1398 assembler->SmiAdd(position, length));
1399 var_done.Bind(assembler->BooleanConstant(false));
1400 assembler->Goto(&return_result);
1401 }
1402
1403 assembler->Bind(&return_result);
1404 {
1405 Node* native_context = assembler->LoadNativeContext(context);
1406 Node* map = assembler->LoadFixedArrayElement(
1407 native_context,
1408 assembler->IntPtrConstant(Context::ITERATOR_RESULT_MAP_INDEX), 0,
1409 CodeStubAssembler::INTPTR_PARAMETERS);
1410 Node* result = assembler->Allocate(JSIteratorResult::kSize);
1411 assembler->StoreMapNoWriteBarrier(result, map);
1412 assembler->StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset,
1413 Heap::kEmptyFixedArrayRootIndex);
1414 assembler->StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset,
1415 Heap::kEmptyFixedArrayRootIndex);
1416 assembler->StoreObjectFieldNoWriteBarrier(
1417 result, JSIteratorResult::kValueOffset, var_value.value());
1418 assembler->StoreObjectFieldNoWriteBarrier(
1419 result, JSIteratorResult::kDoneOffset, var_done.value());
1420 assembler->Return(result);
1421 }
1422
1423 assembler->Bind(&throw_bad_receiver);
1424 {
1425 // The {receiver} is not a valid JSGeneratorObject.
1426 Node* result = assembler->CallRuntime(
1427 Runtime::kThrowIncompatibleMethodReceiver, context,
1428 assembler->HeapConstant(assembler->factory()->NewStringFromAsciiChecked(
1429 "String Iterator.prototype.next", TENURED)),
1430 iterator);
1431 assembler->Return(result); // Never reached.
1432 }
1433 }
1434
1435 } // namespace internal
1436 } // namespace v8
1437