1// Copyright 2019 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-regexp-gen.h' 6 7namespace regexp { 8 9const kATOM: constexpr int31 10 generates 'JSRegExp::ATOM'; 11const kTagIndex: constexpr int31 12 generates 'JSRegExp::kTagIndex'; 13const kAtomPatternIndex: constexpr int31 14 generates 'JSRegExp::kAtomPatternIndex'; 15 16extern transitioning macro RegExpBuiltinsAssembler::FlagGetter( 17 implicit context: Context)(Object, constexpr Flag, constexpr bool): bool; 18 19extern macro UnsafeLoadFixedArrayElement( 20 RegExpMatchInfo, constexpr int31): Object; 21 22transitioning macro RegExpPrototypeMatchBody(implicit context: Context)( 23 regexp: JSReceiver, string: String, isFastPath: constexpr bool): JSAny { 24 if constexpr (isFastPath) { 25 dcheck(Is<FastJSRegExp>(regexp)); 26 } 27 28 const isGlobal: bool = FlagGetter(regexp, Flag::kGlobal, isFastPath); 29 30 if (!isGlobal) { 31 return isFastPath ? RegExpPrototypeExecBodyFast(regexp, string) : 32 RegExpExec(regexp, string); 33 } 34 35 dcheck(isGlobal); 36 const isUnicode: bool = FlagGetter(regexp, Flag::kUnicode, isFastPath); 37 38 StoreLastIndex(regexp, 0, isFastPath); 39 40 // Allocate an array to store the resulting match strings. 41 42 let array = growable_fixed_array::NewGrowableFixedArray(); 43 44 // Check if the regexp is an ATOM type. If so, then keep the literal string 45 // to search for so that we can avoid calling substring in the loop below. 46 let atom: bool = false; 47 let searchString: String = EmptyStringConstant(); 48 if constexpr (isFastPath) { 49 const maybeAtomRegexp = UnsafeCast<JSRegExp>(regexp); 50 const data = UnsafeCast<FixedArray>(maybeAtomRegexp.data); 51 if (UnsafeCast<Smi>(data.objects[kTagIndex]) == kATOM) { 52 searchString = UnsafeCast<String>(data.objects[kAtomPatternIndex]); 53 atom = true; 54 } 55 } 56 57 while (true) { 58 let match: String = EmptyStringConstant(); 59 try { 60 if constexpr (isFastPath) { 61 // On the fast path, grab the matching string from the raw match index 62 // array. 63 const matchIndices: RegExpMatchInfo = 64 RegExpPrototypeExecBodyWithoutResultFast( 65 UnsafeCast<JSRegExp>(regexp), string) otherwise IfDidNotMatch; 66 if (atom) { 67 match = searchString; 68 } else { 69 const matchFrom = UnsafeLoadFixedArrayElement( 70 matchIndices, kRegExpMatchInfoFirstCaptureIndex); 71 const matchTo = UnsafeLoadFixedArrayElement( 72 matchIndices, kRegExpMatchInfoFirstCaptureIndex + 1); 73 match = SubString( 74 string, UnsafeCast<Smi>(matchFrom), UnsafeCast<Smi>(matchTo)); 75 } 76 } else { 77 dcheck(!isFastPath); 78 const resultTemp = RegExpExec(regexp, string); 79 if (resultTemp == Null) { 80 goto IfDidNotMatch; 81 } 82 match = ToString_Inline(GetProperty(resultTemp, SmiConstant(0))); 83 } 84 goto IfDidMatch; 85 } label IfDidNotMatch { 86 return array.length == 0 ? Null : array.ToJSArray(); 87 } label IfDidMatch { 88 // Store the match, growing the fixed array if needed. 89 90 array.Push(match); 91 92 // Advance last index if the match is the empty string. 93 const matchLength: Smi = match.length_smi; 94 if (matchLength != 0) { 95 continue; 96 } 97 let lastIndex = LoadLastIndex(regexp, isFastPath); 98 if constexpr (isFastPath) { 99 dcheck(TaggedIsPositiveSmi(lastIndex)); 100 } else { 101 lastIndex = ToLength_Inline(lastIndex); 102 } 103 104 const newLastIndex: Number = AdvanceStringIndex( 105 string, UnsafeCast<Number>(lastIndex), isUnicode, isFastPath); 106 107 if constexpr (isFastPath) { 108 // On the fast path, we can be certain that lastIndex can never be 109 // incremented to overflow the Smi range since the maximal string 110 // length is less than the maximal Smi value. 111 StaticAssertStringLengthFitsSmi(); 112 dcheck(TaggedIsPositiveSmi(newLastIndex)); 113 } 114 115 StoreLastIndex(regexp, newLastIndex, isFastPath); 116 } 117 } 118 119 VerifiedUnreachable(); 120} 121 122transitioning macro FastRegExpPrototypeMatchBody(implicit context: Context)( 123 receiver: FastJSRegExp, string: String): JSAny { 124 return RegExpPrototypeMatchBody(receiver, string, true); 125} 126 127transitioning macro SlowRegExpPrototypeMatchBody(implicit context: Context)( 128 receiver: JSReceiver, string: String): JSAny { 129 return RegExpPrototypeMatchBody(receiver, string, false); 130} 131 132// Helper that skips a few initial checks. and assumes... 133// 1) receiver is a "fast" RegExp 134// 2) pattern is a string 135transitioning builtin RegExpMatchFast(implicit context: Context)( 136 receiver: FastJSRegExp, string: String): JSAny { 137 return FastRegExpPrototypeMatchBody(receiver, string); 138} 139 140// ES#sec-regexp.prototype-@@match 141// RegExp.prototype [ @@match ] ( string ) 142transitioning javascript builtin RegExpPrototypeMatch( 143 js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny { 144 ThrowIfNotJSReceiver( 145 receiver, MessageTemplate::kIncompatibleMethodReceiver, 146 'RegExp.prototype.@@match'); 147 const receiver = UnsafeCast<JSReceiver>(receiver); 148 const string: String = ToString_Inline(string); 149 150 // Strict: Reads global and unicode properties. 151 // TODO(jgruber): Handle slow flag accesses on the fast path and make this 152 // permissive. 153 const fastRegExp = Cast<FastJSRegExp>(receiver) 154 otherwise return SlowRegExpPrototypeMatchBody(receiver, string); 155 156 // TODO(pwong): Could be optimized to remove the overhead of calling the 157 // builtin (at the cost of a larger builtin). 158 return RegExpMatchFast(fastRegExp, string); 159} 160} 161