1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/builtins/builtins-utils.h"
6 #include "src/builtins/builtins.h"
7 
8 #include "src/code-factory.h"
9 #include "src/regexp/jsregexp.h"
10 #include "src/regexp/regexp-utils.h"
11 #include "src/string-builder.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 // -----------------------------------------------------------------------------
17 // ES6 section 21.2 RegExp Objects
18 
19 namespace {
20 
PatternFlags(Isolate * isolate,Handle<JSRegExp> regexp)21 Handle<String> PatternFlags(Isolate* isolate, Handle<JSRegExp> regexp) {
22   static const int kMaxFlagsLength = 5 + 1;  // 5 flags and '\0';
23   char flags_string[kMaxFlagsLength];
24   int i = 0;
25 
26   const JSRegExp::Flags flags = regexp->GetFlags();
27 
28   if ((flags & JSRegExp::kGlobal) != 0) flags_string[i++] = 'g';
29   if ((flags & JSRegExp::kIgnoreCase) != 0) flags_string[i++] = 'i';
30   if ((flags & JSRegExp::kMultiline) != 0) flags_string[i++] = 'm';
31   if ((flags & JSRegExp::kUnicode) != 0) flags_string[i++] = 'u';
32   if ((flags & JSRegExp::kSticky) != 0) flags_string[i++] = 'y';
33 
34   DCHECK_LT(i, kMaxFlagsLength);
35   memset(&flags_string[i], '\0', kMaxFlagsLength - i);
36 
37   return isolate->factory()->NewStringFromAsciiChecked(flags_string);
38 }
39 
40 // ES#sec-regexpinitialize
41 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
RegExpInitialize(Isolate * isolate,Handle<JSRegExp> regexp,Handle<Object> pattern,Handle<Object> flags)42 MUST_USE_RESULT MaybeHandle<JSRegExp> RegExpInitialize(Isolate* isolate,
43                                                        Handle<JSRegExp> regexp,
44                                                        Handle<Object> pattern,
45                                                        Handle<Object> flags) {
46   Handle<String> pattern_string;
47   if (pattern->IsUndefined(isolate)) {
48     pattern_string = isolate->factory()->empty_string();
49   } else {
50     ASSIGN_RETURN_ON_EXCEPTION(isolate, pattern_string,
51                                Object::ToString(isolate, pattern), JSRegExp);
52   }
53 
54   Handle<String> flags_string;
55   if (flags->IsUndefined(isolate)) {
56     flags_string = isolate->factory()->empty_string();
57   } else {
58     ASSIGN_RETURN_ON_EXCEPTION(isolate, flags_string,
59                                Object::ToString(isolate, flags), JSRegExp);
60   }
61 
62   // TODO(jgruber): We could avoid the flags back and forth conversions.
63   return JSRegExp::Initialize(regexp, pattern_string, flags_string);
64 }
65 
66 }  // namespace
67 
68 // ES#sec-regexp-pattern-flags
69 // RegExp ( pattern, flags )
BUILTIN(RegExpConstructor)70 BUILTIN(RegExpConstructor) {
71   HandleScope scope(isolate);
72 
73   Handle<HeapObject> new_target = args.new_target();
74   Handle<Object> pattern = args.atOrUndefined(isolate, 1);
75   Handle<Object> flags = args.atOrUndefined(isolate, 2);
76 
77   Handle<JSFunction> target = isolate->regexp_function();
78 
79   bool pattern_is_regexp;
80   {
81     Maybe<bool> maybe_pattern_is_regexp =
82         RegExpUtils::IsRegExp(isolate, pattern);
83     if (maybe_pattern_is_regexp.IsNothing()) {
84       DCHECK(isolate->has_pending_exception());
85       return isolate->heap()->exception();
86     }
87     pattern_is_regexp = maybe_pattern_is_regexp.FromJust();
88   }
89 
90   if (new_target->IsUndefined(isolate)) {
91     new_target = target;
92 
93     // ES6 section 21.2.3.1 step 3.b
94     if (pattern_is_regexp && flags->IsUndefined(isolate)) {
95       Handle<Object> pattern_constructor;
96       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
97           isolate, pattern_constructor,
98           Object::GetProperty(pattern,
99                               isolate->factory()->constructor_string()));
100 
101       if (pattern_constructor.is_identical_to(new_target)) {
102         return *pattern;
103       }
104     }
105   }
106 
107   if (pattern->IsJSRegExp()) {
108     Handle<JSRegExp> regexp_pattern = Handle<JSRegExp>::cast(pattern);
109 
110     if (flags->IsUndefined(isolate)) {
111       flags = PatternFlags(isolate, regexp_pattern);
112     }
113     pattern = handle(regexp_pattern->source(), isolate);
114   } else if (pattern_is_regexp) {
115     Handle<Object> pattern_source;
116     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
117         isolate, pattern_source,
118         Object::GetProperty(pattern, isolate->factory()->source_string()));
119 
120     if (flags->IsUndefined(isolate)) {
121       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
122           isolate, flags,
123           Object::GetProperty(pattern, isolate->factory()->flags_string()));
124     }
125     pattern = pattern_source;
126   }
127 
128   Handle<JSReceiver> new_target_receiver = Handle<JSReceiver>::cast(new_target);
129 
130   // TODO(jgruber): Fast-path for target == new_target == unmodified JSRegExp.
131 
132   Handle<JSObject> object;
133   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
134       isolate, object, JSObject::New(target, new_target_receiver));
135   Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object);
136 
137   RETURN_RESULT_OR_FAILURE(isolate,
138                            RegExpInitialize(isolate, regexp, pattern, flags));
139 }
140 
BUILTIN(RegExpPrototypeCompile)141 BUILTIN(RegExpPrototypeCompile) {
142   HandleScope scope(isolate);
143   CHECK_RECEIVER(JSRegExp, regexp, "RegExp.prototype.compile");
144 
145   Handle<Object> pattern = args.atOrUndefined(isolate, 1);
146   Handle<Object> flags = args.atOrUndefined(isolate, 2);
147 
148   if (pattern->IsJSRegExp()) {
149     Handle<JSRegExp> pattern_regexp = Handle<JSRegExp>::cast(pattern);
150 
151     if (!flags->IsUndefined(isolate)) {
152       THROW_NEW_ERROR_RETURN_FAILURE(
153           isolate, NewTypeError(MessageTemplate::kRegExpFlags));
154     }
155 
156     flags = PatternFlags(isolate, pattern_regexp);
157     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
158         isolate, pattern,
159         Object::GetProperty(pattern, isolate->factory()->source_string()));
160   }
161 
162   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
163       isolate, regexp, RegExpInitialize(isolate, regexp, pattern, flags));
164 
165   // Return undefined for compatibility with JSC.
166   // See http://crbug.com/585775 for web compat details.
167 
168   return isolate->heap()->undefined_value();
169 }
170 
171 namespace {
172 
FastLoadLastIndex(CodeStubAssembler * a,compiler::Node * context,compiler::Node * regexp)173 compiler::Node* FastLoadLastIndex(CodeStubAssembler* a, compiler::Node* context,
174                                   compiler::Node* regexp) {
175   // Load the in-object field.
176   static const int field_offset =
177       JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
178   return a->LoadObjectField(regexp, field_offset);
179 }
180 
SlowLoadLastIndex(CodeStubAssembler * a,compiler::Node * context,compiler::Node * regexp)181 compiler::Node* SlowLoadLastIndex(CodeStubAssembler* a, compiler::Node* context,
182                                   compiler::Node* regexp) {
183   // Load through the GetProperty stub.
184   typedef compiler::Node Node;
185 
186   Node* const name =
187       a->HeapConstant(a->isolate()->factory()->lastIndex_string());
188   Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
189   return a->CallStub(getproperty_callable, context, regexp, name);
190 }
191 
LoadLastIndex(CodeStubAssembler * a,compiler::Node * context,compiler::Node * has_initialmap,compiler::Node * regexp)192 compiler::Node* LoadLastIndex(CodeStubAssembler* a, compiler::Node* context,
193                               compiler::Node* has_initialmap,
194                               compiler::Node* regexp) {
195   typedef CodeStubAssembler::Variable Variable;
196   typedef CodeStubAssembler::Label Label;
197 
198   Variable var_value(a, MachineRepresentation::kTagged);
199 
200   Label out(a), if_unmodified(a), if_modified(a);
201   a->Branch(has_initialmap, &if_unmodified, &if_modified);
202 
203   a->Bind(&if_unmodified);
204   {
205     var_value.Bind(FastLoadLastIndex(a, context, regexp));
206     a->Goto(&out);
207   }
208 
209   a->Bind(&if_modified);
210   {
211     var_value.Bind(SlowLoadLastIndex(a, context, regexp));
212     a->Goto(&out);
213   }
214 
215   a->Bind(&out);
216   return var_value.value();
217 }
218 
219 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified
220 // JSRegExp instance.
FastStoreLastIndex(CodeStubAssembler * a,compiler::Node * context,compiler::Node * regexp,compiler::Node * value)221 void FastStoreLastIndex(CodeStubAssembler* a, compiler::Node* context,
222                         compiler::Node* regexp, compiler::Node* value) {
223   // Store the in-object field.
224   static const int field_offset =
225       JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize;
226   a->StoreObjectField(regexp, field_offset, value);
227 }
228 
SlowStoreLastIndex(CodeStubAssembler * a,compiler::Node * context,compiler::Node * regexp,compiler::Node * value)229 void SlowStoreLastIndex(CodeStubAssembler* a, compiler::Node* context,
230                         compiler::Node* regexp, compiler::Node* value) {
231   // Store through runtime.
232   // TODO(ishell): Use SetPropertyStub here once available.
233   typedef compiler::Node Node;
234 
235   Node* const name =
236       a->HeapConstant(a->isolate()->factory()->lastIndex_string());
237   Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT));
238   a->CallRuntime(Runtime::kSetProperty, context, regexp, name, value,
239                  language_mode);
240 }
241 
StoreLastIndex(CodeStubAssembler * a,compiler::Node * context,compiler::Node * has_initialmap,compiler::Node * regexp,compiler::Node * value)242 void StoreLastIndex(CodeStubAssembler* a, compiler::Node* context,
243                     compiler::Node* has_initialmap, compiler::Node* regexp,
244                     compiler::Node* value) {
245   typedef CodeStubAssembler::Label Label;
246 
247   Label out(a), if_unmodified(a), if_modified(a);
248   a->Branch(has_initialmap, &if_unmodified, &if_modified);
249 
250   a->Bind(&if_unmodified);
251   {
252     FastStoreLastIndex(a, context, regexp, value);
253     a->Goto(&out);
254   }
255 
256   a->Bind(&if_modified);
257   {
258     SlowStoreLastIndex(a, context, regexp, value);
259     a->Goto(&out);
260   }
261 
262   a->Bind(&out);
263 }
264 
ConstructNewResultFromMatchInfo(Isolate * isolate,CodeStubAssembler * a,compiler::Node * context,compiler::Node * match_info,compiler::Node * string)265 compiler::Node* ConstructNewResultFromMatchInfo(Isolate* isolate,
266                                                 CodeStubAssembler* a,
267                                                 compiler::Node* context,
268                                                 compiler::Node* match_info,
269                                                 compiler::Node* string) {
270   typedef CodeStubAssembler::Variable Variable;
271   typedef CodeStubAssembler::Label Label;
272   typedef compiler::Node Node;
273 
274   Label out(a);
275 
276   CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
277   Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement(
278       match_info, a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0,
279       mode));
280   Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1));
281   Node* const start = a->LoadFixedArrayElement(
282       match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), 0,
283       mode);
284   Node* const end = a->LoadFixedArrayElement(
285       match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0,
286       mode);
287 
288   // Calculate the substring of the first match before creating the result array
289   // to avoid an unnecessary write barrier storing the first result.
290   Node* const first = a->SubString(context, string, start, end);
291 
292   Node* const result =
293       a->AllocateRegExpResult(context, num_results, start, string);
294   Node* const result_elements = a->LoadElements(result);
295 
296   a->StoreFixedArrayElement(result_elements, a->IntPtrConstant(0), first,
297                             SKIP_WRITE_BARRIER);
298 
299   a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out);
300 
301   // Store all remaining captures.
302   Node* const limit = a->IntPtrAdd(
303       a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices);
304 
305   Variable var_from_cursor(a, MachineType::PointerRepresentation());
306   Variable var_to_cursor(a, MachineType::PointerRepresentation());
307 
308   var_from_cursor.Bind(
309       a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
310   var_to_cursor.Bind(a->IntPtrConstant(1));
311 
312   Variable* vars[] = {&var_from_cursor, &var_to_cursor};
313   Label loop(a, 2, vars);
314 
315   a->Goto(&loop);
316   a->Bind(&loop);
317   {
318     Node* const from_cursor = var_from_cursor.value();
319     Node* const to_cursor = var_to_cursor.value();
320     Node* const start = a->LoadFixedArrayElement(match_info, from_cursor);
321 
322     Label next_iter(a);
323     a->GotoIf(a->SmiEqual(start, a->SmiConstant(Smi::FromInt(-1))), &next_iter);
324 
325     Node* const from_cursor_plus1 =
326         a->IntPtrAdd(from_cursor, a->IntPtrConstant(1));
327     Node* const end = a->LoadFixedArrayElement(match_info, from_cursor_plus1);
328 
329     Node* const capture = a->SubString(context, string, start, end);
330     a->StoreFixedArrayElement(result_elements, to_cursor, capture);
331     a->Goto(&next_iter);
332 
333     a->Bind(&next_iter);
334     var_from_cursor.Bind(a->IntPtrAdd(from_cursor, a->IntPtrConstant(2)));
335     var_to_cursor.Bind(a->IntPtrAdd(to_cursor, a->IntPtrConstant(1)));
336     a->Branch(a->UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out);
337   }
338 
339   a->Bind(&out);
340   return result;
341 }
342 
343 // ES#sec-regexp.prototype.exec
344 // RegExp.prototype.exec ( string )
RegExpPrototypeExecInternal(CodeStubAssembler * a,compiler::Node * context,compiler::Node * maybe_receiver,compiler::Node * maybe_string)345 compiler::Node* RegExpPrototypeExecInternal(CodeStubAssembler* a,
346                                             compiler::Node* context,
347                                             compiler::Node* maybe_receiver,
348                                             compiler::Node* maybe_string) {
349   typedef CodeStubAssembler::Variable Variable;
350   typedef CodeStubAssembler::Label Label;
351   typedef compiler::Node Node;
352 
353   Isolate* const isolate = a->isolate();
354 
355   Node* const null = a->NullConstant();
356   Node* const int_zero = a->IntPtrConstant(0);
357   Node* const smi_zero = a->SmiConstant(Smi::kZero);
358 
359   Variable var_result(a, MachineRepresentation::kTagged);
360   Label out(a);
361 
362   // Ensure {maybe_receiver} is a JSRegExp.
363   Node* const regexp_map = a->ThrowIfNotInstanceType(
364       context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec");
365   Node* const regexp = maybe_receiver;
366 
367   // Check whether the regexp instance is unmodified.
368   Node* const native_context = a->LoadNativeContext(context);
369   Node* const regexp_fun =
370       a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
371   Node* const initial_map =
372       a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
373   Node* const has_initialmap = a->WordEqual(regexp_map, initial_map);
374 
375   // Convert {maybe_string} to a string.
376   Callable tostring_callable = CodeFactory::ToString(isolate);
377   Node* const string = a->CallStub(tostring_callable, context, maybe_string);
378   Node* const string_length = a->LoadStringLength(string);
379 
380   // Check whether the regexp is global or sticky, which determines whether we
381   // update last index later on.
382   Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset);
383   Node* const is_global_or_sticky =
384       a->WordAnd(a->SmiUntag(flags),
385                  a->IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky));
386   Node* const should_update_last_index =
387       a->WordNotEqual(is_global_or_sticky, int_zero);
388 
389   // Grab and possibly update last index.
390   Label run_exec(a);
391   Variable var_lastindex(a, MachineRepresentation::kTagged);
392   {
393     Label if_doupdate(a), if_dontupdate(a);
394     a->Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
395 
396     a->Bind(&if_doupdate);
397     {
398       Node* const regexp_lastindex =
399           LoadLastIndex(a, context, has_initialmap, regexp);
400 
401       Callable tolength_callable = CodeFactory::ToLength(isolate);
402       Node* const lastindex =
403           a->CallStub(tolength_callable, context, regexp_lastindex);
404       var_lastindex.Bind(lastindex);
405 
406       Label if_isoob(a, Label::kDeferred);
407       a->GotoUnless(a->TaggedIsSmi(lastindex), &if_isoob);
408       a->GotoUnless(a->SmiLessThanOrEqual(lastindex, string_length), &if_isoob);
409       a->Goto(&run_exec);
410 
411       a->Bind(&if_isoob);
412       {
413         StoreLastIndex(a, context, has_initialmap, regexp, smi_zero);
414         var_result.Bind(null);
415         a->Goto(&out);
416       }
417     }
418 
419     a->Bind(&if_dontupdate);
420     {
421       var_lastindex.Bind(smi_zero);
422       a->Goto(&run_exec);
423     }
424   }
425 
426   Node* match_indices;
427   Label successful_match(a);
428   a->Bind(&run_exec);
429   {
430     // Get last match info from the context.
431     Node* const last_match_info = a->LoadContextElement(
432         native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
433 
434     // Call the exec stub.
435     Callable exec_callable = CodeFactory::RegExpExec(isolate);
436     match_indices = a->CallStub(exec_callable, context, regexp, string,
437                                 var_lastindex.value(), last_match_info);
438 
439     // {match_indices} is either null or the RegExpMatchInfo array.
440     // Return early if exec failed, possibly updating last index.
441     a->GotoUnless(a->WordEqual(match_indices, null), &successful_match);
442 
443     Label return_null(a);
444     a->GotoUnless(should_update_last_index, &return_null);
445 
446     StoreLastIndex(a, context, has_initialmap, regexp, smi_zero);
447     a->Goto(&return_null);
448 
449     a->Bind(&return_null);
450     var_result.Bind(null);
451     a->Goto(&out);
452   }
453 
454   Label construct_result(a);
455   a->Bind(&successful_match);
456   {
457     a->GotoUnless(should_update_last_index, &construct_result);
458 
459     // Update the new last index from {match_indices}.
460     Node* const new_lastindex = a->LoadFixedArrayElement(
461         match_indices,
462         a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1));
463 
464     StoreLastIndex(a, context, has_initialmap, regexp, new_lastindex);
465     a->Goto(&construct_result);
466 
467     a->Bind(&construct_result);
468     {
469       Node* result = ConstructNewResultFromMatchInfo(isolate, a, context,
470                                                      match_indices, string);
471       var_result.Bind(result);
472       a->Goto(&out);
473     }
474   }
475 
476   a->Bind(&out);
477   return var_result.value();
478 }
479 
480 }  // namespace
481 
482 // ES#sec-regexp.prototype.exec
483 // RegExp.prototype.exec ( string )
Generate_RegExpPrototypeExec(CodeStubAssembler * a)484 void Builtins::Generate_RegExpPrototypeExec(CodeStubAssembler* a) {
485   typedef compiler::Node Node;
486 
487   Node* const maybe_receiver = a->Parameter(0);
488   Node* const maybe_string = a->Parameter(1);
489   Node* const context = a->Parameter(4);
490 
491   Node* const result =
492       RegExpPrototypeExecInternal(a, context, maybe_receiver, maybe_string);
493   a->Return(result);
494 }
495 
496 namespace {
497 
ThrowIfNotJSReceiver(CodeStubAssembler * a,Isolate * isolate,compiler::Node * context,compiler::Node * value,MessageTemplate::Template msg_template,char const * method_name)498 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate,
499                                      compiler::Node* context,
500                                      compiler::Node* value,
501                                      MessageTemplate::Template msg_template,
502                                      char const* method_name) {
503   typedef compiler::Node Node;
504   typedef CodeStubAssembler::Label Label;
505   typedef CodeStubAssembler::Variable Variable;
506 
507   Label out(a), throw_exception(a, Label::kDeferred);
508   Variable var_value_map(a, MachineRepresentation::kTagged);
509 
510   a->GotoIf(a->TaggedIsSmi(value), &throw_exception);
511 
512   // Load the instance type of the {value}.
513   var_value_map.Bind(a->LoadMap(value));
514   Node* const value_instance_type =
515       a->LoadMapInstanceType(var_value_map.value());
516 
517   a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out,
518             &throw_exception);
519 
520   // The {value} is not a compatible receiver for this method.
521   a->Bind(&throw_exception);
522   {
523     Node* const message_id = a->SmiConstant(Smi::FromInt(msg_template));
524     Node* const method_name_str = a->HeapConstant(
525         isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED));
526 
527     Callable callable = CodeFactory::ToString(isolate);
528     Node* const value_str = a->CallStub(callable, context, value);
529 
530     a->CallRuntime(Runtime::kThrowTypeError, context, message_id,
531                    method_name_str, value_str);
532     var_value_map.Bind(a->UndefinedConstant());
533     a->Goto(&out);  // Never reached.
534   }
535 
536   a->Bind(&out);
537   return var_value_map.value();
538 }
539 
IsInitialRegExpMap(CodeStubAssembler * a,compiler::Node * context,compiler::Node * map)540 compiler::Node* IsInitialRegExpMap(CodeStubAssembler* a,
541                                    compiler::Node* context,
542                                    compiler::Node* map) {
543   typedef compiler::Node Node;
544 
545   Node* const native_context = a->LoadNativeContext(context);
546   Node* const regexp_fun =
547       a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
548   Node* const initial_map =
549       a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
550   Node* const has_initialmap = a->WordEqual(map, initial_map);
551 
552   return has_initialmap;
553 }
554 
555 // RegExp fast path implementations rely on unmodified JSRegExp instances.
556 // We use a fairly coarse granularity for this and simply check whether both
557 // the regexp itself is unmodified (i.e. its map has not changed) and its
558 // prototype is unmodified.
BranchIfFastPath(CodeStubAssembler * a,compiler::Node * context,compiler::Node * map,CodeStubAssembler::Label * if_isunmodified,CodeStubAssembler::Label * if_ismodified)559 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context,
560                       compiler::Node* map,
561                       CodeStubAssembler::Label* if_isunmodified,
562                       CodeStubAssembler::Label* if_ismodified) {
563   typedef compiler::Node Node;
564 
565   Node* const native_context = a->LoadNativeContext(context);
566   Node* const regexp_fun =
567       a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
568   Node* const initial_map =
569       a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
570   Node* const has_initialmap = a->WordEqual(map, initial_map);
571 
572   a->GotoUnless(has_initialmap, if_ismodified);
573 
574   Node* const initial_proto_initial_map = a->LoadContextElement(
575       native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
576   Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map));
577   Node* const proto_has_initialmap =
578       a->WordEqual(proto_map, initial_proto_initial_map);
579 
580   // TODO(ishell): Update this check once map changes for constant field
581   // tracking are landing.
582 
583   a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
584 }
585 
586 }  // namespace
587 
Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler * a)588 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) {
589   typedef CodeStubAssembler::Variable Variable;
590   typedef CodeStubAssembler::Label Label;
591   typedef compiler::Node Node;
592 
593   Node* const receiver = a->Parameter(0);
594   Node* const context = a->Parameter(3);
595 
596   Isolate* isolate = a->isolate();
597   Node* const int_zero = a->IntPtrConstant(0);
598   Node* const int_one = a->IntPtrConstant(1);
599 
600   Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver,
601                                          MessageTemplate::kRegExpNonObject,
602                                          "RegExp.prototype.flags");
603 
604   Variable var_length(a, MachineType::PointerRepresentation());
605   Variable var_flags(a, MachineType::PointerRepresentation());
606 
607   // First, count the number of characters we will need and check which flags
608   // are set.
609 
610   var_length.Bind(int_zero);
611 
612   Label if_isunmodifiedjsregexp(a),
613       if_isnotunmodifiedjsregexp(a, Label::kDeferred);
614   a->Branch(IsInitialRegExpMap(a, context, map), &if_isunmodifiedjsregexp,
615             &if_isnotunmodifiedjsregexp);
616 
617   Label construct_string(a);
618   a->Bind(&if_isunmodifiedjsregexp);
619   {
620     // Refer to JSRegExp's flag property on the fast-path.
621     Node* const flags_smi =
622         a->LoadObjectField(receiver, JSRegExp::kFlagsOffset);
623     Node* const flags_intptr = a->SmiUntag(flags_smi);
624     var_flags.Bind(flags_intptr);
625 
626     Label label_global(a), label_ignorecase(a), label_multiline(a),
627         label_unicode(a), label_sticky(a);
628 
629 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL)                        \
630   do {                                                                \
631     a->Bind(&LABEL);                                                  \
632     Node* const mask = a->IntPtrConstant(FLAG);                       \
633     a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \
634               &NEXT_LABEL);                                           \
635     var_length.Bind(a->IntPtrAdd(var_length.value(), int_one));       \
636     a->Goto(&NEXT_LABEL);                                             \
637   } while (false)
638 
639     a->Goto(&label_global);
640     CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase);
641     CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline);
642     CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode);
643     CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky);
644     CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string);
645 #undef CASE_FOR_FLAG
646   }
647 
648   a->Bind(&if_isnotunmodifiedjsregexp);
649   {
650     // Fall back to GetProperty stub on the slow-path.
651     var_flags.Bind(int_zero);
652 
653     Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
654     Label label_global(a), label_ignorecase(a), label_multiline(a),
655         label_unicode(a), label_sticky(a);
656 
657 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL)                          \
658   do {                                                                        \
659     a->Bind(&LABEL);                                                          \
660     Node* const name =                                                        \
661         a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \
662     Node* const flag =                                                        \
663         a->CallStub(getproperty_callable, context, receiver, name);           \
664     Label if_isflagset(a);                                                    \
665     a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL);             \
666     a->Bind(&if_isflagset);                                                   \
667     var_length.Bind(a->IntPtrAdd(var_length.value(), int_one));               \
668     var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG)));    \
669     a->Goto(&NEXT_LABEL);                                                     \
670   } while (false)
671 
672     a->Goto(&label_global);
673     CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase);
674     CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase,
675                   label_multiline);
676     CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline,
677                   label_unicode);
678     CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky);
679     CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string);
680 #undef CASE_FOR_FLAG
681   }
682 
683   // Allocate a string of the required length and fill it with the corresponding
684   // char for each set flag.
685 
686   a->Bind(&construct_string);
687   {
688     Node* const result =
689         a->AllocateSeqOneByteString(context, var_length.value());
690     Node* const flags_intptr = var_flags.value();
691 
692     Variable var_offset(a, MachineType::PointerRepresentation());
693     var_offset.Bind(
694         a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
695 
696     Label label_global(a), label_ignorecase(a), label_multiline(a),
697         label_unicode(a), label_sticky(a), out(a);
698 
699 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL)                  \
700   do {                                                                \
701     a->Bind(&LABEL);                                                  \
702     Node* const mask = a->IntPtrConstant(FLAG);                       \
703     a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \
704               &NEXT_LABEL);                                           \
705     Node* const value = a->IntPtrConstant(CHAR);                      \
706     a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result,     \
707                            var_offset.value(), value);                \
708     var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one));       \
709     a->Goto(&NEXT_LABEL);                                             \
710   } while (false)
711 
712     a->Goto(&label_global);
713     CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase);
714     CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase,
715                   label_multiline);
716     CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode);
717     CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky);
718     CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out);
719 #undef CASE_FOR_FLAG
720 
721     a->Bind(&out);
722     a->Return(result);
723   }
724 }
725 
726 // ES6 21.2.5.10.
BUILTIN(RegExpPrototypeSourceGetter)727 BUILTIN(RegExpPrototypeSourceGetter) {
728   HandleScope scope(isolate);
729 
730   Handle<Object> recv = args.receiver();
731   if (!recv->IsJSRegExp()) {
732     Handle<JSFunction> regexp_fun = isolate->regexp_function();
733     if (*recv == regexp_fun->prototype()) {
734       isolate->CountUsage(v8::Isolate::kRegExpPrototypeSourceGetter);
735       return *isolate->factory()->NewStringFromAsciiChecked("(?:)");
736     }
737     THROW_NEW_ERROR_RETURN_FAILURE(
738         isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp,
739                               isolate->factory()->NewStringFromAsciiChecked(
740                                   "RegExp.prototype.source")));
741   }
742 
743   Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv);
744   return regexp->source();
745 }
746 
BUILTIN(RegExpPrototypeToString)747 BUILTIN(RegExpPrototypeToString) {
748   HandleScope scope(isolate);
749   CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.toString");
750 
751   if (*recv == isolate->regexp_function()->prototype()) {
752     isolate->CountUsage(v8::Isolate::kRegExpPrototypeToString);
753   }
754 
755   IncrementalStringBuilder builder(isolate);
756 
757   builder.AppendCharacter('/');
758   {
759     Handle<Object> source;
760     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
761         isolate, source,
762         JSReceiver::GetProperty(recv, isolate->factory()->source_string()));
763     Handle<String> source_str;
764     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, source_str,
765                                        Object::ToString(isolate, source));
766     builder.AppendString(source_str);
767   }
768 
769   builder.AppendCharacter('/');
770   {
771     Handle<Object> flags;
772     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
773         isolate, flags,
774         JSReceiver::GetProperty(recv, isolate->factory()->flags_string()));
775     Handle<String> flags_str;
776     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags_str,
777                                        Object::ToString(isolate, flags));
778     builder.AppendString(flags_str);
779   }
780 
781   RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
782 }
783 
784 // ES6 21.2.4.2.
BUILTIN(RegExpPrototypeSpeciesGetter)785 BUILTIN(RegExpPrototypeSpeciesGetter) {
786   HandleScope scope(isolate);
787   return *args.receiver();
788 }
789 
790 namespace {
791 
792 // Fast-path implementation for flag checks on an unmodified JSRegExp instance.
FastFlagGetter(CodeStubAssembler * a,compiler::Node * const regexp,JSRegExp::Flag flag)793 compiler::Node* FastFlagGetter(CodeStubAssembler* a,
794                                compiler::Node* const regexp,
795                                JSRegExp::Flag flag) {
796   typedef compiler::Node Node;
797 
798   Node* const smi_zero = a->SmiConstant(Smi::kZero);
799   Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset);
800   Node* const mask = a->SmiConstant(Smi::FromInt(flag));
801   Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero);
802 
803   return is_flag_set;
804 }
805 
Generate_FlagGetter(CodeStubAssembler * a,JSRegExp::Flag flag,v8::Isolate::UseCounterFeature counter,const char * method_name)806 void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag,
807                          v8::Isolate::UseCounterFeature counter,
808                          const char* method_name) {
809   typedef CodeStubAssembler::Label Label;
810   typedef compiler::Node Node;
811 
812   Node* const receiver = a->Parameter(0);
813   Node* const context = a->Parameter(3);
814 
815   Isolate* isolate = a->isolate();
816 
817   // Check whether we have an unmodified regexp instance.
818   Label if_isunmodifiedjsregexp(a),
819       if_isnotunmodifiedjsregexp(a, Label::kDeferred);
820 
821   a->GotoIf(a->TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp);
822 
823   Node* const receiver_map = a->LoadMap(receiver);
824   Node* const instance_type = a->LoadMapInstanceType(receiver_map);
825 
826   a->Branch(a->Word32Equal(instance_type, a->Int32Constant(JS_REGEXP_TYPE)),
827             &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp);
828 
829   a->Bind(&if_isunmodifiedjsregexp);
830   {
831     // Refer to JSRegExp's flag property on the fast-path.
832     Node* const is_flag_set = FastFlagGetter(a, receiver, flag);
833     a->Return(a->Select(is_flag_set, a->TrueConstant(), a->FalseConstant()));
834   }
835 
836   a->Bind(&if_isnotunmodifiedjsregexp);
837   {
838     Node* const native_context = a->LoadNativeContext(context);
839     Node* const regexp_fun =
840         a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
841     Node* const initial_map = a->LoadObjectField(
842         regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
843     Node* const initial_prototype = a->LoadMapPrototype(initial_map);
844 
845     Label if_isprototype(a), if_isnotprototype(a);
846     a->Branch(a->WordEqual(receiver, initial_prototype), &if_isprototype,
847               &if_isnotprototype);
848 
849     a->Bind(&if_isprototype);
850     {
851       Node* const counter_smi = a->SmiConstant(Smi::FromInt(counter));
852       a->CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi);
853       a->Return(a->UndefinedConstant());
854     }
855 
856     a->Bind(&if_isnotprototype);
857     {
858       Node* const message_id =
859           a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp));
860       Node* const method_name_str = a->HeapConstant(
861           isolate->factory()->NewStringFromAsciiChecked(method_name));
862       a->CallRuntime(Runtime::kThrowTypeError, context, message_id,
863                      method_name_str);
864       a->Return(a->UndefinedConstant());  // Never reached.
865     }
866   }
867 }
868 
869 }  // namespace
870 
871 // ES6 21.2.5.4.
Generate_RegExpPrototypeGlobalGetter(CodeStubAssembler * a)872 void Builtins::Generate_RegExpPrototypeGlobalGetter(CodeStubAssembler* a) {
873   Generate_FlagGetter(a, JSRegExp::kGlobal,
874                       v8::Isolate::kRegExpPrototypeOldFlagGetter,
875                       "RegExp.prototype.global");
876 }
877 
878 // ES6 21.2.5.5.
Generate_RegExpPrototypeIgnoreCaseGetter(CodeStubAssembler * a)879 void Builtins::Generate_RegExpPrototypeIgnoreCaseGetter(CodeStubAssembler* a) {
880   Generate_FlagGetter(a, JSRegExp::kIgnoreCase,
881                       v8::Isolate::kRegExpPrototypeOldFlagGetter,
882                       "RegExp.prototype.ignoreCase");
883 }
884 
885 // ES6 21.2.5.7.
Generate_RegExpPrototypeMultilineGetter(CodeStubAssembler * a)886 void Builtins::Generate_RegExpPrototypeMultilineGetter(CodeStubAssembler* a) {
887   Generate_FlagGetter(a, JSRegExp::kMultiline,
888                       v8::Isolate::kRegExpPrototypeOldFlagGetter,
889                       "RegExp.prototype.multiline");
890 }
891 
892 // ES6 21.2.5.12.
Generate_RegExpPrototypeStickyGetter(CodeStubAssembler * a)893 void Builtins::Generate_RegExpPrototypeStickyGetter(CodeStubAssembler* a) {
894   Generate_FlagGetter(a, JSRegExp::kSticky,
895                       v8::Isolate::kRegExpPrototypeStickyGetter,
896                       "RegExp.prototype.sticky");
897 }
898 
899 // ES6 21.2.5.15.
Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler * a)900 void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) {
901   Generate_FlagGetter(a, JSRegExp::kUnicode,
902                       v8::Isolate::kRegExpPrototypeUnicodeGetter,
903                       "RegExp.prototype.unicode");
904 }
905 
906 // The properties $1..$9 are the first nine capturing substrings of the last
907 // successful match, or ''.  The function RegExpMakeCaptureGetter will be
908 // called with indices from 1 to 9.
909 #define DEFINE_CAPTURE_GETTER(i)                        \
910   BUILTIN(RegExpCapture##i##Getter) {                   \
911     HandleScope scope(isolate);                         \
912     return *RegExpUtils::GenericCaptureGetter(          \
913         isolate, isolate->regexp_last_match_info(), i); \
914   }
915 DEFINE_CAPTURE_GETTER(1)
916 DEFINE_CAPTURE_GETTER(2)
917 DEFINE_CAPTURE_GETTER(3)
918 DEFINE_CAPTURE_GETTER(4)
919 DEFINE_CAPTURE_GETTER(5)
920 DEFINE_CAPTURE_GETTER(6)
921 DEFINE_CAPTURE_GETTER(7)
922 DEFINE_CAPTURE_GETTER(8)
923 DEFINE_CAPTURE_GETTER(9)
924 #undef DEFINE_CAPTURE_GETTER
925 
926 // The properties `input` and `$_` are aliases for each other.  When this
927 // value is set, the value it is set to is coerced to a string.
928 // Getter and setter for the input.
929 
BUILTIN(RegExpInputGetter)930 BUILTIN(RegExpInputGetter) {
931   HandleScope scope(isolate);
932   Handle<Object> obj(isolate->regexp_last_match_info()->LastInput(), isolate);
933   return obj->IsUndefined(isolate) ? isolate->heap()->empty_string()
934                                    : String::cast(*obj);
935 }
936 
BUILTIN(RegExpInputSetter)937 BUILTIN(RegExpInputSetter) {
938   HandleScope scope(isolate);
939   Handle<Object> value = args.atOrUndefined(isolate, 1);
940   Handle<String> str;
941   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str,
942                                      Object::ToString(isolate, value));
943   isolate->regexp_last_match_info()->SetLastInput(*str);
944   return isolate->heap()->undefined_value();
945 }
946 
947 // Getters for the static properties lastMatch, lastParen, leftContext, and
948 // rightContext of the RegExp constructor.  The properties are computed based
949 // on the captures array of the last successful match and the subject string
950 // of the last successful match.
BUILTIN(RegExpLastMatchGetter)951 BUILTIN(RegExpLastMatchGetter) {
952   HandleScope scope(isolate);
953   return *RegExpUtils::GenericCaptureGetter(
954       isolate, isolate->regexp_last_match_info(), 0);
955 }
956 
BUILTIN(RegExpLastParenGetter)957 BUILTIN(RegExpLastParenGetter) {
958   HandleScope scope(isolate);
959   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
960   const int length = match_info->NumberOfCaptureRegisters();
961   if (length <= 2) return isolate->heap()->empty_string();  // No captures.
962 
963   DCHECK_EQ(0, length % 2);
964   const int last_capture = (length / 2) - 1;
965 
966   // We match the SpiderMonkey behavior: return the substring defined by the
967   // last pair (after the first pair) of elements of the capture array even if
968   // it is empty.
969   return *RegExpUtils::GenericCaptureGetter(isolate, match_info, last_capture);
970 }
971 
BUILTIN(RegExpLeftContextGetter)972 BUILTIN(RegExpLeftContextGetter) {
973   HandleScope scope(isolate);
974   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
975   const int start_index = match_info->Capture(0);
976   Handle<String> last_subject(match_info->LastSubject());
977   return *isolate->factory()->NewSubString(last_subject, 0, start_index);
978 }
979 
BUILTIN(RegExpRightContextGetter)980 BUILTIN(RegExpRightContextGetter) {
981   HandleScope scope(isolate);
982   Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
983   const int start_index = match_info->Capture(1);
984   Handle<String> last_subject(match_info->LastSubject());
985   const int len = last_subject->length();
986   return *isolate->factory()->NewSubString(last_subject, start_index, len);
987 }
988 
989 namespace {
990 
991 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
RegExpExec(CodeStubAssembler * a,compiler::Node * context,compiler::Node * recv,compiler::Node * string)992 compiler::Node* RegExpExec(CodeStubAssembler* a, compiler::Node* context,
993                            compiler::Node* recv, compiler::Node* string) {
994   typedef CodeStubAssembler::Variable Variable;
995   typedef CodeStubAssembler::Label Label;
996   typedef compiler::Node Node;
997 
998   Isolate* isolate = a->isolate();
999 
1000   Node* const null = a->NullConstant();
1001 
1002   Variable var_result(a, MachineRepresentation::kTagged);
1003   Label out(a), call_builtin_exec(a), slow_path(a, Label::kDeferred);
1004 
1005   Node* const map = a->LoadMap(recv);
1006   BranchIfFastPath(a, context, map, &call_builtin_exec, &slow_path);
1007 
1008   a->Bind(&call_builtin_exec);
1009   {
1010     Node* const result = RegExpPrototypeExecInternal(a, context, recv, string);
1011     var_result.Bind(result);
1012     a->Goto(&out);
1013   }
1014 
1015   a->Bind(&slow_path);
1016   {
1017     // Take the slow path of fetching the exec property, calling it, and
1018     // verifying its return value.
1019 
1020     // Get the exec property.
1021     Node* const name = a->HeapConstant(isolate->factory()->exec_string());
1022     Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
1023     Node* const exec = a->CallStub(getproperty_callable, context, recv, name);
1024 
1025     // Is {exec} callable?
1026     Label if_iscallable(a), if_isnotcallable(a);
1027 
1028     a->GotoIf(a->TaggedIsSmi(exec), &if_isnotcallable);
1029 
1030     Node* const exec_map = a->LoadMap(exec);
1031     a->Branch(a->IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
1032 
1033     a->Bind(&if_iscallable);
1034     {
1035       Callable call_callable = CodeFactory::Call(isolate);
1036       Node* const result =
1037           a->CallJS(call_callable, context, exec, recv, string);
1038 
1039       var_result.Bind(result);
1040       a->GotoIf(a->WordEqual(result, null), &out);
1041 
1042       ThrowIfNotJSReceiver(a, isolate, context, result,
1043                            MessageTemplate::kInvalidRegExpExecResult, "unused");
1044 
1045       a->Goto(&out);
1046     }
1047 
1048     a->Bind(&if_isnotcallable);
1049     {
1050       a->ThrowIfNotInstanceType(context, recv, JS_REGEXP_TYPE,
1051                                 "RegExp.prototype.exec");
1052       a->Goto(&call_builtin_exec);
1053     }
1054   }
1055 
1056   a->Bind(&out);
1057   return var_result.value();
1058 }
1059 
1060 }  // namespace
1061 
1062 // ES#sec-regexp.prototype.test
1063 // RegExp.prototype.test ( S )
Generate_RegExpPrototypeTest(CodeStubAssembler * a)1064 void Builtins::Generate_RegExpPrototypeTest(CodeStubAssembler* a) {
1065   typedef compiler::Node Node;
1066 
1067   Isolate* const isolate = a->isolate();
1068 
1069   Node* const maybe_receiver = a->Parameter(0);
1070   Node* const maybe_string = a->Parameter(1);
1071   Node* const context = a->Parameter(4);
1072 
1073   // Ensure {maybe_receiver} is a JSReceiver.
1074   ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver,
1075                        MessageTemplate::kIncompatibleMethodReceiver,
1076                        "RegExp.prototype.test");
1077   Node* const receiver = maybe_receiver;
1078 
1079   // Convert {maybe_string} to a String.
1080   Node* const string = a->ToString(context, maybe_string);
1081 
1082   // Call exec.
1083   Node* const match_indices = RegExpExec(a, context, receiver, string);
1084 
1085   // Return true iff exec matched successfully.
1086   Node* const result = a->Select(a->WordEqual(match_indices, a->NullConstant()),
1087                                  a->FalseConstant(), a->TrueConstant());
1088   a->Return(result);
1089 }
1090 
1091 // ES#sec-regexp.prototype-@@match
1092 // RegExp.prototype [ @@match ] ( string )
BUILTIN(RegExpPrototypeMatch)1093 BUILTIN(RegExpPrototypeMatch) {
1094   HandleScope scope(isolate);
1095   CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@match");
1096 
1097   Handle<Object> string_obj = args.atOrUndefined(isolate, 1);
1098 
1099   Handle<String> string;
1100   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string,
1101                                      Object::ToString(isolate, string_obj));
1102 
1103   Handle<Object> global_obj;
1104   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1105       isolate, global_obj,
1106       JSReceiver::GetProperty(recv, isolate->factory()->global_string()));
1107   const bool global = global_obj->BooleanValue();
1108 
1109   if (!global) {
1110     RETURN_RESULT_OR_FAILURE(
1111         isolate,
1112         RegExpUtils::RegExpExec(isolate, recv, string,
1113                                 isolate->factory()->undefined_value()));
1114   }
1115 
1116   Handle<Object> unicode_obj;
1117   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1118       isolate, unicode_obj,
1119       JSReceiver::GetProperty(recv, isolate->factory()->unicode_string()));
1120   const bool unicode = unicode_obj->BooleanValue();
1121 
1122   RETURN_FAILURE_ON_EXCEPTION(isolate,
1123                               RegExpUtils::SetLastIndex(isolate, recv, 0));
1124 
1125   static const int kInitialArraySize = 8;
1126   Handle<FixedArray> elems =
1127       isolate->factory()->NewFixedArrayWithHoles(kInitialArraySize);
1128 
1129   int n = 0;
1130   for (;; n++) {
1131     Handle<Object> result;
1132     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1133         isolate, result,
1134         RegExpUtils::RegExpExec(isolate, recv, string,
1135                                 isolate->factory()->undefined_value()));
1136 
1137     if (result->IsNull(isolate)) {
1138       if (n == 0) return isolate->heap()->null_value();
1139       break;
1140     }
1141 
1142     Handle<Object> match_obj;
1143     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj,
1144                                        Object::GetElement(isolate, result, 0));
1145 
1146     Handle<String> match;
1147     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match,
1148                                        Object::ToString(isolate, match_obj));
1149 
1150     elems = FixedArray::SetAndGrow(elems, n, match);
1151 
1152     if (match->length() == 0) {
1153       RETURN_FAILURE_ON_EXCEPTION(isolate, RegExpUtils::SetAdvancedStringIndex(
1154                                                isolate, recv, string, unicode));
1155     }
1156   }
1157 
1158   elems->Shrink(n);
1159   return *isolate->factory()->NewJSArrayWithElements(elems);
1160 }
1161 
1162 namespace {
1163 
Generate_RegExpPrototypeSearchBody(CodeStubAssembler * a,compiler::Node * const receiver,compiler::Node * const string,compiler::Node * const context,bool is_fastpath)1164 void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a,
1165                                         compiler::Node* const receiver,
1166                                         compiler::Node* const string,
1167                                         compiler::Node* const context,
1168                                         bool is_fastpath) {
1169   typedef CodeStubAssembler::Label Label;
1170   typedef compiler::Node Node;
1171 
1172   Isolate* const isolate = a->isolate();
1173 
1174   Node* const smi_zero = a->SmiConstant(Smi::kZero);
1175 
1176   // Grab the initial value of last index.
1177   Node* const previous_last_index =
1178       is_fastpath ? FastLoadLastIndex(a, context, receiver)
1179                   : SlowLoadLastIndex(a, context, receiver);
1180 
1181   // Ensure last index is 0.
1182   if (is_fastpath) {
1183     FastStoreLastIndex(a, context, receiver, smi_zero);
1184   } else {
1185     Label next(a);
1186     a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next);
1187 
1188     SlowStoreLastIndex(a, context, receiver, smi_zero);
1189     a->Goto(&next);
1190     a->Bind(&next);
1191   }
1192 
1193   // Call exec.
1194   Node* const match_indices =
1195       is_fastpath ? RegExpPrototypeExecInternal(a, context, receiver, string)
1196                   : RegExpExec(a, context, receiver, string);
1197 
1198   // Reset last index if necessary.
1199   if (is_fastpath) {
1200     FastStoreLastIndex(a, context, receiver, previous_last_index);
1201   } else {
1202     Label next(a);
1203     Node* const current_last_index = SlowLoadLastIndex(a, context, receiver);
1204 
1205     a->GotoIf(a->SameValue(current_last_index, previous_last_index, context),
1206               &next);
1207 
1208     SlowStoreLastIndex(a, context, receiver, previous_last_index);
1209     a->Goto(&next);
1210     a->Bind(&next);
1211   }
1212 
1213   // Return -1 if no match was found.
1214   {
1215     Label next(a);
1216     a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next);
1217     a->Return(a->SmiConstant(-1));
1218     a->Bind(&next);
1219   }
1220 
1221   // Return the index of the match.
1222   {
1223     Label fast_result(a), slow_result(a, Label::kDeferred);
1224 
1225     Node* const native_context = a->LoadNativeContext(context);
1226     Node* const initial_regexp_result_map =
1227         a->LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
1228     Node* const match_indices_map = a->LoadMap(match_indices);
1229 
1230     a->Branch(a->WordEqual(match_indices_map, initial_regexp_result_map),
1231               &fast_result, &slow_result);
1232 
1233     a->Bind(&fast_result);
1234     {
1235       Node* const index =
1236           a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset,
1237                              MachineType::AnyTagged());
1238       a->Return(index);
1239     }
1240 
1241     a->Bind(&slow_result);
1242     {
1243       Node* const name = a->HeapConstant(isolate->factory()->index_string());
1244       Callable getproperty_callable = CodeFactory::GetProperty(a->isolate());
1245       Node* const index =
1246           a->CallStub(getproperty_callable, context, match_indices, name);
1247       a->Return(index);
1248     }
1249   }
1250 }
1251 
1252 }  // namespace
1253 
1254 // ES#sec-regexp.prototype-@@search
1255 // RegExp.prototype [ @@search ] ( string )
Generate_RegExpPrototypeSearch(CodeStubAssembler * a)1256 void Builtins::Generate_RegExpPrototypeSearch(CodeStubAssembler* a) {
1257   typedef CodeStubAssembler::Label Label;
1258   typedef compiler::Node Node;
1259 
1260   Isolate* const isolate = a->isolate();
1261 
1262   Node* const maybe_receiver = a->Parameter(0);
1263   Node* const maybe_string = a->Parameter(1);
1264   Node* const context = a->Parameter(4);
1265 
1266   // Ensure {maybe_receiver} is a JSReceiver.
1267   Node* const map =
1268       ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver,
1269                            MessageTemplate::kIncompatibleMethodReceiver,
1270                            "RegExp.prototype.@@search");
1271   Node* const receiver = maybe_receiver;
1272 
1273   // Convert {maybe_string} to a String.
1274   Node* const string = a->ToString(context, maybe_string);
1275 
1276   Label fast_path(a), slow_path(a);
1277   BranchIfFastPath(a, context, map, &fast_path, &slow_path);
1278 
1279   a->Bind(&fast_path);
1280   Generate_RegExpPrototypeSearchBody(a, receiver, string, context, true);
1281 
1282   a->Bind(&slow_path);
1283   Generate_RegExpPrototypeSearchBody(a, receiver, string, context, false);
1284 }
1285 
1286 namespace {
1287 
ToUint32(Isolate * isolate,Handle<Object> object,uint32_t * out)1288 MUST_USE_RESULT MaybeHandle<Object> ToUint32(Isolate* isolate,
1289                                              Handle<Object> object,
1290                                              uint32_t* out) {
1291   if (object->IsUndefined(isolate)) {
1292     *out = kMaxUInt32;
1293     return object;
1294   }
1295 
1296   Handle<Object> number;
1297   ASSIGN_RETURN_ON_EXCEPTION(isolate, number, Object::ToNumber(object), Object);
1298   *out = NumberToUint32(*number);
1299   return object;
1300 }
1301 
AtSurrogatePair(Isolate * isolate,Handle<String> string,int index)1302 bool AtSurrogatePair(Isolate* isolate, Handle<String> string, int index) {
1303   if (index + 1 >= string->length()) return false;
1304   const uint16_t first = string->Get(index);
1305   if (first < 0xD800 || first > 0xDBFF) return false;
1306   const uint16_t second = string->Get(index + 1);
1307   return (second >= 0xDC00 && second <= 0xDFFF);
1308 }
1309 
NewJSArrayWithElements(Isolate * isolate,Handle<FixedArray> elems,int num_elems)1310 Handle<JSArray> NewJSArrayWithElements(Isolate* isolate,
1311                                        Handle<FixedArray> elems,
1312                                        int num_elems) {
1313   elems->Shrink(num_elems);
1314   return isolate->factory()->NewJSArrayWithElements(elems);
1315 }
1316 
RegExpSplit(Isolate * isolate,Handle<JSRegExp> regexp,Handle<String> string,Handle<Object> limit_obj)1317 MaybeHandle<JSArray> RegExpSplit(Isolate* isolate, Handle<JSRegExp> regexp,
1318                                  Handle<String> string,
1319                                  Handle<Object> limit_obj) {
1320   Factory* factory = isolate->factory();
1321 
1322   uint32_t limit;
1323   RETURN_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit), JSArray);
1324 
1325   const int length = string->length();
1326 
1327   if (limit == 0) return factory->NewJSArray(0);
1328 
1329   Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info();
1330 
1331   if (length == 0) {
1332     Handle<Object> match_indices;
1333     ASSIGN_RETURN_ON_EXCEPTION(
1334         isolate, match_indices,
1335         RegExpImpl::Exec(regexp, string, 0, last_match_info), JSArray);
1336 
1337     if (!match_indices->IsNull(isolate)) return factory->NewJSArray(0);
1338 
1339     Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1);
1340     elems->set(0, *string);
1341     return factory->NewJSArrayWithElements(elems);
1342   }
1343 
1344   int current_index = 0;
1345   int start_index = 0;
1346   int start_match = 0;
1347 
1348   static const int kInitialArraySize = 8;
1349   Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize);
1350   int num_elems = 0;
1351 
1352   while (true) {
1353     if (start_index == length) {
1354       Handle<String> substr =
1355           factory->NewSubString(string, current_index, length);
1356       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
1357       break;
1358     }
1359 
1360     Handle<Object> match_indices_obj;
1361     ASSIGN_RETURN_ON_EXCEPTION(
1362         isolate, match_indices_obj,
1363         RegExpImpl::Exec(regexp, string, start_index,
1364                          isolate->regexp_last_match_info()),
1365         JSArray);
1366 
1367     if (match_indices_obj->IsNull(isolate)) {
1368       Handle<String> substr =
1369           factory->NewSubString(string, current_index, length);
1370       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
1371       break;
1372     }
1373 
1374     auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj);
1375 
1376     start_match = match_indices->Capture(0);
1377 
1378     if (start_match == length) {
1379       Handle<String> substr =
1380           factory->NewSubString(string, current_index, length);
1381       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
1382       break;
1383     }
1384 
1385     const int end_index = match_indices->Capture(1);
1386 
1387     if (start_index == end_index && end_index == current_index) {
1388       const bool unicode = (regexp->GetFlags() & JSRegExp::kUnicode) != 0;
1389       if (unicode && AtSurrogatePair(isolate, string, start_index)) {
1390         start_index += 2;
1391       } else {
1392         start_index += 1;
1393       }
1394       continue;
1395     }
1396 
1397     {
1398       Handle<String> substr =
1399           factory->NewSubString(string, current_index, start_match);
1400       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
1401     }
1402 
1403     if (static_cast<uint32_t>(num_elems) == limit) break;
1404 
1405     for (int i = 2; i < match_indices->NumberOfCaptureRegisters(); i += 2) {
1406       const int start = match_indices->Capture(i);
1407       const int end = match_indices->Capture(i + 1);
1408 
1409       if (end != -1) {
1410         Handle<String> substr = factory->NewSubString(string, start, end);
1411         elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
1412       } else {
1413         elems = FixedArray::SetAndGrow(elems, num_elems++,
1414                                        factory->undefined_value());
1415       }
1416 
1417       if (static_cast<uint32_t>(num_elems) == limit) {
1418         return NewJSArrayWithElements(isolate, elems, num_elems);
1419       }
1420     }
1421 
1422     start_index = current_index = end_index;
1423   }
1424 
1425   return NewJSArrayWithElements(isolate, elems, num_elems);
1426 }
1427 
1428 // ES##sec-speciesconstructor
1429 // SpeciesConstructor ( O, defaultConstructor )
SpeciesConstructor(Isolate * isolate,Handle<JSReceiver> recv,Handle<JSFunction> default_ctor)1430 MUST_USE_RESULT MaybeHandle<Object> SpeciesConstructor(
1431     Isolate* isolate, Handle<JSReceiver> recv,
1432     Handle<JSFunction> default_ctor) {
1433   Handle<Object> ctor_obj;
1434   ASSIGN_RETURN_ON_EXCEPTION(
1435       isolate, ctor_obj,
1436       JSObject::GetProperty(recv, isolate->factory()->constructor_string()),
1437       Object);
1438 
1439   if (ctor_obj->IsUndefined(isolate)) return default_ctor;
1440 
1441   if (!ctor_obj->IsJSReceiver()) {
1442     THROW_NEW_ERROR(isolate,
1443                     NewTypeError(MessageTemplate::kConstructorNotReceiver),
1444                     Object);
1445   }
1446 
1447   Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj);
1448 
1449   Handle<Object> species;
1450   ASSIGN_RETURN_ON_EXCEPTION(
1451       isolate, species,
1452       JSObject::GetProperty(ctor, isolate->factory()->species_symbol()),
1453       Object);
1454 
1455   if (species->IsNull(isolate) || species->IsUndefined(isolate)) {
1456     return default_ctor;
1457   }
1458 
1459   if (species->IsConstructor()) return species;
1460 
1461   THROW_NEW_ERROR(
1462       isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object);
1463 }
1464 
1465 }  // namespace
1466 
1467 // ES#sec-regexp.prototype-@@split
1468 // RegExp.prototype [ @@split ] ( string, limit )
BUILTIN(RegExpPrototypeSplit)1469 BUILTIN(RegExpPrototypeSplit) {
1470   HandleScope scope(isolate);
1471   CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@split");
1472 
1473   Factory* factory = isolate->factory();
1474 
1475   Handle<Object> string_obj = args.atOrUndefined(isolate, 1);
1476   Handle<Object> limit_obj = args.atOrUndefined(isolate, 2);
1477 
1478   Handle<String> string;
1479   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string,
1480                                      Object::ToString(isolate, string_obj));
1481 
1482   if (RegExpUtils::IsUnmodifiedRegExp(isolate, recv)) {
1483     RETURN_RESULT_OR_FAILURE(
1484         isolate,
1485         RegExpSplit(isolate, Handle<JSRegExp>::cast(recv), string, limit_obj));
1486   }
1487 
1488   Handle<JSFunction> regexp_fun = isolate->regexp_function();
1489   Handle<Object> ctor;
1490   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1491       isolate, ctor, SpeciesConstructor(isolate, recv, regexp_fun));
1492 
1493   Handle<Object> flags_obj;
1494   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1495       isolate, flags_obj, JSObject::GetProperty(recv, factory->flags_string()));
1496 
1497   Handle<String> flags;
1498   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags,
1499                                      Object::ToString(isolate, flags_obj));
1500 
1501   Handle<String> u_str = factory->LookupSingleCharacterStringFromCode('u');
1502   const bool unicode = (String::IndexOf(isolate, flags, u_str, 0) >= 0);
1503 
1504   Handle<String> y_str = factory->LookupSingleCharacterStringFromCode('y');
1505   const bool sticky = (String::IndexOf(isolate, flags, y_str, 0) >= 0);
1506 
1507   Handle<String> new_flags = flags;
1508   if (!sticky) {
1509     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, new_flags,
1510                                        factory->NewConsString(flags, y_str));
1511   }
1512 
1513   Handle<JSReceiver> splitter;
1514   {
1515     const int argc = 2;
1516 
1517     ScopedVector<Handle<Object>> argv(argc);
1518     argv[0] = recv;
1519     argv[1] = new_flags;
1520 
1521     Handle<JSFunction> ctor_fun = Handle<JSFunction>::cast(ctor);
1522     Handle<Object> splitter_obj;
1523     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1524         isolate, splitter_obj, Execution::New(ctor_fun, argc, argv.start()));
1525 
1526     splitter = Handle<JSReceiver>::cast(splitter_obj);
1527   }
1528 
1529   uint32_t limit;
1530   RETURN_FAILURE_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit));
1531 
1532   const int length = string->length();
1533 
1534   if (limit == 0) return *factory->NewJSArray(0);
1535 
1536   if (length == 0) {
1537     Handle<Object> result;
1538     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1539         isolate, result, RegExpUtils::RegExpExec(isolate, splitter, string,
1540                                                  factory->undefined_value()));
1541 
1542     if (!result->IsNull(isolate)) return *factory->NewJSArray(0);
1543 
1544     Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1);
1545     elems->set(0, *string);
1546     return *factory->NewJSArrayWithElements(elems);
1547   }
1548 
1549   // TODO(jgruber): Wrap this in a helper class.
1550   static const int kInitialArraySize = 8;
1551   Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize);
1552   int num_elems = 0;
1553 
1554   int string_index = 0;
1555   int prev_string_index = 0;
1556   while (string_index < length) {
1557     RETURN_FAILURE_ON_EXCEPTION(
1558         isolate, RegExpUtils::SetLastIndex(isolate, splitter, string_index));
1559 
1560     Handle<Object> result;
1561     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1562         isolate, result, RegExpUtils::RegExpExec(isolate, splitter, string,
1563                                                  factory->undefined_value()));
1564 
1565     if (result->IsNull(isolate)) {
1566       string_index = RegExpUtils::AdvanceStringIndex(isolate, string,
1567                                                      string_index, unicode);
1568       continue;
1569     }
1570 
1571     // TODO(jgruber): Extract toLength of some property into function.
1572     Handle<Object> last_index_obj;
1573     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1574         isolate, last_index_obj, RegExpUtils::GetLastIndex(isolate, splitter));
1575 
1576     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1577         isolate, last_index_obj, Object::ToLength(isolate, last_index_obj));
1578     const int last_index = Handle<Smi>::cast(last_index_obj)->value();
1579 
1580     const int end = std::min(last_index, length);
1581     if (end == prev_string_index) {
1582       string_index = RegExpUtils::AdvanceStringIndex(isolate, string,
1583                                                      string_index, unicode);
1584       continue;
1585     }
1586 
1587     {
1588       Handle<String> substr =
1589           factory->NewSubString(string, prev_string_index, string_index);
1590       elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
1591       if (static_cast<uint32_t>(num_elems) == limit) {
1592         return *NewJSArrayWithElements(isolate, elems, num_elems);
1593       }
1594     }
1595 
1596     prev_string_index = end;
1597 
1598     Handle<Object> num_captures_obj;
1599     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1600         isolate, num_captures_obj,
1601         Object::GetProperty(result, isolate->factory()->length_string()));
1602 
1603     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1604         isolate, num_captures_obj, Object::ToLength(isolate, num_captures_obj));
1605     const int num_captures =
1606         std::max(Handle<Smi>::cast(num_captures_obj)->value(), 0);
1607 
1608     for (int i = 1; i < num_captures; i++) {
1609       Handle<Object> capture;
1610       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1611           isolate, capture, Object::GetElement(isolate, result, i));
1612       elems = FixedArray::SetAndGrow(elems, num_elems++, capture);
1613       if (static_cast<uint32_t>(num_elems) == limit) {
1614         return *NewJSArrayWithElements(isolate, elems, num_elems);
1615       }
1616     }
1617 
1618     string_index = prev_string_index;
1619   }
1620 
1621   {
1622     Handle<String> substr =
1623         factory->NewSubString(string, prev_string_index, length);
1624     elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
1625   }
1626 
1627   return *NewJSArrayWithElements(isolate, elems, num_elems);
1628 }
1629 
1630 namespace {
1631 
ReplaceGlobalCallableFastPath(CodeStubAssembler * a,compiler::Node * context,compiler::Node * regexp,compiler::Node * subject_string,compiler::Node * replace_callable)1632 compiler::Node* ReplaceGlobalCallableFastPath(
1633     CodeStubAssembler* a, compiler::Node* context, compiler::Node* regexp,
1634     compiler::Node* subject_string, compiler::Node* replace_callable) {
1635   // The fast path is reached only if {receiver} is a global unmodified
1636   // JSRegExp instance and {replace_callable} is callable.
1637 
1638   typedef CodeStubAssembler::Variable Variable;
1639   typedef CodeStubAssembler::Label Label;
1640   typedef compiler::Node Node;
1641 
1642   Isolate* const isolate = a->isolate();
1643 
1644   Node* const null = a->NullConstant();
1645   Node* const undefined = a->UndefinedConstant();
1646   Node* const int_zero = a->IntPtrConstant(0);
1647   Node* const int_one = a->IntPtrConstant(1);
1648   Node* const smi_zero = a->SmiConstant(Smi::kZero);
1649 
1650   Node* const native_context = a->LoadNativeContext(context);
1651 
1652   Label out(a);
1653   Variable var_result(a, MachineRepresentation::kTagged);
1654 
1655   // Set last index to 0.
1656   FastStoreLastIndex(a, context, regexp, smi_zero);
1657 
1658   // Allocate {result_array}.
1659   Node* result_array;
1660   {
1661     ElementsKind kind = FAST_ELEMENTS;
1662     Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context);
1663     Node* const capacity = a->IntPtrConstant(16);
1664     Node* const length = smi_zero;
1665     Node* const allocation_site = nullptr;
1666     CodeStubAssembler::ParameterMode capacity_mode =
1667         CodeStubAssembler::INTPTR_PARAMETERS;
1668 
1669     result_array = a->AllocateJSArray(kind, array_map, capacity, length,
1670                                       allocation_site, capacity_mode);
1671   }
1672 
1673   // Call into runtime for RegExpExecMultiple.
1674   Node* last_match_info = a->LoadContextElement(
1675       native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
1676   Node* const res =
1677       a->CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
1678                      subject_string, last_match_info, result_array);
1679 
1680   // Reset last index to 0.
1681   FastStoreLastIndex(a, context, regexp, smi_zero);
1682 
1683   // If no matches, return the subject string.
1684   var_result.Bind(subject_string);
1685   a->GotoIf(a->WordEqual(res, null), &out);
1686 
1687   // Reload last match info since it might have changed.
1688   last_match_info = a->LoadContextElement(
1689       native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
1690 
1691   Node* const res_length = a->LoadJSArrayLength(res);
1692   Node* const res_elems = a->LoadElements(res);
1693   CSA_ASSERT(a, a->HasInstanceType(res_elems, FIXED_ARRAY_TYPE));
1694 
1695   CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
1696   Node* const num_capture_registers = a->LoadFixedArrayElement(
1697       last_match_info,
1698       a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, mode);
1699 
1700   Label if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a);
1701   a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))),
1702             &if_noexplicitcaptures, &if_hasexplicitcaptures);
1703 
1704   a->Bind(&if_noexplicitcaptures);
1705   {
1706     // If the number of captures is two then there are no explicit captures in
1707     // the regexp, just the implicit capture that captures the whole match. In
1708     // this case we can simplify quite a bit and end up with something faster.
1709     // The builder will consist of some integers that indicate slices of the
1710     // input string and some replacements that were returned from the replace
1711     // function.
1712 
1713     Variable var_match_start(a, MachineRepresentation::kTagged);
1714     var_match_start.Bind(smi_zero);
1715 
1716     Node* const end = a->SmiUntag(res_length);
1717     Variable var_i(a, MachineType::PointerRepresentation());
1718     var_i.Bind(int_zero);
1719 
1720     Variable* vars[] = {&var_i, &var_match_start};
1721     Label loop(a, 2, vars);
1722     a->Goto(&loop);
1723     a->Bind(&loop);
1724     {
1725       Node* const i = var_i.value();
1726       a->GotoUnless(a->IntPtrLessThan(i, end), &create_result);
1727 
1728       CodeStubAssembler::ParameterMode mode =
1729           CodeStubAssembler::INTPTR_PARAMETERS;
1730       Node* const elem = a->LoadFixedArrayElement(res_elems, i, 0, mode);
1731 
1732       Label if_issmi(a), if_isstring(a), loop_epilogue(a);
1733       a->Branch(a->TaggedIsSmi(elem), &if_issmi, &if_isstring);
1734 
1735       a->Bind(&if_issmi);
1736       {
1737         // Integers represent slices of the original string.
1738         Label if_isnegativeorzero(a), if_ispositive(a);
1739         a->BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero,
1740                                       &if_ispositive);
1741 
1742         a->Bind(&if_ispositive);
1743         {
1744           Node* const int_elem = a->SmiUntag(elem);
1745           Node* const new_match_start =
1746               a->IntPtrAdd(a->WordShr(int_elem, a->IntPtrConstant(11)),
1747                            a->WordAnd(int_elem, a->IntPtrConstant(0x7ff)));
1748           var_match_start.Bind(a->SmiTag(new_match_start));
1749           a->Goto(&loop_epilogue);
1750         }
1751 
1752         a->Bind(&if_isnegativeorzero);
1753         {
1754           Node* const next_i = a->IntPtrAdd(i, int_one);
1755           var_i.Bind(next_i);
1756 
1757           Node* const next_elem =
1758               a->LoadFixedArrayElement(res_elems, next_i, 0, mode);
1759 
1760           Node* const new_match_start = a->SmiSub(next_elem, elem);
1761           var_match_start.Bind(new_match_start);
1762           a->Goto(&loop_epilogue);
1763         }
1764       }
1765 
1766       a->Bind(&if_isstring);
1767       {
1768         CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(elem)));
1769 
1770         Callable call_callable = CodeFactory::Call(isolate);
1771         Node* const replacement_obj =
1772             a->CallJS(call_callable, context, replace_callable, undefined, elem,
1773                       var_match_start.value(), subject_string);
1774 
1775         Node* const replacement_str = a->ToString(context, replacement_obj);
1776         a->StoreFixedArrayElement(res_elems, i, replacement_str);
1777 
1778         Node* const elem_length = a->LoadStringLength(elem);
1779         Node* const new_match_start =
1780             a->SmiAdd(var_match_start.value(), elem_length);
1781         var_match_start.Bind(new_match_start);
1782 
1783         a->Goto(&loop_epilogue);
1784       }
1785 
1786       a->Bind(&loop_epilogue);
1787       {
1788         var_i.Bind(a->IntPtrAdd(var_i.value(), int_one));
1789         a->Goto(&loop);
1790       }
1791     }
1792   }
1793 
1794   a->Bind(&if_hasexplicitcaptures);
1795   {
1796     CodeStubAssembler::ParameterMode mode =
1797         CodeStubAssembler::INTPTR_PARAMETERS;
1798 
1799     Node* const from = int_zero;
1800     Node* const to = a->SmiUntag(res_length);
1801     const int increment = 1;
1802 
1803     a->BuildFastLoop(
1804         MachineType::PointerRepresentation(), from, to,
1805         [res_elems, isolate, native_context, context, undefined,
1806          replace_callable, mode](CodeStubAssembler* a, Node* index) {
1807           Node* const elem =
1808               a->LoadFixedArrayElement(res_elems, index, 0, mode);
1809 
1810           Label do_continue(a);
1811           a->GotoIf(a->TaggedIsSmi(elem), &do_continue);
1812 
1813           // elem must be an Array.
1814           // Use the apply argument as backing for global RegExp properties.
1815 
1816           CSA_ASSERT(a, a->HasInstanceType(elem, JS_ARRAY_TYPE));
1817 
1818           // TODO(jgruber): Remove indirection through Call->ReflectApply.
1819           Callable call_callable = CodeFactory::Call(isolate);
1820           Node* const reflect_apply = a->LoadContextElement(
1821               native_context, Context::REFLECT_APPLY_INDEX);
1822 
1823           Node* const replacement_obj =
1824               a->CallJS(call_callable, context, reflect_apply, undefined,
1825                         replace_callable, undefined, elem);
1826 
1827           // Overwrite the i'th element in the results with the string we got
1828           // back from the callback function.
1829 
1830           Node* const replacement_str = a->ToString(context, replacement_obj);
1831           a->StoreFixedArrayElement(res_elems, index, replacement_str,
1832                                     UPDATE_WRITE_BARRIER, mode);
1833 
1834           a->Goto(&do_continue);
1835           a->Bind(&do_continue);
1836         },
1837         increment, CodeStubAssembler::IndexAdvanceMode::kPost);
1838 
1839     a->Goto(&create_result);
1840   }
1841 
1842   a->Bind(&create_result);
1843   {
1844     Node* const result = a->CallRuntime(Runtime::kStringBuilderConcat, context,
1845                                         res, res_length, subject_string);
1846     var_result.Bind(result);
1847     a->Goto(&out);
1848   }
1849 
1850   a->Bind(&out);
1851   return var_result.value();
1852 }
1853 
ReplaceSimpleStringFastPath(CodeStubAssembler * a,compiler::Node * context,compiler::Node * regexp,compiler::Node * subject_string,compiler::Node * replace_string)1854 compiler::Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a,
1855                                             compiler::Node* context,
1856                                             compiler::Node* regexp,
1857                                             compiler::Node* subject_string,
1858                                             compiler::Node* replace_string) {
1859   // The fast path is reached only if {receiver} is an unmodified
1860   // JSRegExp instance, {replace_value} is non-callable, and
1861   // ToString({replace_value}) does not contain '$', i.e. we're doing a simple
1862   // string replacement.
1863 
1864   typedef CodeStubAssembler::Variable Variable;
1865   typedef CodeStubAssembler::Label Label;
1866   typedef compiler::Node Node;
1867 
1868   Isolate* const isolate = a->isolate();
1869 
1870   Node* const null = a->NullConstant();
1871   Node* const int_zero = a->IntPtrConstant(0);
1872   Node* const smi_zero = a->SmiConstant(Smi::kZero);
1873 
1874   Label out(a);
1875   Variable var_result(a, MachineRepresentation::kTagged);
1876 
1877   // Load the last match info.
1878   Node* const native_context = a->LoadNativeContext(context);
1879   Node* const last_match_info = a->LoadContextElement(
1880       native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
1881 
1882   // Is {regexp} global?
1883   Label if_isglobal(a), if_isnonglobal(a);
1884   Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset);
1885   Node* const is_global =
1886       a->WordAnd(a->SmiUntag(flags), a->IntPtrConstant(JSRegExp::kGlobal));
1887   a->Branch(a->WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal);
1888 
1889   a->Bind(&if_isglobal);
1890   {
1891     // Hand off global regexps to runtime.
1892     FastStoreLastIndex(a, context, regexp, smi_zero);
1893     Node* const result =
1894         a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context,
1895                        subject_string, regexp, replace_string, last_match_info);
1896     var_result.Bind(result);
1897     a->Goto(&out);
1898   }
1899 
1900   a->Bind(&if_isnonglobal);
1901   {
1902     // Run exec, then manually construct the resulting string.
1903     Callable exec_callable = CodeFactory::RegExpExec(isolate);
1904     Node* const match_indices =
1905         a->CallStub(exec_callable, context, regexp, subject_string, smi_zero,
1906                     last_match_info);
1907 
1908     Label if_matched(a), if_didnotmatch(a);
1909     a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched);
1910 
1911     a->Bind(&if_didnotmatch);
1912     {
1913       FastStoreLastIndex(a, context, regexp, smi_zero);
1914       var_result.Bind(subject_string);
1915       a->Goto(&out);
1916     }
1917 
1918     a->Bind(&if_matched);
1919     {
1920       CodeStubAssembler::ParameterMode mode =
1921           CodeStubAssembler::INTPTR_PARAMETERS;
1922 
1923       Node* const subject_start = smi_zero;
1924       Node* const match_start = a->LoadFixedArrayElement(
1925           match_indices, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex),
1926           0, mode);
1927       Node* const match_end = a->LoadFixedArrayElement(
1928           match_indices,
1929           a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, mode);
1930       Node* const subject_end = a->LoadStringLength(subject_string);
1931 
1932       Label if_replaceisempty(a), if_replaceisnotempty(a);
1933       Node* const replace_length = a->LoadStringLength(replace_string);
1934       a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty,
1935                 &if_replaceisnotempty);
1936 
1937       a->Bind(&if_replaceisempty);
1938       {
1939         // TODO(jgruber): We could skip many of the checks that using SubString
1940         // here entails.
1941 
1942         Node* const first_part =
1943             a->SubString(context, subject_string, subject_start, match_start);
1944         Node* const second_part =
1945             a->SubString(context, subject_string, match_end, subject_end);
1946 
1947         Node* const result = a->StringAdd(context, first_part, second_part);
1948         var_result.Bind(result);
1949         a->Goto(&out);
1950       }
1951 
1952       a->Bind(&if_replaceisnotempty);
1953       {
1954         Node* const first_part =
1955             a->SubString(context, subject_string, subject_start, match_start);
1956         Node* const second_part = replace_string;
1957         Node* const third_part =
1958             a->SubString(context, subject_string, match_end, subject_end);
1959 
1960         Node* result = a->StringAdd(context, first_part, second_part);
1961         result = a->StringAdd(context, result, third_part);
1962 
1963         var_result.Bind(result);
1964         a->Goto(&out);
1965       }
1966     }
1967   }
1968 
1969   a->Bind(&out);
1970   return var_result.value();
1971 }
1972 
1973 }  // namespace
1974 
1975 // ES#sec-regexp.prototype-@@replace
1976 // RegExp.prototype [ @@replace ] ( string, replaceValue )
Generate_RegExpPrototypeReplace(CodeStubAssembler * a)1977 void Builtins::Generate_RegExpPrototypeReplace(CodeStubAssembler* a) {
1978   typedef CodeStubAssembler::Label Label;
1979   typedef compiler::Node Node;
1980 
1981   Isolate* const isolate = a->isolate();
1982 
1983   Node* const maybe_receiver = a->Parameter(0);
1984   Node* const maybe_string = a->Parameter(1);
1985   Node* const replace_value = a->Parameter(2);
1986   Node* const context = a->Parameter(5);
1987 
1988   Node* const int_zero = a->IntPtrConstant(0);
1989 
1990   // Ensure {maybe_receiver} is a JSReceiver.
1991   Node* const map =
1992       ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver,
1993                            MessageTemplate::kIncompatibleMethodReceiver,
1994                            "RegExp.prototype.@@replace");
1995   Node* const receiver = maybe_receiver;
1996 
1997   // Convert {maybe_string} to a String.
1998   Callable tostring_callable = CodeFactory::ToString(isolate);
1999   Node* const string = a->CallStub(tostring_callable, context, maybe_string);
2000 
2001   // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
2002   Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a);
2003   BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime);
2004 
2005   a->Bind(&checkreplacecallable);
2006   Node* const regexp = receiver;
2007 
2008   // 2. Is {replace_value} callable?
2009   Label checkreplacestring(a), if_iscallable(a);
2010   a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring);
2011 
2012   Node* const replace_value_map = a->LoadMap(replace_value);
2013   a->Branch(a->IsCallableMap(replace_value_map), &if_iscallable,
2014             &checkreplacestring);
2015 
2016   // 3. Does ToString({replace_value}) contain '$'?
2017   a->Bind(&checkreplacestring);
2018   {
2019     Node* const replace_string =
2020         a->CallStub(tostring_callable, context, replace_value);
2021 
2022     Node* const dollar_char = a->IntPtrConstant('$');
2023     Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1));
2024     a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string,
2025                                                    dollar_char, int_zero),
2026                               smi_minusone),
2027                   &runtime);
2028 
2029     a->Return(ReplaceSimpleStringFastPath(a, context, regexp, string,
2030                                           replace_string));
2031   }
2032 
2033   // {regexp} is unmodified and {replace_value} is callable.
2034   a->Bind(&if_iscallable);
2035   {
2036     Node* const replace_callable = replace_value;
2037 
2038     // Check if the {regexp} is global.
2039     Label if_isglobal(a), if_isnotglobal(a);
2040     Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal);
2041     a->Branch(is_global, &if_isglobal, &if_isnotglobal);
2042 
2043     a->Bind(&if_isglobal);
2044     {
2045       Node* const result = ReplaceGlobalCallableFastPath(
2046           a, context, regexp, string, replace_callable);
2047       a->Return(result);
2048     }
2049 
2050     a->Bind(&if_isnotglobal);
2051     {
2052       Node* const result =
2053           a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
2054                          context, string, regexp, replace_callable);
2055       a->Return(result);
2056     }
2057   }
2058 
2059   a->Bind(&runtime);
2060   {
2061     Node* const result = a->CallRuntime(Runtime::kRegExpReplace, context,
2062                                         receiver, string, replace_value);
2063     a->Return(result);
2064   }
2065 }
2066 
2067 // Simple string matching functionality for internal use which does not modify
2068 // the last match info.
Generate_RegExpInternalMatch(CodeStubAssembler * a)2069 void Builtins::Generate_RegExpInternalMatch(CodeStubAssembler* a) {
2070   typedef CodeStubAssembler::Label Label;
2071   typedef compiler::Node Node;
2072 
2073   Isolate* const isolate = a->isolate();
2074 
2075   Node* const regexp = a->Parameter(1);
2076   Node* const string = a->Parameter(2);
2077   Node* const context = a->Parameter(5);
2078 
2079   Node* const null = a->NullConstant();
2080   Node* const smi_zero = a->SmiConstant(Smi::FromInt(0));
2081 
2082   Node* const native_context = a->LoadNativeContext(context);
2083   Node* const internal_match_info = a->LoadContextElement(
2084       native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX);
2085 
2086   Callable exec_callable = CodeFactory::RegExpExec(isolate);
2087   Node* const match_indices = a->CallStub(
2088       exec_callable, context, regexp, string, smi_zero, internal_match_info);
2089 
2090   Label if_matched(a), if_didnotmatch(a);
2091   a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched);
2092 
2093   a->Bind(&if_didnotmatch);
2094   a->Return(null);
2095 
2096   a->Bind(&if_matched);
2097   {
2098     Node* result = ConstructNewResultFromMatchInfo(isolate, a, context,
2099                                                    match_indices, string);
2100     a->Return(result);
2101   }
2102 }
2103 
2104 }  // namespace internal
2105 }  // namespace v8
2106