1 // Copyright 2017 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-string-gen.h"
6
7 #include "src/builtins/builtins-regexp-gen.h"
8 #include "src/builtins/builtins-utils-gen.h"
9 #include "src/builtins/builtins.h"
10 #include "src/code-factory.h"
11 #include "src/heap/factory-inl.h"
12 #include "src/objects.h"
13
14 namespace v8 {
15 namespace internal {
16
17 typedef compiler::Node Node;
18 template <class T>
19 using TNode = compiler::TNode<T>;
20
DirectStringData(Node * string,Node * string_instance_type)21 Node* StringBuiltinsAssembler::DirectStringData(Node* string,
22 Node* string_instance_type) {
23 // Compute the effective offset of the first character.
24 VARIABLE(var_data, MachineType::PointerRepresentation());
25 Label if_sequential(this), if_external(this), if_join(this);
26 Branch(Word32Equal(Word32And(string_instance_type,
27 Int32Constant(kStringRepresentationMask)),
28 Int32Constant(kSeqStringTag)),
29 &if_sequential, &if_external);
30
31 BIND(&if_sequential);
32 {
33 var_data.Bind(IntPtrAdd(
34 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
35 BitcastTaggedToWord(string)));
36 Goto(&if_join);
37 }
38
39 BIND(&if_external);
40 {
41 // This is only valid for ExternalStrings where the resource data
42 // pointer is cached (i.e. no short external strings).
43 CSA_ASSERT(
44 this, Word32NotEqual(Word32And(string_instance_type,
45 Int32Constant(kShortExternalStringMask)),
46 Int32Constant(kShortExternalStringTag)));
47 var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
48 MachineType::Pointer()));
49 Goto(&if_join);
50 }
51
52 BIND(&if_join);
53 return var_data.value();
54 }
55
DispatchOnStringEncodings(Node * const lhs_instance_type,Node * const rhs_instance_type,Label * if_one_one,Label * if_one_two,Label * if_two_one,Label * if_two_two)56 void StringBuiltinsAssembler::DispatchOnStringEncodings(
57 Node* const lhs_instance_type, Node* const rhs_instance_type,
58 Label* if_one_one, Label* if_one_two, Label* if_two_one,
59 Label* if_two_two) {
60 STATIC_ASSERT(kStringEncodingMask == 0x8);
61 STATIC_ASSERT(kTwoByteStringTag == 0x0);
62 STATIC_ASSERT(kOneByteStringTag == 0x8);
63
64 // First combine the encodings.
65
66 Node* const encoding_mask = Int32Constant(kStringEncodingMask);
67 Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask);
68 Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
69
70 Node* const combined_encodings =
71 Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
72
73 // Then dispatch on the combined encoding.
74
75 Label unreachable(this, Label::kDeferred);
76
77 int32_t values[] = {
78 kOneByteStringTag | (kOneByteStringTag >> 1),
79 kOneByteStringTag | (kTwoByteStringTag >> 1),
80 kTwoByteStringTag | (kOneByteStringTag >> 1),
81 kTwoByteStringTag | (kTwoByteStringTag >> 1),
82 };
83 Label* labels[] = {
84 if_one_one, if_one_two, if_two_one, if_two_two,
85 };
86
87 STATIC_ASSERT(arraysize(values) == arraysize(labels));
88 Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
89
90 BIND(&unreachable);
91 Unreachable();
92 }
93
94 template <typename SubjectChar, typename PatternChar>
CallSearchStringRaw(Node * const subject_ptr,Node * const subject_length,Node * const search_ptr,Node * const search_length,Node * const start_position)95 Node* StringBuiltinsAssembler::CallSearchStringRaw(Node* const subject_ptr,
96 Node* const subject_length,
97 Node* const search_ptr,
98 Node* const search_length,
99 Node* const start_position) {
100 Node* const function_addr = ExternalConstant(
101 ExternalReference::search_string_raw<SubjectChar, PatternChar>());
102 Node* const isolate_ptr =
103 ExternalConstant(ExternalReference::isolate_address(isolate()));
104
105 MachineType type_ptr = MachineType::Pointer();
106 MachineType type_intptr = MachineType::IntPtr();
107
108 Node* const result = CallCFunction6(
109 type_intptr, type_ptr, type_ptr, type_intptr, type_ptr, type_intptr,
110 type_intptr, function_addr, isolate_ptr, subject_ptr, subject_length,
111 search_ptr, search_length, start_position);
112
113 return result;
114 }
115
PointerToStringDataAtIndex(Node * const string_data,Node * const index,String::Encoding encoding)116 Node* StringBuiltinsAssembler::PointerToStringDataAtIndex(
117 Node* const string_data, Node* const index, String::Encoding encoding) {
118 const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
119 ? UINT8_ELEMENTS
120 : UINT16_ELEMENTS;
121 Node* const offset_in_bytes =
122 ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
123 return IntPtrAdd(string_data, offset_in_bytes);
124 }
125
GenerateStringEqual(Node * context,Node * left,Node * right)126 void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
127 Node* right) {
128 VARIABLE(var_left, MachineRepresentation::kTagged, left);
129 VARIABLE(var_right, MachineRepresentation::kTagged, right);
130 Label if_equal(this), if_notequal(this), if_indirect(this, Label::kDeferred),
131 restart(this, {&var_left, &var_right});
132
133 TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(left);
134 TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(right);
135
136 // Strings with different lengths cannot be equal.
137 GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
138
139 Goto(&restart);
140 BIND(&restart);
141 Node* lhs = var_left.value();
142 Node* rhs = var_right.value();
143
144 Node* lhs_instance_type = LoadInstanceType(lhs);
145 Node* rhs_instance_type = LoadInstanceType(rhs);
146
147 StringEqual_Core(context, lhs, lhs_instance_type, rhs, rhs_instance_type,
148 lhs_length, &if_equal, &if_notequal, &if_indirect);
149
150 BIND(&if_indirect);
151 {
152 // Try to unwrap indirect strings, restart the above attempt on success.
153 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
154 rhs_instance_type, &restart);
155
156 TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs);
157 }
158
159 BIND(&if_equal);
160 Return(TrueConstant());
161
162 BIND(&if_notequal);
163 Return(FalseConstant());
164 }
165
StringEqual_Core(Node * context,Node * lhs,Node * lhs_instance_type,Node * rhs,Node * rhs_instance_type,TNode<IntPtrT> length,Label * if_equal,Label * if_not_equal,Label * if_indirect)166 void StringBuiltinsAssembler::StringEqual_Core(
167 Node* context, Node* lhs, Node* lhs_instance_type, Node* rhs,
168 Node* rhs_instance_type, TNode<IntPtrT> length, Label* if_equal,
169 Label* if_not_equal, Label* if_indirect) {
170 CSA_ASSERT(this, IsString(lhs));
171 CSA_ASSERT(this, IsString(rhs));
172 CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(lhs), length));
173 CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(rhs), length));
174 // Fast check to see if {lhs} and {rhs} refer to the same String object.
175 GotoIf(WordEqual(lhs, rhs), if_equal);
176
177 // Combine the instance types into a single 16-bit value, so we can check
178 // both of them at once.
179 Node* both_instance_types = Word32Or(
180 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
181
182 // Check if both {lhs} and {rhs} are internalized. Since we already know
183 // that they're not the same object, they're not equal in that case.
184 int const kBothInternalizedMask =
185 kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
186 int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
187 GotoIf(Word32Equal(Word32And(both_instance_types,
188 Int32Constant(kBothInternalizedMask)),
189 Int32Constant(kBothInternalizedTag)),
190 if_not_equal);
191
192 // Check if both {lhs} and {rhs} are direct strings, and that in case of
193 // ExternalStrings the data pointer is cached.
194 STATIC_ASSERT(kShortExternalStringTag != 0);
195 STATIC_ASSERT(kIsIndirectStringTag != 0);
196 int const kBothDirectStringMask =
197 kIsIndirectStringMask | kShortExternalStringMask |
198 ((kIsIndirectStringMask | kShortExternalStringMask) << 8);
199 GotoIfNot(Word32Equal(Word32And(both_instance_types,
200 Int32Constant(kBothDirectStringMask)),
201 Int32Constant(0)),
202 if_indirect);
203
204 // Dispatch based on the {lhs} and {rhs} string encoding.
205 int const kBothStringEncodingMask =
206 kStringEncodingMask | (kStringEncodingMask << 8);
207 int const kOneOneByteStringTag = kOneByteStringTag | (kOneByteStringTag << 8);
208 int const kTwoTwoByteStringTag = kTwoByteStringTag | (kTwoByteStringTag << 8);
209 int const kOneTwoByteStringTag = kOneByteStringTag | (kTwoByteStringTag << 8);
210 Label if_oneonebytestring(this), if_twotwobytestring(this),
211 if_onetwobytestring(this), if_twoonebytestring(this);
212 Node* masked_instance_types =
213 Word32And(both_instance_types, Int32Constant(kBothStringEncodingMask));
214 GotoIf(
215 Word32Equal(masked_instance_types, Int32Constant(kOneOneByteStringTag)),
216 &if_oneonebytestring);
217 GotoIf(
218 Word32Equal(masked_instance_types, Int32Constant(kTwoTwoByteStringTag)),
219 &if_twotwobytestring);
220 Branch(
221 Word32Equal(masked_instance_types, Int32Constant(kOneTwoByteStringTag)),
222 &if_onetwobytestring, &if_twoonebytestring);
223
224 BIND(&if_oneonebytestring);
225 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
226 rhs_instance_type, MachineType::Uint8(), length, if_equal,
227 if_not_equal);
228
229 BIND(&if_twotwobytestring);
230 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
231 rhs_instance_type, MachineType::Uint16(), length, if_equal,
232 if_not_equal);
233
234 BIND(&if_onetwobytestring);
235 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
236 rhs_instance_type, MachineType::Uint16(), length, if_equal,
237 if_not_equal);
238
239 BIND(&if_twoonebytestring);
240 StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
241 rhs_instance_type, MachineType::Uint8(), length, if_equal,
242 if_not_equal);
243 }
244
StringEqual_Loop(Node * lhs,Node * lhs_instance_type,MachineType lhs_type,Node * rhs,Node * rhs_instance_type,MachineType rhs_type,TNode<IntPtrT> length,Label * if_equal,Label * if_not_equal)245 void StringBuiltinsAssembler::StringEqual_Loop(
246 Node* lhs, Node* lhs_instance_type, MachineType lhs_type, Node* rhs,
247 Node* rhs_instance_type, MachineType rhs_type, TNode<IntPtrT> length,
248 Label* if_equal, Label* if_not_equal) {
249 CSA_ASSERT(this, IsString(lhs));
250 CSA_ASSERT(this, IsString(rhs));
251 CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(lhs), length));
252 CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(rhs), length));
253
254 // Compute the effective offset of the first character.
255 Node* lhs_data = DirectStringData(lhs, lhs_instance_type);
256 Node* rhs_data = DirectStringData(rhs, rhs_instance_type);
257
258 // Loop over the {lhs} and {rhs} strings to see if they are equal.
259 TVARIABLE(IntPtrT, var_offset, IntPtrConstant(0));
260 Label loop(this, &var_offset);
261 Goto(&loop);
262 BIND(&loop);
263 {
264 // If {offset} equals {end}, no difference was found, so the
265 // strings are equal.
266 GotoIf(WordEqual(var_offset.value(), length), if_equal);
267
268 // Load the next characters from {lhs} and {rhs}.
269 Node* lhs_value =
270 Load(lhs_type, lhs_data,
271 WordShl(var_offset.value(),
272 ElementSizeLog2Of(lhs_type.representation())));
273 Node* rhs_value =
274 Load(rhs_type, rhs_data,
275 WordShl(var_offset.value(),
276 ElementSizeLog2Of(rhs_type.representation())));
277
278 // Check if the characters match.
279 GotoIf(Word32NotEqual(lhs_value, rhs_value), if_not_equal);
280
281 // Advance to next character.
282 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
283 Goto(&loop);
284 }
285 }
286
Generate_StringAdd(StringAddFlags flags,PretenureFlag pretenure_flag,Node * context,Node * left,Node * right)287 void StringBuiltinsAssembler::Generate_StringAdd(StringAddFlags flags,
288 PretenureFlag pretenure_flag,
289 Node* context, Node* left,
290 Node* right) {
291 switch (flags) {
292 case STRING_ADD_CONVERT_LEFT: {
293 // TODO(danno): The ToString and JSReceiverToPrimitive below could be
294 // combined to avoid duplicate smi and instance type checks.
295 left = ToString(context, JSReceiverToPrimitive(context, left));
296 Callable callable = CodeFactory::StringAdd(
297 isolate(), STRING_ADD_CHECK_NONE, pretenure_flag);
298 TailCallStub(callable, context, left, right);
299 break;
300 }
301 case STRING_ADD_CONVERT_RIGHT: {
302 // TODO(danno): The ToString and JSReceiverToPrimitive below could be
303 // combined to avoid duplicate smi and instance type checks.
304 right = ToString(context, JSReceiverToPrimitive(context, right));
305 Callable callable = CodeFactory::StringAdd(
306 isolate(), STRING_ADD_CHECK_NONE, pretenure_flag);
307 TailCallStub(callable, context, left, right);
308 break;
309 }
310 case STRING_ADD_CHECK_NONE: {
311 CodeStubAssembler::AllocationFlag allocation_flags =
312 (pretenure_flag == TENURED) ? CodeStubAssembler::kPretenured
313 : CodeStubAssembler::kNone;
314 Return(StringAdd(context, CAST(left), CAST(right), allocation_flags));
315 break;
316 }
317 }
318 }
319
TF_BUILTIN(StringAdd_CheckNone_NotTenured,StringBuiltinsAssembler)320 TF_BUILTIN(StringAdd_CheckNone_NotTenured, StringBuiltinsAssembler) {
321 Node* left = Parameter(Descriptor::kLeft);
322 Node* right = Parameter(Descriptor::kRight);
323 Node* context = Parameter(Descriptor::kContext);
324 Generate_StringAdd(STRING_ADD_CHECK_NONE, NOT_TENURED, context, left, right);
325 }
326
TF_BUILTIN(StringAdd_CheckNone_Tenured,StringBuiltinsAssembler)327 TF_BUILTIN(StringAdd_CheckNone_Tenured, StringBuiltinsAssembler) {
328 Node* left = Parameter(Descriptor::kLeft);
329 Node* right = Parameter(Descriptor::kRight);
330 Node* context = Parameter(Descriptor::kContext);
331 Generate_StringAdd(STRING_ADD_CHECK_NONE, TENURED, context, left, right);
332 }
333
TF_BUILTIN(StringAdd_ConvertLeft_NotTenured,StringBuiltinsAssembler)334 TF_BUILTIN(StringAdd_ConvertLeft_NotTenured, StringBuiltinsAssembler) {
335 Node* left = Parameter(Descriptor::kLeft);
336 Node* right = Parameter(Descriptor::kRight);
337 Node* context = Parameter(Descriptor::kContext);
338 Generate_StringAdd(STRING_ADD_CONVERT_LEFT, NOT_TENURED, context, left,
339 right);
340 }
341
TF_BUILTIN(StringAdd_ConvertRight_NotTenured,StringBuiltinsAssembler)342 TF_BUILTIN(StringAdd_ConvertRight_NotTenured, StringBuiltinsAssembler) {
343 Node* left = Parameter(Descriptor::kLeft);
344 Node* right = Parameter(Descriptor::kRight);
345 Node* context = Parameter(Descriptor::kContext);
346 Generate_StringAdd(STRING_ADD_CONVERT_RIGHT, NOT_TENURED, context, left,
347 right);
348 }
349
GenerateStringAt(char const * method_name,TNode<Context> context,Node * receiver,TNode<Object> maybe_position,TNode<Object> default_return,StringAtAccessor accessor)350 void StringBuiltinsAssembler::GenerateStringAt(char const* method_name,
351 TNode<Context> context,
352 Node* receiver,
353 TNode<Object> maybe_position,
354 TNode<Object> default_return,
355 StringAtAccessor accessor) {
356 // Check that {receiver} is coercible to Object and convert it to a String.
357 TNode<String> string = ToThisString(context, receiver, method_name);
358
359 // Convert the {position} to a Smi and check that it's in bounds of the
360 // {string}.
361 Label if_outofbounds(this, Label::kDeferred);
362 TNode<Number> position = ToInteger_Inline(
363 context, maybe_position, CodeStubAssembler::kTruncateMinusZero);
364 GotoIfNot(TaggedIsSmi(position), &if_outofbounds);
365 TNode<IntPtrT> index = SmiUntag(CAST(position));
366 TNode<IntPtrT> length = LoadStringLengthAsWord(string);
367 GotoIfNot(UintPtrLessThan(index, length), &if_outofbounds);
368 TNode<Object> result = accessor(string, length, index);
369 Return(result);
370
371 BIND(&if_outofbounds);
372 Return(default_return);
373 }
374
GenerateStringRelationalComparison(Node * context,Node * left,Node * right,Operation op)375 void StringBuiltinsAssembler::GenerateStringRelationalComparison(Node* context,
376 Node* left,
377 Node* right,
378 Operation op) {
379 VARIABLE(var_left, MachineRepresentation::kTagged, left);
380 VARIABLE(var_right, MachineRepresentation::kTagged, right);
381
382 Variable* input_vars[2] = {&var_left, &var_right};
383 Label if_less(this), if_equal(this), if_greater(this);
384 Label restart(this, 2, input_vars);
385 Goto(&restart);
386 BIND(&restart);
387
388 Node* lhs = var_left.value();
389 Node* rhs = var_right.value();
390 // Fast check to see if {lhs} and {rhs} refer to the same String object.
391 GotoIf(WordEqual(lhs, rhs), &if_equal);
392
393 // Load instance types of {lhs} and {rhs}.
394 Node* lhs_instance_type = LoadInstanceType(lhs);
395 Node* rhs_instance_type = LoadInstanceType(rhs);
396
397 // Combine the instance types into a single 16-bit value, so we can check
398 // both of them at once.
399 Node* both_instance_types = Word32Or(
400 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
401
402 // Check that both {lhs} and {rhs} are flat one-byte strings.
403 int const kBothSeqOneByteStringMask =
404 kStringEncodingMask | kStringRepresentationMask |
405 ((kStringEncodingMask | kStringRepresentationMask) << 8);
406 int const kBothSeqOneByteStringTag =
407 kOneByteStringTag | kSeqStringTag |
408 ((kOneByteStringTag | kSeqStringTag) << 8);
409 Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this);
410 Branch(Word32Equal(Word32And(both_instance_types,
411 Int32Constant(kBothSeqOneByteStringMask)),
412 Int32Constant(kBothSeqOneByteStringTag)),
413 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
414
415 BIND(&if_bothonebyteseqstrings);
416 {
417 // Load the length of {lhs} and {rhs}.
418 TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(lhs);
419 TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(rhs);
420
421 // Determine the minimum length.
422 TNode<IntPtrT> length = IntPtrMin(lhs_length, rhs_length);
423
424 // Compute the effective offset of the first character.
425 TNode<IntPtrT> begin =
426 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag);
427
428 // Compute the first offset after the string from the length.
429 TNode<IntPtrT> end = IntPtrAdd(begin, length);
430
431 // Loop over the {lhs} and {rhs} strings to see if they are equal.
432 TVARIABLE(IntPtrT, var_offset, begin);
433 Label loop(this, &var_offset);
434 Goto(&loop);
435 BIND(&loop);
436 {
437 // Check if {offset} equals {end}.
438 Label if_done(this), if_notdone(this);
439 Branch(WordEqual(var_offset.value(), end), &if_done, &if_notdone);
440
441 BIND(&if_notdone);
442 {
443 // Load the next characters from {lhs} and {rhs}.
444 Node* lhs_value = Load(MachineType::Uint8(), lhs, var_offset.value());
445 Node* rhs_value = Load(MachineType::Uint8(), rhs, var_offset.value());
446
447 // Check if the characters match.
448 Label if_valueissame(this), if_valueisnotsame(this);
449 Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame,
450 &if_valueisnotsame);
451
452 BIND(&if_valueissame);
453 {
454 // Advance to next character.
455 var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
456 }
457 Goto(&loop);
458
459 BIND(&if_valueisnotsame);
460 Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater);
461 }
462
463 BIND(&if_done);
464 {
465 // All characters up to the min length are equal, decide based on
466 // string length.
467 GotoIf(IntPtrEqual(lhs_length, rhs_length), &if_equal);
468 Branch(IntPtrLessThan(lhs_length, rhs_length), &if_less, &if_greater);
469 }
470 }
471 }
472
473 BIND(&if_notbothonebyteseqstrings);
474 {
475 // Try to unwrap indirect strings, restart the above attempt on success.
476 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
477 rhs_instance_type, &restart);
478 // TODO(bmeurer): Add support for two byte string relational comparisons.
479 switch (op) {
480 case Operation::kLessThan:
481 TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs);
482 break;
483 case Operation::kLessThanOrEqual:
484 TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs);
485 break;
486 case Operation::kGreaterThan:
487 TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs);
488 break;
489 case Operation::kGreaterThanOrEqual:
490 TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, rhs);
491 break;
492 default:
493 UNREACHABLE();
494 }
495 }
496
497 BIND(&if_less);
498 switch (op) {
499 case Operation::kLessThan:
500 case Operation::kLessThanOrEqual:
501 Return(TrueConstant());
502 break;
503
504 case Operation::kGreaterThan:
505 case Operation::kGreaterThanOrEqual:
506 Return(FalseConstant());
507 break;
508 default:
509 UNREACHABLE();
510 }
511
512 BIND(&if_equal);
513 switch (op) {
514 case Operation::kLessThan:
515 case Operation::kGreaterThan:
516 Return(FalseConstant());
517 break;
518
519 case Operation::kLessThanOrEqual:
520 case Operation::kGreaterThanOrEqual:
521 Return(TrueConstant());
522 break;
523 default:
524 UNREACHABLE();
525 }
526
527 BIND(&if_greater);
528 switch (op) {
529 case Operation::kLessThan:
530 case Operation::kLessThanOrEqual:
531 Return(FalseConstant());
532 break;
533
534 case Operation::kGreaterThan:
535 case Operation::kGreaterThanOrEqual:
536 Return(TrueConstant());
537 break;
538 default:
539 UNREACHABLE();
540 }
541 }
542
TF_BUILTIN(StringEqual,StringBuiltinsAssembler)543 TF_BUILTIN(StringEqual, StringBuiltinsAssembler) {
544 Node* context = Parameter(Descriptor::kContext);
545 Node* left = Parameter(Descriptor::kLeft);
546 Node* right = Parameter(Descriptor::kRight);
547 GenerateStringEqual(context, left, right);
548 }
549
TF_BUILTIN(StringLessThan,StringBuiltinsAssembler)550 TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) {
551 Node* context = Parameter(Descriptor::kContext);
552 Node* left = Parameter(Descriptor::kLeft);
553 Node* right = Parameter(Descriptor::kRight);
554 GenerateStringRelationalComparison(context, left, right,
555 Operation::kLessThan);
556 }
557
TF_BUILTIN(StringLessThanOrEqual,StringBuiltinsAssembler)558 TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
559 Node* context = Parameter(Descriptor::kContext);
560 Node* left = Parameter(Descriptor::kLeft);
561 Node* right = Parameter(Descriptor::kRight);
562 GenerateStringRelationalComparison(context, left, right,
563 Operation::kLessThanOrEqual);
564 }
565
TF_BUILTIN(StringGreaterThan,StringBuiltinsAssembler)566 TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) {
567 Node* context = Parameter(Descriptor::kContext);
568 Node* left = Parameter(Descriptor::kLeft);
569 Node* right = Parameter(Descriptor::kRight);
570 GenerateStringRelationalComparison(context, left, right,
571 Operation::kGreaterThan);
572 }
573
TF_BUILTIN(StringGreaterThanOrEqual,StringBuiltinsAssembler)574 TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
575 Node* context = Parameter(Descriptor::kContext);
576 Node* left = Parameter(Descriptor::kLeft);
577 Node* right = Parameter(Descriptor::kRight);
578 GenerateStringRelationalComparison(context, left, right,
579 Operation::kGreaterThanOrEqual);
580 }
581
TF_BUILTIN(StringCharAt,StringBuiltinsAssembler)582 TF_BUILTIN(StringCharAt, StringBuiltinsAssembler) {
583 Node* receiver = Parameter(Descriptor::kReceiver);
584 Node* position = Parameter(Descriptor::kPosition);
585
586 // Load the character code at the {position} from the {receiver}.
587 TNode<Int32T> code = StringCharCodeAt(receiver, position);
588
589 // And return the single character string with only that {code}
590 TNode<String> result = StringFromSingleCharCode(code);
591 Return(result);
592 }
593
TF_BUILTIN(StringCodePointAtUTF16,StringBuiltinsAssembler)594 TF_BUILTIN(StringCodePointAtUTF16, StringBuiltinsAssembler) {
595 Node* receiver = Parameter(Descriptor::kReceiver);
596 Node* position = Parameter(Descriptor::kPosition);
597 // TODO(sigurds) Figure out if passing length as argument pays off.
598 TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
599 // Load the character code at the {position} from the {receiver}.
600 TNode<Int32T> code =
601 LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF16);
602 // And return it as TaggedSigned value.
603 // TODO(turbofan): Allow builtins to return values untagged.
604 TNode<Smi> result = SmiFromInt32(code);
605 Return(result);
606 }
607
TF_BUILTIN(StringCodePointAtUTF32,StringBuiltinsAssembler)608 TF_BUILTIN(StringCodePointAtUTF32, StringBuiltinsAssembler) {
609 Node* receiver = Parameter(Descriptor::kReceiver);
610 Node* position = Parameter(Descriptor::kPosition);
611
612 // TODO(sigurds) Figure out if passing length as argument pays off.
613 TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
614 // Load the character code at the {position} from the {receiver}.
615 TNode<Int32T> code =
616 LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF32);
617 // And return it as TaggedSigned value.
618 // TODO(turbofan): Allow builtins to return values untagged.
619 TNode<Smi> result = SmiFromInt32(code);
620 Return(result);
621 }
622
623 // -----------------------------------------------------------------------------
624 // ES6 section 21.1 String Objects
625
626 // ES6 #sec-string.fromcharcode
TF_BUILTIN(StringFromCharCode,CodeStubAssembler)627 TF_BUILTIN(StringFromCharCode, CodeStubAssembler) {
628 // TODO(ishell): use constants from Descriptor once the JSFunction linkage
629 // arguments are reordered.
630 TNode<Int32T> argc =
631 UncheckedCast<Int32T>(Parameter(BuiltinDescriptor::kArgumentsCount));
632 Node* context = Parameter(BuiltinDescriptor::kContext);
633
634 CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
635 TNode<Smi> smi_argc = SmiTag(arguments.GetLength(INTPTR_PARAMETERS));
636 // Check if we have exactly one argument (plus the implicit receiver), i.e.
637 // if the parent frame is not an arguments adaptor frame.
638 Label if_oneargument(this), if_notoneargument(this);
639 Branch(Word32Equal(argc, Int32Constant(1)), &if_oneargument,
640 &if_notoneargument);
641
642 BIND(&if_oneargument);
643 {
644 // Single argument case, perform fast single character string cache lookup
645 // for one-byte code units, or fall back to creating a single character
646 // string on the fly otherwise.
647 Node* code = arguments.AtIndex(0);
648 Node* code32 = TruncateTaggedToWord32(context, code);
649 TNode<Int32T> code16 =
650 Signed(Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)));
651 Node* result = StringFromSingleCharCode(code16);
652 arguments.PopAndReturn(result);
653 }
654
655 Node* code16 = nullptr;
656 BIND(&if_notoneargument);
657 {
658 Label two_byte(this);
659 // Assume that the resulting string contains only one-byte characters.
660 Node* one_byte_result = AllocateSeqOneByteString(context, smi_argc);
661
662 TVARIABLE(IntPtrT, var_max_index);
663 var_max_index = IntPtrConstant(0);
664
665 // Iterate over the incoming arguments, converting them to 8-bit character
666 // codes. Stop if any of the conversions generates a code that doesn't fit
667 // in 8 bits.
668 CodeStubAssembler::VariableList vars({&var_max_index}, zone());
669 arguments.ForEach(vars, [this, context, &two_byte, &var_max_index, &code16,
670 one_byte_result](Node* arg) {
671 Node* code32 = TruncateTaggedToWord32(context, arg);
672 code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
673
674 GotoIf(
675 Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)),
676 &two_byte);
677
678 // The {code16} fits into the SeqOneByteString {one_byte_result}.
679 Node* offset = ElementOffsetFromIndex(
680 var_max_index.value(), UINT8_ELEMENTS,
681 CodeStubAssembler::INTPTR_PARAMETERS,
682 SeqOneByteString::kHeaderSize - kHeapObjectTag);
683 StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
684 offset, code16);
685 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
686 });
687 arguments.PopAndReturn(one_byte_result);
688
689 BIND(&two_byte);
690
691 // At least one of the characters in the string requires a 16-bit
692 // representation. Allocate a SeqTwoByteString to hold the resulting
693 // string.
694 Node* two_byte_result = AllocateSeqTwoByteString(context, smi_argc);
695
696 // Copy the characters that have already been put in the 8-bit string into
697 // their corresponding positions in the new 16-bit string.
698 TNode<IntPtrT> zero = IntPtrConstant(0);
699 CopyStringCharacters(one_byte_result, two_byte_result, zero, zero,
700 var_max_index.value(), String::ONE_BYTE_ENCODING,
701 String::TWO_BYTE_ENCODING);
702
703 // Write the character that caused the 8-bit to 16-bit fault.
704 Node* max_index_offset =
705 ElementOffsetFromIndex(var_max_index.value(), UINT16_ELEMENTS,
706 CodeStubAssembler::INTPTR_PARAMETERS,
707 SeqTwoByteString::kHeaderSize - kHeapObjectTag);
708 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
709 max_index_offset, code16);
710 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
711
712 // Resume copying the passed-in arguments from the same place where the
713 // 8-bit copy stopped, but this time copying over all of the characters
714 // using a 16-bit representation.
715 arguments.ForEach(
716 vars,
717 [this, context, two_byte_result, &var_max_index](Node* arg) {
718 Node* code32 = TruncateTaggedToWord32(context, arg);
719 Node* code16 =
720 Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
721
722 Node* offset = ElementOffsetFromIndex(
723 var_max_index.value(), UINT16_ELEMENTS,
724 CodeStubAssembler::INTPTR_PARAMETERS,
725 SeqTwoByteString::kHeaderSize - kHeapObjectTag);
726 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
727 offset, code16);
728 var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
729 },
730 var_max_index.value());
731
732 arguments.PopAndReturn(two_byte_result);
733 }
734 }
735
736 // ES6 #sec-string.prototype.charat
TF_BUILTIN(StringPrototypeCharAt,StringBuiltinsAssembler)737 TF_BUILTIN(StringPrototypeCharAt, StringBuiltinsAssembler) {
738 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
739 Node* receiver = Parameter(Descriptor::kReceiver);
740 TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
741
742 GenerateStringAt("String.prototype.charAt", context, receiver, maybe_position,
743 EmptyStringConstant(),
744 [this](TNode<String> string, TNode<IntPtrT> length,
745 TNode<IntPtrT> index) {
746 TNode<Int32T> code = StringCharCodeAt(string, index);
747 return StringFromSingleCharCode(code);
748 });
749 }
750
751 // ES6 #sec-string.prototype.charcodeat
TF_BUILTIN(StringPrototypeCharCodeAt,StringBuiltinsAssembler)752 TF_BUILTIN(StringPrototypeCharCodeAt, StringBuiltinsAssembler) {
753 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
754 Node* receiver = Parameter(Descriptor::kReceiver);
755 TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
756
757 GenerateStringAt("String.prototype.charCodeAt", context, receiver,
758 maybe_position, NanConstant(),
759 [this](TNode<String> receiver, TNode<IntPtrT> length,
760 TNode<IntPtrT> index) {
761 Node* value = StringCharCodeAt(receiver, index);
762 return SmiFromInt32(value);
763 });
764 }
765
766 // ES6 #sec-string.prototype.codepointat
TF_BUILTIN(StringPrototypeCodePointAt,StringBuiltinsAssembler)767 TF_BUILTIN(StringPrototypeCodePointAt, StringBuiltinsAssembler) {
768 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
769 Node* receiver = Parameter(Descriptor::kReceiver);
770 TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
771
772 GenerateStringAt("String.prototype.codePointAt", context, receiver,
773 maybe_position, UndefinedConstant(),
774 [this](TNode<String> receiver, TNode<IntPtrT> length,
775 TNode<IntPtrT> index) {
776 // This is always a call to a builtin from Javascript,
777 // so we need to produce UTF32.
778 Node* value = LoadSurrogatePairAt(receiver, length, index,
779 UnicodeEncoding::UTF32);
780 return SmiFromInt32(value);
781 });
782 }
783
784 // ES6 String.prototype.concat(...args)
785 // ES6 #sec-string.prototype.concat
TF_BUILTIN(StringPrototypeConcat,CodeStubAssembler)786 TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) {
787 // TODO(ishell): use constants from Descriptor once the JSFunction linkage
788 // arguments are reordered.
789 CodeStubArguments arguments(
790 this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
791 Node* receiver = arguments.GetReceiver();
792 Node* context = Parameter(BuiltinDescriptor::kContext);
793
794 // Check that {receiver} is coercible to Object and convert it to a String.
795 receiver = ToThisString(context, receiver, "String.prototype.concat");
796
797 // Concatenate all the arguments passed to this builtin.
798 VARIABLE(var_result, MachineRepresentation::kTagged);
799 var_result.Bind(receiver);
800 arguments.ForEach(
801 CodeStubAssembler::VariableList({&var_result}, zone()),
802 [this, context, &var_result](Node* arg) {
803 arg = ToString_Inline(context, arg);
804 var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
805 var_result.value(), arg));
806 });
807 arguments.PopAndReturn(var_result.value());
808 }
809
StringIndexOf(Node * const subject_string,Node * const search_string,Node * const position,std::function<void (Node *)> f_return)810 void StringBuiltinsAssembler::StringIndexOf(
811 Node* const subject_string, Node* const search_string, Node* const position,
812 std::function<void(Node*)> f_return) {
813 CSA_ASSERT(this, IsString(subject_string));
814 CSA_ASSERT(this, IsString(search_string));
815 CSA_ASSERT(this, TaggedIsSmi(position));
816
817 TNode<IntPtrT> const int_zero = IntPtrConstant(0);
818 TNode<IntPtrT> const search_length = LoadStringLengthAsWord(search_string);
819 TNode<IntPtrT> const subject_length = LoadStringLengthAsWord(subject_string);
820 TNode<IntPtrT> const start_position = IntPtrMax(SmiUntag(position), int_zero);
821
822 Label zero_length_needle(this), return_minus_1(this);
823 {
824 GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle);
825
826 // Check that the needle fits in the start position.
827 GotoIfNot(IntPtrLessThanOrEqual(search_length,
828 IntPtrSub(subject_length, start_position)),
829 &return_minus_1);
830 }
831
832 // If the string pointers are identical, we can just return 0. Note that this
833 // implies {start_position} == 0 since we've passed the check above.
834 Label return_zero(this);
835 GotoIf(WordEqual(subject_string, search_string), &return_zero);
836
837 // Try to unpack subject and search strings. Bail to runtime if either needs
838 // to be flattened.
839 ToDirectStringAssembler subject_to_direct(state(), subject_string);
840 ToDirectStringAssembler search_to_direct(state(), search_string);
841
842 Label call_runtime_unchecked(this, Label::kDeferred);
843
844 subject_to_direct.TryToDirect(&call_runtime_unchecked);
845 search_to_direct.TryToDirect(&call_runtime_unchecked);
846
847 // Load pointers to string data.
848 Node* const subject_ptr =
849 subject_to_direct.PointerToData(&call_runtime_unchecked);
850 Node* const search_ptr =
851 search_to_direct.PointerToData(&call_runtime_unchecked);
852
853 Node* const subject_offset = subject_to_direct.offset();
854 Node* const search_offset = search_to_direct.offset();
855
856 // Like String::IndexOf, the actual matching is done by the optimized
857 // SearchString method in string-search.h. Dispatch based on string instance
858 // types, then call straight into C++ for matching.
859
860 CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero));
861 CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero));
862 CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position));
863 CSA_ASSERT(this,
864 IntPtrLessThanOrEqual(search_length,
865 IntPtrSub(subject_length, start_position)));
866
867 Label one_one(this), one_two(this), two_one(this), two_two(this);
868 DispatchOnStringEncodings(subject_to_direct.instance_type(),
869 search_to_direct.instance_type(), &one_one,
870 &one_two, &two_one, &two_two);
871
872 typedef const uint8_t onebyte_t;
873 typedef const uc16 twobyte_t;
874
875 BIND(&one_one);
876 {
877 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
878 subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
879 Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
880 search_ptr, search_offset, String::ONE_BYTE_ENCODING);
881
882 Label direct_memchr_call(this), generic_fast_path(this);
883 Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call,
884 &generic_fast_path);
885
886 // An additional fast path that calls directly into memchr for 1-length
887 // search strings.
888 BIND(&direct_memchr_call);
889 {
890 Node* const string_addr = IntPtrAdd(adjusted_subject_ptr, start_position);
891 Node* const search_length = IntPtrSub(subject_length, start_position);
892 Node* const search_byte =
893 ChangeInt32ToIntPtr(Load(MachineType::Uint8(), adjusted_search_ptr));
894
895 Node* const memchr =
896 ExternalConstant(ExternalReference::libc_memchr_function());
897 Node* const result_address =
898 CallCFunction3(MachineType::Pointer(), MachineType::Pointer(),
899 MachineType::IntPtr(), MachineType::UintPtr(), memchr,
900 string_addr, search_byte, search_length);
901 GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
902 Node* const result_index =
903 IntPtrAdd(IntPtrSub(result_address, string_addr), start_position);
904 f_return(SmiTag(result_index));
905 }
906
907 BIND(&generic_fast_path);
908 {
909 Node* const result = CallSearchStringRaw<onebyte_t, onebyte_t>(
910 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
911 search_length, start_position);
912 f_return(SmiTag(result));
913 }
914 }
915
916 BIND(&one_two);
917 {
918 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
919 subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
920 Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
921 search_ptr, search_offset, String::TWO_BYTE_ENCODING);
922
923 Node* const result = CallSearchStringRaw<onebyte_t, twobyte_t>(
924 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
925 search_length, start_position);
926 f_return(SmiTag(result));
927 }
928
929 BIND(&two_one);
930 {
931 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
932 subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
933 Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
934 search_ptr, search_offset, String::ONE_BYTE_ENCODING);
935
936 Node* const result = CallSearchStringRaw<twobyte_t, onebyte_t>(
937 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
938 search_length, start_position);
939 f_return(SmiTag(result));
940 }
941
942 BIND(&two_two);
943 {
944 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
945 subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
946 Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
947 search_ptr, search_offset, String::TWO_BYTE_ENCODING);
948
949 Node* const result = CallSearchStringRaw<twobyte_t, twobyte_t>(
950 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
951 search_length, start_position);
952 f_return(SmiTag(result));
953 }
954
955 BIND(&return_minus_1);
956 f_return(SmiConstant(-1));
957
958 BIND(&return_zero);
959 f_return(SmiConstant(0));
960
961 BIND(&zero_length_needle);
962 {
963 Comment("0-length search_string");
964 f_return(SmiTag(IntPtrMin(subject_length, start_position)));
965 }
966
967 BIND(&call_runtime_unchecked);
968 {
969 // Simplified version of the runtime call where the types of the arguments
970 // are already known due to type checks in this stub.
971 Comment("Call Runtime Unchecked");
972 Node* result =
973 CallRuntime(Runtime::kStringIndexOfUnchecked, NoContextConstant(),
974 subject_string, search_string, position);
975 f_return(result);
976 }
977 }
978
979 // ES6 String.prototype.indexOf(searchString [, position])
980 // #sec-string.prototype.indexof
981 // Unchecked helper for builtins lowering.
TF_BUILTIN(StringIndexOf,StringBuiltinsAssembler)982 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
983 Node* receiver = Parameter(Descriptor::kReceiver);
984 Node* search_string = Parameter(Descriptor::kSearchString);
985 Node* position = Parameter(Descriptor::kPosition);
986 StringIndexOf(receiver, search_string, position,
987 [this](Node* result) { this->Return(result); });
988 }
989
990 // ES6 String.prototype.includes(searchString [, position])
991 // #sec-string.prototype.includes
TF_BUILTIN(StringPrototypeIncludes,StringIncludesIndexOfAssembler)992 TF_BUILTIN(StringPrototypeIncludes, StringIncludesIndexOfAssembler) {
993 Generate(kIncludes);
994 }
995
996 // ES6 String.prototype.indexOf(searchString [, position])
997 // #sec-string.prototype.indexof
TF_BUILTIN(StringPrototypeIndexOf,StringIncludesIndexOfAssembler)998 TF_BUILTIN(StringPrototypeIndexOf, StringIncludesIndexOfAssembler) {
999 Generate(kIndexOf);
1000 }
1001
Generate(SearchVariant variant)1002 void StringIncludesIndexOfAssembler::Generate(SearchVariant variant) {
1003 // TODO(ishell): use constants from Descriptor once the JSFunction linkage
1004 // arguments are reordered.
1005 Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
1006 Node* const context = Parameter(BuiltinDescriptor::kContext);
1007 CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
1008 Node* const receiver = arguments.GetReceiver();
1009 // From now on use word-size argc value.
1010 argc = arguments.GetLength(INTPTR_PARAMETERS);
1011
1012 VARIABLE(var_search_string, MachineRepresentation::kTagged);
1013 VARIABLE(var_position, MachineRepresentation::kTagged);
1014 Label argc_1(this), argc_2(this), call_runtime(this, Label::kDeferred),
1015 fast_path(this);
1016
1017 GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1);
1018 GotoIf(IntPtrGreaterThan(argc, IntPtrConstant(1)), &argc_2);
1019 {
1020 Comment("0 Argument case");
1021 CSA_ASSERT(this, IntPtrEqual(argc, IntPtrConstant(0)));
1022 Node* const undefined = UndefinedConstant();
1023 var_search_string.Bind(undefined);
1024 var_position.Bind(undefined);
1025 Goto(&call_runtime);
1026 }
1027 BIND(&argc_1);
1028 {
1029 Comment("1 Argument case");
1030 var_search_string.Bind(arguments.AtIndex(0));
1031 var_position.Bind(SmiConstant(0));
1032 Goto(&fast_path);
1033 }
1034 BIND(&argc_2);
1035 {
1036 Comment("2 Argument case");
1037 var_search_string.Bind(arguments.AtIndex(0));
1038 var_position.Bind(arguments.AtIndex(1));
1039 GotoIfNot(TaggedIsSmi(var_position.value()), &call_runtime);
1040 Goto(&fast_path);
1041 }
1042 BIND(&fast_path);
1043 {
1044 Comment("Fast Path");
1045 Node* const search = var_search_string.value();
1046 Node* const position = var_position.value();
1047 GotoIf(TaggedIsSmi(receiver), &call_runtime);
1048 GotoIf(TaggedIsSmi(search), &call_runtime);
1049 GotoIfNot(IsString(receiver), &call_runtime);
1050 GotoIfNot(IsString(search), &call_runtime);
1051
1052 StringIndexOf(receiver, search, position, [&](Node* result) {
1053 CSA_ASSERT(this, TaggedIsSmi(result));
1054 arguments.PopAndReturn((variant == kIndexOf)
1055 ? result
1056 : SelectBooleanConstant(SmiGreaterThanOrEqual(
1057 CAST(result), SmiConstant(0))));
1058 });
1059 }
1060 BIND(&call_runtime);
1061 {
1062 Comment("Call Runtime");
1063 Runtime::FunctionId runtime = variant == kIndexOf
1064 ? Runtime::kStringIndexOf
1065 : Runtime::kStringIncludes;
1066 Node* const result =
1067 CallRuntime(runtime, context, receiver, var_search_string.value(),
1068 var_position.value());
1069 arguments.PopAndReturn(result);
1070 }
1071 }
1072
RequireObjectCoercible(Node * const context,Node * const value,const char * method_name)1073 void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context,
1074 Node* const value,
1075 const char* method_name) {
1076 Label out(this), throw_exception(this, Label::kDeferred);
1077 Branch(IsNullOrUndefined(value), &throw_exception, &out);
1078
1079 BIND(&throw_exception);
1080 ThrowTypeError(context, MessageTemplate::kCalledOnNullOrUndefined,
1081 method_name);
1082
1083 BIND(&out);
1084 }
1085
MaybeCallFunctionAtSymbol(Node * const context,Node * const object,Node * const maybe_string,Handle<Symbol> symbol,const NodeFunction0 & regexp_call,const NodeFunction1 & generic_call)1086 void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
1087 Node* const context, Node* const object, Node* const maybe_string,
1088 Handle<Symbol> symbol, const NodeFunction0& regexp_call,
1089 const NodeFunction1& generic_call) {
1090 Label out(this);
1091
1092 // Smis definitely don't have an attached symbol.
1093 GotoIf(TaggedIsSmi(object), &out);
1094
1095 Node* const object_map = LoadMap(object);
1096
1097 // Skip the slow lookup for Strings.
1098 {
1099 Label next(this);
1100
1101 GotoIfNot(IsStringInstanceType(LoadMapInstanceType(object_map)), &next);
1102
1103 Node* const native_context = LoadNativeContext(context);
1104 Node* const initial_proto_initial_map = LoadContextElement(
1105 native_context, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX);
1106
1107 Node* const string_fun =
1108 LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX);
1109 Node* const initial_map =
1110 LoadObjectField(string_fun, JSFunction::kPrototypeOrInitialMapOffset);
1111 Node* const proto_map = LoadMap(LoadMapPrototype(initial_map));
1112
1113 Branch(WordEqual(proto_map, initial_proto_initial_map), &out, &next);
1114
1115 BIND(&next);
1116 }
1117
1118 // Take the fast path for RegExps.
1119 // There's two conditions: {object} needs to be a fast regexp, and
1120 // {maybe_string} must be a string (we can't call ToString on the fast path
1121 // since it may mutate {object}).
1122 {
1123 Label stub_call(this), slow_lookup(this);
1124
1125 GotoIf(TaggedIsSmi(maybe_string), &slow_lookup);
1126 GotoIfNot(IsString(maybe_string), &slow_lookup);
1127
1128 RegExpBuiltinsAssembler regexp_asm(state());
1129 regexp_asm.BranchIfFastRegExp(context, object, object_map, &stub_call,
1130 &slow_lookup);
1131
1132 BIND(&stub_call);
1133 // TODO(jgruber): Add a no-JS scope once it exists.
1134 regexp_call();
1135
1136 BIND(&slow_lookup);
1137 }
1138
1139 GotoIf(IsNullOrUndefined(object), &out);
1140
1141 // Fall back to a slow lookup of {object[symbol]}.
1142 //
1143 // The spec uses GetMethod({object}, {symbol}), which has a few quirks:
1144 // * null values are turned into undefined, and
1145 // * an exception is thrown if the value is not undefined, null, or callable.
1146 // We handle the former by jumping to {out} for null values as well, while
1147 // the latter is already handled by the Call({maybe_func}) operation.
1148
1149 Node* const maybe_func = GetProperty(context, object, symbol);
1150 GotoIf(IsUndefined(maybe_func), &out);
1151 GotoIf(IsNull(maybe_func), &out);
1152
1153 // Attempt to call the function.
1154 generic_call(maybe_func);
1155
1156 BIND(&out);
1157 }
1158
IndexOfDollarChar(Node * const context,Node * const string)1159 TNode<Smi> StringBuiltinsAssembler::IndexOfDollarChar(Node* const context,
1160 Node* const string) {
1161 CSA_ASSERT(this, IsString(string));
1162
1163 TNode<String> const dollar_string = HeapConstant(
1164 isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
1165 TNode<Smi> const dollar_ix =
1166 CAST(CallBuiltin(Builtins::kStringIndexOf, context, string, dollar_string,
1167 SmiConstant(0)));
1168 return dollar_ix;
1169 }
1170
GetSubstitution(Node * context,Node * subject_string,Node * match_start_index,Node * match_end_index,Node * replace_string)1171 compiler::Node* StringBuiltinsAssembler::GetSubstitution(
1172 Node* context, Node* subject_string, Node* match_start_index,
1173 Node* match_end_index, Node* replace_string) {
1174 CSA_ASSERT(this, IsString(subject_string));
1175 CSA_ASSERT(this, IsString(replace_string));
1176 CSA_ASSERT(this, TaggedIsPositiveSmi(match_start_index));
1177 CSA_ASSERT(this, TaggedIsPositiveSmi(match_end_index));
1178
1179 VARIABLE(var_result, MachineRepresentation::kTagged, replace_string);
1180 Label runtime(this), out(this);
1181
1182 // In this primitive implementation we simply look for the next '$' char in
1183 // {replace_string}. If it doesn't exist, we can simply return
1184 // {replace_string} itself. If it does, then we delegate to
1185 // String::GetSubstitution, passing in the index of the first '$' to avoid
1186 // repeated scanning work.
1187 // TODO(jgruber): Possibly extend this in the future to handle more complex
1188 // cases without runtime calls.
1189
1190 TNode<Smi> const dollar_index = IndexOfDollarChar(context, replace_string);
1191 Branch(SmiIsNegative(dollar_index), &out, &runtime);
1192
1193 BIND(&runtime);
1194 {
1195 CSA_ASSERT(this, TaggedIsPositiveSmi(dollar_index));
1196
1197 Node* const matched =
1198 CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1199 SmiUntag(match_start_index), SmiUntag(match_end_index));
1200 Node* const replacement_string =
1201 CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
1202 match_start_index, replace_string, dollar_index);
1203 var_result.Bind(replacement_string);
1204
1205 Goto(&out);
1206 }
1207
1208 BIND(&out);
1209 return var_result.value();
1210 }
1211
1212 // ES6 #sec-string.prototype.repeat
TF_BUILTIN(StringPrototypeRepeat,StringBuiltinsAssembler)1213 TF_BUILTIN(StringPrototypeRepeat, StringBuiltinsAssembler) {
1214 Label invalid_count(this), invalid_string_length(this),
1215 return_emptystring(this);
1216
1217 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1218 Node* const receiver = Parameter(Descriptor::kReceiver);
1219 TNode<Object> count = CAST(Parameter(Descriptor::kCount));
1220 Node* const string =
1221 ToThisString(context, receiver, "String.prototype.repeat");
1222 Node* const is_stringempty =
1223 SmiEqual(LoadStringLengthAsSmi(string), SmiConstant(0));
1224
1225 VARIABLE(
1226 var_count, MachineRepresentation::kTagged,
1227 ToInteger_Inline(context, count, CodeStubAssembler::kTruncateMinusZero));
1228
1229 // Verifies a valid count and takes a fast path when the result will be an
1230 // empty string.
1231 {
1232 Label if_count_isheapnumber(this, Label::kDeferred);
1233
1234 GotoIfNot(TaggedIsSmi(var_count.value()), &if_count_isheapnumber);
1235 {
1236 // If count is a SMI, throw a RangeError if less than 0 or greater than
1237 // the maximum string length.
1238 TNode<Smi> smi_count = CAST(var_count.value());
1239 GotoIf(SmiLessThan(smi_count, SmiConstant(0)), &invalid_count);
1240 GotoIf(SmiEqual(smi_count, SmiConstant(0)), &return_emptystring);
1241 GotoIf(is_stringempty, &return_emptystring);
1242 GotoIf(SmiGreaterThan(smi_count, SmiConstant(String::kMaxLength)),
1243 &invalid_string_length);
1244 Return(CallBuiltin(Builtins::kStringRepeat, context, string, smi_count));
1245 }
1246
1247 // If count is a Heap Number...
1248 // 1) If count is Infinity, throw a RangeError exception
1249 // 2) If receiver is an empty string, return an empty string
1250 // 3) Otherwise, throw RangeError exception
1251 BIND(&if_count_isheapnumber);
1252 {
1253 CSA_ASSERT(this, IsNumberNormalized(var_count.value()));
1254 Node* const number_value = LoadHeapNumberValue(var_count.value());
1255 GotoIf(Float64Equal(number_value, Float64Constant(V8_INFINITY)),
1256 &invalid_count);
1257 GotoIf(Float64LessThan(number_value, Float64Constant(0.0)),
1258 &invalid_count);
1259 Branch(is_stringempty, &return_emptystring, &invalid_string_length);
1260 }
1261 }
1262
1263 BIND(&return_emptystring);
1264 Return(EmptyStringConstant());
1265
1266 BIND(&invalid_count);
1267 {
1268 ThrowRangeError(context, MessageTemplate::kInvalidCountValue,
1269 var_count.value());
1270 }
1271
1272 BIND(&invalid_string_length);
1273 {
1274 CallRuntime(Runtime::kThrowInvalidStringLength, context);
1275 Unreachable();
1276 }
1277 }
1278
1279 // Helper with less checks
TF_BUILTIN(StringRepeat,StringBuiltinsAssembler)1280 TF_BUILTIN(StringRepeat, StringBuiltinsAssembler) {
1281 Node* const context = Parameter(Descriptor::kContext);
1282 Node* const string = Parameter(Descriptor::kString);
1283 TNode<Smi> const count = CAST(Parameter(Descriptor::kCount));
1284
1285 CSA_ASSERT(this, IsString(string));
1286 CSA_ASSERT(this, Word32BinaryNot(IsEmptyString(string)));
1287 CSA_ASSERT(this, TaggedIsPositiveSmi(count));
1288 CSA_ASSERT(this, SmiLessThanOrEqual(count, SmiConstant(String::kMaxLength)));
1289
1290 // The string is repeated with the following algorithm:
1291 // let n = count;
1292 // let power_of_two_repeats = string;
1293 // let result = "";
1294 // while (true) {
1295 // if (n & 1) result += s;
1296 // n >>= 1;
1297 // if (n === 0) return result;
1298 // power_of_two_repeats += power_of_two_repeats;
1299 // }
1300 VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
1301 VARIABLE(var_temp, MachineRepresentation::kTagged, string);
1302 TVARIABLE(Smi, var_count, count);
1303
1304 Callable stringadd_callable =
1305 CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
1306
1307 Label loop(this, {&var_count, &var_result, &var_temp}), return_result(this);
1308 Goto(&loop);
1309 BIND(&loop);
1310 {
1311 {
1312 Label next(this);
1313 GotoIfNot(SmiToInt32(SmiAnd(var_count.value(), SmiConstant(1))), &next);
1314 var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
1315 var_temp.value()));
1316 Goto(&next);
1317 BIND(&next);
1318 }
1319
1320 var_count = SmiShr(var_count.value(), 1);
1321 GotoIf(SmiEqual(var_count.value(), SmiConstant(0)), &return_result);
1322 var_temp.Bind(CallStub(stringadd_callable, context, var_temp.value(),
1323 var_temp.value()));
1324 Goto(&loop);
1325 }
1326
1327 BIND(&return_result);
1328 Return(var_result.value());
1329 }
1330
1331 // ES6 #sec-string.prototype.replace
TF_BUILTIN(StringPrototypeReplace,StringBuiltinsAssembler)1332 TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
1333 Label out(this);
1334
1335 Node* const receiver = Parameter(Descriptor::kReceiver);
1336 Node* const search = Parameter(Descriptor::kSearch);
1337 Node* const replace = Parameter(Descriptor::kReplace);
1338 Node* const context = Parameter(Descriptor::kContext);
1339
1340 TNode<Smi> const smi_zero = SmiConstant(0);
1341
1342 RequireObjectCoercible(context, receiver, "String.prototype.replace");
1343
1344 // Redirect to replacer method if {search[@@replace]} is not undefined.
1345
1346 MaybeCallFunctionAtSymbol(
1347 context, search, receiver, isolate()->factory()->replace_symbol(),
1348 [=]() {
1349 Return(CallBuiltin(Builtins::kRegExpReplace, context, search, receiver,
1350 replace));
1351 },
1352 [=](Node* fn) {
1353 Callable call_callable = CodeFactory::Call(isolate());
1354 Return(CallJS(call_callable, context, fn, search, receiver, replace));
1355 });
1356
1357 // Convert {receiver} and {search} to strings.
1358
1359 TNode<String> const subject_string = ToString_Inline(context, receiver);
1360 TNode<String> const search_string = ToString_Inline(context, search);
1361
1362 TNode<Smi> const subject_length = LoadStringLengthAsSmi(subject_string);
1363 TNode<Smi> const search_length = LoadStringLengthAsSmi(search_string);
1364
1365 // Fast-path single-char {search}, long cons {receiver}, and simple string
1366 // {replace}.
1367 {
1368 Label next(this);
1369
1370 GotoIfNot(SmiEqual(search_length, SmiConstant(1)), &next);
1371 GotoIfNot(SmiGreaterThan(subject_length, SmiConstant(0xFF)), &next);
1372 GotoIf(TaggedIsSmi(replace), &next);
1373 GotoIfNot(IsString(replace), &next);
1374
1375 Node* const subject_instance_type = LoadInstanceType(subject_string);
1376 GotoIfNot(IsConsStringInstanceType(subject_instance_type), &next);
1377
1378 GotoIf(TaggedIsPositiveSmi(IndexOfDollarChar(context, replace)), &next);
1379
1380 // Searching by traversing a cons string tree and replace with cons of
1381 // slices works only when the replaced string is a single character, being
1382 // replaced by a simple string and only pays off for long strings.
1383 // TODO(jgruber): Reevaluate if this is still beneficial.
1384 // TODO(jgruber): TailCallRuntime when it correctly handles adapter frames.
1385 Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context,
1386 subject_string, search_string, replace));
1387
1388 BIND(&next);
1389 }
1390
1391 // TODO(jgruber): Extend StringIndexOf to handle two-byte strings and
1392 // longer substrings - we can handle up to 8 chars (one-byte) / 4 chars
1393 // (2-byte).
1394
1395 TNode<Smi> const match_start_index =
1396 CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string,
1397 search_string, smi_zero));
1398
1399 // Early exit if no match found.
1400 {
1401 Label next(this), return_subject(this);
1402
1403 GotoIfNot(SmiIsNegative(match_start_index), &next);
1404
1405 // The spec requires to perform ToString(replace) if the {replace} is not
1406 // callable even if we are going to exit here.
1407 // Since ToString() being applied to Smi does not have side effects for
1408 // numbers we can skip it.
1409 GotoIf(TaggedIsSmi(replace), &return_subject);
1410 GotoIf(IsCallableMap(LoadMap(replace)), &return_subject);
1411
1412 // TODO(jgruber): Could introduce ToStringSideeffectsStub which only
1413 // performs observable parts of ToString.
1414 ToString_Inline(context, replace);
1415 Goto(&return_subject);
1416
1417 BIND(&return_subject);
1418 Return(subject_string);
1419
1420 BIND(&next);
1421 }
1422
1423 TNode<Smi> const match_end_index = SmiAdd(match_start_index, search_length);
1424
1425 Callable stringadd_callable =
1426 CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
1427
1428 VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
1429
1430 // Compute the prefix.
1431 {
1432 Label next(this);
1433
1434 GotoIf(SmiEqual(match_start_index, smi_zero), &next);
1435 Node* const prefix =
1436 CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1437 IntPtrConstant(0), SmiUntag(match_start_index));
1438 var_result.Bind(prefix);
1439
1440 Goto(&next);
1441 BIND(&next);
1442 }
1443
1444 // Compute the string to replace with.
1445
1446 Label if_iscallablereplace(this), if_notcallablereplace(this);
1447 GotoIf(TaggedIsSmi(replace), &if_notcallablereplace);
1448 Branch(IsCallableMap(LoadMap(replace)), &if_iscallablereplace,
1449 &if_notcallablereplace);
1450
1451 BIND(&if_iscallablereplace);
1452 {
1453 Callable call_callable = CodeFactory::Call(isolate());
1454 Node* const replacement =
1455 CallJS(call_callable, context, replace, UndefinedConstant(),
1456 search_string, match_start_index, subject_string);
1457 Node* const replacement_string = ToString_Inline(context, replacement);
1458 var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
1459 replacement_string));
1460 Goto(&out);
1461 }
1462
1463 BIND(&if_notcallablereplace);
1464 {
1465 Node* const replace_string = ToString_Inline(context, replace);
1466 Node* const replacement =
1467 GetSubstitution(context, subject_string, match_start_index,
1468 match_end_index, replace_string);
1469 var_result.Bind(
1470 CallStub(stringadd_callable, context, var_result.value(), replacement));
1471 Goto(&out);
1472 }
1473
1474 BIND(&out);
1475 {
1476 Node* const suffix =
1477 CallBuiltin(Builtins::kStringSubstring, context, subject_string,
1478 SmiUntag(match_end_index), SmiUntag(subject_length));
1479 Node* const result =
1480 CallStub(stringadd_callable, context, var_result.value(), suffix);
1481 Return(result);
1482 }
1483 }
1484
1485 class StringMatchSearchAssembler : public StringBuiltinsAssembler {
1486 public:
StringMatchSearchAssembler(compiler::CodeAssemblerState * state)1487 explicit StringMatchSearchAssembler(compiler::CodeAssemblerState* state)
1488 : StringBuiltinsAssembler(state) {}
1489
1490 protected:
1491 enum Variant { kMatch, kSearch };
1492
Generate(Variant variant,const char * method_name,TNode<Object> receiver,TNode<Object> maybe_regexp,TNode<Context> context)1493 void Generate(Variant variant, const char* method_name,
1494 TNode<Object> receiver, TNode<Object> maybe_regexp,
1495 TNode<Context> context) {
1496 Label call_regexp_match_search(this);
1497
1498 Builtins::Name builtin;
1499 Handle<Symbol> symbol;
1500 if (variant == kMatch) {
1501 builtin = Builtins::kRegExpMatchFast;
1502 symbol = isolate()->factory()->match_symbol();
1503 } else {
1504 builtin = Builtins::kRegExpSearchFast;
1505 symbol = isolate()->factory()->search_symbol();
1506 }
1507
1508 RequireObjectCoercible(context, receiver, method_name);
1509
1510 MaybeCallFunctionAtSymbol(
1511 context, maybe_regexp, receiver, symbol,
1512 [=] { Return(CallBuiltin(builtin, context, maybe_regexp, receiver)); },
1513 [=](Node* fn) {
1514 Callable call_callable = CodeFactory::Call(isolate());
1515 Return(CallJS(call_callable, context, fn, maybe_regexp, receiver));
1516 });
1517
1518 // maybe_regexp is not a RegExp nor has [@@match / @@search] property.
1519 {
1520 RegExpBuiltinsAssembler regexp_asm(state());
1521
1522 TNode<String> receiver_string = ToString_Inline(context, receiver);
1523 TNode<Context> native_context = LoadNativeContext(context);
1524 TNode<HeapObject> regexp_function = CAST(
1525 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
1526 TNode<Map> initial_map = CAST(LoadObjectField(
1527 regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
1528 TNode<Object> regexp = regexp_asm.RegExpCreate(
1529 context, initial_map, maybe_regexp, EmptyStringConstant());
1530
1531 Label fast_path(this), slow_path(this);
1532 regexp_asm.BranchIfFastRegExp(context, regexp, initial_map, &fast_path,
1533 &slow_path);
1534
1535 BIND(&fast_path);
1536 Return(CallBuiltin(builtin, context, regexp, receiver_string));
1537
1538 BIND(&slow_path);
1539 {
1540 TNode<Object> maybe_func = GetProperty(context, regexp, symbol);
1541 Callable call_callable = CodeFactory::Call(isolate());
1542 Return(CallJS(call_callable, context, maybe_func, regexp,
1543 receiver_string));
1544 }
1545 }
1546 }
1547 };
1548
1549 // ES6 #sec-string.prototype.match
TF_BUILTIN(StringPrototypeMatch,StringMatchSearchAssembler)1550 TF_BUILTIN(StringPrototypeMatch, StringMatchSearchAssembler) {
1551 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1552 TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1553 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1554
1555 Generate(kMatch, "String.prototype.match", receiver, maybe_regexp, context);
1556 }
1557
1558 // ES #sec-string.prototype.matchAll
TF_BUILTIN(StringPrototypeMatchAll,StringBuiltinsAssembler)1559 TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) {
1560 char const* method_name = "String.prototype.matchAll";
1561
1562 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1563 TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1564 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1565 TNode<Context> native_context = LoadNativeContext(context);
1566
1567 // 1. Let O be ? RequireObjectCoercible(this value).
1568 RequireObjectCoercible(context, receiver, method_name);
1569
1570 // 2. If regexp is neither undefined nor null, then
1571 Label return_match_all_iterator(this),
1572 tostring_and_return_match_all_iterator(this, Label::kDeferred);
1573 TVARIABLE(BoolT, var_is_fast_regexp);
1574 TVARIABLE(String, var_receiver_string);
1575 GotoIf(IsNullOrUndefined(maybe_regexp),
1576 &tostring_and_return_match_all_iterator);
1577 {
1578 // a. Let matcher be ? GetMethod(regexp, @@matchAll).
1579 // b. If matcher is not undefined, then
1580 // i. Return ? Call(matcher, regexp, « O »).
1581 auto if_regexp_call = [&] {
1582 // MaybeCallFunctionAtSymbol guarantees fast path is chosen only if
1583 // maybe_regexp is a fast regexp and receiver is a string.
1584 var_receiver_string = CAST(receiver);
1585 CSA_ASSERT(this, IsString(var_receiver_string.value()));
1586 var_is_fast_regexp = Int32TrueConstant();
1587 Goto(&return_match_all_iterator);
1588 };
1589 auto if_generic_call = [=](Node* fn) {
1590 Callable call_callable = CodeFactory::Call(isolate());
1591 Return(CallJS(call_callable, context, fn, maybe_regexp, receiver));
1592 };
1593 MaybeCallFunctionAtSymbol(context, maybe_regexp, receiver,
1594 isolate()->factory()->match_all_symbol(),
1595 if_regexp_call, if_generic_call);
1596 Goto(&tostring_and_return_match_all_iterator);
1597 }
1598 BIND(&tostring_and_return_match_all_iterator);
1599 {
1600 var_receiver_string = ToString_Inline(context, receiver);
1601 var_is_fast_regexp = Int32FalseConstant();
1602 Goto(&return_match_all_iterator);
1603 }
1604 BIND(&return_match_all_iterator);
1605 {
1606 // 3. Return ? MatchAllIterator(regexp, O).
1607 RegExpBuiltinsAssembler regexp_asm(state());
1608 TNode<Object> iterator = regexp_asm.MatchAllIterator(
1609 context, native_context, maybe_regexp, var_receiver_string.value(),
1610 var_is_fast_regexp.value(), method_name);
1611 Return(iterator);
1612 }
1613 }
1614
1615 class StringPadAssembler : public StringBuiltinsAssembler {
1616 public:
StringPadAssembler(compiler::CodeAssemblerState * state)1617 explicit StringPadAssembler(compiler::CodeAssemblerState* state)
1618 : StringBuiltinsAssembler(state) {}
1619
1620 protected:
1621 enum Variant { kStart, kEnd };
1622
Generate(Variant variant,const char * method_name)1623 void Generate(Variant variant, const char* method_name) {
1624 Node* const context = Parameter(BuiltinDescriptor::kContext);
1625 Node* argc =
1626 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1627 CodeStubArguments arguments(this, argc);
1628 Node* const receiver = arguments.GetReceiver();
1629 Node* const receiver_string = ToThisString(context, receiver, method_name);
1630 TNode<Smi> const string_length = LoadStringLengthAsSmi(receiver_string);
1631
1632 TVARIABLE(String, var_fill_string, StringConstant(" "));
1633 TVARIABLE(IntPtrT, var_fill_length, IntPtrConstant(1));
1634
1635 Label argc_2(this), dont_pad(this), invalid_string_length(this), pad(this);
1636
1637 // If no max_length was provided, return the string.
1638 GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &dont_pad);
1639
1640 TNode<Number> const max_length =
1641 ToLength_Inline(context, arguments.AtIndex(0));
1642 CSA_ASSERT(this, IsNumberNormalized(max_length));
1643
1644 // Throw if max_length is not a smi or greater than the max string length.
1645 GotoIfNot(TaggedIsSmi(max_length), &invalid_string_length);
1646 TNode<Smi> smi_max_length = CAST(max_length);
1647 GotoIfNot(
1648 SmiLessThanOrEqual(smi_max_length, SmiConstant(String::kMaxLength)),
1649 &invalid_string_length);
1650
1651 // If the max_length is less than length of the string, return the string.
1652 CSA_ASSERT(this, TaggedIsPositiveSmi(smi_max_length));
1653 GotoIf(SmiLessThanOrEqual(smi_max_length, string_length), &dont_pad);
1654
1655 Branch(IntPtrEqual(argc, IntPtrConstant(1)), &pad, &argc_2);
1656 BIND(&argc_2);
1657 {
1658 Node* const fill = arguments.AtIndex(1);
1659 GotoIf(IsUndefined(fill), &pad);
1660
1661 var_fill_string = ToString_Inline(context, fill);
1662 var_fill_length = LoadStringLengthAsWord(var_fill_string.value());
1663
1664 Branch(IntPtrGreaterThan(var_fill_length.value(), IntPtrConstant(0)),
1665 &pad, &dont_pad);
1666 }
1667 BIND(&pad);
1668 {
1669 CSA_ASSERT(this,
1670 IntPtrGreaterThan(var_fill_length.value(), IntPtrConstant(0)));
1671 CSA_ASSERT(this, SmiGreaterThan(smi_max_length, string_length));
1672
1673 Callable stringadd_callable =
1674 CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
1675 TNode<Smi> const pad_length = SmiSub(smi_max_length, string_length);
1676
1677 VARIABLE(var_pad, MachineRepresentation::kTagged);
1678
1679 Label single_char_fill(this), multi_char_fill(this), return_result(this);
1680 Branch(IntPtrEqual(var_fill_length.value(), IntPtrConstant(1)),
1681 &single_char_fill, &multi_char_fill);
1682
1683 // Fast path for a single character fill. No need to calculate number of
1684 // repetitions or remainder.
1685 BIND(&single_char_fill);
1686 {
1687 var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
1688 static_cast<Node*>(var_fill_string.value()),
1689 pad_length));
1690 Goto(&return_result);
1691 }
1692 BIND(&multi_char_fill);
1693 {
1694 TNode<Int32T> const fill_length_word32 =
1695 TruncateIntPtrToInt32(var_fill_length.value());
1696 TNode<Int32T> const pad_length_word32 = SmiToInt32(pad_length);
1697 TNode<Int32T> const repetitions_word32 =
1698 Int32Div(pad_length_word32, fill_length_word32);
1699 TNode<Int32T> const remaining_word32 =
1700 Int32Mod(pad_length_word32, fill_length_word32);
1701
1702 var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
1703 var_fill_string.value(),
1704 SmiFromInt32(repetitions_word32)));
1705
1706 GotoIfNot(remaining_word32, &return_result);
1707 {
1708 Node* const remainder_string = CallBuiltin(
1709 Builtins::kStringSubstring, context, var_fill_string.value(),
1710 IntPtrConstant(0), ChangeInt32ToIntPtr(remaining_word32));
1711 var_pad.Bind(CallStub(stringadd_callable, context, var_pad.value(),
1712 remainder_string));
1713 Goto(&return_result);
1714 }
1715 }
1716 BIND(&return_result);
1717 CSA_ASSERT(this,
1718 SmiEqual(pad_length, LoadStringLengthAsSmi(var_pad.value())));
1719 arguments.PopAndReturn(variant == kStart
1720 ? CallStub(stringadd_callable, context,
1721 var_pad.value(), receiver_string)
1722 : CallStub(stringadd_callable, context,
1723 receiver_string, var_pad.value()));
1724 }
1725 BIND(&dont_pad);
1726 arguments.PopAndReturn(receiver_string);
1727 BIND(&invalid_string_length);
1728 {
1729 CallRuntime(Runtime::kThrowInvalidStringLength, context);
1730 Unreachable();
1731 }
1732 }
1733 };
1734
TF_BUILTIN(StringPrototypePadEnd,StringPadAssembler)1735 TF_BUILTIN(StringPrototypePadEnd, StringPadAssembler) {
1736 Generate(kEnd, "String.prototype.padEnd");
1737 }
1738
TF_BUILTIN(StringPrototypePadStart,StringPadAssembler)1739 TF_BUILTIN(StringPrototypePadStart, StringPadAssembler) {
1740 Generate(kStart, "String.prototype.padStart");
1741 }
1742
1743 // ES6 #sec-string.prototype.search
TF_BUILTIN(StringPrototypeSearch,StringMatchSearchAssembler)1744 TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
1745 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1746 TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
1747 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1748 Generate(kSearch, "String.prototype.search", receiver, maybe_regexp, context);
1749 }
1750
1751 // ES6 section 21.1.3.18 String.prototype.slice ( start, end )
TF_BUILTIN(StringPrototypeSlice,StringBuiltinsAssembler)1752 TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
1753 Label out(this);
1754 TVARIABLE(IntPtrT, var_start);
1755 TVARIABLE(IntPtrT, var_end);
1756
1757 const int kStart = 0;
1758 const int kEnd = 1;
1759 Node* argc =
1760 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1761 CodeStubArguments args(this, argc);
1762 Node* const receiver = args.GetReceiver();
1763 TNode<Object> start = args.GetOptionalArgumentValue(kStart);
1764 TNode<Object> end = args.GetOptionalArgumentValue(kEnd);
1765 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1766
1767 // 1. Let O be ? RequireObjectCoercible(this value).
1768 RequireObjectCoercible(context, receiver, "String.prototype.slice");
1769
1770 // 2. Let S be ? ToString(O).
1771 TNode<String> const subject_string =
1772 CAST(CallBuiltin(Builtins::kToString, context, receiver));
1773
1774 // 3. Let len be the number of elements in S.
1775 TNode<IntPtrT> const length = LoadStringLengthAsWord(subject_string);
1776
1777 // Convert {start} to a relative index.
1778 var_start = ConvertToRelativeIndex(context, start, length);
1779
1780 // 5. If end is undefined, let intEnd be len;
1781 var_end = length;
1782 GotoIf(IsUndefined(end), &out);
1783
1784 // Convert {end} to a relative index.
1785 var_end = ConvertToRelativeIndex(context, end, length);
1786 Goto(&out);
1787
1788 Label return_emptystring(this);
1789 BIND(&out);
1790 {
1791 GotoIf(IntPtrLessThanOrEqual(var_end.value(), var_start.value()),
1792 &return_emptystring);
1793 TNode<String> const result =
1794 SubString(subject_string, var_start.value(), var_end.value());
1795 args.PopAndReturn(result);
1796 }
1797
1798 BIND(&return_emptystring);
1799 args.PopAndReturn(EmptyStringConstant());
1800 }
1801
StringToArray(TNode<Context> context,TNode<String> subject_string,TNode<Smi> subject_length,TNode<Number> limit_number)1802 TNode<JSArray> StringBuiltinsAssembler::StringToArray(
1803 TNode<Context> context, TNode<String> subject_string,
1804 TNode<Smi> subject_length, TNode<Number> limit_number) {
1805 CSA_ASSERT(this, SmiGreaterThan(subject_length, SmiConstant(0)));
1806
1807 Label done(this), call_runtime(this, Label::kDeferred),
1808 fill_thehole_and_call_runtime(this, Label::kDeferred);
1809 TVARIABLE(JSArray, result_array);
1810
1811 TNode<Int32T> instance_type = LoadInstanceType(subject_string);
1812 GotoIfNot(IsOneByteStringInstanceType(instance_type), &call_runtime);
1813
1814 // Try to use cached one byte characters.
1815 {
1816 TNode<Smi> length_smi =
1817 Select<Smi>(TaggedIsSmi(limit_number),
1818 [=] { return SmiMin(CAST(limit_number), subject_length); },
1819 [=] { return subject_length; });
1820 TNode<IntPtrT> length = SmiToIntPtr(length_smi);
1821
1822 ToDirectStringAssembler to_direct(state(), subject_string);
1823 to_direct.TryToDirect(&call_runtime);
1824 TNode<FixedArray> elements =
1825 AllocateFixedArray(PACKED_ELEMENTS, length_smi,
1826 AllocationFlag::kAllowLargeObjectAllocation);
1827 // Don't allocate anything while {string_data} is live!
1828 TNode<RawPtrT> string_data = UncheckedCast<RawPtrT>(
1829 to_direct.PointerToData(&fill_thehole_and_call_runtime));
1830 TNode<IntPtrT> string_data_offset = to_direct.offset();
1831 TNode<Object> cache = LoadRoot(Heap::kSingleCharacterStringCacheRootIndex);
1832
1833 BuildFastLoop(
1834 IntPtrConstant(0), length,
1835 [&](Node* index) {
1836 // TODO(jkummerow): Implement a CSA version of DisallowHeapAllocation
1837 // and use that to guard ToDirectStringAssembler.PointerToData().
1838 CSA_ASSERT(this, WordEqual(to_direct.PointerToData(&call_runtime),
1839 string_data));
1840 TNode<Int32T> char_code =
1841 UncheckedCast<Int32T>(Load(MachineType::Uint8(), string_data,
1842 IntPtrAdd(index, string_data_offset)));
1843 Node* code_index = ChangeUint32ToWord(char_code);
1844 TNode<Object> entry = LoadFixedArrayElement(CAST(cache), code_index);
1845
1846 // If we cannot find a char in the cache, fill the hole for the fixed
1847 // array, and call runtime.
1848 GotoIf(IsUndefined(entry), &fill_thehole_and_call_runtime);
1849
1850 StoreFixedArrayElement(elements, index, entry);
1851 },
1852 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
1853
1854 TNode<Map> array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, context);
1855 result_array = CAST(
1856 AllocateUninitializedJSArrayWithoutElements(array_map, length_smi));
1857 StoreObjectField(result_array.value(), JSObject::kElementsOffset, elements);
1858 Goto(&done);
1859
1860 BIND(&fill_thehole_and_call_runtime);
1861 {
1862 FillFixedArrayWithValue(PACKED_ELEMENTS, elements, IntPtrConstant(0),
1863 length, Heap::kTheHoleValueRootIndex);
1864 Goto(&call_runtime);
1865 }
1866 }
1867
1868 BIND(&call_runtime);
1869 {
1870 result_array = CAST(CallRuntime(Runtime::kStringToArray, context,
1871 subject_string, limit_number));
1872 Goto(&done);
1873 }
1874
1875 BIND(&done);
1876 return result_array.value();
1877 }
1878
1879 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
TF_BUILTIN(StringPrototypeSplit,StringBuiltinsAssembler)1880 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
1881 const int kSeparatorArg = 0;
1882 const int kLimitArg = 1;
1883
1884 Node* const argc =
1885 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1886 CodeStubArguments args(this, argc);
1887
1888 Node* const receiver = args.GetReceiver();
1889 Node* const separator = args.GetOptionalArgumentValue(kSeparatorArg);
1890 Node* const limit = args.GetOptionalArgumentValue(kLimitArg);
1891 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1892
1893 TNode<Smi> smi_zero = SmiConstant(0);
1894
1895 RequireObjectCoercible(context, receiver, "String.prototype.split");
1896
1897 // Redirect to splitter method if {separator[@@split]} is not undefined.
1898
1899 MaybeCallFunctionAtSymbol(
1900 context, separator, receiver, isolate()->factory()->split_symbol(),
1901 [&]() {
1902 args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context,
1903 separator, receiver, limit));
1904 },
1905 [&](Node* fn) {
1906 Callable call_callable = CodeFactory::Call(isolate());
1907 args.PopAndReturn(
1908 CallJS(call_callable, context, fn, separator, receiver, limit));
1909 });
1910
1911 // String and integer conversions.
1912
1913 TNode<String> subject_string = ToString_Inline(context, receiver);
1914 TNode<Number> limit_number = Select<Number>(
1915 IsUndefined(limit), [=] { return NumberConstant(kMaxUInt32); },
1916 [=] { return ToUint32(context, limit); });
1917 Node* const separator_string = ToString_Inline(context, separator);
1918
1919 Label return_empty_array(this);
1920
1921 // Shortcut for {limit} == 0.
1922 GotoIf(WordEqual<Object, Object>(limit_number, smi_zero),
1923 &return_empty_array);
1924
1925 // ECMA-262 says that if {separator} is undefined, the result should
1926 // be an array of size 1 containing the entire string.
1927 {
1928 Label next(this);
1929 GotoIfNot(IsUndefined(separator), &next);
1930
1931 const ElementsKind kind = PACKED_ELEMENTS;
1932 Node* const native_context = LoadNativeContext(context);
1933 Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
1934
1935 Node* const length = SmiConstant(1);
1936 Node* const capacity = IntPtrConstant(1);
1937 Node* const result = AllocateJSArray(kind, array_map, capacity, length);
1938
1939 Node* const fixed_array = LoadElements(result);
1940 StoreFixedArrayElement(fixed_array, 0, subject_string);
1941
1942 args.PopAndReturn(result);
1943
1944 BIND(&next);
1945 }
1946
1947 // If the separator string is empty then return the elements in the subject.
1948 {
1949 Label next(this);
1950 GotoIfNot(SmiEqual(LoadStringLengthAsSmi(separator_string), smi_zero),
1951 &next);
1952
1953 TNode<Smi> subject_length = LoadStringLengthAsSmi(subject_string);
1954 GotoIf(SmiEqual(subject_length, smi_zero), &return_empty_array);
1955
1956 args.PopAndReturn(
1957 StringToArray(context, subject_string, subject_length, limit_number));
1958
1959 BIND(&next);
1960 }
1961
1962 Node* const result =
1963 CallRuntime(Runtime::kStringSplit, context, subject_string,
1964 separator_string, limit_number);
1965 args.PopAndReturn(result);
1966
1967 BIND(&return_empty_array);
1968 {
1969 const ElementsKind kind = PACKED_ELEMENTS;
1970 Node* const native_context = LoadNativeContext(context);
1971 Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
1972
1973 Node* const length = smi_zero;
1974 Node* const capacity = IntPtrConstant(0);
1975 Node* const result = AllocateJSArray(kind, array_map, capacity, length);
1976
1977 args.PopAndReturn(result);
1978 }
1979 }
1980
1981 // ES6 #sec-string.prototype.substr
TF_BUILTIN(StringPrototypeSubstr,StringBuiltinsAssembler)1982 TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
1983 const int kStartArg = 0;
1984 const int kLengthArg = 1;
1985
1986 Node* const argc =
1987 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1988 CodeStubArguments args(this, argc);
1989
1990 Node* const receiver = args.GetReceiver();
1991 TNode<Object> start = args.GetOptionalArgumentValue(kStartArg);
1992 TNode<Object> length = args.GetOptionalArgumentValue(kLengthArg);
1993 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1994
1995 Label out(this);
1996
1997 TVARIABLE(IntPtrT, var_start);
1998 TVARIABLE(Number, var_length);
1999
2000 TNode<IntPtrT> const zero = IntPtrConstant(0);
2001
2002 // Check that {receiver} is coercible to Object and convert it to a String.
2003 TNode<String> const string =
2004 ToThisString(context, receiver, "String.prototype.substr");
2005
2006 TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
2007
2008 // Convert {start} to a relative index.
2009 var_start = ConvertToRelativeIndex(context, start, string_length);
2010
2011 // Conversions and bounds-checks for {length}.
2012 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
2013
2014 // Default to {string_length} if {length} is undefined.
2015 {
2016 Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
2017 Branch(IsUndefined(length), &if_isundefined, &if_isnotundefined);
2018
2019 BIND(&if_isundefined);
2020 var_length = SmiTag(string_length);
2021 Goto(&if_issmi);
2022
2023 BIND(&if_isnotundefined);
2024 var_length = ToInteger_Inline(context, length,
2025 CodeStubAssembler::kTruncateMinusZero);
2026 }
2027
2028 TVARIABLE(IntPtrT, var_result_length);
2029
2030 Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
2031
2032 // Set {length} to min(max({length}, 0), {string_length} - {start}
2033 BIND(&if_issmi);
2034 {
2035 TNode<IntPtrT> const positive_length =
2036 IntPtrMax(SmiUntag(CAST(var_length.value())), zero);
2037 TNode<IntPtrT> const minimal_length =
2038 IntPtrSub(string_length, var_start.value());
2039 var_result_length = IntPtrMin(positive_length, minimal_length);
2040
2041 GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
2042 args.PopAndReturn(EmptyStringConstant());
2043 }
2044
2045 BIND(&if_isheapnumber);
2046 {
2047 // If {length} is a heap number, it is definitely out of bounds. There are
2048 // two cases according to the spec: if it is negative, "" is returned; if
2049 // it is positive, then length is set to {string_length} - {start}.
2050
2051 CSA_ASSERT(this, IsHeapNumber(CAST(var_length.value())));
2052
2053 Label if_isnegative(this), if_ispositive(this);
2054 TNode<Float64T> const float_zero = Float64Constant(0.);
2055 TNode<Float64T> const length_float =
2056 LoadHeapNumberValue(CAST(var_length.value()));
2057 Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
2058 &if_ispositive);
2059
2060 BIND(&if_isnegative);
2061 args.PopAndReturn(EmptyStringConstant());
2062
2063 BIND(&if_ispositive);
2064 {
2065 var_result_length = IntPtrSub(string_length, var_start.value());
2066 GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
2067 args.PopAndReturn(EmptyStringConstant());
2068 }
2069 }
2070
2071 BIND(&out);
2072 {
2073 TNode<IntPtrT> const end =
2074 IntPtrAdd(var_start.value(), var_result_length.value());
2075 args.PopAndReturn(SubString(string, var_start.value(), end));
2076 }
2077 }
2078
ToSmiBetweenZeroAnd(SloppyTNode<Context> context,SloppyTNode<Object> value,SloppyTNode<Smi> limit)2079 TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
2080 SloppyTNode<Context> context, SloppyTNode<Object> value,
2081 SloppyTNode<Smi> limit) {
2082 Label out(this);
2083 TVARIABLE(Smi, var_result);
2084
2085 TNode<Number> const value_int =
2086 ToInteger_Inline(context, value, CodeStubAssembler::kTruncateMinusZero);
2087
2088 Label if_issmi(this), if_isnotsmi(this, Label::kDeferred);
2089 Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
2090
2091 BIND(&if_issmi);
2092 {
2093 TNode<Smi> value_smi = CAST(value_int);
2094 Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred);
2095 Branch(SmiAbove(value_smi, limit), &if_isoutofbounds, &if_isinbounds);
2096
2097 BIND(&if_isinbounds);
2098 {
2099 var_result = CAST(value_int);
2100 Goto(&out);
2101 }
2102
2103 BIND(&if_isoutofbounds);
2104 {
2105 TNode<Smi> const zero = SmiConstant(0);
2106 var_result =
2107 SelectConstant<Smi>(SmiLessThan(value_smi, zero), zero, limit);
2108 Goto(&out);
2109 }
2110 }
2111
2112 BIND(&if_isnotsmi);
2113 {
2114 // {value} is a heap number - in this case, it is definitely out of bounds.
2115 TNode<HeapNumber> value_int_hn = CAST(value_int);
2116
2117 TNode<Float64T> const float_zero = Float64Constant(0.);
2118 TNode<Smi> const smi_zero = SmiConstant(0);
2119 TNode<Float64T> const value_float = LoadHeapNumberValue(value_int_hn);
2120 var_result = SelectConstant<Smi>(Float64LessThan(value_float, float_zero),
2121 smi_zero, limit);
2122 Goto(&out);
2123 }
2124
2125 BIND(&out);
2126 return var_result.value();
2127 }
2128
TF_BUILTIN(StringSubstring,CodeStubAssembler)2129 TF_BUILTIN(StringSubstring, CodeStubAssembler) {
2130 TNode<String> string = CAST(Parameter(Descriptor::kString));
2131 TNode<IntPtrT> from = UncheckedCast<IntPtrT>(Parameter(Descriptor::kFrom));
2132 TNode<IntPtrT> to = UncheckedCast<IntPtrT>(Parameter(Descriptor::kTo));
2133
2134 Return(SubString(string, from, to));
2135 }
2136
2137 // ES6 #sec-string.prototype.substring
TF_BUILTIN(StringPrototypeSubstring,StringBuiltinsAssembler)2138 TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
2139 const int kStartArg = 0;
2140 const int kEndArg = 1;
2141
2142 Node* const argc =
2143 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2144 CodeStubArguments args(this, argc);
2145
2146 Node* const receiver = args.GetReceiver();
2147 Node* const start = args.GetOptionalArgumentValue(kStartArg);
2148 Node* const end = args.GetOptionalArgumentValue(kEndArg);
2149 Node* const context = Parameter(BuiltinDescriptor::kContext);
2150
2151 Label out(this);
2152
2153 TVARIABLE(Smi, var_start);
2154 TVARIABLE(Smi, var_end);
2155
2156 // Check that {receiver} is coercible to Object and convert it to a String.
2157 TNode<String> const string =
2158 ToThisString(context, receiver, "String.prototype.substring");
2159
2160 TNode<Smi> const length = LoadStringLengthAsSmi(string);
2161
2162 // Conversion and bounds-checks for {start}.
2163 var_start = ToSmiBetweenZeroAnd(context, start, length);
2164
2165 // Conversion and bounds-checks for {end}.
2166 {
2167 var_end = length;
2168 GotoIf(IsUndefined(end), &out);
2169
2170 var_end = ToSmiBetweenZeroAnd(context, end, length);
2171
2172 Label if_endislessthanstart(this);
2173 Branch(SmiLessThan(var_end.value(), var_start.value()),
2174 &if_endislessthanstart, &out);
2175
2176 BIND(&if_endislessthanstart);
2177 {
2178 TNode<Smi> const tmp = var_end.value();
2179 var_end = var_start.value();
2180 var_start = tmp;
2181 Goto(&out);
2182 }
2183 }
2184
2185 BIND(&out);
2186 {
2187 args.PopAndReturn(SubString(string, SmiUntag(var_start.value()),
2188 SmiUntag(var_end.value())));
2189 }
2190 }
2191
2192 // ES6 #sec-string.prototype.trim
TF_BUILTIN(StringPrototypeTrim,StringTrimAssembler)2193 TF_BUILTIN(StringPrototypeTrim, StringTrimAssembler) {
2194 Generate(String::kTrim, "String.prototype.trim");
2195 }
2196
2197 // https://github.com/tc39/proposal-string-left-right-trim
TF_BUILTIN(StringPrototypeTrimStart,StringTrimAssembler)2198 TF_BUILTIN(StringPrototypeTrimStart, StringTrimAssembler) {
2199 Generate(String::kTrimStart, "String.prototype.trimLeft");
2200 }
2201
2202 // https://github.com/tc39/proposal-string-left-right-trim
TF_BUILTIN(StringPrototypeTrimEnd,StringTrimAssembler)2203 TF_BUILTIN(StringPrototypeTrimEnd, StringTrimAssembler) {
2204 Generate(String::kTrimEnd, "String.prototype.trimRight");
2205 }
2206
Generate(String::TrimMode mode,const char * method_name)2207 void StringTrimAssembler::Generate(String::TrimMode mode,
2208 const char* method_name) {
2209 Label return_emptystring(this), if_runtime(this);
2210
2211 Node* const argc = Parameter(BuiltinDescriptor::kArgumentsCount);
2212 Node* const context = Parameter(BuiltinDescriptor::kContext);
2213 CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
2214 Node* const receiver = arguments.GetReceiver();
2215
2216 // Check that {receiver} is coercible to Object and convert it to a String.
2217 TNode<String> const string = ToThisString(context, receiver, method_name);
2218 TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
2219
2220 ToDirectStringAssembler to_direct(state(), string);
2221 to_direct.TryToDirect(&if_runtime);
2222 Node* const string_data = to_direct.PointerToData(&if_runtime);
2223 Node* const instance_type = to_direct.instance_type();
2224 Node* const is_stringonebyte = IsOneByteStringInstanceType(instance_type);
2225 Node* const string_data_offset = to_direct.offset();
2226
2227 TVARIABLE(IntPtrT, var_start, IntPtrConstant(0));
2228 TVARIABLE(IntPtrT, var_end, IntPtrSub(string_length, IntPtrConstant(1)));
2229
2230 if (mode == String::kTrimStart || mode == String::kTrim) {
2231 ScanForNonWhiteSpaceOrLineTerminator(string_data, string_data_offset,
2232 is_stringonebyte, &var_start,
2233 string_length, 1, &return_emptystring);
2234 }
2235 if (mode == String::kTrimEnd || mode == String::kTrim) {
2236 ScanForNonWhiteSpaceOrLineTerminator(
2237 string_data, string_data_offset, is_stringonebyte, &var_end,
2238 IntPtrConstant(-1), -1, &return_emptystring);
2239 }
2240
2241 arguments.PopAndReturn(
2242 SubString(string, var_start.value(),
2243 IntPtrAdd(var_end.value(), IntPtrConstant(1))));
2244
2245 BIND(&if_runtime);
2246 arguments.PopAndReturn(
2247 CallRuntime(Runtime::kStringTrim, context, string, SmiConstant(mode)));
2248
2249 BIND(&return_emptystring);
2250 arguments.PopAndReturn(EmptyStringConstant());
2251 }
2252
ScanForNonWhiteSpaceOrLineTerminator(Node * const string_data,Node * const string_data_offset,Node * const is_stringonebyte,Variable * const var_index,Node * const end,int increment,Label * const if_none_found)2253 void StringTrimAssembler::ScanForNonWhiteSpaceOrLineTerminator(
2254 Node* const string_data, Node* const string_data_offset,
2255 Node* const is_stringonebyte, Variable* const var_index, Node* const end,
2256 int increment, Label* const if_none_found) {
2257 Label if_stringisonebyte(this), out(this);
2258
2259 GotoIf(is_stringonebyte, &if_stringisonebyte);
2260
2261 // Two Byte String
2262 BuildLoop(
2263 var_index, end, increment, if_none_found, &out, [&](Node* const index) {
2264 return Load(
2265 MachineType::Uint16(), string_data,
2266 WordShl(IntPtrAdd(index, string_data_offset), IntPtrConstant(1)));
2267 });
2268
2269 BIND(&if_stringisonebyte);
2270 BuildLoop(var_index, end, increment, if_none_found, &out,
2271 [&](Node* const index) {
2272 return Load(MachineType::Uint8(), string_data,
2273 IntPtrAdd(index, string_data_offset));
2274 });
2275
2276 BIND(&out);
2277 }
2278
BuildLoop(Variable * const var_index,Node * const end,int increment,Label * const if_none_found,Label * const out,std::function<Node * (Node *)> get_character)2279 void StringTrimAssembler::BuildLoop(Variable* const var_index, Node* const end,
2280 int increment, Label* const if_none_found,
2281 Label* const out,
2282 std::function<Node*(Node*)> get_character) {
2283 Label loop(this, var_index);
2284 Goto(&loop);
2285 BIND(&loop);
2286 {
2287 Node* const index = var_index->value();
2288 GotoIf(IntPtrEqual(index, end), if_none_found);
2289 GotoIfNotWhiteSpaceOrLineTerminator(
2290 UncheckedCast<Uint32T>(get_character(index)), out);
2291 Increment(var_index, increment);
2292 Goto(&loop);
2293 }
2294 }
2295
GotoIfNotWhiteSpaceOrLineTerminator(Node * const char_code,Label * const if_not_whitespace)2296 void StringTrimAssembler::GotoIfNotWhiteSpaceOrLineTerminator(
2297 Node* const char_code, Label* const if_not_whitespace) {
2298 Label out(this);
2299
2300 // 0x0020 - SPACE (Intentionally out of order to fast path a commmon case)
2301 GotoIf(Word32Equal(char_code, Int32Constant(0x0020)), &out);
2302
2303 // 0x0009 - HORIZONTAL TAB
2304 GotoIf(Uint32LessThan(char_code, Int32Constant(0x0009)), if_not_whitespace);
2305 // 0x000A - LINE FEED OR NEW LINE
2306 // 0x000B - VERTICAL TAB
2307 // 0x000C - FORMFEED
2308 // 0x000D - HORIZONTAL TAB
2309 GotoIf(Uint32LessThanOrEqual(char_code, Int32Constant(0x000D)), &out);
2310
2311 // Common Non-whitespace characters
2312 GotoIf(Uint32LessThan(char_code, Int32Constant(0x00A0)), if_not_whitespace);
2313
2314 // 0x00A0 - NO-BREAK SPACE
2315 GotoIf(Word32Equal(char_code, Int32Constant(0x00A0)), &out);
2316
2317 // 0x1680 - Ogham Space Mark
2318 GotoIf(Word32Equal(char_code, Int32Constant(0x1680)), &out);
2319
2320 // 0x2000 - EN QUAD
2321 GotoIf(Uint32LessThan(char_code, Int32Constant(0x2000)), if_not_whitespace);
2322 // 0x2001 - EM QUAD
2323 // 0x2002 - EN SPACE
2324 // 0x2003 - EM SPACE
2325 // 0x2004 - THREE-PER-EM SPACE
2326 // 0x2005 - FOUR-PER-EM SPACE
2327 // 0x2006 - SIX-PER-EM SPACE
2328 // 0x2007 - FIGURE SPACE
2329 // 0x2008 - PUNCTUATION SPACE
2330 // 0x2009 - THIN SPACE
2331 // 0x200A - HAIR SPACE
2332 GotoIf(Uint32LessThanOrEqual(char_code, Int32Constant(0x200A)), &out);
2333
2334 // 0x2028 - LINE SEPARATOR
2335 GotoIf(Word32Equal(char_code, Int32Constant(0x2028)), &out);
2336 // 0x2029 - PARAGRAPH SEPARATOR
2337 GotoIf(Word32Equal(char_code, Int32Constant(0x2029)), &out);
2338 // 0x202F - NARROW NO-BREAK SPACE
2339 GotoIf(Word32Equal(char_code, Int32Constant(0x202F)), &out);
2340 // 0x205F - MEDIUM MATHEMATICAL SPACE
2341 GotoIf(Word32Equal(char_code, Int32Constant(0x205F)), &out);
2342 // 0xFEFF - BYTE ORDER MARK
2343 GotoIf(Word32Equal(char_code, Int32Constant(0xFEFF)), &out);
2344 // 0x3000 - IDEOGRAPHIC SPACE
2345 Branch(Word32Equal(char_code, Int32Constant(0x3000)), &out,
2346 if_not_whitespace);
2347
2348 BIND(&out);
2349 }
2350
2351 // ES6 #sec-string.prototype.tostring
TF_BUILTIN(StringPrototypeToString,CodeStubAssembler)2352 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) {
2353 Node* context = Parameter(Descriptor::kContext);
2354 Node* receiver = Parameter(Descriptor::kReceiver);
2355
2356 Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
2357 "String.prototype.toString");
2358 Return(result);
2359 }
2360
2361 // ES6 #sec-string.prototype.valueof
TF_BUILTIN(StringPrototypeValueOf,CodeStubAssembler)2362 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) {
2363 Node* context = Parameter(Descriptor::kContext);
2364 Node* receiver = Parameter(Descriptor::kReceiver);
2365
2366 Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
2367 "String.prototype.valueOf");
2368 Return(result);
2369 }
2370
TF_BUILTIN(StringPrototypeIterator,CodeStubAssembler)2371 TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) {
2372 Node* context = Parameter(Descriptor::kContext);
2373 Node* receiver = Parameter(Descriptor::kReceiver);
2374
2375 Node* string =
2376 ToThisString(context, receiver, "String.prototype[Symbol.iterator]");
2377
2378 Node* native_context = LoadNativeContext(context);
2379 Node* map =
2380 LoadContextElement(native_context, Context::STRING_ITERATOR_MAP_INDEX);
2381 Node* iterator = Allocate(JSStringIterator::kSize);
2382 StoreMapNoWriteBarrier(iterator, map);
2383 StoreObjectFieldRoot(iterator, JSValue::kPropertiesOrHashOffset,
2384 Heap::kEmptyFixedArrayRootIndex);
2385 StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
2386 Heap::kEmptyFixedArrayRootIndex);
2387 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset,
2388 string);
2389 Node* index = SmiConstant(0);
2390 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
2391 index);
2392 Return(iterator);
2393 }
2394
2395 // Return the |word32| codepoint at {index}. Supports SeqStrings and
2396 // ExternalStrings.
LoadSurrogatePairAt(SloppyTNode<String> string,SloppyTNode<IntPtrT> length,SloppyTNode<IntPtrT> index,UnicodeEncoding encoding)2397 TNode<Int32T> StringBuiltinsAssembler::LoadSurrogatePairAt(
2398 SloppyTNode<String> string, SloppyTNode<IntPtrT> length,
2399 SloppyTNode<IntPtrT> index, UnicodeEncoding encoding) {
2400 Label handle_surrogate_pair(this), return_result(this);
2401 TVARIABLE(Int32T, var_result);
2402 TVARIABLE(Int32T, var_trail);
2403 var_result = StringCharCodeAt(string, index);
2404 var_trail = Int32Constant(0);
2405
2406 GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)),
2407 Int32Constant(0xD800)),
2408 &return_result);
2409 TNode<IntPtrT> next_index = IntPtrAdd(index, IntPtrConstant(1));
2410
2411 GotoIfNot(IntPtrLessThan(next_index, length), &return_result);
2412 var_trail = StringCharCodeAt(string, next_index);
2413 Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)),
2414 Int32Constant(0xDC00)),
2415 &handle_surrogate_pair, &return_result);
2416
2417 BIND(&handle_surrogate_pair);
2418 {
2419 TNode<Int32T> lead = var_result.value();
2420 TNode<Int32T> trail = var_trail.value();
2421
2422 // Check that this path is only taken if a surrogate pair is found
2423 CSA_SLOW_ASSERT(this,
2424 Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800)));
2425 CSA_SLOW_ASSERT(this, Uint32LessThan(lead, Int32Constant(0xDC00)));
2426 CSA_SLOW_ASSERT(this,
2427 Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00)));
2428 CSA_SLOW_ASSERT(this, Uint32LessThan(trail, Int32Constant(0xE000)));
2429
2430 switch (encoding) {
2431 case UnicodeEncoding::UTF16:
2432 var_result = Signed(Word32Or(
2433 // Need to swap the order for big-endian platforms
2434 #if V8_TARGET_BIG_ENDIAN
2435 Word32Shl(lead, Int32Constant(16)), trail));
2436 #else
2437 Word32Shl(trail, Int32Constant(16)), lead));
2438 #endif
2439 break;
2440
2441 case UnicodeEncoding::UTF32: {
2442 // Convert UTF16 surrogate pair into |word32| code point, encoded as
2443 // UTF32.
2444 TNode<Int32T> surrogate_offset =
2445 Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
2446
2447 // (lead << 10) + trail + SURROGATE_OFFSET
2448 var_result = Signed(Int32Add(Word32Shl(lead, Int32Constant(10)),
2449 Int32Add(trail, surrogate_offset)));
2450 break;
2451 }
2452 }
2453 Goto(&return_result);
2454 }
2455
2456 BIND(&return_result);
2457 return var_result.value();
2458 }
2459
2460 // ES6 #sec-%stringiteratorprototype%.next
TF_BUILTIN(StringIteratorPrototypeNext,StringBuiltinsAssembler)2461 TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) {
2462 VARIABLE(var_value, MachineRepresentation::kTagged);
2463 VARIABLE(var_done, MachineRepresentation::kTagged);
2464
2465 var_value.Bind(UndefinedConstant());
2466 var_done.Bind(TrueConstant());
2467
2468 Label throw_bad_receiver(this), next_codepoint(this), return_result(this);
2469
2470 Node* context = Parameter(Descriptor::kContext);
2471 Node* iterator = Parameter(Descriptor::kReceiver);
2472
2473 GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver);
2474 GotoIfNot(
2475 InstanceTypeEqual(LoadInstanceType(iterator), JS_STRING_ITERATOR_TYPE),
2476 &throw_bad_receiver);
2477
2478 Node* string = LoadObjectField(iterator, JSStringIterator::kStringOffset);
2479 TNode<IntPtrT> position = SmiUntag(
2480 CAST(LoadObjectField(iterator, JSStringIterator::kNextIndexOffset)));
2481 TNode<IntPtrT> length = LoadStringLengthAsWord(string);
2482
2483 Branch(IntPtrLessThan(position, length), &next_codepoint, &return_result);
2484
2485 BIND(&next_codepoint);
2486 {
2487 UnicodeEncoding encoding = UnicodeEncoding::UTF16;
2488 TNode<Int32T> ch = LoadSurrogatePairAt(string, length, position, encoding);
2489 TNode<String> value = StringFromSingleCodePoint(ch, encoding);
2490 var_value.Bind(value);
2491 TNode<IntPtrT> length = LoadStringLengthAsWord(value);
2492 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
2493 SmiTag(Signed(IntPtrAdd(position, length))));
2494 var_done.Bind(FalseConstant());
2495 Goto(&return_result);
2496 }
2497
2498 BIND(&return_result);
2499 {
2500 Node* result =
2501 AllocateJSIteratorResult(context, var_value.value(), var_done.value());
2502 Return(result);
2503 }
2504
2505 BIND(&throw_bad_receiver);
2506 {
2507 // The {receiver} is not a valid JSGeneratorObject.
2508 ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
2509 StringConstant("String Iterator.prototype.next"), iterator);
2510 }
2511 }
2512
2513 // -----------------------------------------------------------------------------
2514 // ES6 section B.2.3 Additional Properties of the String.prototype object
2515
2516 class StringHtmlAssembler : public StringBuiltinsAssembler {
2517 public:
StringHtmlAssembler(compiler::CodeAssemblerState * state)2518 explicit StringHtmlAssembler(compiler::CodeAssemblerState* state)
2519 : StringBuiltinsAssembler(state) {}
2520
2521 protected:
Generate(Node * const context,Node * const receiver,const char * method_name,const char * tag_name)2522 void Generate(Node* const context, Node* const receiver,
2523 const char* method_name, const char* tag_name) {
2524 Node* const string = ToThisString(context, receiver, method_name);
2525 std::string open_tag = "<" + std::string(tag_name) + ">";
2526 std::string close_tag = "</" + std::string(tag_name) + ">";
2527
2528 Node* strings[] = {StringConstant(open_tag.c_str()), string,
2529 StringConstant(close_tag.c_str())};
2530 Return(ConcatStrings(context, strings, arraysize(strings)));
2531 }
2532
GenerateWithAttribute(Node * const context,Node * const receiver,const char * method_name,const char * tag_name,const char * attr,Node * const value)2533 void GenerateWithAttribute(Node* const context, Node* const receiver,
2534 const char* method_name, const char* tag_name,
2535 const char* attr, Node* const value) {
2536 Node* const string = ToThisString(context, receiver, method_name);
2537 Node* const value_string =
2538 EscapeQuotes(context, ToString_Inline(context, value));
2539 std::string open_tag_attr =
2540 "<" + std::string(tag_name) + " " + std::string(attr) + "=\"";
2541 std::string close_tag = "</" + std::string(tag_name) + ">";
2542
2543 Node* strings[] = {StringConstant(open_tag_attr.c_str()), value_string,
2544 StringConstant("\">"), string,
2545 StringConstant(close_tag.c_str())};
2546 Return(ConcatStrings(context, strings, arraysize(strings)));
2547 }
2548
ConcatStrings(Node * const context,Node ** strings,int len)2549 Node* ConcatStrings(Node* const context, Node** strings, int len) {
2550 VARIABLE(var_result, MachineRepresentation::kTagged, strings[0]);
2551 for (int i = 1; i < len; i++) {
2552 var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
2553 var_result.value(), strings[i]));
2554 }
2555 return var_result.value();
2556 }
2557
EscapeQuotes(Node * const context,Node * const string)2558 Node* EscapeQuotes(Node* const context, Node* const string) {
2559 CSA_ASSERT(this, IsString(string));
2560 Node* const regexp_function = LoadContextElement(
2561 LoadNativeContext(context), Context::REGEXP_FUNCTION_INDEX);
2562 Node* const initial_map = LoadObjectField(
2563 regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
2564 // TODO(pwong): Refactor to not allocate RegExp
2565 Node* const regexp =
2566 CallRuntime(Runtime::kRegExpInitializeAndCompile, context,
2567 AllocateJSObjectFromMap(initial_map), StringConstant("\""),
2568 StringConstant("g"));
2569
2570 return CallRuntime(Runtime::kRegExpInternalReplace, context, regexp, string,
2571 StringConstant("""));
2572 }
2573 };
2574
2575 // ES6 #sec-string.prototype.anchor
TF_BUILTIN(StringPrototypeAnchor,StringHtmlAssembler)2576 TF_BUILTIN(StringPrototypeAnchor, StringHtmlAssembler) {
2577 Node* const context = Parameter(Descriptor::kContext);
2578 Node* const receiver = Parameter(Descriptor::kReceiver);
2579 Node* const value = Parameter(Descriptor::kValue);
2580 GenerateWithAttribute(context, receiver, "String.prototype.anchor", "a",
2581 "name", value);
2582 }
2583
2584 // ES6 #sec-string.prototype.big
TF_BUILTIN(StringPrototypeBig,StringHtmlAssembler)2585 TF_BUILTIN(StringPrototypeBig, StringHtmlAssembler) {
2586 Node* const context = Parameter(Descriptor::kContext);
2587 Node* const receiver = Parameter(Descriptor::kReceiver);
2588 Generate(context, receiver, "String.prototype.big", "big");
2589 }
2590
2591 // ES6 #sec-string.prototype.blink
TF_BUILTIN(StringPrototypeBlink,StringHtmlAssembler)2592 TF_BUILTIN(StringPrototypeBlink, StringHtmlAssembler) {
2593 Node* const context = Parameter(Descriptor::kContext);
2594 Node* const receiver = Parameter(Descriptor::kReceiver);
2595 Generate(context, receiver, "String.prototype.blink", "blink");
2596 }
2597
2598 // ES6 #sec-string.prototype.bold
TF_BUILTIN(StringPrototypeBold,StringHtmlAssembler)2599 TF_BUILTIN(StringPrototypeBold, StringHtmlAssembler) {
2600 Node* const context = Parameter(Descriptor::kContext);
2601 Node* const receiver = Parameter(Descriptor::kReceiver);
2602 Generate(context, receiver, "String.prototype.bold", "b");
2603 }
2604
2605 // ES6 #sec-string.prototype.fontcolor
TF_BUILTIN(StringPrototypeFontcolor,StringHtmlAssembler)2606 TF_BUILTIN(StringPrototypeFontcolor, StringHtmlAssembler) {
2607 Node* const context = Parameter(Descriptor::kContext);
2608 Node* const receiver = Parameter(Descriptor::kReceiver);
2609 Node* const value = Parameter(Descriptor::kValue);
2610 GenerateWithAttribute(context, receiver, "String.prototype.fontcolor", "font",
2611 "color", value);
2612 }
2613
2614 // ES6 #sec-string.prototype.fontsize
TF_BUILTIN(StringPrototypeFontsize,StringHtmlAssembler)2615 TF_BUILTIN(StringPrototypeFontsize, StringHtmlAssembler) {
2616 Node* const context = Parameter(Descriptor::kContext);
2617 Node* const receiver = Parameter(Descriptor::kReceiver);
2618 Node* const value = Parameter(Descriptor::kValue);
2619 GenerateWithAttribute(context, receiver, "String.prototype.fontsize", "font",
2620 "size", value);
2621 }
2622
2623 // ES6 #sec-string.prototype.fixed
TF_BUILTIN(StringPrototypeFixed,StringHtmlAssembler)2624 TF_BUILTIN(StringPrototypeFixed, StringHtmlAssembler) {
2625 Node* const context = Parameter(Descriptor::kContext);
2626 Node* const receiver = Parameter(Descriptor::kReceiver);
2627 Generate(context, receiver, "String.prototype.fixed", "tt");
2628 }
2629
2630 // ES6 #sec-string.prototype.italics
TF_BUILTIN(StringPrototypeItalics,StringHtmlAssembler)2631 TF_BUILTIN(StringPrototypeItalics, StringHtmlAssembler) {
2632 Node* const context = Parameter(Descriptor::kContext);
2633 Node* const receiver = Parameter(Descriptor::kReceiver);
2634 Generate(context, receiver, "String.prototype.italics", "i");
2635 }
2636
2637 // ES6 #sec-string.prototype.link
TF_BUILTIN(StringPrototypeLink,StringHtmlAssembler)2638 TF_BUILTIN(StringPrototypeLink, StringHtmlAssembler) {
2639 Node* const context = Parameter(Descriptor::kContext);
2640 Node* const receiver = Parameter(Descriptor::kReceiver);
2641 Node* const value = Parameter(Descriptor::kValue);
2642 GenerateWithAttribute(context, receiver, "String.prototype.link", "a", "href",
2643 value);
2644 }
2645
2646 // ES6 #sec-string.prototype.small
TF_BUILTIN(StringPrototypeSmall,StringHtmlAssembler)2647 TF_BUILTIN(StringPrototypeSmall, StringHtmlAssembler) {
2648 Node* const context = Parameter(Descriptor::kContext);
2649 Node* const receiver = Parameter(Descriptor::kReceiver);
2650 Generate(context, receiver, "String.prototype.small", "small");
2651 }
2652
2653 // ES6 #sec-string.prototype.strike
TF_BUILTIN(StringPrototypeStrike,StringHtmlAssembler)2654 TF_BUILTIN(StringPrototypeStrike, StringHtmlAssembler) {
2655 Node* const context = Parameter(Descriptor::kContext);
2656 Node* const receiver = Parameter(Descriptor::kReceiver);
2657 Generate(context, receiver, "String.prototype.strike", "strike");
2658 }
2659
2660 // ES6 #sec-string.prototype.sub
TF_BUILTIN(StringPrototypeSub,StringHtmlAssembler)2661 TF_BUILTIN(StringPrototypeSub, StringHtmlAssembler) {
2662 Node* const context = Parameter(Descriptor::kContext);
2663 Node* const receiver = Parameter(Descriptor::kReceiver);
2664 Generate(context, receiver, "String.prototype.sub", "sub");
2665 }
2666
2667 // ES6 #sec-string.prototype.sup
TF_BUILTIN(StringPrototypeSup,StringHtmlAssembler)2668 TF_BUILTIN(StringPrototypeSup, StringHtmlAssembler) {
2669 Node* const context = Parameter(Descriptor::kContext);
2670 Node* const receiver = Parameter(Descriptor::kReceiver);
2671 Generate(context, receiver, "String.prototype.sup", "sup");
2672 }
2673
2674 } // namespace internal
2675 } // namespace v8
2676