Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1221)

Side by Side Diff: src/builtins/builtins-string.cc

Issue 2752143004: [refactor] Separate generated builtins and C++ builtins into separate files (Closed)
Patch Set: tentative gcmole fix Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/builtins/builtins-sharedarraybuffer-gen.cc ('k') | src/builtins/builtins-string-gen.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/builtins/builtins-regexp.h"
6 #include "src/builtins/builtins-utils.h" 5 #include "src/builtins/builtins-utils.h"
7 #include "src/builtins/builtins.h" 6 #include "src/builtins/builtins.h"
8 #include "src/code-factory.h"
9 #include "src/code-stub-assembler.h"
10 #include "src/conversions.h" 7 #include "src/conversions.h"
11 #include "src/counters.h" 8 #include "src/counters.h"
12 #include "src/objects-inl.h" 9 #include "src/objects-inl.h"
13 #include "src/regexp/regexp-utils.h" 10 #include "src/regexp/regexp-utils.h"
14 #include "src/string-case.h" 11 #include "src/string-case.h"
15 #include "src/unicode-inl.h" 12 #include "src/unicode-inl.h"
16 #include "src/unicode.h" 13 #include "src/unicode.h"
17 14
18 namespace v8 { 15 namespace v8 {
19 namespace internal { 16 namespace internal {
20 17
21 typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode;
22
23 class StringBuiltinsAssembler : public CodeStubAssembler {
24 public:
25 explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state)
26 : CodeStubAssembler(state) {}
27
28 protected:
29 Node* DirectStringData(Node* string, Node* string_instance_type) {
30 // Compute the effective offset of the first character.
31 Variable var_data(this, MachineType::PointerRepresentation());
32 Label if_sequential(this), if_external(this), if_join(this);
33 Branch(Word32Equal(Word32And(string_instance_type,
34 Int32Constant(kStringRepresentationMask)),
35 Int32Constant(kSeqStringTag)),
36 &if_sequential, &if_external);
37
38 Bind(&if_sequential);
39 {
40 var_data.Bind(IntPtrAdd(
41 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
42 BitcastTaggedToWord(string)));
43 Goto(&if_join);
44 }
45
46 Bind(&if_external);
47 {
48 // This is only valid for ExternalStrings where the resource data
49 // pointer is cached (i.e. no short external strings).
50 CSA_ASSERT(this, Word32NotEqual(
51 Word32And(string_instance_type,
52 Int32Constant(kShortExternalStringMask)),
53 Int32Constant(kShortExternalStringTag)));
54 var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
55 MachineType::Pointer()));
56 Goto(&if_join);
57 }
58
59 Bind(&if_join);
60 return var_data.value();
61 }
62
63 Node* LoadOneByteChar(Node* string, Node* index) {
64 return Load(MachineType::Uint8(), string, OneByteCharOffset(index));
65 }
66
67 Node* OneByteCharAddress(Node* string, Node* index) {
68 Node* offset = OneByteCharOffset(index);
69 return IntPtrAdd(string, offset);
70 }
71
72 Node* OneByteCharOffset(Node* index) {
73 return CharOffset(String::ONE_BYTE_ENCODING, index);
74 }
75
76 Node* CharOffset(String::Encoding encoding, Node* index) {
77 const int header = SeqOneByteString::kHeaderSize - kHeapObjectTag;
78 Node* offset = index;
79 if (encoding == String::TWO_BYTE_ENCODING) {
80 offset = IntPtrAdd(offset, offset);
81 }
82 offset = IntPtrAdd(offset, IntPtrConstant(header));
83 return offset;
84 }
85
86 void DispatchOnStringInstanceType(Node* const instance_type,
87 Label* if_onebyte_sequential,
88 Label* if_onebyte_external,
89 Label* if_otherwise) {
90 const int kMask = kStringRepresentationMask | kStringEncodingMask;
91 Node* const encoding_and_representation =
92 Word32And(instance_type, Int32Constant(kMask));
93
94 int32_t values[] = {
95 kOneByteStringTag | kSeqStringTag,
96 kOneByteStringTag | kExternalStringTag,
97 };
98 Label* labels[] = {
99 if_onebyte_sequential, if_onebyte_external,
100 };
101 STATIC_ASSERT(arraysize(values) == arraysize(labels));
102
103 Switch(encoding_and_representation, if_otherwise, values, labels,
104 arraysize(values));
105 }
106
107 void GenerateStringEqual();
108 void GenerateStringRelationalComparison(RelationalComparisonMode mode);
109
110 Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit);
111
112 Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index,
113 UnicodeEncoding encoding);
114
115 void StringIndexOf(Node* receiver, Node* instance_type, Node* search_string,
116 Node* search_string_instance_type, Node* position,
117 std::function<void(Node*)> f_return);
118
119 Node* IsNullOrUndefined(Node* const value);
120 void RequireObjectCoercible(Node* const context, Node* const value,
121 const char* method_name);
122
123 Node* SmiIsNegative(Node* const value) {
124 return SmiLessThan(value, SmiConstant(0));
125 }
126
127 // Implements boilerplate logic for {match, split, replace, search} of the
128 // form:
129 //
130 // if (!IS_NULL_OR_UNDEFINED(object)) {
131 // var maybe_function = object[symbol];
132 // if (!IS_UNDEFINED(maybe_function)) {
133 // return %_Call(maybe_function, ...);
134 // }
135 // }
136 //
137 // Contains fast paths for Smi and RegExp objects.
138 typedef std::function<Node*()> NodeFunction0;
139 typedef std::function<Node*(Node* fn)> NodeFunction1;
140 void MaybeCallFunctionAtSymbol(Node* const context, Node* const object,
141 Handle<Symbol> symbol,
142 const NodeFunction0& regexp_call,
143 const NodeFunction1& generic_call);
144 };
145
146 void StringBuiltinsAssembler::GenerateStringEqual() {
147 // Here's pseudo-code for the algorithm below:
148 //
149 // if (lhs == rhs) return true;
150 // if (lhs->length() != rhs->length()) return false;
151 // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) {
152 // return false;
153 // }
154 // if (lhs->IsSeqOneByteString() && rhs->IsSeqOneByteString()) {
155 // for (i = 0; i != lhs->length(); ++i) {
156 // if (lhs[i] != rhs[i]) return false;
157 // }
158 // return true;
159 // }
160 // if (lhs and/or rhs are indirect strings) {
161 // unwrap them and restart from the beginning;
162 // }
163 // return %StringEqual(lhs, rhs);
164
165 Variable var_left(this, MachineRepresentation::kTagged);
166 Variable var_right(this, MachineRepresentation::kTagged);
167 var_left.Bind(Parameter(0));
168 var_right.Bind(Parameter(1));
169 Node* context = Parameter(2);
170
171 Variable* input_vars[2] = {&var_left, &var_right};
172 Label if_equal(this), if_notequal(this), restart(this, 2, input_vars);
173 Goto(&restart);
174 Bind(&restart);
175 Node* lhs = var_left.value();
176 Node* rhs = var_right.value();
177
178 // Fast check to see if {lhs} and {rhs} refer to the same String object.
179 GotoIf(WordEqual(lhs, rhs), &if_equal);
180
181 // Load the length of {lhs} and {rhs}.
182 Node* lhs_length = LoadStringLength(lhs);
183 Node* rhs_length = LoadStringLength(rhs);
184
185 // Strings with different lengths cannot be equal.
186 GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
187
188 // Load instance types of {lhs} and {rhs}.
189 Node* lhs_instance_type = LoadInstanceType(lhs);
190 Node* rhs_instance_type = LoadInstanceType(rhs);
191
192 // Combine the instance types into a single 16-bit value, so we can check
193 // both of them at once.
194 Node* both_instance_types = Word32Or(
195 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
196
197 // Check if both {lhs} and {rhs} are internalized. Since we already know
198 // that they're not the same object, they're not equal in that case.
199 int const kBothInternalizedMask =
200 kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
201 int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
202 GotoIf(Word32Equal(Word32And(both_instance_types,
203 Int32Constant(kBothInternalizedMask)),
204 Int32Constant(kBothInternalizedTag)),
205 &if_notequal);
206
207 // Check that both {lhs} and {rhs} are flat one-byte strings, and that
208 // in case of ExternalStrings the data pointer is cached..
209 STATIC_ASSERT(kShortExternalStringTag != 0);
210 int const kBothDirectOneByteStringMask =
211 kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask |
212 ((kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask)
213 << 8);
214 int const kBothDirectOneByteStringTag =
215 kOneByteStringTag | (kOneByteStringTag << 8);
216 Label if_bothdirectonebytestrings(this), if_notbothdirectonebytestrings(this);
217 Branch(Word32Equal(Word32And(both_instance_types,
218 Int32Constant(kBothDirectOneByteStringMask)),
219 Int32Constant(kBothDirectOneByteStringTag)),
220 &if_bothdirectonebytestrings, &if_notbothdirectonebytestrings);
221
222 Bind(&if_bothdirectonebytestrings);
223 {
224 // Compute the effective offset of the first character.
225 Node* lhs_data = DirectStringData(lhs, lhs_instance_type);
226 Node* rhs_data = DirectStringData(rhs, rhs_instance_type);
227
228 // Compute the first offset after the string from the length.
229 Node* length = SmiUntag(lhs_length);
230
231 // Loop over the {lhs} and {rhs} strings to see if they are equal.
232 Variable var_offset(this, MachineType::PointerRepresentation());
233 Label loop(this, &var_offset);
234 var_offset.Bind(IntPtrConstant(0));
235 Goto(&loop);
236 Bind(&loop);
237 {
238 // If {offset} equals {end}, no difference was found, so the
239 // strings are equal.
240 Node* offset = var_offset.value();
241 GotoIf(WordEqual(offset, length), &if_equal);
242
243 // Load the next characters from {lhs} and {rhs}.
244 Node* lhs_value = Load(MachineType::Uint8(), lhs_data, offset);
245 Node* rhs_value = Load(MachineType::Uint8(), rhs_data, offset);
246
247 // Check if the characters match.
248 GotoIf(Word32NotEqual(lhs_value, rhs_value), &if_notequal);
249
250 // Advance to next character.
251 var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
252 Goto(&loop);
253 }
254 }
255
256 Bind(&if_notbothdirectonebytestrings);
257 {
258 // Try to unwrap indirect strings, restart the above attempt on success.
259 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
260 rhs_instance_type, &restart);
261 // TODO(bmeurer): Add support for two byte string equality checks.
262
263 TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs);
264 }
265
266 Bind(&if_equal);
267 Return(TrueConstant());
268
269 Bind(&if_notequal);
270 Return(FalseConstant());
271 }
272
273 void StringBuiltinsAssembler::GenerateStringRelationalComparison(
274 RelationalComparisonMode mode) {
275 Variable var_left(this, MachineRepresentation::kTagged);
276 Variable var_right(this, MachineRepresentation::kTagged);
277 var_left.Bind(Parameter(0));
278 var_right.Bind(Parameter(1));
279 Node* context = Parameter(2);
280
281 Variable* input_vars[2] = {&var_left, &var_right};
282 Label if_less(this), if_equal(this), if_greater(this);
283 Label restart(this, 2, input_vars);
284 Goto(&restart);
285 Bind(&restart);
286
287 Node* lhs = var_left.value();
288 Node* rhs = var_right.value();
289 // Fast check to see if {lhs} and {rhs} refer to the same String object.
290 GotoIf(WordEqual(lhs, rhs), &if_equal);
291
292 // Load instance types of {lhs} and {rhs}.
293 Node* lhs_instance_type = LoadInstanceType(lhs);
294 Node* rhs_instance_type = LoadInstanceType(rhs);
295
296 // Combine the instance types into a single 16-bit value, so we can check
297 // both of them at once.
298 Node* both_instance_types = Word32Or(
299 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
300
301 // Check that both {lhs} and {rhs} are flat one-byte strings.
302 int const kBothSeqOneByteStringMask =
303 kStringEncodingMask | kStringRepresentationMask |
304 ((kStringEncodingMask | kStringRepresentationMask) << 8);
305 int const kBothSeqOneByteStringTag =
306 kOneByteStringTag | kSeqStringTag |
307 ((kOneByteStringTag | kSeqStringTag) << 8);
308 Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this);
309 Branch(Word32Equal(Word32And(both_instance_types,
310 Int32Constant(kBothSeqOneByteStringMask)),
311 Int32Constant(kBothSeqOneByteStringTag)),
312 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
313
314 Bind(&if_bothonebyteseqstrings);
315 {
316 // Load the length of {lhs} and {rhs}.
317 Node* lhs_length = LoadStringLength(lhs);
318 Node* rhs_length = LoadStringLength(rhs);
319
320 // Determine the minimum length.
321 Node* length = SmiMin(lhs_length, rhs_length);
322
323 // Compute the effective offset of the first character.
324 Node* begin =
325 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag);
326
327 // Compute the first offset after the string from the length.
328 Node* end = IntPtrAdd(begin, SmiUntag(length));
329
330 // Loop over the {lhs} and {rhs} strings to see if they are equal.
331 Variable var_offset(this, MachineType::PointerRepresentation());
332 Label loop(this, &var_offset);
333 var_offset.Bind(begin);
334 Goto(&loop);
335 Bind(&loop);
336 {
337 // Check if {offset} equals {end}.
338 Node* offset = var_offset.value();
339 Label if_done(this), if_notdone(this);
340 Branch(WordEqual(offset, end), &if_done, &if_notdone);
341
342 Bind(&if_notdone);
343 {
344 // Load the next characters from {lhs} and {rhs}.
345 Node* lhs_value = Load(MachineType::Uint8(), lhs, offset);
346 Node* rhs_value = Load(MachineType::Uint8(), rhs, offset);
347
348 // Check if the characters match.
349 Label if_valueissame(this), if_valueisnotsame(this);
350 Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame,
351 &if_valueisnotsame);
352
353 Bind(&if_valueissame);
354 {
355 // Advance to next character.
356 var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
357 }
358 Goto(&loop);
359
360 Bind(&if_valueisnotsame);
361 Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater);
362 }
363
364 Bind(&if_done);
365 {
366 // All characters up to the min length are equal, decide based on
367 // string length.
368 GotoIf(SmiEqual(lhs_length, rhs_length), &if_equal);
369 BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, &if_greater);
370 }
371 }
372 }
373
374 Bind(&if_notbothonebyteseqstrings);
375 {
376 // Try to unwrap indirect strings, restart the above attempt on success.
377 MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
378 rhs_instance_type, &restart);
379 // TODO(bmeurer): Add support for two byte string relational comparisons.
380 switch (mode) {
381 case RelationalComparisonMode::kLessThan:
382 TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs);
383 break;
384 case RelationalComparisonMode::kLessThanOrEqual:
385 TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs);
386 break;
387 case RelationalComparisonMode::kGreaterThan:
388 TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs);
389 break;
390 case RelationalComparisonMode::kGreaterThanOrEqual:
391 TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs,
392 rhs);
393 break;
394 }
395 }
396
397 Bind(&if_less);
398 switch (mode) {
399 case RelationalComparisonMode::kLessThan:
400 case RelationalComparisonMode::kLessThanOrEqual:
401 Return(BooleanConstant(true));
402 break;
403
404 case RelationalComparisonMode::kGreaterThan:
405 case RelationalComparisonMode::kGreaterThanOrEqual:
406 Return(BooleanConstant(false));
407 break;
408 }
409
410 Bind(&if_equal);
411 switch (mode) {
412 case RelationalComparisonMode::kLessThan:
413 case RelationalComparisonMode::kGreaterThan:
414 Return(BooleanConstant(false));
415 break;
416
417 case RelationalComparisonMode::kLessThanOrEqual:
418 case RelationalComparisonMode::kGreaterThanOrEqual:
419 Return(BooleanConstant(true));
420 break;
421 }
422
423 Bind(&if_greater);
424 switch (mode) {
425 case RelationalComparisonMode::kLessThan:
426 case RelationalComparisonMode::kLessThanOrEqual:
427 Return(BooleanConstant(false));
428 break;
429
430 case RelationalComparisonMode::kGreaterThan:
431 case RelationalComparisonMode::kGreaterThanOrEqual:
432 Return(BooleanConstant(true));
433 break;
434 }
435 }
436
437 TF_BUILTIN(StringEqual, StringBuiltinsAssembler) { GenerateStringEqual(); }
438
439 TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) {
440 GenerateStringRelationalComparison(RelationalComparisonMode::kLessThan);
441 }
442
443 TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
444 GenerateStringRelationalComparison(
445 RelationalComparisonMode::kLessThanOrEqual);
446 }
447
448 TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) {
449 GenerateStringRelationalComparison(RelationalComparisonMode::kGreaterThan);
450 }
451
452 TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
453 GenerateStringRelationalComparison(
454 RelationalComparisonMode::kGreaterThanOrEqual);
455 }
456
457 TF_BUILTIN(StringCharAt, CodeStubAssembler) {
458 Node* receiver = Parameter(0);
459 Node* position = Parameter(1);
460
461 // Load the character code at the {position} from the {receiver}.
462 Node* code = StringCharCodeAt(receiver, position, INTPTR_PARAMETERS);
463
464 // And return the single character string with only that {code}
465 Node* result = StringFromCharCode(code);
466 Return(result);
467 }
468
469 TF_BUILTIN(StringCharCodeAt, CodeStubAssembler) {
470 Node* receiver = Parameter(0);
471 Node* position = Parameter(1);
472
473 // Load the character code at the {position} from the {receiver}.
474 Node* code = StringCharCodeAt(receiver, position, INTPTR_PARAMETERS);
475
476 // And return it as TaggedSigned value.
477 // TODO(turbofan): Allow builtins to return values untagged.
478 Node* result = SmiFromWord32(code);
479 Return(result);
480 }
481
482 // -----------------------------------------------------------------------------
483 // ES6 section 21.1 String Objects
484
485 // ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
486 TF_BUILTIN(StringFromCharCode, CodeStubAssembler) {
487 Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
488 Node* context = Parameter(BuiltinDescriptor::kContext);
489
490 CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
491 // From now on use word-size argc value.
492 argc = arguments.GetLength();
493
494 // Check if we have exactly one argument (plus the implicit receiver), i.e.
495 // if the parent frame is not an arguments adaptor frame.
496 Label if_oneargument(this), if_notoneargument(this);
497 Branch(WordEqual(argc, IntPtrConstant(1)), &if_oneargument,
498 &if_notoneargument);
499
500 Bind(&if_oneargument);
501 {
502 // Single argument case, perform fast single character string cache lookup
503 // for one-byte code units, or fall back to creating a single character
504 // string on the fly otherwise.
505 Node* code = arguments.AtIndex(0);
506 Node* code32 = TruncateTaggedToWord32(context, code);
507 Node* code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
508 Node* result = StringFromCharCode(code16);
509 arguments.PopAndReturn(result);
510 }
511
512 Node* code16 = nullptr;
513 Bind(&if_notoneargument);
514 {
515 Label two_byte(this);
516 // Assume that the resulting string contains only one-byte characters.
517 Node* one_byte_result = AllocateSeqOneByteString(context, argc);
518
519 Variable max_index(this, MachineType::PointerRepresentation());
520 max_index.Bind(IntPtrConstant(0));
521
522 // Iterate over the incoming arguments, converting them to 8-bit character
523 // codes. Stop if any of the conversions generates a code that doesn't fit
524 // in 8 bits.
525 CodeStubAssembler::VariableList vars({&max_index}, zone());
526 arguments.ForEach(vars, [this, context, &two_byte, &max_index, &code16,
527 one_byte_result](Node* arg) {
528 Node* code32 = TruncateTaggedToWord32(context, arg);
529 code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
530
531 GotoIf(
532 Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)),
533 &two_byte);
534
535 // The {code16} fits into the SeqOneByteString {one_byte_result}.
536 Node* offset = ElementOffsetFromIndex(
537 max_index.value(), UINT8_ELEMENTS,
538 CodeStubAssembler::INTPTR_PARAMETERS,
539 SeqOneByteString::kHeaderSize - kHeapObjectTag);
540 StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
541 offset, code16);
542 max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
543 });
544 arguments.PopAndReturn(one_byte_result);
545
546 Bind(&two_byte);
547
548 // At least one of the characters in the string requires a 16-bit
549 // representation. Allocate a SeqTwoByteString to hold the resulting
550 // string.
551 Node* two_byte_result = AllocateSeqTwoByteString(context, argc);
552
553 // Copy the characters that have already been put in the 8-bit string into
554 // their corresponding positions in the new 16-bit string.
555 Node* zero = IntPtrConstant(0);
556 CopyStringCharacters(one_byte_result, two_byte_result, zero, zero,
557 max_index.value(), String::ONE_BYTE_ENCODING,
558 String::TWO_BYTE_ENCODING,
559 CodeStubAssembler::INTPTR_PARAMETERS);
560
561 // Write the character that caused the 8-bit to 16-bit fault.
562 Node* max_index_offset =
563 ElementOffsetFromIndex(max_index.value(), UINT16_ELEMENTS,
564 CodeStubAssembler::INTPTR_PARAMETERS,
565 SeqTwoByteString::kHeaderSize - kHeapObjectTag);
566 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
567 max_index_offset, code16);
568 max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
569
570 // Resume copying the passed-in arguments from the same place where the
571 // 8-bit copy stopped, but this time copying over all of the characters
572 // using a 16-bit representation.
573 arguments.ForEach(
574 vars,
575 [this, context, two_byte_result, &max_index](Node* arg) {
576 Node* code32 = TruncateTaggedToWord32(context, arg);
577 Node* code16 =
578 Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
579
580 Node* offset = ElementOffsetFromIndex(
581 max_index.value(), UINT16_ELEMENTS,
582 CodeStubAssembler::INTPTR_PARAMETERS,
583 SeqTwoByteString::kHeaderSize - kHeapObjectTag);
584 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
585 offset, code16);
586 max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
587 },
588 max_index.value());
589
590 arguments.PopAndReturn(two_byte_result);
591 }
592 }
593
594 namespace { // for String.fromCodePoint 18 namespace { // for String.fromCodePoint
595 19
596 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) { 20 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
597 if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) { 21 if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) {
598 return false; 22 return false;
599 } 23 }
600 24
601 if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() != 25 if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
602 value->Number()) { 26 value->Number()) {
603 return false; 27 return false;
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
677 two_byte_buffer.length())); 101 two_byte_buffer.length()));
678 102
679 CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(), 103 CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(),
680 one_byte_buffer.length()); 104 one_byte_buffer.length());
681 CopyChars(result->GetChars() + one_byte_buffer.length(), 105 CopyChars(result->GetChars() + one_byte_buffer.length(),
682 two_byte_buffer.ToConstVector().start(), two_byte_buffer.length()); 106 two_byte_buffer.ToConstVector().start(), two_byte_buffer.length());
683 107
684 return *result; 108 return *result;
685 } 109 }
686 110
687 // ES6 section 21.1.3.1 String.prototype.charAt ( pos )
688 TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) {
689 Node* receiver = Parameter(0);
690 Node* position = Parameter(1);
691 Node* context = Parameter(4);
692
693 // Check that {receiver} is coercible to Object and convert it to a String.
694 receiver = ToThisString(context, receiver, "String.prototype.charAt");
695
696 // Convert the {position} to a Smi and check that it's in bounds of the
697 // {receiver}.
698 {
699 Label return_emptystring(this, Label::kDeferred);
700 position =
701 ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero);
702 GotoIfNot(TaggedIsSmi(position), &return_emptystring);
703
704 // Determine the actual length of the {receiver} String.
705 Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset);
706
707 // Return "" if the Smi {position} is outside the bounds of the {receiver}.
708 Label if_positioninbounds(this);
709 Branch(SmiAboveOrEqual(position, receiver_length), &return_emptystring,
710 &if_positioninbounds);
711
712 Bind(&return_emptystring);
713 Return(EmptyStringConstant());
714
715 Bind(&if_positioninbounds);
716 }
717
718 // Load the character code at the {position} from the {receiver}.
719 Node* code = StringCharCodeAt(receiver, position);
720
721 // And return the single character string with only that {code}.
722 Node* result = StringFromCharCode(code);
723 Return(result);
724 }
725
726 // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
727 TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) {
728 Node* receiver = Parameter(0);
729 Node* position = Parameter(1);
730 Node* context = Parameter(4);
731
732 // Check that {receiver} is coercible to Object and convert it to a String.
733 receiver = ToThisString(context, receiver, "String.prototype.charCodeAt");
734
735 // Convert the {position} to a Smi and check that it's in bounds of the
736 // {receiver}.
737 {
738 Label return_nan(this, Label::kDeferred);
739 position =
740 ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero);
741 GotoIfNot(TaggedIsSmi(position), &return_nan);
742
743 // Determine the actual length of the {receiver} String.
744 Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset);
745
746 // Return NaN if the Smi {position} is outside the bounds of the {receiver}.
747 Label if_positioninbounds(this);
748 Branch(SmiAboveOrEqual(position, receiver_length), &return_nan,
749 &if_positioninbounds);
750
751 Bind(&return_nan);
752 Return(NaNConstant());
753
754 Bind(&if_positioninbounds);
755 }
756
757 // Load the character at the {position} from the {receiver}.
758 Node* value = StringCharCodeAt(receiver, position);
759 Node* result = SmiFromWord32(value);
760 Return(result);
761 }
762
763 // ES6 section 21.1.3.6 111 // ES6 section 21.1.3.6
764 // String.prototype.endsWith ( searchString [ , endPosition ] ) 112 // String.prototype.endsWith ( searchString [ , endPosition ] )
765 BUILTIN(StringPrototypeEndsWith) { 113 BUILTIN(StringPrototypeEndsWith) {
766 HandleScope handle_scope(isolate); 114 HandleScope handle_scope(isolate);
767 TO_THIS_STRING(str, "String.prototype.endsWith"); 115 TO_THIS_STRING(str, "String.prototype.endsWith");
768 116
769 // Check if the search string is a regExp and fail if it is. 117 // Check if the search string is a regExp and fail if it is.
770 Handle<Object> search = args.atOrUndefined(isolate, 1); 118 Handle<Object> search = args.atOrUndefined(isolate, 1);
771 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); 119 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
772 if (is_reg_exp.IsNothing()) { 120 if (is_reg_exp.IsNothing()) {
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 Handle<Object> position; 197 Handle<Object> position;
850 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 198 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
851 isolate, position, 199 isolate, position,
852 Object::ToInteger(isolate, args.atOrUndefined(isolate, 2))); 200 Object::ToInteger(isolate, args.atOrUndefined(isolate, 2)));
853 201
854 uint32_t index = str->ToValidIndex(*position); 202 uint32_t index = str->ToValidIndex(*position);
855 int index_in_str = String::IndexOf(isolate, str, search_string, index); 203 int index_in_str = String::IndexOf(isolate, str, search_string, index);
856 return *isolate->factory()->ToBoolean(index_in_str != -1); 204 return *isolate->factory()->ToBoolean(index_in_str != -1);
857 } 205 }
858 206
859 void StringBuiltinsAssembler::StringIndexOf(
860 Node* receiver, Node* instance_type, Node* search_string,
861 Node* search_string_instance_type, Node* position,
862 std::function<void(Node*)> f_return) {
863 CSA_ASSERT(this, IsString(receiver));
864 CSA_ASSERT(this, IsString(search_string));
865 CSA_ASSERT(this, TaggedIsSmi(position));
866
867 Label zero_length_needle(this),
868 call_runtime_unchecked(this, Label::kDeferred), return_minus_1(this),
869 check_search_string(this), continue_fast_path(this);
870
871 Node* const int_zero = IntPtrConstant(0);
872 Variable var_needle_byte(this, MachineType::PointerRepresentation(),
873 int_zero);
874 Variable var_string_addr(this, MachineType::PointerRepresentation(),
875 int_zero);
876
877 Node* needle_length = SmiUntag(LoadStringLength(search_string));
878 // Use faster/complex runtime fallback for long search strings.
879 GotoIf(IntPtrLessThan(IntPtrConstant(1), needle_length),
880 &call_runtime_unchecked);
881 Node* string_length = SmiUntag(LoadStringLength(receiver));
882 Node* start_position = IntPtrMax(SmiUntag(position), int_zero);
883
884 GotoIf(IntPtrEqual(int_zero, needle_length), &zero_length_needle);
885 // Check that the needle fits in the start position.
886 GotoIfNot(IntPtrLessThanOrEqual(needle_length,
887 IntPtrSub(string_length, start_position)),
888 &return_minus_1);
889
890 // Load the string address.
891 {
892 Label if_onebyte_sequential(this);
893 Label if_onebyte_external(this, Label::kDeferred);
894
895 // Only support one-byte strings on the fast path.
896 DispatchOnStringInstanceType(instance_type, &if_onebyte_sequential,
897 &if_onebyte_external, &call_runtime_unchecked);
898
899 Bind(&if_onebyte_sequential);
900 {
901 var_string_addr.Bind(
902 OneByteCharAddress(BitcastTaggedToWord(receiver), start_position));
903 Goto(&check_search_string);
904 }
905
906 Bind(&if_onebyte_external);
907 {
908 Node* const unpacked = TryDerefExternalString(receiver, instance_type,
909 &call_runtime_unchecked);
910 var_string_addr.Bind(OneByteCharAddress(unpacked, start_position));
911 Goto(&check_search_string);
912 }
913 }
914
915 // Load the needle character.
916 Bind(&check_search_string);
917 {
918 Label if_onebyte_sequential(this);
919 Label if_onebyte_external(this, Label::kDeferred);
920
921 DispatchOnStringInstanceType(search_string_instance_type,
922 &if_onebyte_sequential, &if_onebyte_external,
923 &call_runtime_unchecked);
924
925 Bind(&if_onebyte_sequential);
926 {
927 var_needle_byte.Bind(
928 ChangeInt32ToIntPtr(LoadOneByteChar(search_string, int_zero)));
929 Goto(&continue_fast_path);
930 }
931
932 Bind(&if_onebyte_external);
933 {
934 Node* const unpacked = TryDerefExternalString(
935 search_string, search_string_instance_type, &call_runtime_unchecked);
936 var_needle_byte.Bind(
937 ChangeInt32ToIntPtr(LoadOneByteChar(unpacked, int_zero)));
938 Goto(&continue_fast_path);
939 }
940 }
941
942 Bind(&continue_fast_path);
943 {
944 Node* needle_byte = var_needle_byte.value();
945 Node* string_addr = var_string_addr.value();
946 Node* search_length = IntPtrSub(string_length, start_position);
947 // Call out to the highly optimized memchr to perform the actual byte
948 // search.
949 Node* memchr =
950 ExternalConstant(ExternalReference::libc_memchr_function(isolate()));
951 Node* result_address =
952 CallCFunction3(MachineType::Pointer(), MachineType::Pointer(),
953 MachineType::IntPtr(), MachineType::UintPtr(), memchr,
954 string_addr, needle_byte, search_length);
955 GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
956 Node* result_index =
957 IntPtrAdd(IntPtrSub(result_address, string_addr), start_position);
958 f_return(SmiTag(result_index));
959 }
960
961 Bind(&return_minus_1);
962 f_return(SmiConstant(-1));
963
964 Bind(&zero_length_needle);
965 {
966 Comment("0-length search_string");
967 f_return(SmiTag(IntPtrMin(string_length, start_position)));
968 }
969
970 Bind(&call_runtime_unchecked);
971 {
972 // Simplified version of the runtime call where the types of the arguments
973 // are already known due to type checks in this stub.
974 Comment("Call Runtime Unchecked");
975 Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0),
976 receiver, search_string, position);
977 f_return(result);
978 }
979 }
980
981 // ES6 String.prototype.indexOf(searchString [, position])
982 // #sec-string.prototype.indexof
983 // Unchecked helper for builtins lowering.
984 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
985 Node* receiver = Parameter(0);
986 Node* search_string = Parameter(1);
987 Node* position = Parameter(2);
988
989 Node* instance_type = LoadInstanceType(receiver);
990 Node* search_string_instance_type = LoadInstanceType(search_string);
991
992 StringIndexOf(receiver, instance_type, search_string,
993 search_string_instance_type, position,
994 [this](Node* result) { this->Return(result); });
995 }
996
997 // ES6 String.prototype.indexOf(searchString [, position])
998 // #sec-string.prototype.indexof
999 TF_BUILTIN(StringPrototypeIndexOf, StringBuiltinsAssembler) {
1000 Variable search_string(this, MachineRepresentation::kTagged),
1001 position(this, MachineRepresentation::kTagged);
1002 Label call_runtime(this), call_runtime_unchecked(this), argc_0(this),
1003 no_argc_0(this), argc_1(this), no_argc_1(this), argc_2(this),
1004 fast_path(this), return_minus_1(this);
1005
1006 Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
1007 Node* context = Parameter(BuiltinDescriptor::kContext);
1008
1009 CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
1010 Node* receiver = arguments.GetReceiver();
1011 // From now on use word-size argc value.
1012 argc = arguments.GetLength();
1013
1014 GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &argc_0);
1015 GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1);
1016 Goto(&argc_2);
1017 Bind(&argc_0);
1018 {
1019 Comment("0 Argument case");
1020 Node* undefined = UndefinedConstant();
1021 search_string.Bind(undefined);
1022 position.Bind(undefined);
1023 Goto(&call_runtime);
1024 }
1025 Bind(&argc_1);
1026 {
1027 Comment("1 Argument case");
1028 search_string.Bind(arguments.AtIndex(0));
1029 position.Bind(SmiConstant(0));
1030 Goto(&fast_path);
1031 }
1032 Bind(&argc_2);
1033 {
1034 Comment("2 Argument case");
1035 search_string.Bind(arguments.AtIndex(0));
1036 position.Bind(arguments.AtIndex(1));
1037 GotoIfNot(TaggedIsSmi(position.value()), &call_runtime);
1038 Goto(&fast_path);
1039 }
1040
1041 Bind(&fast_path);
1042 {
1043 Comment("Fast Path");
1044 GotoIf(TaggedIsSmi(receiver), &call_runtime);
1045 Node* needle = search_string.value();
1046 GotoIf(TaggedIsSmi(needle), &call_runtime);
1047
1048 Node* instance_type = LoadInstanceType(receiver);
1049 GotoIfNot(IsStringInstanceType(instance_type), &call_runtime);
1050
1051 Node* needle_instance_type = LoadInstanceType(needle);
1052 GotoIfNot(IsStringInstanceType(needle_instance_type), &call_runtime);
1053
1054 StringIndexOf(
1055 receiver, instance_type, needle, needle_instance_type, position.value(),
1056 [&arguments](Node* result) { arguments.PopAndReturn(result); });
1057 }
1058
1059 Bind(&call_runtime);
1060 {
1061 Comment("Call Runtime");
1062 Node* result = CallRuntime(Runtime::kStringIndexOf, context, receiver,
1063 search_string.value(), position.value());
1064 arguments.PopAndReturn(result);
1065 }
1066 }
1067
1068 // ES6 section 21.1.3.9 207 // ES6 section 21.1.3.9
1069 // String.prototype.lastIndexOf ( searchString [ , position ] ) 208 // String.prototype.lastIndexOf ( searchString [ , position ] )
1070 BUILTIN(StringPrototypeLastIndexOf) { 209 BUILTIN(StringPrototypeLastIndexOf) {
1071 HandleScope handle_scope(isolate); 210 HandleScope handle_scope(isolate);
1072 return String::LastIndexOf(isolate, args.receiver(), 211 return String::LastIndexOf(isolate, args.receiver(),
1073 args.atOrUndefined(isolate, 1), 212 args.atOrUndefined(isolate, 1),
1074 args.atOrUndefined(isolate, 2)); 213 args.atOrUndefined(isolate, 2));
1075 } 214 }
1076 215
1077 // ES6 section 21.1.3.10 String.prototype.localeCompare ( that ) 216 // ES6 section 21.1.3.10 String.prototype.localeCompare ( that )
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
1152 Handle<String> valid_forms = 291 Handle<String> valid_forms =
1153 isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD"); 292 isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
1154 THROW_NEW_ERROR_RETURN_FAILURE( 293 THROW_NEW_ERROR_RETURN_FAILURE(
1155 isolate, 294 isolate,
1156 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms)); 295 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
1157 } 296 }
1158 297
1159 return *string; 298 return *string;
1160 } 299 }
1161 300
1162 compiler::Node* StringBuiltinsAssembler::IsNullOrUndefined(Node* const value) {
1163 return Word32Or(IsUndefined(value), IsNull(value));
1164 }
1165
1166 void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context,
1167 Node* const value,
1168 const char* method_name) {
1169 Label out(this), throw_exception(this, Label::kDeferred);
1170 Branch(IsNullOrUndefined(value), &throw_exception, &out);
1171
1172 Bind(&throw_exception);
1173 TailCallRuntime(
1174 Runtime::kThrowCalledOnNullOrUndefined, context,
1175 HeapConstant(factory()->NewStringFromAsciiChecked(method_name, TENURED)));
1176
1177 Bind(&out);
1178 }
1179
1180 void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
1181 Node* const context, Node* const object, Handle<Symbol> symbol,
1182 const NodeFunction0& regexp_call, const NodeFunction1& generic_call) {
1183 Label out(this);
1184
1185 // Smis definitely don't have an attached symbol.
1186 GotoIf(TaggedIsSmi(object), &out);
1187
1188 Node* const object_map = LoadMap(object);
1189
1190 // Skip the slow lookup for Strings.
1191 {
1192 Label next(this);
1193
1194 GotoIfNot(IsStringInstanceType(LoadMapInstanceType(object_map)), &next);
1195
1196 Node* const native_context = LoadNativeContext(context);
1197 Node* const initial_proto_initial_map = LoadContextElement(
1198 native_context, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX);
1199
1200 Node* const string_fun =
1201 LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX);
1202 Node* const initial_map =
1203 LoadObjectField(string_fun, JSFunction::kPrototypeOrInitialMapOffset);
1204 Node* const proto_map = LoadMap(LoadMapPrototype(initial_map));
1205
1206 Branch(WordEqual(proto_map, initial_proto_initial_map), &out, &next);
1207
1208 Bind(&next);
1209 }
1210
1211 // Take the fast path for RegExps.
1212 {
1213 Label stub_call(this), slow_lookup(this);
1214
1215 RegExpBuiltinsAssembler regexp_asm(state());
1216 regexp_asm.BranchIfFastRegExp(context, object_map, &stub_call,
1217 &slow_lookup);
1218
1219 Bind(&stub_call);
1220 Return(regexp_call());
1221
1222 Bind(&slow_lookup);
1223 }
1224
1225 GotoIf(IsNullOrUndefined(object), &out);
1226
1227 // Fall back to a slow lookup of {object[symbol]}.
1228
1229 Node* const maybe_func = GetProperty(context, object, symbol);
1230 GotoIf(IsUndefined(maybe_func), &out);
1231
1232 // Attempt to call the function.
1233
1234 Node* const result = generic_call(maybe_func);
1235 Return(result);
1236
1237 Bind(&out);
1238 }
1239
1240 // ES6 section 21.1.3.16 String.prototype.replace ( search, replace )
1241 TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
1242 Label out(this);
1243
1244 Node* const receiver = Parameter(0);
1245 Node* const search = Parameter(1);
1246 Node* const replace = Parameter(2);
1247 Node* const context = Parameter(5);
1248
1249 Node* const smi_zero = SmiConstant(0);
1250
1251 RequireObjectCoercible(context, receiver, "String.prototype.replace");
1252
1253 // Redirect to replacer method if {search[@@replace]} is not undefined.
1254
1255 MaybeCallFunctionAtSymbol(
1256 context, search, isolate()->factory()->replace_symbol(),
1257 [=]() {
1258 Callable tostring_callable = CodeFactory::ToString(isolate());
1259 Node* const subject_string =
1260 CallStub(tostring_callable, context, receiver);
1261
1262 Callable replace_callable = CodeFactory::RegExpReplace(isolate());
1263 return CallStub(replace_callable, context, search, subject_string,
1264 replace);
1265 },
1266 [=](Node* fn) {
1267 Callable call_callable = CodeFactory::Call(isolate());
1268 return CallJS(call_callable, context, fn, search, receiver, replace);
1269 });
1270
1271 // Convert {receiver} and {search} to strings.
1272
1273 Callable tostring_callable = CodeFactory::ToString(isolate());
1274 Callable indexof_callable = CodeFactory::StringIndexOf(isolate());
1275
1276 Node* const subject_string = CallStub(tostring_callable, context, receiver);
1277 Node* const search_string = CallStub(tostring_callable, context, search);
1278
1279 Node* const subject_length = LoadStringLength(subject_string);
1280 Node* const search_length = LoadStringLength(search_string);
1281
1282 // Fast-path single-char {search}, long {receiver}, and simple string
1283 // {replace}.
1284 {
1285 Label next(this);
1286
1287 GotoIfNot(SmiEqual(search_length, SmiConstant(1)), &next);
1288 GotoIfNot(SmiGreaterThan(subject_length, SmiConstant(0xFF)), &next);
1289 GotoIf(TaggedIsSmi(replace), &next);
1290 GotoIfNot(IsString(replace), &next);
1291
1292 Node* const dollar_string = HeapConstant(
1293 isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
1294 Node* const dollar_ix =
1295 CallStub(indexof_callable, context, replace, dollar_string, smi_zero);
1296 GotoIfNot(SmiIsNegative(dollar_ix), &next);
1297
1298 // Searching by traversing a cons string tree and replace with cons of
1299 // slices works only when the replaced string is a single character, being
1300 // replaced by a simple string and only pays off for long strings.
1301 // TODO(jgruber): Reevaluate if this is still beneficial.
1302 // TODO(jgruber): TailCallRuntime when it correctly handles adapter frames.
1303 Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context,
1304 subject_string, search_string, replace));
1305
1306 Bind(&next);
1307 }
1308
1309 // TODO(jgruber): Extend StringIndexOf to handle two-byte strings and
1310 // longer substrings - we can handle up to 8 chars (one-byte) / 4 chars
1311 // (2-byte).
1312
1313 Node* const match_start_index = CallStub(
1314 indexof_callable, context, subject_string, search_string, smi_zero);
1315 CSA_ASSERT(this, TaggedIsSmi(match_start_index));
1316
1317 // Early exit if no match found.
1318 {
1319 Label next(this), return_subject(this);
1320
1321 GotoIfNot(SmiIsNegative(match_start_index), &next);
1322
1323 // The spec requires to perform ToString(replace) if the {replace} is not
1324 // callable even if we are going to exit here.
1325 // Since ToString() being applied to Smi does not have side effects for
1326 // numbers we can skip it.
1327 GotoIf(TaggedIsSmi(replace), &return_subject);
1328 GotoIf(IsCallableMap(LoadMap(replace)), &return_subject);
1329
1330 // TODO(jgruber): Could introduce ToStringSideeffectsStub which only
1331 // performs observable parts of ToString.
1332 CallStub(tostring_callable, context, replace);
1333 Goto(&return_subject);
1334
1335 Bind(&return_subject);
1336 Return(subject_string);
1337
1338 Bind(&next);
1339 }
1340
1341 Node* const match_end_index = SmiAdd(match_start_index, search_length);
1342
1343 Callable substring_callable = CodeFactory::SubString(isolate());
1344 Callable stringadd_callable =
1345 CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
1346
1347 Variable var_result(this, MachineRepresentation::kTagged,
1348 EmptyStringConstant());
1349
1350 // Compute the prefix.
1351 {
1352 Label next(this);
1353
1354 GotoIf(SmiEqual(match_start_index, smi_zero), &next);
1355 Node* const prefix = CallStub(substring_callable, context, subject_string,
1356 smi_zero, match_start_index);
1357 var_result.Bind(prefix);
1358
1359 Goto(&next);
1360 Bind(&next);
1361 }
1362
1363 // Compute the string to replace with.
1364
1365 Label if_iscallablereplace(this), if_notcallablereplace(this);
1366 GotoIf(TaggedIsSmi(replace), &if_notcallablereplace);
1367 Branch(IsCallableMap(LoadMap(replace)), &if_iscallablereplace,
1368 &if_notcallablereplace);
1369
1370 Bind(&if_iscallablereplace);
1371 {
1372 Callable call_callable = CodeFactory::Call(isolate());
1373 Node* const replacement =
1374 CallJS(call_callable, context, replace, UndefinedConstant(),
1375 search_string, match_start_index, subject_string);
1376 Node* const replacement_string =
1377 CallStub(tostring_callable, context, replacement);
1378 var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
1379 replacement_string));
1380 Goto(&out);
1381 }
1382
1383 Bind(&if_notcallablereplace);
1384 {
1385 Node* const replace_string = CallStub(tostring_callable, context, replace);
1386
1387 // TODO(jgruber): Simplified GetSubstitution implementation in CSA.
1388 Node* const matched = CallStub(substring_callable, context, subject_string,
1389 match_start_index, match_end_index);
1390 Node* const replacement_string =
1391 CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
1392 match_start_index, replace_string);
1393 var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
1394 replacement_string));
1395 Goto(&out);
1396 }
1397
1398 Bind(&out);
1399 {
1400 Node* const suffix = CallStub(substring_callable, context, subject_string,
1401 match_end_index, subject_length);
1402 Node* const result =
1403 CallStub(stringadd_callable, context, var_result.value(), suffix);
1404 Return(result);
1405 }
1406 }
1407
1408 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
1409 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
1410 Label out(this);
1411
1412 Node* const receiver = Parameter(0);
1413 Node* const separator = Parameter(1);
1414 Node* const limit = Parameter(2);
1415 Node* const context = Parameter(5);
1416
1417 Node* const smi_zero = SmiConstant(0);
1418
1419 RequireObjectCoercible(context, receiver, "String.prototype.split");
1420
1421 // Redirect to splitter method if {separator[@@split]} is not undefined.
1422
1423 MaybeCallFunctionAtSymbol(
1424 context, separator, isolate()->factory()->split_symbol(),
1425 [=]() {
1426 Callable tostring_callable = CodeFactory::ToString(isolate());
1427 Node* const subject_string =
1428 CallStub(tostring_callable, context, receiver);
1429
1430 Callable split_callable = CodeFactory::RegExpSplit(isolate());
1431 return CallStub(split_callable, context, separator, subject_string,
1432 limit);
1433 },
1434 [=](Node* fn) {
1435 Callable call_callable = CodeFactory::Call(isolate());
1436 return CallJS(call_callable, context, fn, separator, receiver, limit);
1437 });
1438
1439 // String and integer conversions.
1440 // TODO(jgruber): The old implementation used Uint32Max instead of SmiMax -
1441 // but AFAIK there should not be a difference since arrays are capped at Smi
1442 // lengths.
1443
1444 Callable tostring_callable = CodeFactory::ToString(isolate());
1445 Node* const subject_string = CallStub(tostring_callable, context, receiver);
1446 Node* const limit_number =
1447 Select(IsUndefined(limit), [=]() { return SmiConstant(Smi::kMaxValue); },
1448 [=]() { return ToUint32(context, limit); },
1449 MachineRepresentation::kTagged);
1450 Node* const separator_string =
1451 CallStub(tostring_callable, context, separator);
1452
1453 // Shortcut for {limit} == 0.
1454 {
1455 Label next(this);
1456 GotoIfNot(SmiEqual(limit_number, smi_zero), &next);
1457
1458 const ElementsKind kind = FAST_ELEMENTS;
1459 Node* const native_context = LoadNativeContext(context);
1460 Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
1461
1462 Node* const length = smi_zero;
1463 Node* const capacity = IntPtrConstant(0);
1464 Node* const result = AllocateJSArray(kind, array_map, capacity, length);
1465
1466 Return(result);
1467
1468 Bind(&next);
1469 }
1470
1471 // ECMA-262 says that if {separator} is undefined, the result should
1472 // be an array of size 1 containing the entire string.
1473 {
1474 Label next(this);
1475 GotoIfNot(IsUndefined(separator), &next);
1476
1477 const ElementsKind kind = FAST_ELEMENTS;
1478 Node* const native_context = LoadNativeContext(context);
1479 Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
1480
1481 Node* const length = SmiConstant(1);
1482 Node* const capacity = IntPtrConstant(1);
1483 Node* const result = AllocateJSArray(kind, array_map, capacity, length);
1484
1485 Node* const fixed_array = LoadElements(result);
1486 StoreFixedArrayElement(fixed_array, 0, subject_string);
1487
1488 Return(result);
1489
1490 Bind(&next);
1491 }
1492
1493 // If the separator string is empty then return the elements in the subject.
1494 {
1495 Label next(this);
1496 GotoIfNot(SmiEqual(LoadStringLength(separator_string), smi_zero), &next);
1497
1498 Node* const result = CallRuntime(Runtime::kStringToArray, context,
1499 subject_string, limit_number);
1500 Return(result);
1501
1502 Bind(&next);
1503 }
1504
1505 Node* const result =
1506 CallRuntime(Runtime::kStringSplit, context, subject_string,
1507 separator_string, limit_number);
1508 Return(result);
1509 }
1510
1511 // ES6 section B.2.3.1 String.prototype.substr ( start, length )
1512 TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) {
1513 Label out(this), handle_length(this);
1514
1515 Variable var_start(this, MachineRepresentation::kTagged);
1516 Variable var_length(this, MachineRepresentation::kTagged);
1517
1518 Node* const receiver = Parameter(0);
1519 Node* const start = Parameter(1);
1520 Node* const length = Parameter(2);
1521 Node* const context = Parameter(5);
1522
1523 Node* const zero = SmiConstant(Smi::kZero);
1524
1525 // Check that {receiver} is coercible to Object and convert it to a String.
1526 Node* const string =
1527 ToThisString(context, receiver, "String.prototype.substr");
1528
1529 Node* const string_length = LoadStringLength(string);
1530
1531 // Conversions and bounds-checks for {start}.
1532 {
1533 Node* const start_int =
1534 ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
1535
1536 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
1537 Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
1538
1539 Bind(&if_issmi);
1540 {
1541 Node* const length_plus_start = SmiAdd(string_length, start_int);
1542 var_start.Bind(Select(SmiLessThan(start_int, zero),
1543 [&] { return SmiMax(length_plus_start, zero); },
1544 [&] { return start_int; },
1545 MachineRepresentation::kTagged));
1546 Goto(&handle_length);
1547 }
1548
1549 Bind(&if_isheapnumber);
1550 {
1551 // If {start} is a heap number, it is definitely out of bounds. If it is
1552 // negative, {start} = max({string_length} + {start}),0) = 0'. If it is
1553 // positive, set {start} to {string_length} which ultimately results in
1554 // returning an empty string.
1555 Node* const float_zero = Float64Constant(0.);
1556 Node* const start_float = LoadHeapNumberValue(start_int);
1557 var_start.Bind(SelectTaggedConstant(
1558 Float64LessThan(start_float, float_zero), zero, string_length));
1559 Goto(&handle_length);
1560 }
1561 }
1562
1563 // Conversions and bounds-checks for {length}.
1564 Bind(&handle_length);
1565 {
1566 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
1567
1568 // Default to {string_length} if {length} is undefined.
1569 {
1570 Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
1571 Branch(WordEqual(length, UndefinedConstant()), &if_isundefined,
1572 &if_isnotundefined);
1573
1574 Bind(&if_isundefined);
1575 var_length.Bind(string_length);
1576 Goto(&if_issmi);
1577
1578 Bind(&if_isnotundefined);
1579 var_length.Bind(
1580 ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
1581 }
1582
1583 Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
1584
1585 // Set {length} to min(max({length}, 0), {string_length} - {start}
1586 Bind(&if_issmi);
1587 {
1588 Node* const positive_length = SmiMax(var_length.value(), zero);
1589
1590 Node* const minimal_length = SmiSub(string_length, var_start.value());
1591 var_length.Bind(SmiMin(positive_length, minimal_length));
1592
1593 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
1594 Return(EmptyStringConstant());
1595 }
1596
1597 Bind(&if_isheapnumber);
1598 {
1599 // If {length} is a heap number, it is definitely out of bounds. There are
1600 // two cases according to the spec: if it is negative, "" is returned; if
1601 // it is positive, then length is set to {string_length} - {start}.
1602
1603 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value())));
1604
1605 Label if_isnegative(this), if_ispositive(this);
1606 Node* const float_zero = Float64Constant(0.);
1607 Node* const length_float = LoadHeapNumberValue(var_length.value());
1608 Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
1609 &if_ispositive);
1610
1611 Bind(&if_isnegative);
1612 Return(EmptyStringConstant());
1613
1614 Bind(&if_ispositive);
1615 {
1616 var_length.Bind(SmiSub(string_length, var_start.value()));
1617 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
1618 Return(EmptyStringConstant());
1619 }
1620 }
1621 }
1622
1623 Bind(&out);
1624 {
1625 Node* const end = SmiAdd(var_start.value(), var_length.value());
1626 Node* const result = SubString(context, string, var_start.value(), end);
1627 Return(result);
1628 }
1629 }
1630
1631 compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context,
1632 Node* value,
1633 Node* limit) {
1634 Label out(this);
1635 Variable var_result(this, MachineRepresentation::kTagged);
1636
1637 Node* const value_int =
1638 this->ToInteger(context, value, CodeStubAssembler::kTruncateMinusZero);
1639
1640 Label if_issmi(this), if_isnotsmi(this, Label::kDeferred);
1641 Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
1642
1643 Bind(&if_issmi);
1644 {
1645 Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred);
1646 Branch(SmiAbove(value_int, limit), &if_isoutofbounds, &if_isinbounds);
1647
1648 Bind(&if_isinbounds);
1649 {
1650 var_result.Bind(value_int);
1651 Goto(&out);
1652 }
1653
1654 Bind(&if_isoutofbounds);
1655 {
1656 Node* const zero = SmiConstant(Smi::kZero);
1657 var_result.Bind(
1658 SelectTaggedConstant(SmiLessThan(value_int, zero), zero, limit));
1659 Goto(&out);
1660 }
1661 }
1662
1663 Bind(&if_isnotsmi);
1664 {
1665 // {value} is a heap number - in this case, it is definitely out of bounds.
1666 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(value_int)));
1667
1668 Node* const float_zero = Float64Constant(0.);
1669 Node* const smi_zero = SmiConstant(Smi::kZero);
1670 Node* const value_float = LoadHeapNumberValue(value_int);
1671 var_result.Bind(SelectTaggedConstant(
1672 Float64LessThan(value_float, float_zero), smi_zero, limit));
1673 Goto(&out);
1674 }
1675
1676 Bind(&out);
1677 return var_result.value();
1678 }
1679
1680 // ES6 section 21.1.3.19 String.prototype.substring ( start, end )
1681 TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
1682 Label out(this);
1683
1684 Variable var_start(this, MachineRepresentation::kTagged);
1685 Variable var_end(this, MachineRepresentation::kTagged);
1686
1687 Node* const receiver = Parameter(0);
1688 Node* const start = Parameter(1);
1689 Node* const end = Parameter(2);
1690 Node* const context = Parameter(5);
1691
1692 // Check that {receiver} is coercible to Object and convert it to a String.
1693 Node* const string =
1694 ToThisString(context, receiver, "String.prototype.substring");
1695
1696 Node* const length = LoadStringLength(string);
1697
1698 // Conversion and bounds-checks for {start}.
1699 var_start.Bind(ToSmiBetweenZeroAnd(context, start, length));
1700
1701 // Conversion and bounds-checks for {end}.
1702 {
1703 var_end.Bind(length);
1704 GotoIf(WordEqual(end, UndefinedConstant()), &out);
1705
1706 var_end.Bind(ToSmiBetweenZeroAnd(context, end, length));
1707
1708 Label if_endislessthanstart(this);
1709 Branch(SmiLessThan(var_end.value(), var_start.value()),
1710 &if_endislessthanstart, &out);
1711
1712 Bind(&if_endislessthanstart);
1713 {
1714 Node* const tmp = var_end.value();
1715 var_end.Bind(var_start.value());
1716 var_start.Bind(tmp);
1717 Goto(&out);
1718 }
1719 }
1720
1721 Bind(&out);
1722 {
1723 Node* result =
1724 SubString(context, string, var_start.value(), var_end.value());
1725 Return(result);
1726 }
1727 }
1728
1729 BUILTIN(StringPrototypeStartsWith) { 301 BUILTIN(StringPrototypeStartsWith) {
1730 HandleScope handle_scope(isolate); 302 HandleScope handle_scope(isolate);
1731 TO_THIS_STRING(str, "String.prototype.startsWith"); 303 TO_THIS_STRING(str, "String.prototype.startsWith");
1732 304
1733 // Check if the search string is a regExp and fail if it is. 305 // Check if the search string is a regExp and fail if it is.
1734 Handle<Object> search = args.atOrUndefined(isolate, 1); 306 Handle<Object> search = args.atOrUndefined(isolate, 1);
1735 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); 307 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
1736 if (is_reg_exp.IsNothing()) { 308 if (is_reg_exp.IsNothing()) {
1737 DCHECK(isolate->has_pending_exception()); 309 DCHECK(isolate->has_pending_exception());
1738 return isolate->heap()->exception(); 310 return isolate->heap()->exception();
(...skipping 27 matching lines...) Expand all
1766 FlatStringReader search_reader(isolate, String::Flatten(search_string)); 338 FlatStringReader search_reader(isolate, String::Flatten(search_string));
1767 339
1768 for (int i = 0; i < search_string->length(); i++) { 340 for (int i = 0; i < search_string->length(); i++) {
1769 if (str_reader.Get(start + i) != search_reader.Get(i)) { 341 if (str_reader.Get(start + i) != search_reader.Get(i)) {
1770 return isolate->heap()->false_value(); 342 return isolate->heap()->false_value();
1771 } 343 }
1772 } 344 }
1773 return isolate->heap()->true_value(); 345 return isolate->heap()->true_value();
1774 } 346 }
1775 347
1776 // ES6 section 21.1.3.25 String.prototype.toString ()
1777 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) {
1778 Node* receiver = Parameter(0);
1779 Node* context = Parameter(3);
1780
1781 Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
1782 "String.prototype.toString");
1783 Return(result);
1784 }
1785
1786 // ES6 section 21.1.3.27 String.prototype.trim () 348 // ES6 section 21.1.3.27 String.prototype.trim ()
1787 BUILTIN(StringPrototypeTrim) { 349 BUILTIN(StringPrototypeTrim) {
1788 HandleScope scope(isolate); 350 HandleScope scope(isolate);
1789 TO_THIS_STRING(string, "String.prototype.trim"); 351 TO_THIS_STRING(string, "String.prototype.trim");
1790 return *String::Trim(string, String::kTrim); 352 return *String::Trim(string, String::kTrim);
1791 } 353 }
1792 354
1793 // Non-standard WebKit extension 355 // Non-standard WebKit extension
1794 BUILTIN(StringPrototypeTrimLeft) { 356 BUILTIN(StringPrototypeTrimLeft) {
1795 HandleScope scope(isolate); 357 HandleScope scope(isolate);
1796 TO_THIS_STRING(string, "String.prototype.trimLeft"); 358 TO_THIS_STRING(string, "String.prototype.trimLeft");
1797 return *String::Trim(string, String::kTrimLeft); 359 return *String::Trim(string, String::kTrimLeft);
1798 } 360 }
1799 361
1800 // Non-standard WebKit extension 362 // Non-standard WebKit extension
1801 BUILTIN(StringPrototypeTrimRight) { 363 BUILTIN(StringPrototypeTrimRight) {
1802 HandleScope scope(isolate); 364 HandleScope scope(isolate);
1803 TO_THIS_STRING(string, "String.prototype.trimRight"); 365 TO_THIS_STRING(string, "String.prototype.trimRight");
1804 return *String::Trim(string, String::kTrimRight); 366 return *String::Trim(string, String::kTrimRight);
1805 } 367 }
1806 368
1807 // ES6 section 21.1.3.28 String.prototype.valueOf ( )
1808 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) {
1809 Node* receiver = Parameter(0);
1810 Node* context = Parameter(3);
1811
1812 Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
1813 "String.prototype.valueOf");
1814 Return(result);
1815 }
1816
1817 TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) {
1818 Node* receiver = Parameter(0);
1819 Node* context = Parameter(3);
1820
1821 Node* string =
1822 ToThisString(context, receiver, "String.prototype[Symbol.iterator]");
1823
1824 Node* native_context = LoadNativeContext(context);
1825 Node* map =
1826 LoadContextElement(native_context, Context::STRING_ITERATOR_MAP_INDEX);
1827 Node* iterator = Allocate(JSStringIterator::kSize);
1828 StoreMapNoWriteBarrier(iterator, map);
1829 StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset,
1830 Heap::kEmptyFixedArrayRootIndex);
1831 StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
1832 Heap::kEmptyFixedArrayRootIndex);
1833 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset,
1834 string);
1835 Node* index = SmiConstant(Smi::kZero);
1836 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
1837 index);
1838 Return(iterator);
1839 }
1840
1841 // Return the |word32| codepoint at {index}. Supports SeqStrings and
1842 // ExternalStrings.
1843 compiler::Node* StringBuiltinsAssembler::LoadSurrogatePairAt(
1844 compiler::Node* string, compiler::Node* length, compiler::Node* index,
1845 UnicodeEncoding encoding) {
1846 Label handle_surrogate_pair(this), return_result(this);
1847 Variable var_result(this, MachineRepresentation::kWord32);
1848 Variable var_trail(this, MachineRepresentation::kWord32);
1849 var_result.Bind(StringCharCodeAt(string, index));
1850 var_trail.Bind(Int32Constant(0));
1851
1852 GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)),
1853 Int32Constant(0xD800)),
1854 &return_result);
1855 Node* next_index = SmiAdd(index, SmiConstant(Smi::FromInt(1)));
1856
1857 GotoIfNot(SmiLessThan(next_index, length), &return_result);
1858 var_trail.Bind(StringCharCodeAt(string, next_index));
1859 Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)),
1860 Int32Constant(0xDC00)),
1861 &handle_surrogate_pair, &return_result);
1862
1863 Bind(&handle_surrogate_pair);
1864 {
1865 Node* lead = var_result.value();
1866 Node* trail = var_trail.value();
1867
1868 // Check that this path is only taken if a surrogate pair is found
1869 CSA_SLOW_ASSERT(this,
1870 Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800)));
1871 CSA_SLOW_ASSERT(this, Uint32LessThan(lead, Int32Constant(0xDC00)));
1872 CSA_SLOW_ASSERT(this,
1873 Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00)));
1874 CSA_SLOW_ASSERT(this, Uint32LessThan(trail, Int32Constant(0xE000)));
1875
1876 switch (encoding) {
1877 case UnicodeEncoding::UTF16:
1878 var_result.Bind(Word32Or(
1879 // Need to swap the order for big-endian platforms
1880 #if V8_TARGET_BIG_ENDIAN
1881 Word32Shl(lead, Int32Constant(16)), trail));
1882 #else
1883 Word32Shl(trail, Int32Constant(16)), lead));
1884 #endif
1885 break;
1886
1887 case UnicodeEncoding::UTF32: {
1888 // Convert UTF16 surrogate pair into |word32| code point, encoded as
1889 // UTF32.
1890 Node* surrogate_offset =
1891 Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
1892
1893 // (lead << 10) + trail + SURROGATE_OFFSET
1894 var_result.Bind(Int32Add(WordShl(lead, Int32Constant(10)),
1895 Int32Add(trail, surrogate_offset)));
1896 break;
1897 }
1898 }
1899 Goto(&return_result);
1900 }
1901
1902 Bind(&return_result);
1903 return var_result.value();
1904 }
1905
1906 TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) {
1907 Variable var_value(this, MachineRepresentation::kTagged);
1908 Variable var_done(this, MachineRepresentation::kTagged);
1909
1910 var_value.Bind(UndefinedConstant());
1911 var_done.Bind(BooleanConstant(true));
1912
1913 Label throw_bad_receiver(this), next_codepoint(this), return_result(this);
1914
1915 Node* iterator = Parameter(0);
1916 Node* context = Parameter(3);
1917
1918 GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver);
1919 GotoIfNot(Word32Equal(LoadInstanceType(iterator),
1920 Int32Constant(JS_STRING_ITERATOR_TYPE)),
1921 &throw_bad_receiver);
1922
1923 Node* string = LoadObjectField(iterator, JSStringIterator::kStringOffset);
1924 Node* position =
1925 LoadObjectField(iterator, JSStringIterator::kNextIndexOffset);
1926 Node* length = LoadObjectField(string, String::kLengthOffset);
1927
1928 Branch(SmiLessThan(position, length), &next_codepoint, &return_result);
1929
1930 Bind(&next_codepoint);
1931 {
1932 UnicodeEncoding encoding = UnicodeEncoding::UTF16;
1933 Node* ch = LoadSurrogatePairAt(string, length, position, encoding);
1934 Node* value = StringFromCodePoint(ch, encoding);
1935 var_value.Bind(value);
1936 Node* length = LoadObjectField(value, String::kLengthOffset);
1937 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
1938 SmiAdd(position, length));
1939 var_done.Bind(BooleanConstant(false));
1940 Goto(&return_result);
1941 }
1942
1943 Bind(&return_result);
1944 {
1945 Node* native_context = LoadNativeContext(context);
1946 Node* map =
1947 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
1948 Node* result = Allocate(JSIteratorResult::kSize);
1949 StoreMapNoWriteBarrier(result, map);
1950 StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset,
1951 Heap::kEmptyFixedArrayRootIndex);
1952 StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset,
1953 Heap::kEmptyFixedArrayRootIndex);
1954 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset,
1955 var_value.value());
1956 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset,
1957 var_done.value());
1958 Return(result);
1959 }
1960
1961 Bind(&throw_bad_receiver);
1962 {
1963 // The {receiver} is not a valid JSGeneratorObject.
1964 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
1965 HeapConstant(factory()->NewStringFromAsciiChecked(
1966 "String Iterator.prototype.next", TENURED)),
1967 iterator);
1968 Unreachable();
1969 }
1970 }
1971
1972 namespace { 369 namespace {
1973 370
1974 inline bool ToUpperOverflows(uc32 character) { 371 inline bool ToUpperOverflows(uc32 character) {
1975 // y with umlauts and the micro sign are the only characters that stop 372 // y with umlauts and the micro sign are the only characters that stop
1976 // fitting into one-byte when converting to uppercase. 373 // fitting into one-byte when converting to uppercase.
1977 static const uc32 yuml_code = 0xff; 374 static const uc32 yuml_code = 0xff;
1978 static const uc32 micro_code = 0xb5; 375 static const uc32 micro_code = 0xb5;
1979 return (character == yuml_code || character == micro_code); 376 return (character == yuml_code || character == micro_code);
1980 } 377 }
1981 378
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after
2156 553
2157 BUILTIN(StringPrototypeToUpperCase) { 554 BUILTIN(StringPrototypeToUpperCase) {
2158 HandleScope scope(isolate); 555 HandleScope scope(isolate);
2159 TO_THIS_STRING(string, "String.prototype.toUpperCase"); 556 TO_THIS_STRING(string, "String.prototype.toUpperCase");
2160 return ConvertCase(string, isolate, 557 return ConvertCase(string, isolate,
2161 isolate->runtime_state()->to_upper_mapping()); 558 isolate->runtime_state()->to_upper_mapping());
2162 } 559 }
2163 560
2164 } // namespace internal 561 } // namespace internal
2165 } // namespace v8 562 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins-sharedarraybuffer-gen.cc ('k') | src/builtins/builtins-string-gen.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698