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