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