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

Side by Side Diff: src/builtins/builtins-regexp.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-regexp.h ('k') | src/builtins/builtins-regexp-gen.h » ('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
7 #include "src/builtins/builtins-constructor.h"
8 #include "src/builtins/builtins-utils.h" 5 #include "src/builtins/builtins-utils.h"
9 #include "src/builtins/builtins.h" 6 #include "src/builtins/builtins.h"
10 #include "src/code-factory.h"
11 #include "src/code-stub-assembler.h"
12 #include "src/counters.h" 7 #include "src/counters.h"
13 #include "src/objects-inl.h" 8 #include "src/objects-inl.h"
14 #include "src/objects/regexp-match-info.h"
15 #include "src/regexp/jsregexp.h" 9 #include "src/regexp/jsregexp.h"
16 #include "src/regexp/regexp-utils.h" 10 #include "src/regexp/regexp-utils.h"
17 #include "src/string-builder.h" 11 #include "src/string-builder.h"
18 12
19 namespace v8 { 13 namespace v8 {
20 namespace internal { 14 namespace internal {
21 15
22 typedef CodeStubAssembler::ParameterMode ParameterMode;
23
24
25 // ----------------------------------------------------------------------------- 16 // -----------------------------------------------------------------------------
26 // ES6 section 21.2 RegExp Objects 17 // ES6 section 21.2 RegExp Objects
27 18
28 Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) {
29 // Load the in-object field.
30 static const int field_offset =
31 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
32 return LoadObjectField(regexp, field_offset);
33 }
34
35 Node* RegExpBuiltinsAssembler::SlowLoadLastIndex(Node* context, Node* regexp) {
36 // Load through the GetProperty stub.
37 return GetProperty(context, regexp, isolate()->factory()->lastIndex_string());
38 }
39
40 Node* RegExpBuiltinsAssembler::LoadLastIndex(Node* context, Node* regexp,
41 bool is_fastpath) {
42 return is_fastpath ? FastLoadLastIndex(regexp)
43 : SlowLoadLastIndex(context, regexp);
44 }
45
46 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified
47 // JSRegExp instance.
48 void RegExpBuiltinsAssembler::FastStoreLastIndex(Node* regexp, Node* value) {
49 // Store the in-object field.
50 static const int field_offset =
51 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
52 StoreObjectField(regexp, field_offset, value);
53 }
54
55 void RegExpBuiltinsAssembler::SlowStoreLastIndex(Node* context, Node* regexp,
56 Node* value) {
57 // Store through runtime.
58 // TODO(ishell): Use SetPropertyStub here once available.
59 Node* const name = HeapConstant(isolate()->factory()->lastIndex_string());
60 Node* const language_mode = SmiConstant(Smi::FromInt(STRICT));
61 CallRuntime(Runtime::kSetProperty, context, regexp, name, value,
62 language_mode);
63 }
64
65 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp,
66 Node* value, bool is_fastpath) {
67 if (is_fastpath) {
68 FastStoreLastIndex(regexp, value);
69 } else {
70 SlowStoreLastIndex(context, regexp, value);
71 }
72 }
73
74 Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
75 Node* const context, Node* const regexp, Node* const match_info,
76 Node* const string) {
77 Label named_captures(this), out(this);
78
79 Node* const num_indices = SmiUntag(LoadFixedArrayElement(
80 match_info, RegExpMatchInfo::kNumberOfCapturesIndex));
81 Node* const num_results = SmiTag(WordShr(num_indices, 1));
82 Node* const start =
83 LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex);
84 Node* const end = LoadFixedArrayElement(
85 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1);
86
87 // Calculate the substring of the first match before creating the result array
88 // to avoid an unnecessary write barrier storing the first result.
89 Node* const first = SubString(context, string, start, end);
90
91 Node* const result =
92 AllocateRegExpResult(context, num_results, start, string);
93 Node* const result_elements = LoadElements(result);
94
95 StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER);
96
97 // If no captures exist we can skip named capture handling as well.
98 GotoIf(SmiEqual(num_results, SmiConstant(1)), &out);
99
100 // Store all remaining captures.
101 Node* const limit = IntPtrAdd(
102 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices);
103
104 Variable var_from_cursor(
105 this, MachineType::PointerRepresentation(),
106 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
107 Variable var_to_cursor(this, MachineType::PointerRepresentation(),
108 IntPtrConstant(1));
109
110 Variable* vars[] = {&var_from_cursor, &var_to_cursor};
111 Label loop(this, 2, vars);
112
113 Goto(&loop);
114 Bind(&loop);
115 {
116 Node* const from_cursor = var_from_cursor.value();
117 Node* const to_cursor = var_to_cursor.value();
118 Node* const start = LoadFixedArrayElement(match_info, from_cursor);
119
120 Label next_iter(this);
121 GotoIf(SmiEqual(start, SmiConstant(-1)), &next_iter);
122
123 Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1));
124 Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1);
125
126 Node* const capture = SubString(context, string, start, end);
127 StoreFixedArrayElement(result_elements, to_cursor, capture);
128 Goto(&next_iter);
129
130 Bind(&next_iter);
131 var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2)));
132 var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1)));
133 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop,
134 &named_captures);
135 }
136
137 Bind(&named_captures);
138 {
139 // We reach this point only if captures exist, implying that this is an
140 // IRREGEXP JSRegExp.
141
142 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE));
143 CSA_ASSERT(this, SmiGreaterThan(num_results, SmiConstant(1)));
144
145 // Preparations for named capture properties. Exit early if the result does
146 // not have any named captures to minimize performance impact.
147
148 Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset);
149 CSA_ASSERT(this, SmiEqual(LoadFixedArrayElement(data, JSRegExp::kTagIndex),
150 SmiConstant(JSRegExp::IRREGEXP)));
151
152 // The names fixed array associates names at even indices with a capture
153 // index at odd indices.
154 Node* const names =
155 LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex);
156 GotoIf(SmiEqual(names, SmiConstant(0)), &out);
157
158 // Allocate a new object to store the named capture properties.
159 // TODO(jgruber): Could be optimized by adding the object map to the heap
160 // root list.
161
162 Node* const native_context = LoadNativeContext(context);
163 Node* const map = LoadContextElement(
164 native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP);
165 Node* const properties =
166 AllocateNameDictionary(NameDictionary::kInitialCapacity);
167
168 Node* const group_object = AllocateJSObjectFromMap(map, properties);
169
170 // Store it on the result as a 'group' property.
171
172 {
173 Node* const name = HeapConstant(isolate()->factory()->group_string());
174 CallRuntime(Runtime::kCreateDataProperty, context, result, name,
175 group_object);
176 }
177
178 // One or more named captures exist, add a property for each one.
179
180 CSA_ASSERT(this, HasInstanceType(names, FIXED_ARRAY_TYPE));
181 Node* const names_length = LoadAndUntagFixedArrayBaseLength(names);
182 CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrConstant(0)));
183
184 Variable var_i(this, MachineType::PointerRepresentation());
185 var_i.Bind(IntPtrConstant(0));
186
187 Variable* vars[] = {&var_i};
188 const int vars_count = sizeof(vars) / sizeof(vars[0]);
189 Label loop(this, vars_count, vars);
190
191 Goto(&loop);
192 Bind(&loop);
193 {
194 Node* const i = var_i.value();
195 Node* const i_plus_1 = IntPtrAdd(i, IntPtrConstant(1));
196 Node* const i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1));
197
198 Node* const name = LoadFixedArrayElement(names, i);
199 Node* const index = LoadFixedArrayElement(names, i_plus_1);
200 Node* const capture =
201 LoadFixedArrayElement(result_elements, SmiUntag(index));
202
203 CallRuntime(Runtime::kCreateDataProperty, context, group_object, name,
204 capture);
205
206 var_i.Bind(i_plus_2);
207 Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out,
208 &loop);
209 }
210 }
211
212 Bind(&out);
213 return result;
214 }
215
216 void RegExpBuiltinsAssembler::GetStringPointers(
217 Node* const string_data, Node* const offset, Node* const last_index,
218 Node* const string_length, String::Encoding encoding,
219 Variable* var_string_start, Variable* var_string_end) {
220 DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
221 DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
222
223 const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
224 ? UINT8_ELEMENTS
225 : UINT16_ELEMENTS;
226
227 Node* const from_offset = ElementOffsetFromIndex(
228 IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS);
229 var_string_start->Bind(IntPtrAdd(string_data, from_offset));
230
231 Node* const to_offset = ElementOffsetFromIndex(
232 IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS);
233 var_string_end->Bind(IntPtrAdd(string_data, to_offset));
234 }
235
236 Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
237 Node* const regexp,
238 Node* const string,
239 Node* const last_index,
240 Node* const match_info) {
241 // Just jump directly to runtime if native RegExp is not selected at compile
242 // time or if regexp entry in generated code is turned off runtime switch or
243 // at compilation.
244 #ifdef V8_INTERPRETED_REGEXP
245 return CallRuntime(Runtime::kRegExpExec, context, regexp, string, last_index,
246 match_info);
247 #else // V8_INTERPRETED_REGEXP
248 CSA_ASSERT(this, TaggedIsNotSmi(regexp));
249 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE));
250
251 CSA_ASSERT(this, TaggedIsNotSmi(string));
252 CSA_ASSERT(this, IsString(string));
253
254 CSA_ASSERT(this, IsHeapNumberMap(LoadReceiverMap(last_index)));
255 CSA_ASSERT(this, IsFixedArrayMap(LoadReceiverMap(match_info)));
256
257 Node* const int_zero = IntPtrConstant(0);
258
259 ToDirectStringAssembler to_direct(state(), string);
260
261 Variable var_result(this, MachineRepresentation::kTagged);
262 Label out(this), runtime(this, Label::kDeferred);
263
264 // External constants.
265 Node* const regexp_stack_memory_size_address = ExternalConstant(
266 ExternalReference::address_of_regexp_stack_memory_size(isolate()));
267 Node* const static_offsets_vector_address = ExternalConstant(
268 ExternalReference::address_of_static_offsets_vector(isolate()));
269 Node* const pending_exception_address = ExternalConstant(
270 ExternalReference(Isolate::kPendingExceptionAddress, isolate()));
271
272 // Ensure that a RegExp stack is allocated.
273 {
274 Node* const stack_size =
275 Load(MachineType::IntPtr(), regexp_stack_memory_size_address);
276 GotoIf(IntPtrEqual(stack_size, int_zero), &runtime);
277 }
278
279 Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset);
280 {
281 // Check that the RegExp has been compiled (data contains a fixed array).
282 CSA_ASSERT(this, TaggedIsNotSmi(data));
283 CSA_ASSERT(this, HasInstanceType(data, FIXED_ARRAY_TYPE));
284
285 // Check the type of the RegExp. Only continue if type is
286 // JSRegExp::IRREGEXP.
287 Node* const tag = LoadFixedArrayElement(data, JSRegExp::kTagIndex);
288 GotoIfNot(SmiEqual(tag, SmiConstant(JSRegExp::IRREGEXP)), &runtime);
289
290 // Check (number_of_captures + 1) * 2 <= offsets vector size
291 // Or number_of_captures <= offsets vector size / 2 - 1
292 Node* const capture_count =
293 LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex);
294 CSA_ASSERT(this, TaggedIsSmi(capture_count));
295
296 STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
297 GotoIf(SmiAbove(
298 capture_count,
299 SmiConstant(Isolate::kJSRegexpStaticOffsetsVectorSize / 2 - 1)),
300 &runtime);
301 }
302
303 // Unpack the string if possible.
304
305 to_direct.TryToDirect(&runtime);
306
307 Node* const smi_string_length = LoadStringLength(string);
308
309 // Bail out to runtime for invalid {last_index} values.
310 GotoIfNot(TaggedIsSmi(last_index), &runtime);
311 GotoIf(SmiAboveOrEqual(last_index, smi_string_length), &runtime);
312
313 // Load the irregexp code object and offsets into the subject string. Both
314 // depend on whether the string is one- or two-byte.
315
316 Node* const int_last_index = SmiUntag(last_index);
317
318 Variable var_string_start(this, MachineType::PointerRepresentation());
319 Variable var_string_end(this, MachineType::PointerRepresentation());
320 Variable var_code(this, MachineRepresentation::kTagged);
321
322 {
323 Node* const int_string_length = SmiUntag(smi_string_length);
324 Node* const direct_string_data = to_direct.PointerToData(&runtime);
325
326 Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred);
327 Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
328 &if_isonebyte, &if_istwobyte);
329
330 Bind(&if_isonebyte);
331 {
332 GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
333 int_string_length, String::ONE_BYTE_ENCODING,
334 &var_string_start, &var_string_end);
335 var_code.Bind(
336 LoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex));
337 Goto(&next);
338 }
339
340 Bind(&if_istwobyte);
341 {
342 GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
343 int_string_length, String::TWO_BYTE_ENCODING,
344 &var_string_start, &var_string_end);
345 var_code.Bind(
346 LoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex));
347 Goto(&next);
348 }
349
350 Bind(&next);
351 }
352
353 // Check that the irregexp code has been generated for the actual string
354 // encoding. If it has, the field contains a code object otherwise it contains
355 // smi (code flushing support).
356
357 Node* const code = var_code.value();
358 GotoIf(TaggedIsSmi(code), &runtime);
359 CSA_ASSERT(this, HasInstanceType(code, CODE_TYPE));
360
361 Label if_success(this), if_failure(this),
362 if_exception(this, Label::kDeferred);
363 {
364 IncrementCounter(isolate()->counters()->regexp_entry_native(), 1);
365
366 Callable exec_callable = CodeFactory::RegExpExec(isolate());
367 Node* const result = CallStub(
368 exec_callable, context, string, TruncateWordToWord32(int_last_index),
369 var_string_start.value(), var_string_end.value(), code);
370
371 // Check the result.
372 // We expect exactly one result since the stub forces the called regexp to
373 // behave as non-global.
374 GotoIf(SmiEqual(result, SmiConstant(1)), &if_success);
375 GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::FAILURE)),
376 &if_failure);
377 GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::EXCEPTION)),
378 &if_exception);
379
380 CSA_ASSERT(
381 this, SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::RETRY)));
382 Goto(&runtime);
383 }
384
385 Bind(&if_success);
386 {
387 // Check that the last match info has space for the capture registers and
388 // the additional information. Ensure no overflow in add.
389 STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
390 Node* const available_slots =
391 SmiSub(LoadFixedArrayBaseLength(match_info),
392 SmiConstant(RegExpMatchInfo::kLastMatchOverhead));
393 Node* const capture_count =
394 LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex);
395 // Calculate number of register_count = (capture_count + 1) * 2.
396 Node* const register_count =
397 SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1);
398 GotoIf(SmiGreaterThan(register_count, available_slots), &runtime);
399
400 // Fill match_info.
401
402 StoreFixedArrayElement(match_info, RegExpMatchInfo::kNumberOfCapturesIndex,
403 register_count, SKIP_WRITE_BARRIER);
404 StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
405 string);
406 StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
407 string);
408
409 // Fill match and capture offsets in match_info.
410 {
411 Node* const limit_offset = ElementOffsetFromIndex(
412 register_count, INT32_ELEMENTS, SMI_PARAMETERS, 0);
413
414 Node* const to_offset = ElementOffsetFromIndex(
415 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), FAST_ELEMENTS,
416 INTPTR_PARAMETERS, RegExpMatchInfo::kHeaderSize - kHeapObjectTag);
417 Variable var_to_offset(this, MachineType::PointerRepresentation(),
418 to_offset);
419
420 VariableList vars({&var_to_offset}, zone());
421 BuildFastLoop(
422 vars, int_zero, limit_offset,
423 [=, &var_to_offset](Node* offset) {
424 Node* const value = Load(MachineType::Int32(),
425 static_offsets_vector_address, offset);
426 Node* const smi_value = SmiFromWord32(value);
427 StoreNoWriteBarrier(MachineRepresentation::kTagged, match_info,
428 var_to_offset.value(), smi_value);
429 Increment(var_to_offset, kPointerSize);
430 },
431 kInt32Size, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
432 }
433
434 var_result.Bind(match_info);
435 Goto(&out);
436 }
437
438 Bind(&if_failure);
439 {
440 var_result.Bind(NullConstant());
441 Goto(&out);
442 }
443
444 Bind(&if_exception);
445 {
446 Node* const pending_exception =
447 Load(MachineType::AnyTagged(), pending_exception_address);
448
449 // If there is no pending exception, a
450 // stack overflow (on the backtrack stack) was detected in RegExp code.
451
452 Label stack_overflow(this), rethrow(this);
453 Branch(IsTheHole(pending_exception), &stack_overflow, &rethrow);
454
455 Bind(&stack_overflow);
456 TailCallRuntime(Runtime::kThrowStackOverflow, context);
457
458 Bind(&rethrow);
459 TailCallRuntime(Runtime::kRegExpExecReThrow, context);
460 }
461
462 Bind(&runtime);
463 {
464 Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp,
465 string, last_index, match_info);
466 var_result.Bind(result);
467 Goto(&out);
468 }
469
470 Bind(&out);
471 return var_result.value();
472 #endif // V8_INTERPRETED_REGEXP
473 }
474
475 // ES#sec-regexp.prototype.exec
476 // RegExp.prototype.exec ( string )
477 // Implements the core of RegExp.prototype.exec but without actually
478 // constructing the JSRegExpResult. Returns either null (if the RegExp did not
479 // match) or a fixed array containing match indices as returned by
480 // RegExpExecStub.
481 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
482 Node* const context, Node* const regexp, Node* const string,
483 Label* if_didnotmatch, const bool is_fastpath) {
484 Isolate* const isolate = this->isolate();
485
486 Node* const null = NullConstant();
487 Node* const int_zero = IntPtrConstant(0);
488 Node* const smi_zero = SmiConstant(Smi::kZero);
489
490 if (!is_fastpath) {
491 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
492 "RegExp.prototype.exec");
493 }
494
495 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string)));
496 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE));
497
498 Variable var_result(this, MachineRepresentation::kTagged);
499 Label out(this);
500
501 // Load lastIndex.
502 Variable var_lastindex(this, MachineRepresentation::kTagged);
503 {
504 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath);
505 var_lastindex.Bind(regexp_lastindex);
506
507 // Omit ToLength if lastindex is a non-negative smi.
508 Label call_tolength(this, Label::kDeferred), next(this);
509 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength);
510
511 Bind(&call_tolength);
512 {
513 Callable tolength_callable = CodeFactory::ToLength(isolate);
514 var_lastindex.Bind(
515 CallStub(tolength_callable, context, regexp_lastindex));
516 Goto(&next);
517 }
518
519 Bind(&next);
520 }
521
522 // Check whether the regexp is global or sticky, which determines whether we
523 // update last index later on.
524 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
525 Node* const is_global_or_sticky = WordAnd(
526 SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky));
527 Node* const should_update_last_index =
528 WordNotEqual(is_global_or_sticky, int_zero);
529
530 // Grab and possibly update last index.
531 Label run_exec(this);
532 {
533 Label if_doupdate(this), if_dontupdate(this);
534 Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
535
536 Bind(&if_doupdate);
537 {
538 Node* const lastindex = var_lastindex.value();
539
540 Label if_isoob(this, Label::kDeferred);
541 GotoIfNot(TaggedIsSmi(lastindex), &if_isoob);
542 Node* const string_length = LoadStringLength(string);
543 GotoIfNot(SmiLessThanOrEqual(lastindex, string_length), &if_isoob);
544 Goto(&run_exec);
545
546 Bind(&if_isoob);
547 {
548 StoreLastIndex(context, regexp, smi_zero, is_fastpath);
549 var_result.Bind(null);
550 Goto(if_didnotmatch);
551 }
552 }
553
554 Bind(&if_dontupdate);
555 {
556 var_lastindex.Bind(smi_zero);
557 Goto(&run_exec);
558 }
559 }
560
561 Node* match_indices;
562 Label successful_match(this);
563 Bind(&run_exec);
564 {
565 // Get last match info from the context.
566 Node* const native_context = LoadNativeContext(context);
567 Node* const last_match_info = LoadContextElement(
568 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
569
570 // Call the exec stub.
571 match_indices = IrregexpExec(context, regexp, string, var_lastindex.value(),
572 last_match_info);
573 var_result.Bind(match_indices);
574
575 // {match_indices} is either null or the RegExpMatchInfo array.
576 // Return early if exec failed, possibly updating last index.
577 GotoIfNot(WordEqual(match_indices, null), &successful_match);
578
579 GotoIfNot(should_update_last_index, if_didnotmatch);
580
581 StoreLastIndex(context, regexp, smi_zero, is_fastpath);
582 Goto(if_didnotmatch);
583 }
584
585 Bind(&successful_match);
586 {
587 GotoIfNot(should_update_last_index, &out);
588
589 // Update the new last index from {match_indices}.
590 Node* const new_lastindex = LoadFixedArrayElement(
591 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
592
593 StoreLastIndex(context, regexp, new_lastindex, is_fastpath);
594 Goto(&out);
595 }
596
597 Bind(&out);
598 return var_result.value();
599 }
600
601 // ES#sec-regexp.prototype.exec
602 // RegExp.prototype.exec ( string )
603 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBody(Node* const context,
604 Node* const regexp,
605 Node* const string,
606 const bool is_fastpath) {
607 Node* const null = NullConstant();
608
609 Variable var_result(this, MachineRepresentation::kTagged);
610
611 Label if_didnotmatch(this), out(this);
612 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult(
613 context, regexp, string, &if_didnotmatch, is_fastpath);
614
615 // Successful match.
616 {
617 Node* const match_indices = indices_or_null;
618 Node* const result =
619 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string);
620 var_result.Bind(result);
621 Goto(&out);
622 }
623
624 Bind(&if_didnotmatch);
625 {
626 var_result.Bind(null);
627 Goto(&out);
628 }
629
630 Bind(&out);
631 return var_result.value();
632 }
633
634 Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver(
635 Node* context, Node* maybe_receiver, MessageTemplate::Template msg_template,
636 char const* method_name) {
637 Label out(this), throw_exception(this, Label::kDeferred);
638 Variable var_value_map(this, MachineRepresentation::kTagged);
639
640 GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception);
641
642 // Load the instance type of the {value}.
643 var_value_map.Bind(LoadMap(maybe_receiver));
644 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value());
645
646 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception);
647
648 // The {value} is not a compatible receiver for this method.
649 Bind(&throw_exception);
650 {
651 Node* const message_id = SmiConstant(Smi::FromInt(msg_template));
652 Node* const method_name_str = HeapConstant(
653 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED));
654
655 Callable callable = CodeFactory::ToString(isolate());
656 Node* const value_str = CallStub(callable, context, maybe_receiver);
657
658 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str,
659 value_str);
660 Unreachable();
661 }
662
663 Bind(&out);
664 return var_value_map.value();
665 }
666
667 Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) {
668 Node* const native_context = LoadNativeContext(context);
669 Node* const regexp_fun =
670 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
671 Node* const initial_map =
672 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
673 Node* const has_initialmap = WordEqual(map, initial_map);
674
675 return has_initialmap;
676 }
677
678 // RegExp fast path implementations rely on unmodified JSRegExp instances.
679 // We use a fairly coarse granularity for this and simply check whether both
680 // the regexp itself is unmodified (i.e. its map has not changed) and its
681 // prototype is unmodified.
682 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
683 Node* const map,
684 Label* const if_isunmodified,
685 Label* const if_ismodified) {
686 Node* const native_context = LoadNativeContext(context);
687 Node* const regexp_fun =
688 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
689 Node* const initial_map =
690 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
691 Node* const has_initialmap = WordEqual(map, initial_map);
692
693 GotoIfNot(has_initialmap, if_ismodified);
694
695 Node* const initial_proto_initial_map =
696 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
697 Node* const proto_map = LoadMap(LoadMapPrototype(map));
698 Node* const proto_has_initialmap =
699 WordEqual(proto_map, initial_proto_initial_map);
700
701 // TODO(ishell): Update this check once map changes for constant field
702 // tracking are landing.
703
704 Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
705 }
706
707 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context,
708 Node* const map) {
709 Label yup(this), nope(this), out(this);
710 Variable var_result(this, MachineRepresentation::kWord32);
711
712 BranchIfFastRegExp(context, map, &yup, &nope);
713
714 Bind(&yup);
715 var_result.Bind(Int32Constant(1));
716 Goto(&out);
717
718 Bind(&nope);
719 var_result.Bind(Int32Constant(0));
720 Goto(&out);
721
722 Bind(&out);
723 return var_result.value();
724 }
725
726 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map,
727 Label* if_isunmodified,
728 Label* if_ismodified) {
729 Node* const native_context = LoadNativeContext(context);
730 Node* const initial_regexp_result_map =
731 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
732
733 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified,
734 if_ismodified);
735 }
736
737 // ES#sec-regexp.prototype.exec
738 // RegExp.prototype.exec ( string )
739 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
740 Node* const maybe_receiver = Parameter(0);
741 Node* const maybe_string = Parameter(1);
742 Node* const context = Parameter(4);
743
744 // Ensure {maybe_receiver} is a JSRegExp.
745 Node* const regexp_map = ThrowIfNotInstanceType(
746 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec");
747 Node* const receiver = maybe_receiver;
748
749 // Convert {maybe_string} to a String.
750 Node* const string = ToString(context, maybe_string);
751
752 Label if_isfastpath(this), if_isslowpath(this);
753 Branch(IsInitialRegExpMap(context, regexp_map), &if_isfastpath,
754 &if_isslowpath);
755
756 Bind(&if_isfastpath);
757 {
758 Node* const result =
759 RegExpPrototypeExecBody(context, receiver, string, true);
760 Return(result);
761 }
762
763 Bind(&if_isslowpath);
764 {
765 Node* const result =
766 RegExpPrototypeExecBody(context, receiver, string, false);
767 Return(result);
768 }
769 }
770
771 Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context,
772 Node* const regexp,
773 bool is_fastpath) {
774 Isolate* isolate = this->isolate();
775
776 Node* const int_zero = IntPtrConstant(0);
777 Node* const int_one = IntPtrConstant(1);
778 Variable var_length(this, MachineType::PointerRepresentation(), int_zero);
779 Variable var_flags(this, MachineType::PointerRepresentation());
780
781 // First, count the number of characters we will need and check which flags
782 // are set.
783
784 if (is_fastpath) {
785 // Refer to JSRegExp's flag property on the fast-path.
786 Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
787 Node* const flags_intptr = SmiUntag(flags_smi);
788 var_flags.Bind(flags_intptr);
789
790 #define CASE_FOR_FLAG(FLAG) \
791 do { \
792 Label next(this); \
793 GotoIfNot(IsSetWord(flags_intptr, FLAG), &next); \
794 var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \
795 Goto(&next); \
796 Bind(&next); \
797 } while (false)
798
799 CASE_FOR_FLAG(JSRegExp::kGlobal);
800 CASE_FOR_FLAG(JSRegExp::kIgnoreCase);
801 CASE_FOR_FLAG(JSRegExp::kMultiline);
802 CASE_FOR_FLAG(JSRegExp::kUnicode);
803 CASE_FOR_FLAG(JSRegExp::kSticky);
804 #undef CASE_FOR_FLAG
805 } else {
806 DCHECK(!is_fastpath);
807
808 // Fall back to GetProperty stub on the slow-path.
809 var_flags.Bind(int_zero);
810
811 #define CASE_FOR_FLAG(NAME, FLAG) \
812 do { \
813 Label next(this); \
814 Node* const flag = GetProperty( \
815 context, regexp, isolate->factory()->InternalizeUtf8String(NAME)); \
816 Label if_isflagset(this); \
817 BranchIfToBooleanIsTrue(flag, &if_isflagset, &next); \
818 Bind(&if_isflagset); \
819 var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \
820 var_flags.Bind(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \
821 Goto(&next); \
822 Bind(&next); \
823 } while (false)
824
825 CASE_FOR_FLAG("global", JSRegExp::kGlobal);
826 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase);
827 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline);
828 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode);
829 CASE_FOR_FLAG("sticky", JSRegExp::kSticky);
830 #undef CASE_FOR_FLAG
831 }
832
833 // Allocate a string of the required length and fill it with the corresponding
834 // char for each set flag.
835
836 {
837 Node* const result = AllocateSeqOneByteString(context, var_length.value());
838 Node* const flags_intptr = var_flags.value();
839
840 Variable var_offset(
841 this, MachineType::PointerRepresentation(),
842 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
843
844 #define CASE_FOR_FLAG(FLAG, CHAR) \
845 do { \
846 Label next(this); \
847 GotoIfNot(IsSetWord(flags_intptr, FLAG), &next); \
848 Node* const value = Int32Constant(CHAR); \
849 StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \
850 var_offset.value(), value); \
851 var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \
852 Goto(&next); \
853 Bind(&next); \
854 } while (false)
855
856 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g');
857 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i');
858 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm');
859 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u');
860 CASE_FOR_FLAG(JSRegExp::kSticky, 'y');
861 #undef CASE_FOR_FLAG
862
863 return result;
864 }
865 }
866
867 // ES#sec-isregexp IsRegExp ( argument )
868 Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context,
869 Node* const maybe_receiver) {
870 Label out(this), if_isregexp(this);
871
872 Variable var_result(this, MachineRepresentation::kWord32, Int32Constant(0));
873
874 GotoIf(TaggedIsSmi(maybe_receiver), &out);
875 GotoIfNot(IsJSReceiver(maybe_receiver), &out);
876
877 Node* const receiver = maybe_receiver;
878
879 // Check @@match.
880 {
881 Node* const value =
882 GetProperty(context, receiver, isolate()->factory()->match_symbol());
883
884 Label match_isundefined(this), match_isnotundefined(this);
885 Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined);
886
887 Bind(&match_isundefined);
888 Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out);
889
890 Bind(&match_isnotundefined);
891 BranchIfToBooleanIsTrue(value, &if_isregexp, &out);
892 }
893
894 Bind(&if_isregexp);
895 var_result.Bind(Int32Constant(1));
896 Goto(&out);
897
898 Bind(&out);
899 return var_result.value();
900 }
901
902 // ES#sec-regexpinitialize
903 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
904 Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context,
905 Node* const regexp,
906 Node* const maybe_pattern,
907 Node* const maybe_flags) {
908 // Normalize pattern.
909 Node* const pattern =
910 Select(IsUndefined(maybe_pattern), [=] { return EmptyStringConstant(); },
911 [=] { return ToString(context, maybe_pattern); },
912 MachineRepresentation::kTagged);
913
914 // Normalize flags.
915 Node* const flags =
916 Select(IsUndefined(maybe_flags), [=] { return EmptyStringConstant(); },
917 [=] { return ToString(context, maybe_flags); },
918 MachineRepresentation::kTagged);
919
920 // Initialize.
921
922 return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
923 pattern, flags);
924 }
925
926 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) {
927 Node* const maybe_receiver = Parameter(0);
928 Node* const context = Parameter(3);
929
930 Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver,
931 MessageTemplate::kRegExpNonObject,
932 "RegExp.prototype.flags");
933 Node* const receiver = maybe_receiver;
934
935 Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred);
936 Branch(IsInitialRegExpMap(context, map), &if_isfastpath, &if_isslowpath);
937
938 Bind(&if_isfastpath);
939 Return(FlagsGetter(context, receiver, true));
940
941 Bind(&if_isslowpath);
942 Return(FlagsGetter(context, receiver, false));
943 }
944
945 // ES#sec-regexp-pattern-flags
946 // RegExp ( pattern, flags )
947 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
948 Node* const pattern = Parameter(1);
949 Node* const flags = Parameter(2);
950 Node* const new_target = Parameter(3);
951 Node* const context = Parameter(5);
952
953 Isolate* isolate = this->isolate();
954
955 Variable var_flags(this, MachineRepresentation::kTagged, flags);
956 Variable var_pattern(this, MachineRepresentation::kTagged, pattern);
957 Variable var_new_target(this, MachineRepresentation::kTagged, new_target);
958
959 Node* const native_context = LoadNativeContext(context);
960 Node* const regexp_function =
961 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
962
963 Node* const pattern_is_regexp = IsRegExp(context, pattern);
964
965 {
966 Label next(this);
967
968 GotoIfNot(IsUndefined(new_target), &next);
969 var_new_target.Bind(regexp_function);
970
971 GotoIfNot(pattern_is_regexp, &next);
972 GotoIfNot(IsUndefined(flags), &next);
973
974 Node* const value =
975 GetProperty(context, pattern, isolate->factory()->constructor_string());
976
977 GotoIfNot(WordEqual(value, regexp_function), &next);
978 Return(pattern);
979
980 Bind(&next);
981 }
982
983 {
984 Label next(this), if_patternisfastregexp(this),
985 if_patternisslowregexp(this);
986 GotoIf(TaggedIsSmi(pattern), &next);
987
988 GotoIf(HasInstanceType(pattern, JS_REGEXP_TYPE), &if_patternisfastregexp);
989
990 Branch(pattern_is_regexp, &if_patternisslowregexp, &next);
991
992 Bind(&if_patternisfastregexp);
993 {
994 Node* const source = LoadObjectField(pattern, JSRegExp::kSourceOffset);
995 var_pattern.Bind(source);
996
997 {
998 Label inner_next(this);
999 GotoIfNot(IsUndefined(flags), &inner_next);
1000
1001 Node* const value = FlagsGetter(context, pattern, true);
1002 var_flags.Bind(value);
1003 Goto(&inner_next);
1004
1005 Bind(&inner_next);
1006 }
1007
1008 Goto(&next);
1009 }
1010
1011 Bind(&if_patternisslowregexp);
1012 {
1013 {
1014 Node* const value =
1015 GetProperty(context, pattern, isolate->factory()->source_string());
1016 var_pattern.Bind(value);
1017 }
1018
1019 {
1020 Label inner_next(this);
1021 GotoIfNot(IsUndefined(flags), &inner_next);
1022
1023 Node* const value =
1024 GetProperty(context, pattern, isolate->factory()->flags_string());
1025 var_flags.Bind(value);
1026 Goto(&inner_next);
1027
1028 Bind(&inner_next);
1029 }
1030
1031 Goto(&next);
1032 }
1033
1034 Bind(&next);
1035 }
1036
1037 // Allocate.
1038
1039 Variable var_regexp(this, MachineRepresentation::kTagged);
1040 {
1041 Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred),
1042 next(this);
1043 Branch(WordEqual(var_new_target.value(), regexp_function),
1044 &allocate_jsregexp, &allocate_generic);
1045
1046 Bind(&allocate_jsregexp);
1047 {
1048 Node* const initial_map = LoadObjectField(
1049 regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
1050 Node* const regexp = AllocateJSObjectFromMap(initial_map);
1051 var_regexp.Bind(regexp);
1052 Goto(&next);
1053 }
1054
1055 Bind(&allocate_generic);
1056 {
1057 ConstructorBuiltinsAssembler constructor_assembler(this->state());
1058 Node* const regexp = constructor_assembler.EmitFastNewObject(
1059 context, regexp_function, var_new_target.value());
1060 var_regexp.Bind(regexp);
1061 Goto(&next);
1062 }
1063
1064 Bind(&next);
1065 }
1066
1067 Node* const result = RegExpInitialize(context, var_regexp.value(),
1068 var_pattern.value(), var_flags.value());
1069 Return(result);
1070 }
1071
1072 // ES#sec-regexp.prototype.compile
1073 // RegExp.prototype.compile ( pattern, flags )
1074 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
1075 Node* const maybe_receiver = Parameter(0);
1076 Node* const maybe_pattern = Parameter(1);
1077 Node* const maybe_flags = Parameter(2);
1078 Node* const context = Parameter(5);
1079
1080 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
1081 "RegExp.prototype.compile");
1082 Node* const receiver = maybe_receiver;
1083
1084 Variable var_flags(this, MachineRepresentation::kTagged, maybe_flags);
1085 Variable var_pattern(this, MachineRepresentation::kTagged, maybe_pattern);
1086
1087 // Handle a JSRegExp pattern.
1088 {
1089 Label next(this);
1090
1091 GotoIf(TaggedIsSmi(maybe_pattern), &next);
1092 GotoIfNot(HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next);
1093
1094 Node* const pattern = maybe_pattern;
1095
1096 // {maybe_flags} must be undefined in this case, otherwise throw.
1097 {
1098 Label next(this);
1099 GotoIf(IsUndefined(maybe_flags), &next);
1100
1101 Node* const message_id = SmiConstant(MessageTemplate::kRegExpFlags);
1102 TailCallRuntime(Runtime::kThrowTypeError, context, message_id);
1103
1104 Bind(&next);
1105 }
1106
1107 Node* const new_flags = FlagsGetter(context, pattern, true);
1108 Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset);
1109
1110 var_flags.Bind(new_flags);
1111 var_pattern.Bind(new_pattern);
1112
1113 Goto(&next);
1114 Bind(&next);
1115 }
1116
1117 Node* const result = RegExpInitialize(context, receiver, var_pattern.value(),
1118 var_flags.value());
1119 Return(result);
1120 }
1121
1122 // ES6 21.2.5.10.
1123 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) {
1124 Node* const receiver = Parameter(0);
1125 Node* const context = Parameter(3);
1126
1127 // Check whether we have an unmodified regexp instance.
1128 Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred);
1129
1130 GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp);
1131 Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isjsregexp,
1132 &if_isnotjsregexp);
1133
1134 Bind(&if_isjsregexp);
1135 {
1136 Node* const source = LoadObjectField(receiver, JSRegExp::kSourceOffset);
1137 Return(source);
1138 }
1139
1140 Bind(&if_isnotjsregexp);
1141 {
1142 Isolate* isolate = this->isolate();
1143 Node* const native_context = LoadNativeContext(context);
1144 Node* const regexp_fun =
1145 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1146 Node* const initial_map =
1147 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
1148 Node* const initial_prototype = LoadMapPrototype(initial_map);
1149
1150 Label if_isprototype(this), if_isnotprototype(this);
1151 Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
1152 &if_isnotprototype);
1153
1154 Bind(&if_isprototype);
1155 {
1156 const int counter = v8::Isolate::kRegExpPrototypeSourceGetter;
1157 Node* const counter_smi = SmiConstant(counter);
1158 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
1159
1160 Node* const result =
1161 HeapConstant(isolate->factory()->NewStringFromAsciiChecked("(?:)"));
1162 Return(result);
1163 }
1164
1165 Bind(&if_isnotprototype);
1166 {
1167 Node* const message_id =
1168 SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp));
1169 Node* const method_name_str =
1170 HeapConstant(isolate->factory()->NewStringFromAsciiChecked(
1171 "RegExp.prototype.source"));
1172 TailCallRuntime(Runtime::kThrowTypeError, context, message_id,
1173 method_name_str);
1174 }
1175 }
1176 }
1177
1178 BUILTIN(RegExpPrototypeToString) { 19 BUILTIN(RegExpPrototypeToString) {
1179 HandleScope scope(isolate); 20 HandleScope scope(isolate);
1180 CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.toString"); 21 CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.toString");
1181 22
1182 if (*recv == isolate->regexp_function()->prototype()) { 23 if (*recv == isolate->regexp_function()->prototype()) {
1183 isolate->CountUsage(v8::Isolate::kRegExpPrototypeToString); 24 isolate->CountUsage(v8::Isolate::kRegExpPrototypeToString);
1184 } 25 }
1185 26
1186 IncrementalStringBuilder builder(isolate); 27 IncrementalStringBuilder builder(isolate);
1187 28
(...skipping 17 matching lines...) Expand all
1205 JSReceiver::GetProperty(recv, isolate->factory()->flags_string())); 46 JSReceiver::GetProperty(recv, isolate->factory()->flags_string()));
1206 Handle<String> flags_str; 47 Handle<String> flags_str;
1207 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags_str, 48 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags_str,
1208 Object::ToString(isolate, flags)); 49 Object::ToString(isolate, flags));
1209 builder.AppendString(flags_str); 50 builder.AppendString(flags_str);
1210 } 51 }
1211 52
1212 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); 53 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
1213 } 54 }
1214 55
1215 // Fast-path implementation for flag checks on an unmodified JSRegExp instance.
1216 Node* RegExpBuiltinsAssembler::FastFlagGetter(Node* const regexp,
1217 JSRegExp::Flag flag) {
1218 Node* const smi_zero = SmiConstant(Smi::kZero);
1219 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
1220 Node* const mask = SmiConstant(Smi::FromInt(flag));
1221 Node* const is_flag_set = WordNotEqual(SmiAnd(flags, mask), smi_zero);
1222
1223 return is_flag_set;
1224 }
1225
1226 // Load through the GetProperty stub.
1227 Node* RegExpBuiltinsAssembler::SlowFlagGetter(Node* const context,
1228 Node* const regexp,
1229 JSRegExp::Flag flag) {
1230 Factory* factory = isolate()->factory();
1231
1232 Label out(this);
1233 Variable var_result(this, MachineRepresentation::kWord32);
1234
1235 Handle<String> name;
1236 switch (flag) {
1237 case JSRegExp::kGlobal:
1238 name = factory->global_string();
1239 break;
1240 case JSRegExp::kIgnoreCase:
1241 name = factory->ignoreCase_string();
1242 break;
1243 case JSRegExp::kMultiline:
1244 name = factory->multiline_string();
1245 break;
1246 case JSRegExp::kSticky:
1247 name = factory->sticky_string();
1248 break;
1249 case JSRegExp::kUnicode:
1250 name = factory->unicode_string();
1251 break;
1252 default:
1253 UNREACHABLE();
1254 }
1255
1256 Node* const value = GetProperty(context, regexp, name);
1257
1258 Label if_true(this), if_false(this);
1259 BranchIfToBooleanIsTrue(value, &if_true, &if_false);
1260
1261 Bind(&if_true);
1262 {
1263 var_result.Bind(Int32Constant(1));
1264 Goto(&out);
1265 }
1266
1267 Bind(&if_false);
1268 {
1269 var_result.Bind(Int32Constant(0));
1270 Goto(&out);
1271 }
1272
1273 Bind(&out);
1274 return var_result.value();
1275 }
1276
1277 Node* RegExpBuiltinsAssembler::FlagGetter(Node* const context,
1278 Node* const regexp,
1279 JSRegExp::Flag flag,
1280 bool is_fastpath) {
1281 return is_fastpath ? FastFlagGetter(regexp, flag)
1282 : SlowFlagGetter(context, regexp, flag);
1283 }
1284
1285 void RegExpBuiltinsAssembler::FlagGetter(JSRegExp::Flag flag,
1286 v8::Isolate::UseCounterFeature counter,
1287 const char* method_name) {
1288 Node* const receiver = Parameter(0);
1289 Node* const context = Parameter(3);
1290
1291 Isolate* isolate = this->isolate();
1292
1293 // Check whether we have an unmodified regexp instance.
1294 Label if_isunmodifiedjsregexp(this),
1295 if_isnotunmodifiedjsregexp(this, Label::kDeferred);
1296
1297 GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp);
1298
1299 Node* const receiver_map = LoadMap(receiver);
1300 Node* const instance_type = LoadMapInstanceType(receiver_map);
1301
1302 Branch(Word32Equal(instance_type, Int32Constant(JS_REGEXP_TYPE)),
1303 &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp);
1304
1305 Bind(&if_isunmodifiedjsregexp);
1306 {
1307 // Refer to JSRegExp's flag property on the fast-path.
1308 Node* const is_flag_set = FastFlagGetter(receiver, flag);
1309 Return(SelectBooleanConstant(is_flag_set));
1310 }
1311
1312 Bind(&if_isnotunmodifiedjsregexp);
1313 {
1314 Node* const native_context = LoadNativeContext(context);
1315 Node* const regexp_fun =
1316 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
1317 Node* const initial_map =
1318 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
1319 Node* const initial_prototype = LoadMapPrototype(initial_map);
1320
1321 Label if_isprototype(this), if_isnotprototype(this);
1322 Branch(WordEqual(receiver, initial_prototype), &if_isprototype,
1323 &if_isnotprototype);
1324
1325 Bind(&if_isprototype);
1326 {
1327 Node* const counter_smi = SmiConstant(Smi::FromInt(counter));
1328 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
1329 Return(UndefinedConstant());
1330 }
1331
1332 Bind(&if_isnotprototype);
1333 {
1334 Node* const message_id =
1335 SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp));
1336 Node* const method_name_str = HeapConstant(
1337 isolate->factory()->NewStringFromAsciiChecked(method_name));
1338 CallRuntime(Runtime::kThrowTypeError, context, message_id,
1339 method_name_str);
1340 Unreachable();
1341 }
1342 }
1343 }
1344
1345 // ES6 21.2.5.4.
1346 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) {
1347 FlagGetter(JSRegExp::kGlobal, v8::Isolate::kRegExpPrototypeOldFlagGetter,
1348 "RegExp.prototype.global");
1349 }
1350
1351 // ES6 21.2.5.5.
1352 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) {
1353 FlagGetter(JSRegExp::kIgnoreCase, v8::Isolate::kRegExpPrototypeOldFlagGetter,
1354 "RegExp.prototype.ignoreCase");
1355 }
1356
1357 // ES6 21.2.5.7.
1358 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) {
1359 FlagGetter(JSRegExp::kMultiline, v8::Isolate::kRegExpPrototypeOldFlagGetter,
1360 "RegExp.prototype.multiline");
1361 }
1362
1363 // ES6 21.2.5.12.
1364 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) {
1365 FlagGetter(JSRegExp::kSticky, v8::Isolate::kRegExpPrototypeStickyGetter,
1366 "RegExp.prototype.sticky");
1367 }
1368
1369 // ES6 21.2.5.15.
1370 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
1371 FlagGetter(JSRegExp::kUnicode, v8::Isolate::kRegExpPrototypeUnicodeGetter,
1372 "RegExp.prototype.unicode");
1373 }
1374
1375 // The properties $1..$9 are the first nine capturing substrings of the last 56 // The properties $1..$9 are the first nine capturing substrings of the last
1376 // successful match, or ''. The function RegExpMakeCaptureGetter will be 57 // successful match, or ''. The function RegExpMakeCaptureGetter will be
1377 // called with indices from 1 to 9. 58 // called with indices from 1 to 9.
1378 #define DEFINE_CAPTURE_GETTER(i) \ 59 #define DEFINE_CAPTURE_GETTER(i) \
1379 BUILTIN(RegExpCapture##i##Getter) { \ 60 BUILTIN(RegExpCapture##i##Getter) { \
1380 HandleScope scope(isolate); \ 61 HandleScope scope(isolate); \
1381 return *RegExpUtils::GenericCaptureGetter( \ 62 return *RegExpUtils::GenericCaptureGetter( \
1382 isolate, isolate->regexp_last_match_info(), i); \ 63 isolate, isolate->regexp_last_match_info(), i); \
1383 } 64 }
1384 DEFINE_CAPTURE_GETTER(1) 65 DEFINE_CAPTURE_GETTER(1)
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
1448 129
1449 BUILTIN(RegExpRightContextGetter) { 130 BUILTIN(RegExpRightContextGetter) {
1450 HandleScope scope(isolate); 131 HandleScope scope(isolate);
1451 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info(); 132 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
1452 const int start_index = match_info->Capture(1); 133 const int start_index = match_info->Capture(1);
1453 Handle<String> last_subject(match_info->LastSubject()); 134 Handle<String> last_subject(match_info->LastSubject());
1454 const int len = last_subject->length(); 135 const int len = last_subject->length();
1455 return *isolate->factory()->NewSubString(last_subject, start_index, len); 136 return *isolate->factory()->NewSubString(last_subject, start_index, len);
1456 } 137 }
1457 138
1458 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
1459 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
1460 Node* string) {
1461 Isolate* isolate = this->isolate();
1462
1463 Node* const null = NullConstant();
1464
1465 Variable var_result(this, MachineRepresentation::kTagged);
1466 Label out(this), if_isfastpath(this), if_isslowpath(this);
1467
1468 Node* const map = LoadMap(regexp);
1469 BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath);
1470
1471 Bind(&if_isfastpath);
1472 {
1473 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true);
1474 var_result.Bind(result);
1475 Goto(&out);
1476 }
1477
1478 Bind(&if_isslowpath);
1479 {
1480 // Take the slow path of fetching the exec property, calling it, and
1481 // verifying its return value.
1482
1483 // Get the exec property.
1484 Node* const exec =
1485 GetProperty(context, regexp, isolate->factory()->exec_string());
1486
1487 // Is {exec} callable?
1488 Label if_iscallable(this), if_isnotcallable(this);
1489
1490 GotoIf(TaggedIsSmi(exec), &if_isnotcallable);
1491
1492 Node* const exec_map = LoadMap(exec);
1493 Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
1494
1495 Bind(&if_iscallable);
1496 {
1497 Callable call_callable = CodeFactory::Call(isolate);
1498 Node* const result = CallJS(call_callable, context, exec, regexp, string);
1499
1500 var_result.Bind(result);
1501 GotoIf(WordEqual(result, null), &out);
1502
1503 ThrowIfNotJSReceiver(context, result,
1504 MessageTemplate::kInvalidRegExpExecResult, "unused");
1505
1506 Goto(&out);
1507 }
1508
1509 Bind(&if_isnotcallable);
1510 {
1511 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
1512 "RegExp.prototype.exec");
1513
1514 Node* const result =
1515 RegExpPrototypeExecBody(context, regexp, string, false);
1516 var_result.Bind(result);
1517 Goto(&out);
1518 }
1519 }
1520
1521 Bind(&out);
1522 return var_result.value();
1523 }
1524
1525 // ES#sec-regexp.prototype.test
1526 // RegExp.prototype.test ( S )
1527 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
1528 Node* const maybe_receiver = Parameter(0);
1529 Node* const maybe_string = Parameter(1);
1530 Node* const context = Parameter(4);
1531
1532 // Ensure {maybe_receiver} is a JSReceiver.
1533 Node* const map = ThrowIfNotJSReceiver(
1534 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
1535 "RegExp.prototype.test");
1536 Node* const receiver = maybe_receiver;
1537
1538 // Convert {maybe_string} to a String.
1539 Node* const string = ToString(context, maybe_string);
1540
1541 Label fast_path(this), slow_path(this);
1542 BranchIfFastRegExp(context, map, &fast_path, &slow_path);
1543
1544 Bind(&fast_path);
1545 {
1546 Label if_didnotmatch(this);
1547 RegExpPrototypeExecBodyWithoutResult(context, receiver, string,
1548 &if_didnotmatch, true);
1549 Return(TrueConstant());
1550
1551 Bind(&if_didnotmatch);
1552 Return(FalseConstant());
1553 }
1554
1555 Bind(&slow_path);
1556 {
1557 // Call exec.
1558 Node* const match_indices = RegExpExec(context, receiver, string);
1559
1560 // Return true iff exec matched successfully.
1561 Node* const result =
1562 SelectBooleanConstant(WordNotEqual(match_indices, NullConstant()));
1563 Return(result);
1564 }
1565 }
1566
1567 Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string,
1568 Node* const index,
1569 Node* const is_unicode) {
1570 // Default to last_index + 1.
1571 Node* const index_plus_one = SmiAdd(index, SmiConstant(1));
1572 Variable var_result(this, MachineRepresentation::kTagged, index_plus_one);
1573
1574 Label if_isunicode(this), out(this);
1575 Branch(is_unicode, &if_isunicode, &out);
1576
1577 Bind(&if_isunicode);
1578 {
1579 Node* const string_length = LoadStringLength(string);
1580 GotoIfNot(SmiLessThan(index_plus_one, string_length), &out);
1581
1582 Node* const lead = StringCharCodeAt(string, index);
1583 GotoIfNot(Word32Equal(Word32And(lead, Int32Constant(0xFC00)),
1584 Int32Constant(0xD800)),
1585 &out);
1586
1587 Node* const trail = StringCharCodeAt(string, index_plus_one);
1588 GotoIfNot(Word32Equal(Word32And(trail, Int32Constant(0xFC00)),
1589 Int32Constant(0xDC00)),
1590 &out);
1591
1592 // At a surrogate pair, return index + 2.
1593 Node* const index_plus_two = SmiAdd(index, SmiConstant(2));
1594 var_result.Bind(index_plus_two);
1595
1596 Goto(&out);
1597 }
1598
1599 Bind(&out);
1600 return var_result.value();
1601 }
1602
1603 namespace {
1604
1605 // Utility class implementing a growable fixed array through CSA.
1606 class GrowableFixedArray {
1607 typedef CodeStubAssembler::Label Label;
1608 typedef CodeStubAssembler::Variable Variable;
1609
1610 public:
1611 explicit GrowableFixedArray(CodeStubAssembler* a)
1612 : assembler_(a),
1613 var_array_(a, MachineRepresentation::kTagged),
1614 var_length_(a, MachineType::PointerRepresentation()),
1615 var_capacity_(a, MachineType::PointerRepresentation()) {
1616 Initialize();
1617 }
1618
1619 Node* length() const { return var_length_.value(); }
1620
1621 Variable* var_array() { return &var_array_; }
1622 Variable* var_length() { return &var_length_; }
1623 Variable* var_capacity() { return &var_capacity_; }
1624
1625 void Push(Node* const value) {
1626 CodeStubAssembler* a = assembler_;
1627
1628 Node* const length = var_length_.value();
1629 Node* const capacity = var_capacity_.value();
1630
1631 Label grow(a), store(a);
1632 a->Branch(a->IntPtrEqual(capacity, length), &grow, &store);
1633
1634 a->Bind(&grow);
1635 {
1636 Node* const new_capacity = NewCapacity(a, capacity);
1637 Node* const new_array = ResizeFixedArray(length, new_capacity);
1638
1639 var_capacity_.Bind(new_capacity);
1640 var_array_.Bind(new_array);
1641 a->Goto(&store);
1642 }
1643
1644 a->Bind(&store);
1645 {
1646 Node* const array = var_array_.value();
1647 a->StoreFixedArrayElement(array, length, value);
1648
1649 Node* const new_length = a->IntPtrAdd(length, a->IntPtrConstant(1));
1650 var_length_.Bind(new_length);
1651 }
1652 }
1653
1654 Node* ToJSArray(Node* const context) {
1655 CodeStubAssembler* a = assembler_;
1656
1657 const ElementsKind kind = FAST_ELEMENTS;
1658
1659 Node* const native_context = a->LoadNativeContext(context);
1660 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context);
1661
1662 // Shrink to fit if necessary.
1663 {
1664 Label next(a);
1665
1666 Node* const length = var_length_.value();
1667 Node* const capacity = var_capacity_.value();
1668
1669 a->GotoIf(a->WordEqual(length, capacity), &next);
1670
1671 Node* const array = ResizeFixedArray(length, length);
1672 var_array_.Bind(array);
1673 var_capacity_.Bind(length);
1674 a->Goto(&next);
1675
1676 a->Bind(&next);
1677 }
1678
1679 Node* const result_length = a->SmiTag(length());
1680 Node* const result = a->AllocateUninitializedJSArrayWithoutElements(
1681 kind, array_map, result_length, nullptr);
1682
1683 // Note: We do not currently shrink the fixed array.
1684
1685 a->StoreObjectField(result, JSObject::kElementsOffset, var_array_.value());
1686
1687 return result;
1688 }
1689
1690 private:
1691 void Initialize() {
1692 CodeStubAssembler* a = assembler_;
1693
1694 const ElementsKind kind = FAST_ELEMENTS;
1695
1696 static const int kInitialArraySize = 8;
1697 Node* const capacity = a->IntPtrConstant(kInitialArraySize);
1698 Node* const array = a->AllocateFixedArray(kind, capacity);
1699
1700 a->FillFixedArrayWithValue(kind, array, a->IntPtrConstant(0), capacity,
1701 Heap::kTheHoleValueRootIndex);
1702
1703 var_array_.Bind(array);
1704 var_capacity_.Bind(capacity);
1705 var_length_.Bind(a->IntPtrConstant(0));
1706 }
1707
1708 Node* NewCapacity(CodeStubAssembler* a, Node* const current_capacity) {
1709 CSA_ASSERT(a, a->IntPtrGreaterThan(current_capacity, a->IntPtrConstant(0)));
1710
1711 // Growth rate is analog to JSObject::NewElementsCapacity:
1712 // new_capacity = (current_capacity + (current_capacity >> 1)) + 16.
1713
1714 Node* const new_capacity = a->IntPtrAdd(
1715 a->IntPtrAdd(current_capacity, a->WordShr(current_capacity, 1)),
1716 a->IntPtrConstant(16));
1717
1718 return new_capacity;
1719 }
1720
1721 // Creates a new array with {new_capacity} and copies the first
1722 // {element_count} elements from the current array.
1723 Node* ResizeFixedArray(Node* const element_count, Node* const new_capacity) {
1724 CodeStubAssembler* a = assembler_;
1725
1726 CSA_ASSERT(a, a->IntPtrGreaterThan(element_count, a->IntPtrConstant(0)));
1727 CSA_ASSERT(a, a->IntPtrGreaterThan(new_capacity, a->IntPtrConstant(0)));
1728 CSA_ASSERT(a, a->IntPtrGreaterThanOrEqual(new_capacity, element_count));
1729
1730 const ElementsKind kind = FAST_ELEMENTS;
1731 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
1732 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
1733 const CodeStubAssembler::AllocationFlags flags =
1734 CodeStubAssembler::kAllowLargeObjectAllocation;
1735
1736 Node* const from_array = var_array_.value();
1737 Node* const to_array =
1738 a->AllocateFixedArray(kind, new_capacity, mode, flags);
1739 a->CopyFixedArrayElements(kind, from_array, kind, to_array, element_count,
1740 new_capacity, barrier_mode, mode);
1741
1742 return to_array;
1743 }
1744
1745 private:
1746 CodeStubAssembler* const assembler_;
1747 Variable var_array_;
1748 Variable var_length_;
1749 Variable var_capacity_;
1750 };
1751
1752 } // namespace
1753
1754 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
1755 Node* const regexp,
1756 Node* const string,
1757 const bool is_fastpath) {
1758 Isolate* const isolate = this->isolate();
1759
1760 Node* const null = NullConstant();
1761 Node* const int_zero = IntPtrConstant(0);
1762 Node* const smi_zero = SmiConstant(Smi::kZero);
1763
1764 Node* const is_global =
1765 FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath);
1766
1767 Label if_isglobal(this), if_isnotglobal(this);
1768 Branch(is_global, &if_isglobal, &if_isnotglobal);
1769
1770 Bind(&if_isnotglobal);
1771 {
1772 Node* const result =
1773 is_fastpath ? RegExpPrototypeExecBody(context, regexp, string, true)
1774 : RegExpExec(context, regexp, string);
1775 Return(result);
1776 }
1777
1778 Bind(&if_isglobal);
1779 {
1780 Node* const is_unicode =
1781 FlagGetter(context, regexp, JSRegExp::kUnicode, is_fastpath);
1782
1783 StoreLastIndex(context, regexp, smi_zero, is_fastpath);
1784
1785 // Allocate an array to store the resulting match strings.
1786
1787 GrowableFixedArray array(this);
1788
1789 // Loop preparations. Within the loop, collect results from RegExpExec
1790 // and store match strings in the array.
1791
1792 Variable* vars[] = {array.var_array(), array.var_length(),
1793 array.var_capacity()};
1794 Label loop(this, 3, vars), out(this);
1795 Goto(&loop);
1796
1797 Bind(&loop);
1798 {
1799 Variable var_match(this, MachineRepresentation::kTagged);
1800
1801 Label if_didmatch(this), if_didnotmatch(this);
1802 if (is_fastpath) {
1803 // On the fast path, grab the matching string from the raw match index
1804 // array.
1805 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
1806 context, regexp, string, &if_didnotmatch, true);
1807
1808 Node* const match_from = LoadFixedArrayElement(
1809 match_indices, RegExpMatchInfo::kFirstCaptureIndex);
1810 Node* const match_to = LoadFixedArrayElement(
1811 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
1812
1813 Node* match = SubString(context, string, match_from, match_to);
1814 var_match.Bind(match);
1815
1816 Goto(&if_didmatch);
1817 } else {
1818 DCHECK(!is_fastpath);
1819 Node* const result = RegExpExec(context, regexp, string);
1820
1821 Label load_match(this);
1822 Branch(WordEqual(result, null), &if_didnotmatch, &load_match);
1823
1824 Bind(&load_match);
1825 {
1826 Label fast_result(this), slow_result(this);
1827 BranchIfFastRegExpResult(context, LoadMap(result), &fast_result,
1828 &slow_result);
1829
1830 Bind(&fast_result);
1831 {
1832 Node* const result_fixed_array = LoadElements(result);
1833 Node* const match = LoadFixedArrayElement(result_fixed_array, 0);
1834
1835 // The match is guaranteed to be a string on the fast path.
1836 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(match)));
1837
1838 var_match.Bind(match);
1839 Goto(&if_didmatch);
1840 }
1841
1842 Bind(&slow_result);
1843 {
1844 // TODO(ishell): Use GetElement stub once it's available.
1845 Node* const match = GetProperty(context, result, smi_zero);
1846 var_match.Bind(ToString(context, match));
1847 Goto(&if_didmatch);
1848 }
1849 }
1850 }
1851
1852 Bind(&if_didnotmatch);
1853 {
1854 // Return null if there were no matches, otherwise just exit the loop.
1855 GotoIfNot(IntPtrEqual(array.length(), int_zero), &out);
1856 Return(null);
1857 }
1858
1859 Bind(&if_didmatch);
1860 {
1861 Node* match = var_match.value();
1862
1863 // Store the match, growing the fixed array if needed.
1864
1865 array.Push(match);
1866
1867 // Advance last index if the match is the empty string.
1868
1869 Node* const match_length = LoadStringLength(match);
1870 GotoIfNot(SmiEqual(match_length, smi_zero), &loop);
1871
1872 Node* last_index = LoadLastIndex(context, regexp, is_fastpath);
1873
1874 Callable tolength_callable = CodeFactory::ToLength(isolate);
1875 last_index = CallStub(tolength_callable, context, last_index);
1876
1877 Node* const new_last_index =
1878 AdvanceStringIndex(string, last_index, is_unicode);
1879
1880 StoreLastIndex(context, regexp, new_last_index, is_fastpath);
1881
1882 Goto(&loop);
1883 }
1884 }
1885
1886 Bind(&out);
1887 {
1888 // Wrap the match in a JSArray.
1889
1890 Node* const result = array.ToJSArray(context);
1891 Return(result);
1892 }
1893 }
1894 }
1895
1896 // ES#sec-regexp.prototype-@@match
1897 // RegExp.prototype [ @@match ] ( string )
1898 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
1899 Node* const maybe_receiver = Parameter(0);
1900 Node* const maybe_string = Parameter(1);
1901 Node* const context = Parameter(4);
1902
1903 // Ensure {maybe_receiver} is a JSReceiver.
1904 Node* const map = ThrowIfNotJSReceiver(
1905 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
1906 "RegExp.prototype.@@match");
1907 Node* const receiver = maybe_receiver;
1908
1909 // Convert {maybe_string} to a String.
1910 Node* const string = ToString(context, maybe_string);
1911
1912 Label fast_path(this), slow_path(this);
1913 BranchIfFastRegExp(context, map, &fast_path, &slow_path);
1914
1915 Bind(&fast_path);
1916 RegExpPrototypeMatchBody(context, receiver, string, true);
1917
1918 Bind(&slow_path);
1919 RegExpPrototypeMatchBody(context, receiver, string, false);
1920 }
1921
1922 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
1923 Node* const context, Node* const regexp, Node* const string) {
1924 // Grab the initial value of last index.
1925 Node* const previous_last_index = FastLoadLastIndex(regexp);
1926
1927 // Ensure last index is 0.
1928 FastStoreLastIndex(regexp, SmiConstant(Smi::kZero));
1929
1930 // Call exec.
1931 Label if_didnotmatch(this);
1932 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
1933 context, regexp, string, &if_didnotmatch, true);
1934
1935 // Successful match.
1936 {
1937 // Reset last index.
1938 FastStoreLastIndex(regexp, previous_last_index);
1939
1940 // Return the index of the match.
1941 Node* const index = LoadFixedArrayElement(
1942 match_indices, RegExpMatchInfo::kFirstCaptureIndex);
1943 Return(index);
1944 }
1945
1946 Bind(&if_didnotmatch);
1947 {
1948 // Reset last index and return -1.
1949 FastStoreLastIndex(regexp, previous_last_index);
1950 Return(SmiConstant(-1));
1951 }
1952 }
1953
1954 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow(
1955 Node* const context, Node* const regexp, Node* const string) {
1956 Isolate* const isolate = this->isolate();
1957
1958 Node* const smi_zero = SmiConstant(Smi::kZero);
1959
1960 // Grab the initial value of last index.
1961 Node* const previous_last_index = SlowLoadLastIndex(context, regexp);
1962
1963 // Ensure last index is 0.
1964 {
1965 Label next(this);
1966 GotoIf(SameValue(previous_last_index, smi_zero), &next);
1967
1968 SlowStoreLastIndex(context, regexp, smi_zero);
1969 Goto(&next);
1970 Bind(&next);
1971 }
1972
1973 // Call exec.
1974 Node* const exec_result = RegExpExec(context, regexp, string);
1975
1976 // Reset last index if necessary.
1977 {
1978 Label next(this);
1979 Node* const current_last_index = SlowLoadLastIndex(context, regexp);
1980
1981 GotoIf(SameValue(current_last_index, previous_last_index), &next);
1982
1983 SlowStoreLastIndex(context, regexp, previous_last_index);
1984 Goto(&next);
1985
1986 Bind(&next);
1987 }
1988
1989 // Return -1 if no match was found.
1990 {
1991 Label next(this);
1992 GotoIfNot(WordEqual(exec_result, NullConstant()), &next);
1993 Return(SmiConstant(-1));
1994 Bind(&next);
1995 }
1996
1997 // Return the index of the match.
1998 {
1999 Label fast_result(this), slow_result(this, Label::kDeferred);
2000 BranchIfFastRegExpResult(context, LoadMap(exec_result), &fast_result,
2001 &slow_result);
2002
2003 Bind(&fast_result);
2004 {
2005 Node* const index =
2006 LoadObjectField(exec_result, JSRegExpResult::kIndexOffset);
2007 Return(index);
2008 }
2009
2010 Bind(&slow_result);
2011 {
2012 Return(GetProperty(context, exec_result,
2013 isolate->factory()->index_string()));
2014 }
2015 }
2016 }
2017
2018 // ES#sec-regexp.prototype-@@search
2019 // RegExp.prototype [ @@search ] ( string )
2020 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) {
2021 Node* const maybe_receiver = Parameter(0);
2022 Node* const maybe_string = Parameter(1);
2023 Node* const context = Parameter(4);
2024
2025 // Ensure {maybe_receiver} is a JSReceiver.
2026 Node* const map = ThrowIfNotJSReceiver(
2027 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
2028 "RegExp.prototype.@@search");
2029 Node* const receiver = maybe_receiver;
2030
2031 // Convert {maybe_string} to a String.
2032 Node* const string = ToString(context, maybe_string);
2033
2034 Label fast_path(this), slow_path(this);
2035 BranchIfFastRegExp(context, map, &fast_path, &slow_path);
2036
2037 Bind(&fast_path);
2038 RegExpPrototypeSearchBodyFast(context, receiver, string);
2039
2040 Bind(&slow_path);
2041 RegExpPrototypeSearchBodySlow(context, receiver, string);
2042 }
2043
2044 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp,
2045 // {string} is a String, and {limit} is a Smi.
2046 void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
2047 Node* const regexp,
2048 Node* const string,
2049 Node* const limit) {
2050 Node* const null = NullConstant();
2051 Node* const smi_zero = SmiConstant(0);
2052 Node* const int_zero = IntPtrConstant(0);
2053 Node* const int_limit = SmiUntag(limit);
2054
2055 const ElementsKind kind = FAST_ELEMENTS;
2056 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
2057
2058 Node* const allocation_site = nullptr;
2059 Node* const native_context = LoadNativeContext(context);
2060 Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
2061
2062 Label return_empty_array(this, Label::kDeferred);
2063
2064 // If limit is zero, return an empty array.
2065 {
2066 Label next(this), if_limitiszero(this, Label::kDeferred);
2067 Branch(SmiEqual(limit, smi_zero), &return_empty_array, &next);
2068 Bind(&next);
2069 }
2070
2071 Node* const string_length = LoadStringLength(string);
2072
2073 // If passed the empty {string}, return either an empty array or a singleton
2074 // array depending on whether the {regexp} matches.
2075 {
2076 Label next(this), if_stringisempty(this, Label::kDeferred);
2077 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next);
2078
2079 Bind(&if_stringisempty);
2080 {
2081 Node* const last_match_info = LoadContextElement(
2082 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2083
2084 Node* const match_indices =
2085 IrregexpExec(context, regexp, string, smi_zero, last_match_info);
2086
2087 Label return_singleton_array(this);
2088 Branch(WordEqual(match_indices, null), &return_singleton_array,
2089 &return_empty_array);
2090
2091 Bind(&return_singleton_array);
2092 {
2093 Node* const length = SmiConstant(1);
2094 Node* const capacity = IntPtrConstant(1);
2095 Node* const result = AllocateJSArray(kind, array_map, capacity, length,
2096 allocation_site, mode);
2097
2098 Node* const fixed_array = LoadElements(result);
2099 StoreFixedArrayElement(fixed_array, 0, string);
2100
2101 Return(result);
2102 }
2103 }
2104
2105 Bind(&next);
2106 }
2107
2108 // Loop preparations.
2109
2110 GrowableFixedArray array(this);
2111
2112 Variable var_last_matched_until(this, MachineRepresentation::kTagged);
2113 Variable var_next_search_from(this, MachineRepresentation::kTagged);
2114
2115 var_last_matched_until.Bind(smi_zero);
2116 var_next_search_from.Bind(smi_zero);
2117
2118 Variable* vars[] = {array.var_array(), array.var_length(),
2119 array.var_capacity(), &var_last_matched_until,
2120 &var_next_search_from};
2121 const int vars_count = sizeof(vars) / sizeof(vars[0]);
2122 Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this);
2123 Goto(&loop);
2124
2125 Bind(&loop);
2126 {
2127 Node* const next_search_from = var_next_search_from.value();
2128 Node* const last_matched_until = var_last_matched_until.value();
2129
2130 // We're done if we've reached the end of the string.
2131 {
2132 Label next(this);
2133 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out,
2134 &next);
2135 Bind(&next);
2136 }
2137
2138 // Search for the given {regexp}.
2139
2140 Node* const last_match_info = LoadContextElement(
2141 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2142
2143 Node* const match_indices = IrregexpExec(context, regexp, string,
2144 next_search_from, last_match_info);
2145
2146 // We're done if no match was found.
2147 {
2148 Label next(this);
2149 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next);
2150 Bind(&next);
2151 }
2152
2153 Node* const match_from = LoadFixedArrayElement(
2154 match_indices, RegExpMatchInfo::kFirstCaptureIndex);
2155
2156 // We're done if the match starts beyond the string.
2157 {
2158 Label next(this);
2159 Branch(WordEqual(match_from, string_length), &push_suffix_and_out, &next);
2160 Bind(&next);
2161 }
2162
2163 Node* const match_to = LoadFixedArrayElement(
2164 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
2165
2166 // Advance index and continue if the match is empty.
2167 {
2168 Label next(this);
2169
2170 GotoIfNot(SmiEqual(match_to, next_search_from), &next);
2171 GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
2172
2173 Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode);
2174 Node* const new_next_search_from =
2175 AdvanceStringIndex(string, next_search_from, is_unicode);
2176 var_next_search_from.Bind(new_next_search_from);
2177 Goto(&loop);
2178
2179 Bind(&next);
2180 }
2181
2182 // A valid match was found, add the new substring to the array.
2183 {
2184 Node* const from = last_matched_until;
2185 Node* const to = match_from;
2186
2187 Node* const substr = SubString(context, string, from, to);
2188 array.Push(substr);
2189
2190 GotoIf(WordEqual(array.length(), int_limit), &out);
2191 }
2192
2193 // Add all captures to the array.
2194 {
2195 Node* const num_registers = LoadFixedArrayElement(
2196 match_indices, RegExpMatchInfo::kNumberOfCapturesIndex);
2197 Node* const int_num_registers = SmiUntag(num_registers);
2198
2199 Variable var_reg(this, MachineType::PointerRepresentation());
2200 var_reg.Bind(IntPtrConstant(2));
2201
2202 Variable* vars[] = {array.var_array(), array.var_length(),
2203 array.var_capacity(), &var_reg};
2204 const int vars_count = sizeof(vars) / sizeof(vars[0]);
2205 Label nested_loop(this, vars_count, vars), nested_loop_out(this);
2206 Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
2207 &nested_loop_out);
2208
2209 Bind(&nested_loop);
2210 {
2211 Node* const reg = var_reg.value();
2212 Node* const from = LoadFixedArrayElement(
2213 match_indices, reg,
2214 RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode);
2215 Node* const to = LoadFixedArrayElement(
2216 match_indices, reg,
2217 (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode);
2218
2219 Label select_capture(this), select_undefined(this), store_value(this);
2220 Variable var_value(this, MachineRepresentation::kTagged);
2221 Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
2222 &select_capture);
2223
2224 Bind(&select_capture);
2225 {
2226 Node* const substr = SubString(context, string, from, to);
2227 var_value.Bind(substr);
2228 Goto(&store_value);
2229 }
2230
2231 Bind(&select_undefined);
2232 {
2233 Node* const undefined = UndefinedConstant();
2234 var_value.Bind(undefined);
2235 Goto(&store_value);
2236 }
2237
2238 Bind(&store_value);
2239 {
2240 array.Push(var_value.value());
2241 GotoIf(WordEqual(array.length(), int_limit), &out);
2242
2243 Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2));
2244 var_reg.Bind(new_reg);
2245
2246 Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
2247 &nested_loop_out);
2248 }
2249 }
2250
2251 Bind(&nested_loop_out);
2252 }
2253
2254 var_last_matched_until.Bind(match_to);
2255 var_next_search_from.Bind(match_to);
2256 Goto(&loop);
2257 }
2258
2259 Bind(&push_suffix_and_out);
2260 {
2261 Node* const from = var_last_matched_until.value();
2262 Node* const to = string_length;
2263
2264 Node* const substr = SubString(context, string, from, to);
2265 array.Push(substr);
2266
2267 Goto(&out);
2268 }
2269
2270 Bind(&out);
2271 {
2272 Node* const result = array.ToJSArray(context);
2273 Return(result);
2274 }
2275
2276 Bind(&return_empty_array);
2277 {
2278 Node* const length = smi_zero;
2279 Node* const capacity = int_zero;
2280 Node* const result = AllocateJSArray(kind, array_map, capacity, length,
2281 allocation_site, mode);
2282 Return(result);
2283 }
2284 }
2285
2286 // Helper that skips a few initial checks.
2287 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
2288 typedef RegExpSplitDescriptor Descriptor;
2289
2290 Node* const regexp = Parameter(Descriptor::kReceiver);
2291 Node* const string = Parameter(Descriptor::kString);
2292 Node* const maybe_limit = Parameter(Descriptor::kLimit);
2293 Node* const context = Parameter(Descriptor::kContext);
2294
2295 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
2296 CSA_ASSERT(this, IsString(string));
2297
2298 // TODO(jgruber): Even if map checks send us to the fast path, we still need
2299 // to verify the constructor property and jump to the slow path if it has
2300 // been changed.
2301
2302 // Convert {maybe_limit} to a uint32, capping at the maximal smi value.
2303 Variable var_limit(this, MachineRepresentation::kTagged);
2304 Label if_limitissmimax(this), limit_done(this);
2305
2306 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
2307
2308 {
2309 Node* const limit = ToUint32(context, maybe_limit);
2310 GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax);
2311
2312 var_limit.Bind(limit);
2313 Goto(&limit_done);
2314 }
2315
2316 Bind(&if_limitissmimax);
2317 {
2318 // TODO(jgruber): In this case, we can probably avoid generation of limit
2319 // checks in Generate_RegExpPrototypeSplitBody.
2320 var_limit.Bind(SmiConstant(Smi::kMaxValue));
2321 Goto(&limit_done);
2322 }
2323
2324 Bind(&limit_done);
2325 {
2326 Node* const limit = var_limit.value();
2327 RegExpPrototypeSplitBody(context, regexp, string, limit);
2328 }
2329 }
2330
2331 // ES#sec-regexp.prototype-@@split
2332 // RegExp.prototype [ @@split ] ( string, limit )
2333 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
2334 Node* const maybe_receiver = Parameter(0);
2335 Node* const maybe_string = Parameter(1);
2336 Node* const maybe_limit = Parameter(2);
2337 Node* const context = Parameter(5);
2338
2339 // Ensure {maybe_receiver} is a JSReceiver.
2340 Node* const map = ThrowIfNotJSReceiver(
2341 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
2342 "RegExp.prototype.@@split");
2343 Node* const receiver = maybe_receiver;
2344
2345 // Convert {maybe_string} to a String.
2346 Node* const string = ToString(context, maybe_string);
2347
2348 Label stub(this), runtime(this, Label::kDeferred);
2349 BranchIfFastRegExp(context, map, &stub, &runtime);
2350
2351 Bind(&stub);
2352 Callable split_callable = CodeFactory::RegExpSplit(isolate());
2353 Return(CallStub(split_callable, context, receiver, string, maybe_limit));
2354
2355 Bind(&runtime);
2356 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string,
2357 maybe_limit));
2358 }
2359
2360 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
2361 Node* context, Node* regexp, Node* string, Node* replace_callable) {
2362 // The fast path is reached only if {receiver} is a global unmodified
2363 // JSRegExp instance and {replace_callable} is callable.
2364
2365 Isolate* const isolate = this->isolate();
2366
2367 Node* const null = NullConstant();
2368 Node* const undefined = UndefinedConstant();
2369 Node* const int_zero = IntPtrConstant(0);
2370 Node* const int_one = IntPtrConstant(1);
2371 Node* const smi_zero = SmiConstant(Smi::kZero);
2372
2373 Node* const native_context = LoadNativeContext(context);
2374
2375 Label out(this);
2376 Variable var_result(this, MachineRepresentation::kTagged);
2377
2378 // Set last index to 0.
2379 FastStoreLastIndex(regexp, smi_zero);
2380
2381 // Allocate {result_array}.
2382 Node* result_array;
2383 {
2384 ElementsKind kind = FAST_ELEMENTS;
2385 Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
2386 Node* const capacity = IntPtrConstant(16);
2387 Node* const length = smi_zero;
2388 Node* const allocation_site = nullptr;
2389 ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS;
2390
2391 result_array = AllocateJSArray(kind, array_map, capacity, length,
2392 allocation_site, capacity_mode);
2393 }
2394
2395 // Call into runtime for RegExpExecMultiple.
2396 Node* last_match_info =
2397 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2398 Node* const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
2399 string, last_match_info, result_array);
2400
2401 // Reset last index to 0.
2402 FastStoreLastIndex(regexp, smi_zero);
2403
2404 // If no matches, return the subject string.
2405 var_result.Bind(string);
2406 GotoIf(WordEqual(res, null), &out);
2407
2408 // Reload last match info since it might have changed.
2409 last_match_info =
2410 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2411
2412 Node* const res_length = LoadJSArrayLength(res);
2413 Node* const res_elems = LoadElements(res);
2414 CSA_ASSERT(this, HasInstanceType(res_elems, FIXED_ARRAY_TYPE));
2415
2416 Node* const num_capture_registers = LoadFixedArrayElement(
2417 last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex);
2418
2419 Label if_hasexplicitcaptures(this), if_noexplicitcaptures(this),
2420 create_result(this);
2421 Branch(SmiEqual(num_capture_registers, SmiConstant(Smi::FromInt(2))),
2422 &if_noexplicitcaptures, &if_hasexplicitcaptures);
2423
2424 Bind(&if_noexplicitcaptures);
2425 {
2426 // If the number of captures is two then there are no explicit captures in
2427 // the regexp, just the implicit capture that captures the whole match. In
2428 // this case we can simplify quite a bit and end up with something faster.
2429 // The builder will consist of some integers that indicate slices of the
2430 // input string and some replacements that were returned from the replace
2431 // function.
2432
2433 Variable var_match_start(this, MachineRepresentation::kTagged);
2434 var_match_start.Bind(smi_zero);
2435
2436 Node* const end = SmiUntag(res_length);
2437 Variable var_i(this, MachineType::PointerRepresentation());
2438 var_i.Bind(int_zero);
2439
2440 Variable* vars[] = {&var_i, &var_match_start};
2441 Label loop(this, 2, vars);
2442 Goto(&loop);
2443 Bind(&loop);
2444 {
2445 Node* const i = var_i.value();
2446 GotoIfNot(IntPtrLessThan(i, end), &create_result);
2447
2448 Node* const elem = LoadFixedArrayElement(res_elems, i);
2449
2450 Label if_issmi(this), if_isstring(this), loop_epilogue(this);
2451 Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring);
2452
2453 Bind(&if_issmi);
2454 {
2455 // Integers represent slices of the original string.
2456 Label if_isnegativeorzero(this), if_ispositive(this);
2457 BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero,
2458 &if_ispositive);
2459
2460 Bind(&if_ispositive);
2461 {
2462 Node* const int_elem = SmiUntag(elem);
2463 Node* const new_match_start =
2464 IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)),
2465 WordAnd(int_elem, IntPtrConstant(0x7ff)));
2466 var_match_start.Bind(SmiTag(new_match_start));
2467 Goto(&loop_epilogue);
2468 }
2469
2470 Bind(&if_isnegativeorzero);
2471 {
2472 Node* const next_i = IntPtrAdd(i, int_one);
2473 var_i.Bind(next_i);
2474
2475 Node* const next_elem = LoadFixedArrayElement(res_elems, next_i);
2476
2477 Node* const new_match_start = SmiSub(next_elem, elem);
2478 var_match_start.Bind(new_match_start);
2479 Goto(&loop_epilogue);
2480 }
2481 }
2482
2483 Bind(&if_isstring);
2484 {
2485 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(elem)));
2486
2487 Callable call_callable = CodeFactory::Call(isolate);
2488 Node* const replacement_obj =
2489 CallJS(call_callable, context, replace_callable, undefined, elem,
2490 var_match_start.value(), string);
2491
2492 Node* const replacement_str = ToString(context, replacement_obj);
2493 StoreFixedArrayElement(res_elems, i, replacement_str);
2494
2495 Node* const elem_length = LoadStringLength(elem);
2496 Node* const new_match_start =
2497 SmiAdd(var_match_start.value(), elem_length);
2498 var_match_start.Bind(new_match_start);
2499
2500 Goto(&loop_epilogue);
2501 }
2502
2503 Bind(&loop_epilogue);
2504 {
2505 var_i.Bind(IntPtrAdd(var_i.value(), int_one));
2506 Goto(&loop);
2507 }
2508 }
2509 }
2510
2511 Bind(&if_hasexplicitcaptures);
2512 {
2513 Node* const from = int_zero;
2514 Node* const to = SmiUntag(res_length);
2515 const int increment = 1;
2516
2517 BuildFastLoop(
2518 from, to,
2519 [this, res_elems, isolate, native_context, context, undefined,
2520 replace_callable](Node* index) {
2521 Node* const elem = LoadFixedArrayElement(res_elems, index);
2522
2523 Label do_continue(this);
2524 GotoIf(TaggedIsSmi(elem), &do_continue);
2525
2526 // elem must be an Array.
2527 // Use the apply argument as backing for global RegExp properties.
2528
2529 CSA_ASSERT(this, HasInstanceType(elem, JS_ARRAY_TYPE));
2530
2531 // TODO(jgruber): Remove indirection through Call->ReflectApply.
2532 Callable call_callable = CodeFactory::Call(isolate);
2533 Node* const reflect_apply =
2534 LoadContextElement(native_context, Context::REFLECT_APPLY_INDEX);
2535
2536 Node* const replacement_obj =
2537 CallJS(call_callable, context, reflect_apply, undefined,
2538 replace_callable, undefined, elem);
2539
2540 // Overwrite the i'th element in the results with the string we got
2541 // back from the callback function.
2542
2543 Node* const replacement_str = ToString(context, replacement_obj);
2544 StoreFixedArrayElement(res_elems, index, replacement_str);
2545
2546 Goto(&do_continue);
2547 Bind(&do_continue);
2548 },
2549 increment, CodeStubAssembler::INTPTR_PARAMETERS,
2550 CodeStubAssembler::IndexAdvanceMode::kPost);
2551
2552 Goto(&create_result);
2553 }
2554
2555 Bind(&create_result);
2556 {
2557 Node* const result = CallRuntime(Runtime::kStringBuilderConcat, context,
2558 res, res_length, string);
2559 var_result.Bind(result);
2560 Goto(&out);
2561 }
2562
2563 Bind(&out);
2564 return var_result.value();
2565 }
2566
2567 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
2568 Node* context, Node* regexp, Node* string, Node* replace_string) {
2569 // The fast path is reached only if {receiver} is an unmodified
2570 // JSRegExp instance, {replace_value} is non-callable, and
2571 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple
2572 // string replacement.
2573
2574 Node* const int_zero = IntPtrConstant(0);
2575 Node* const smi_zero = SmiConstant(Smi::kZero);
2576
2577 Label out(this);
2578 Variable var_result(this, MachineRepresentation::kTagged);
2579
2580 // Load the last match info.
2581 Node* const native_context = LoadNativeContext(context);
2582 Node* const last_match_info =
2583 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
2584
2585 // Is {regexp} global?
2586 Label if_isglobal(this), if_isnonglobal(this);
2587 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
2588 Node* const is_global =
2589 WordAnd(SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal));
2590 Branch(WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal);
2591
2592 Bind(&if_isglobal);
2593 {
2594 // Hand off global regexps to runtime.
2595 FastStoreLastIndex(regexp, smi_zero);
2596 Node* const result =
2597 CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context,
2598 string, regexp, replace_string, last_match_info);
2599 var_result.Bind(result);
2600 Goto(&out);
2601 }
2602
2603 Bind(&if_isnonglobal);
2604 {
2605 // Run exec, then manually construct the resulting string.
2606 Label if_didnotmatch(this);
2607 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult(
2608 context, regexp, string, &if_didnotmatch, true);
2609
2610 // Successful match.
2611 {
2612 Node* const subject_start = smi_zero;
2613 Node* const match_start = LoadFixedArrayElement(
2614 match_indices, RegExpMatchInfo::kFirstCaptureIndex);
2615 Node* const match_end = LoadFixedArrayElement(
2616 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
2617 Node* const subject_end = LoadStringLength(string);
2618
2619 Label if_replaceisempty(this), if_replaceisnotempty(this);
2620 Node* const replace_length = LoadStringLength(replace_string);
2621 Branch(SmiEqual(replace_length, smi_zero), &if_replaceisempty,
2622 &if_replaceisnotempty);
2623
2624 Bind(&if_replaceisempty);
2625 {
2626 // TODO(jgruber): We could skip many of the checks that using SubString
2627 // here entails.
2628
2629 Node* const first_part =
2630 SubString(context, string, subject_start, match_start);
2631 Node* const second_part =
2632 SubString(context, string, match_end, subject_end);
2633
2634 Node* const result = StringAdd(context, first_part, second_part);
2635 var_result.Bind(result);
2636 Goto(&out);
2637 }
2638
2639 Bind(&if_replaceisnotempty);
2640 {
2641 Node* const first_part =
2642 SubString(context, string, subject_start, match_start);
2643 Node* const second_part = replace_string;
2644 Node* const third_part =
2645 SubString(context, string, match_end, subject_end);
2646
2647 Node* result = StringAdd(context, first_part, second_part);
2648 result = StringAdd(context, result, third_part);
2649
2650 var_result.Bind(result);
2651 Goto(&out);
2652 }
2653 }
2654
2655 Bind(&if_didnotmatch);
2656 {
2657 var_result.Bind(string);
2658 Goto(&out);
2659 }
2660 }
2661
2662 Bind(&out);
2663 return var_result.value();
2664 }
2665
2666 // Helper that skips a few initial checks.
2667 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
2668 typedef RegExpReplaceDescriptor Descriptor;
2669
2670 Node* const regexp = Parameter(Descriptor::kReceiver);
2671 Node* const string = Parameter(Descriptor::kString);
2672 Node* const replace_value = Parameter(Descriptor::kReplaceValue);
2673 Node* const context = Parameter(Descriptor::kContext);
2674
2675 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
2676 CSA_ASSERT(this, IsString(string));
2677
2678 Label checkreplacestring(this), if_iscallable(this),
2679 runtime(this, Label::kDeferred);
2680
2681 // 2. Is {replace_value} callable?
2682 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
2683 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable,
2684 &checkreplacestring);
2685
2686 // 3. Does ToString({replace_value}) contain '$'?
2687 Bind(&checkreplacestring);
2688 {
2689 Callable tostring_callable = CodeFactory::ToString(isolate());
2690 Node* const replace_string =
2691 CallStub(tostring_callable, context, replace_value);
2692
2693 Callable indexof_callable = CodeFactory::StringIndexOf(isolate());
2694 Node* const dollar_string = HeapConstant(
2695 isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
2696 Node* const dollar_ix = CallStub(indexof_callable, context, replace_string,
2697 dollar_string, SmiConstant(0));
2698 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime);
2699
2700 Return(
2701 ReplaceSimpleStringFastPath(context, regexp, string, replace_string));
2702 }
2703
2704 // {regexp} is unmodified and {replace_value} is callable.
2705 Bind(&if_iscallable);
2706 {
2707 Node* const replace_fn = replace_value;
2708
2709 // Check if the {regexp} is global.
2710 Label if_isglobal(this), if_isnotglobal(this);
2711
2712 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
2713 Branch(is_global, &if_isglobal, &if_isnotglobal);
2714
2715 Bind(&if_isglobal);
2716 Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn));
2717
2718 Bind(&if_isnotglobal);
2719 Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
2720 context, string, regexp, replace_fn));
2721 }
2722
2723 Bind(&runtime);
2724 Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string,
2725 replace_value));
2726 }
2727
2728 // ES#sec-regexp.prototype-@@replace
2729 // RegExp.prototype [ @@replace ] ( string, replaceValue )
2730 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
2731 Node* const maybe_receiver = Parameter(0);
2732 Node* const maybe_string = Parameter(1);
2733 Node* const replace_value = Parameter(2);
2734 Node* const context = Parameter(5);
2735
2736 // RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic:
2737 //
2738 // if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace)
2739 // if (IsCallable(replace)) {
2740 // if (IsGlobal(receiver)) {
2741 // // Called 'fast-path' but contains several runtime calls.
2742 // ReplaceGlobalCallableFastPath()
2743 // } else {
2744 // CallRuntime(StringReplaceNonGlobalRegExpWithFunction)
2745 // }
2746 // } else {
2747 // if (replace.contains("$")) {
2748 // CallRuntime(RegExpReplace)
2749 // } else {
2750 // ReplaceSimpleStringFastPath() // Bails to runtime for global regexps.
2751 // }
2752 // }
2753
2754 // Ensure {maybe_receiver} is a JSReceiver.
2755 Node* const map = ThrowIfNotJSReceiver(
2756 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
2757 "RegExp.prototype.@@replace");
2758 Node* const receiver = maybe_receiver;
2759
2760 // Convert {maybe_string} to a String.
2761 Callable tostring_callable = CodeFactory::ToString(isolate());
2762 Node* const string = CallStub(tostring_callable, context, maybe_string);
2763
2764 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
2765 Label stub(this), runtime(this, Label::kDeferred);
2766 BranchIfFastRegExp(context, map, &stub, &runtime);
2767
2768 Bind(&stub);
2769 Callable replace_callable = CodeFactory::RegExpReplace(isolate());
2770 Return(CallStub(replace_callable, context, receiver, string, replace_value));
2771
2772 Bind(&runtime);
2773 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string,
2774 replace_value));
2775 }
2776
2777 // Simple string matching functionality for internal use which does not modify
2778 // the last match info.
2779 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) {
2780 Node* const regexp = Parameter(1);
2781 Node* const string = Parameter(2);
2782 Node* const context = Parameter(5);
2783
2784 Node* const null = NullConstant();
2785 Node* const smi_zero = SmiConstant(Smi::FromInt(0));
2786
2787 Node* const native_context = LoadNativeContext(context);
2788 Node* const internal_match_info = LoadContextElement(
2789 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX);
2790
2791 Node* const match_indices =
2792 IrregexpExec(context, regexp, string, smi_zero, internal_match_info);
2793
2794 Label if_matched(this), if_didnotmatch(this);
2795 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched);
2796
2797 Bind(&if_didnotmatch);
2798 Return(null);
2799
2800 Bind(&if_matched);
2801 {
2802 Node* result =
2803 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string);
2804 Return(result);
2805 }
2806 }
2807
2808 } // namespace internal 139 } // namespace internal
2809 } // namespace v8 140 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins-regexp.h ('k') | src/builtins/builtins-regexp-gen.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698