• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/builtins/builtins_regexp.h"
17 #include "ecmascript/builtins/builtins_regexp-inl.h"
18 #include "ecmascript/checkpoint/thread_state_transition.h"
19 
20 #include <cmath>
21 
22 #include "ecmascript/ecma_string-inl.h"
23 #include "ecmascript/interpreter/interpreter.h"
24 #include "ecmascript/js_regexp.h"
25 #include "ecmascript/js_regexp_iterator.h"
26 #include "ecmascript/js_tagged_value-inl.h"
27 #include "ecmascript/object_fast_operator-inl.h"
28 #include "ecmascript/property_detector-inl.h"
29 #include "ecmascript/regexp/regexp_executor.h"
30 #include "ecmascript/regexp/regexp_parser_cache.h"
31 #include "ecmascript/tagged_array-inl.h"
32 
33 namespace panda::ecmascript::builtins {
34 // 21.2.3.1
RegExpConstructor(EcmaRuntimeCallInfo * argv)35 JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv)
36 {
37     ASSERT(argv);
38     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Constructor);
39     JSThread *thread = argv->GetThread();
40     [[maybe_unused]] EcmaHandleScope handleScope(thread);
41     JSHandle<JSTaggedValue> newTargetTemp = GetNewTarget(argv);
42     JSHandle<JSTaggedValue> pattern = GetCallArg(argv, 0);
43     JSHandle<JSTaggedValue> flags = GetCallArg(argv, 1);
44     // 1. Let patternIsRegExp be IsRegExp(pattern).
45     bool patternIsRegExp = JSObject::IsRegExp(thread, pattern);
46     // 2. ReturnIfAbrupt(patternIsRegExp).
47     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
48     // 3. If NewTarget is not undefined, let newTarget be NewTarget.
49     JSHandle<JSTaggedValue> newTarget;
50     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
51     if (!newTargetTemp->IsUndefined()) {
52         newTarget = newTargetTemp;
53     } else {
54         auto ecmaVm = thread->GetEcmaVM();
55         JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
56         // disable gc
57         [[maybe_unused]] DisallowGarbageCollection noGc;
58         // 4.a Let newTarget be the active function object.
59         newTarget = env->GetRegExpFunction();
60         JSHandle<JSTaggedValue> constructorString = globalConst->GetHandledConstructorString();
61         // 4.b If patternIsRegExp is true and flags is undefined
62         if (patternIsRegExp && flags->IsUndefined()) {
63             // 4.b.i Let patternConstructor be Get(pattern, "constructor").
64             JSTaggedValue patternConstructor = ObjectFastOperator::FastGetPropertyByValue(
65                 thread, pattern.GetTaggedValue(), constructorString.GetTaggedValue());
66             // 4.b.ii ReturnIfAbrupt(patternConstructor).
67             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
68             // 4.b.iii If SameValue(newTarget, patternConstructor) is true, return pattern.
69             if (JSTaggedValue::SameValue(newTarget.GetTaggedValue(), patternConstructor)) {
70                 return pattern.GetTaggedValue();
71             }
72         }
73     }
74     // 5. If Type(pattern) is Object and pattern has a [[RegExpMatcher]] internal slot
75     bool isJsReg = false;
76     if (pattern->IsECMAObject()) {
77         JSHandle<JSObject> patternObj = JSHandle<JSObject>::Cast(pattern);
78         isJsReg = patternObj->IsJSRegExp();
79     }
80     JSHandle<JSTaggedValue> patternTemp;
81     JSHandle<JSTaggedValue> flagsTemp;
82     if (isJsReg) {
83         JSHandle<JSRegExp> patternReg(thread, JSRegExp::Cast(pattern->GetTaggedObject()));
84         // 5.a Let P be the value of pattern’s [[OriginalSource]] internal slot.
85         patternTemp = JSHandle<JSTaggedValue>(thread, patternReg->GetOriginalSource());
86         if (flags->IsUndefined()) {
87             // 5.b If flags is undefined, let F be the value of pattern’s [[OriginalFlags]] internal slot.
88             flagsTemp = JSHandle<JSTaggedValue>(thread, patternReg->GetOriginalFlags());
89         } else {
90             // 5.c Else, let F be flags.
91             flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
92         }
93         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
94         // 6. Else if patternIsRegExp is true
95     } else if (patternIsRegExp) {
96         JSHandle<JSTaggedValue> sourceString(globalConst->GetHandledSourceString());
97         JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
98         // disable gc
99         [[maybe_unused]] DisallowGarbageCollection noGc;
100         // 6.a Let P be Get(pattern, "source").
101         patternTemp = JSObject::GetProperty(thread, pattern, sourceString).GetValue();
102         // 6.b ReturnIfAbrupt(P).
103         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
104         // 6.c If flags is undefined
105         if (flags->IsUndefined()) {
106             // 6.c.i Let F be Get(pattern, "flags").
107             flagsTemp = JSObject::GetProperty(thread, pattern, flagsString).GetValue();
108             // 6.c.ii ReturnIfAbrupt(F).
109             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
110         } else {
111             // 6.d Else, let F be flags.
112             flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
113             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
114         }
115     } else {
116         // 7.a Let P be pattern.
117         patternTemp = pattern;
118         // 7.b Let F be flags.
119         if (flags->IsUndefined()) {
120             flagsTemp = flags;
121         } else {
122             flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
123             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
124         }
125     }
126     // 8. Let O be RegExpAlloc(newTarget).
127     JSHandle<JSTaggedValue> object(thread, RegExpAlloc(thread, newTarget));
128     // 9. ReturnIfAbrupt(O).
129     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
130     // 10. Return RegExpInitialize(O, P, F).
131     JSTaggedValue result = RegExpInitialize(thread, object, patternTemp, flagsTemp);
132     return JSTaggedValue(result);
133 }
134 
135 // prototype
136 // 20.2.5.2
Exec(EcmaRuntimeCallInfo * argv)137 JSTaggedValue BuiltinsRegExp::Exec(EcmaRuntimeCallInfo *argv)
138 {
139     ASSERT(argv);
140     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Exec);
141     JSThread *thread = argv->GetThread();
142     [[maybe_unused]] EcmaHandleScope handleScope(thread);
143     // 1. Let R be the this value.
144     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
145     // 4. Let S be ToString(string).
146     JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
147     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
148     // 5. ReturnIfAbrupt(S).
149     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
150     JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
151     // 2. If Type(R) is not Object, throw a TypeError exception.
152     if (!thisObj->IsECMAObject()) {
153         // throw a TypeError exception.
154         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
155     }
156     // 3. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
157     if (!thisObj->IsJSRegExp()) {
158         // throw a TypeError exception.
159         THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[RegExpMatcher]]", JSTaggedValue::Exception());
160     }
161 
162     bool useCache = true;
163     bool isFastPath = IsFastRegExp(thread, thisObj);
164     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
165     if (!isFastPath || cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) {
166         useCache = false;
167     }
168 
169     // 6. Return RegExpBuiltinExec(R, S).
170     return RegExpBuiltinExec(thread, thisObj, string, isFastPath, useCache);
171 }
172 
173 // 20.2.5.13
Test(EcmaRuntimeCallInfo * argv)174 JSTaggedValue BuiltinsRegExp::Test(EcmaRuntimeCallInfo *argv)
175 {
176     ASSERT(argv);
177     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Test);
178     JSThread *thread = argv->GetThread();
179     [[maybe_unused]] EcmaHandleScope handleScope(thread);
180     // 1. Let R be the this value.
181     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
182     JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
183     // 2. If Type(R) is not Object, throw a TypeError exception.
184     if (!thisObj->IsECMAObject()) {
185         // throw a TypeError exception.
186         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
187     }
188     // 3. Let string be ToString(S).
189     // 4. ReturnIfAbrupt(string).
190     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
191     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
192     JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
193     // test fast path
194     if (IsFastRegExp(thread, thisObj)) {
195         return RegExpTestFast(thread, thisObj, string, true);
196     }
197 
198     // 5. Let match be RegExpExec(R, string).
199     JSTaggedValue matchResult = RegExpExec(thread, thisObj, string, false);
200     // 6. ReturnIfAbrupt(match).
201     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
202     // 7. If match is not null, return true; else return false.
203     return GetTaggedBoolean(!matchResult.IsNull());
204 }
205 
IsFastRegExp(JSThread * thread,JSHandle<JSTaggedValue> regexp,RegExpSymbol symbolTag)206 bool BuiltinsRegExp::IsFastRegExp(JSThread *thread, JSHandle<JSTaggedValue> regexp,
207                                   RegExpSymbol symbolTag)
208 {
209     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
210     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
211     JSHClass *hclass = JSHandle<JSObject>::Cast(regexp)->GetJSHClass();
212     JSHClass *originHClass = JSHClass::Cast(globalConst->GetJSRegExpClass().GetTaggedObject());
213     // regexp instance hclass
214     if (hclass != originHClass) {
215         return false;
216     }
217     // lastIndex type is Int
218     JSTaggedValue lastIndex = JSHandle<JSObject>::Cast(regexp)->GetPropertyInlinedProps(LAST_INDEX_OFFSET);
219     if (!lastIndex.IsInt() || lastIndex.GetInt() < 0) {
220         return false;
221     }
222     // RegExp.prototype hclass
223     JSTaggedValue proto = hclass->GetPrototype();
224     JSHClass *regexpHclass = proto.GetTaggedObject()->GetClass();
225     JSHandle<JSTaggedValue> originRegexpClassValue = env->GetRegExpPrototypeClass();
226     JSHClass *originRegexpHclass = JSHClass::Cast(originRegexpClassValue.GetTaggedValue().GetTaggedObject());
227     if (regexpHclass != originRegexpHclass) {
228         return false;
229     }
230     JSObject* protoObj = JSObject::Cast(proto);
231     // RegExp.prototype.exec
232     JSTaggedValue execVal = protoObj->GetPropertyInlinedProps(JSRegExp::EXEC_INLINE_PROPERTY_INDEX);
233     if (execVal != env->GetTaggedRegExpExecFunction()) {
234         return false;
235     }
236     JSTaggedValue symbolFunc = JSTaggedValue::Hole();
237     switch (symbolTag) {
238         case RegExpSymbol::UNKNOWN:
239             break;
240 #define V(UpperCase, Camel)                                                     \
241         case RegExpSymbol::UpperCase:                                           \
242             symbolFunc = protoObj->GetPropertyInlinedProps(                     \
243                 JSRegExp::UpperCase##_INLINE_PROPERTY_INDEX);                   \
244             if (symbolFunc != env->GetTaggedRegExp##Camel##Function()) {        \
245                 return false;                                                   \
246             }                                                                   \
247             break;
248             REGEXP_SYMBOL_FUNCTION_LIST(V)
249 #undef V
250     }
251     if (!PropertyDetector::IsRegExpFlagsDetectorValid(env)) {
252         return false;
253     }
254     return true;
255 }
256 
RegExpTestFast(JSThread * thread,JSHandle<JSTaggedValue> regexp,const JSHandle<JSTaggedValue> inputStr,bool useCache)257 JSTaggedValue BuiltinsRegExp::RegExpTestFast(JSThread *thread, JSHandle<JSTaggedValue> regexp,
258                                              const JSHandle<JSTaggedValue> inputStr, bool useCache)
259 {
260     // 1. Assert: Type(S) is String.
261     ASSERT(inputStr->IsString());
262     uint32_t lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, true));
263     // 2. Search RegExpExecResult cache
264     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
265     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
266     if (useCache) {
267         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, inputStr,
268                                                                  RegExpExecResultCache::TEST_TYPE, regexp,
269                                                                  JSTaggedValue(lastIndex), undefined);
270         if (!cacheResult.IsUndefined()) {
271             return cacheResult;
272         }
273     }
274 
275     uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
276     if (lastIndex > length) {
277         SetLastIndex(thread, regexp, JSTaggedValue(0), true);
278         return JSTaggedValue::False();
279     }
280     JSHandle<EcmaString> inputString = JSHandle<EcmaString>::Cast(inputStr);
281     bool matchResult = RegExpExecInternal(thread, regexp, inputString, lastIndex);
282     // 2. Check whether the regexp is global or sticky, which determines whether we update last index later on.
283     bool global = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
284     bool sticky = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_STICKY);
285     bool ifUpdateLastIndex = global || sticky;
286     if (!matchResult) {
287         if (ifUpdateLastIndex) {
288             SetLastIndex(thread, regexp, JSTaggedValue(0), true);
289         }
290         if (useCache) {
291             RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, inputStr,
292                                                     JSHandle<JSTaggedValue>(thread, JSTaggedValue(matchResult)),
293                                                     RegExpExecResultCache::TEST_TYPE,
294                                                     lastIndex, 0, undefined); // 0: match fail so lastIndex is 0
295         }
296         return JSTaggedValue::False();
297     }
298     JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
299     JSTaggedValue endIndex = globalTable->GetEndIndex();
300     uint32_t newLastIndex = lastIndex;
301     if (ifUpdateLastIndex) {
302         newLastIndex = static_cast<uint32_t>(endIndex.GetInt());
303         SetLastIndex(thread, regexp, endIndex, true);
304     }
305     if (useCache) {
306         RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, inputStr,
307                                                 JSHandle<JSTaggedValue>(thread, JSTaggedValue(matchResult)),
308                                                 RegExpExecResultCache::TEST_TYPE,
309                                                 lastIndex, newLastIndex, undefined);
310     }
311     return GetTaggedBoolean(matchResult);
312 }
313 
314 // 20.2.5.14
ToString(EcmaRuntimeCallInfo * argv)315 JSTaggedValue BuiltinsRegExp::ToString(EcmaRuntimeCallInfo *argv)
316 {
317     ASSERT(argv);
318     BUILTINS_API_TRACE(argv->GetThread(), RegExp, ToString);
319     JSThread *thread = argv->GetThread();
320     [[maybe_unused]] EcmaHandleScope handleScope(thread);
321     // 1. Let R be the this value.
322     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
323     auto ecmaVm = thread->GetEcmaVM();
324     // 2. If Type(R) is not Object, throw a TypeError exception.
325     if (!thisObj->IsECMAObject()) {
326         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
327     }
328     const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
329     JSMutableHandle<JSTaggedValue> getSource(thread, JSTaggedValue::Undefined());
330     JSMutableHandle<JSTaggedValue> getFlags(thread, JSTaggedValue::Undefined());
331     JSHandle<EcmaString> sourceStrHandle;
332     JSHandle<EcmaString> flagsStrHandle;
333     if (IsFastRegExp(thread, thisObj)) {
334         JSHandle<JSRegExp> regexp(thread, JSRegExp::Cast(thisObj->GetTaggedObject()));
335         // 3. Let pattern be ToString(Get(R, "source")).
336         getSource.Update(regexp->GetOriginalSource());
337         sourceStrHandle = JSTaggedValue::ToString(thread, getSource);
338         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
339         uint8_t flagsBits = static_cast<uint8_t>(regexp->GetOriginalFlags().GetInt());
340         getFlags.Update(FlagsBitsToString(thread, flagsBits));
341         flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
342         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
343     } else {
344         JSHandle<JSTaggedValue> sourceString(globalConstants->GetHandledSourceString());
345         JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
346         // 3. Let pattern be ToString(Get(R, "source")).
347         getSource.Update(JSObject::GetProperty(thread, thisObj, sourceString).GetValue());
348         sourceStrHandle = JSTaggedValue::ToString(thread, getSource);
349         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
350         getFlags.Update(JSObject::GetProperty(thread, thisObj, flagsString).GetValue());
351         flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
352         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
353     }
354     JSHandle<EcmaString> slashStr = JSHandle<EcmaString>::Cast(globalConstants->GetHandledBackslashString());
355     // 7. Let result be the String value formed by concatenating "/", pattern, and "/", and flags.
356     ObjectFactory *factory = ecmaVm->GetFactory();
357     JSHandle<EcmaString> tempStr = factory->ConcatFromString(slashStr, sourceStrHandle);
358     JSHandle<EcmaString> resultTemp = factory->ConcatFromString(tempStr, slashStr);
359     return factory->ConcatFromString(resultTemp, flagsStrHandle).GetTaggedValue();
360 }
361 
362 // 20.2.5.3
GetFlags(EcmaRuntimeCallInfo * argv)363 JSTaggedValue BuiltinsRegExp::GetFlags(EcmaRuntimeCallInfo *argv)
364 {
365     ASSERT(argv);
366     BUILTINS_API_TRACE(argv->GetThread(), RegExp, GetFlags);
367     JSThread *thread = argv->GetThread();
368     [[maybe_unused]] EcmaHandleScope handleScope(thread);
369     // 1. Let R be the this value.
370     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
371     // 2. If Type(R) is not Object, throw a TypeError exception.
372     if (!thisObj->IsECMAObject()) {
373         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
374     }
375     // 3. Let result be the empty String.
376     // 4. ~ 19.
377     if (!IsFastRegExp(thread, thisObj)) {
378         return GetAllFlagsInternal(thread, thisObj);
379     }
380     uint8_t flagsBits = static_cast<uint8_t>(JSRegExp::Cast(thisObj->GetTaggedObject())->GetOriginalFlags().GetInt());
381     return FlagsBitsToString(thread, flagsBits);
382 }
383 
GetAllFlagsInternal(JSThread * thread,JSHandle<JSTaggedValue> & thisObj)384 JSTaggedValue BuiltinsRegExp::GetAllFlagsInternal(JSThread *thread, JSHandle<JSTaggedValue> &thisObj)
385 {
386     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
387     const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
388     uint8_t *flagsStr = new uint8_t[RegExpParser::FLAG_NUM + 1];  // FLAG_NUM flags + '\0'
389     if (flagsStr == nullptr) {
390         LOG_ECMA(FATAL) << "BuiltinsRegExp::GetAllFlagsInternal:flagsStr is nullptr";
391     }
392     size_t flagsLen = 0;
393     JSHandle<EcmaString> emptyString = factory->GetEmptyString();
394     JSHandle<JSTaggedValue> hasIndicesKey(factory->NewFromASCII("hasIndices"));
395     JSHandle<JSTaggedValue> hasIndicesResult = JSObject::GetProperty(thread, thisObj, hasIndicesKey).GetValue();
396     RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
397     if (hasIndicesResult->ToBoolean()) {
398         flagsStr[flagsLen] = 'd';
399         flagsLen++;
400     }
401     JSHandle<JSTaggedValue> globalKey(globalConstants->GetHandledGlobalString());
402     JSHandle<JSTaggedValue> globalResult = JSObject::GetProperty(thread, thisObj, globalKey).GetValue();
403     RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
404     if (globalResult->ToBoolean()) {
405         flagsStr[flagsLen] = 'g';
406         flagsLen++;
407     }
408     JSHandle<JSTaggedValue> ignoreCaseKey(factory->NewFromASCII("ignoreCase"));
409     JSHandle<JSTaggedValue> ignoreCaseResult = JSObject::GetProperty(thread, thisObj, ignoreCaseKey).GetValue();
410     RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
411     if (ignoreCaseResult->ToBoolean()) {
412         flagsStr[flagsLen] = 'i';
413         flagsLen++;
414     }
415     JSHandle<JSTaggedValue> multilineKey(factory->NewFromASCII("multiline"));
416     JSHandle<JSTaggedValue> multilineResult = JSObject::GetProperty(thread, thisObj, multilineKey).GetValue();
417     RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
418     if (multilineResult->ToBoolean()) {
419         flagsStr[flagsLen] = 'm';
420         flagsLen++;
421     }
422     JSHandle<JSTaggedValue> dotAllKey(factory->NewFromASCII("dotAll"));
423     JSHandle<JSTaggedValue> dotAllResult = JSObject::GetProperty(thread, thisObj, dotAllKey).GetValue();
424     RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
425     if (dotAllResult->ToBoolean()) {
426         flagsStr[flagsLen] = 's';
427         flagsLen++;
428     }
429     JSHandle<JSTaggedValue> unicodeKey(globalConstants->GetHandledUnicodeString());
430     JSHandle<JSTaggedValue> unicodeResult = JSObject::GetProperty(thread, thisObj, unicodeKey).GetValue();
431     RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
432     if (unicodeResult->ToBoolean()) {
433         flagsStr[flagsLen] = 'u';
434         flagsLen++;
435     }
436     JSHandle<JSTaggedValue> stickyKey(globalConstants->GetHandledStickyString());
437     JSHandle<JSTaggedValue> stickyResult = JSObject::GetProperty(thread, thisObj, stickyKey).GetValue();
438     RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
439     if (stickyResult->ToBoolean()) {
440         flagsStr[flagsLen] = 'y';
441         flagsLen++;
442     }
443     flagsStr[flagsLen] = '\0';
444     JSHandle<EcmaString> flagsString = factory->NewFromUtf8(flagsStr, flagsLen);
445     delete[] flagsStr;
446 
447     return flagsString.GetTaggedValue();
448 }
449 
450 // 20.2.5.4
GetGlobal(EcmaRuntimeCallInfo * argv)451 JSTaggedValue BuiltinsRegExp::GetGlobal(EcmaRuntimeCallInfo *argv)
452 {
453     ASSERT(argv);
454     JSThread *thread = argv->GetThread();
455     BUILTINS_API_TRACE(thread, RegExp, GetGlobal);
456     [[maybe_unused]] EcmaHandleScope handleScope(thread);
457     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
458     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
459     return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_GLOBAL);
460 }
461 
462 // 22.2.6.6
GetHasIndices(EcmaRuntimeCallInfo * argv)463 JSTaggedValue BuiltinsRegExp::GetHasIndices(EcmaRuntimeCallInfo *argv)
464 {
465     ASSERT(argv);
466     JSThread *thread = argv->GetThread();
467     BUILTINS_API_TRACE(thread, RegExp, GetHasIndices);
468     [[maybe_unused]] EcmaHandleScope handleScope(thread);
469     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
470     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
471     return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_HASINDICES);
472 }
473 
474 // 20.2.5.5
GetIgnoreCase(EcmaRuntimeCallInfo * argv)475 JSTaggedValue BuiltinsRegExp::GetIgnoreCase(EcmaRuntimeCallInfo *argv)
476 {
477     ASSERT(argv);
478     JSThread *thread = argv->GetThread();
479     BUILTINS_API_TRACE(thread, RegExp, GetIgnoreCase);
480     [[maybe_unused]] EcmaHandleScope handleScope(thread);
481     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
482     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
483     return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_IGNORECASE);
484 }
485 
486 // 20.2.5.7
GetMultiline(EcmaRuntimeCallInfo * argv)487 JSTaggedValue BuiltinsRegExp::GetMultiline(EcmaRuntimeCallInfo *argv)
488 {
489     ASSERT(argv);
490     JSThread *thread = argv->GetThread();
491     BUILTINS_API_TRACE(thread, RegExp, GetMultiline);
492     [[maybe_unused]] EcmaHandleScope handleScope(thread);
493     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
494     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
495     return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_MULTILINE);
496 }
497 
GetDotAll(EcmaRuntimeCallInfo * argv)498 JSTaggedValue BuiltinsRegExp::GetDotAll(EcmaRuntimeCallInfo *argv)
499 {
500     ASSERT(argv);
501     JSThread *thread = argv->GetThread();
502     BUILTINS_API_TRACE(thread, RegExp, GetDotAll);
503     [[maybe_unused]] EcmaHandleScope handleScope(thread);
504     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
505     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
506     return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_DOTALL);
507 }
508 
509 // 20.2.5.10
GetSource(EcmaRuntimeCallInfo * argv)510 JSTaggedValue BuiltinsRegExp::GetSource(EcmaRuntimeCallInfo *argv)
511 {
512     ASSERT(argv);
513     JSThread *thread = argv->GetThread();
514     BUILTINS_API_TRACE(thread, RegExp, GetSource);
515     [[maybe_unused]] EcmaHandleScope handleScope(thread);
516     // 1. Let R be the this value.
517     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
518     // 2. If Type(R) is not Object, throw a TypeError exception.
519     // 3. If R does not have an [[OriginalSource]] internal slot, throw a TypeError exception.
520     // 4. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception.
521     if (!thisObj->IsECMAObject()) {
522         // throw a TypeError exception.
523         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
524     }
525     if (!thisObj->IsJSRegExp()) {
526         // a. If SameValue(R, %RegExp.prototype%) is true, return "(?:)".
527         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
528         JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
529         JSHandle<JSTaggedValue> objConstructor = JSTaggedValue::GetProperty(thread, thisObj, constructorKey).GetValue();
530         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(false));
531         JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
532         if (objConstructor->IsJSFunction() && constructor->IsJSFunction()) {
533             JSHandle<GlobalEnv> objRealm = JSObject::GetFunctionRealm(thread, objConstructor);
534             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
535             JSHandle<GlobalEnv> ctorRealm = JSObject::GetFunctionRealm(thread, constructor);
536             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
537             if (objRealm->GetRegExpPrototype() == thisObj && *objRealm == *ctorRealm) {
538                 JSHandle<EcmaString> result = thread->GetEcmaVM()->GetFactory()->NewFromASCII("(?:)");
539                 return result.GetTaggedValue();
540             }
541         }
542         // b. throw a TypeError exception.
543         THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalSource]]", JSTaggedValue::Exception());
544     }
545     // 5. Let src be the value of R’s [[OriginalSource]] internal slot.
546     JSHandle<JSRegExp> regexpObj(thread, JSRegExp::Cast(thisObj->GetTaggedObject()));
547     JSHandle<JSTaggedValue> source(thread, regexpObj->GetOriginalSource());
548     // 6. Let flags be the value of R’s [[OriginalFlags]] internal slot.
549     uint8_t flagsBits = static_cast<uint8_t>(regexpObj->GetOriginalFlags().GetInt());
550     JSHandle<JSTaggedValue> flags(thread, FlagsBitsToString(thread, flagsBits));
551     // 7. Return EscapeRegExpPattern(src, flags).
552     return JSTaggedValue(EscapeRegExpPattern(thread, source, flags));
553 }
554 
555 // 20.2.5.12
GetSticky(EcmaRuntimeCallInfo * argv)556 JSTaggedValue BuiltinsRegExp::GetSticky(EcmaRuntimeCallInfo *argv)
557 {
558     ASSERT(argv);
559     JSThread *thread = argv->GetThread();
560     BUILTINS_API_TRACE(thread, RegExp, GetSticky);
561     [[maybe_unused]] EcmaHandleScope handleScope(thread);
562     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
563     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
564     return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_STICKY);
565 }
566 
567 // 20.2.5.15
GetUnicode(EcmaRuntimeCallInfo * argv)568 JSTaggedValue BuiltinsRegExp::GetUnicode(EcmaRuntimeCallInfo *argv)
569 {
570     ASSERT(argv);
571     JSThread *thread = argv->GetThread();
572     BUILTINS_API_TRACE(thread, RegExp, GetUnicode);
573     [[maybe_unused]] EcmaHandleScope handleScope(thread);
574     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
575     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
576     return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_UTF16);
577 }
578 
579 // 21.2.4.2
GetSpecies(EcmaRuntimeCallInfo * argv)580 JSTaggedValue BuiltinsRegExp::GetSpecies(EcmaRuntimeCallInfo *argv)
581 {
582     ASSERT(argv);
583     BUILTINS_API_TRACE(argv->GetThread(), RegExp, GetSpecies);
584     return GetThis(argv).GetTaggedValue();
585 }
586 
587 // 21.2.5.6
Match(EcmaRuntimeCallInfo * argv)588 JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv)
589 {
590     ASSERT(argv);
591     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Match);
592     JSThread *thread = argv->GetThread();
593     [[maybe_unused]] EcmaHandleScope handleScope(thread);
594     // 1. Let rx be the this value.
595     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
596     // 3. Let S be ToString(string)
597     JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
598     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
599     // 4. ReturnIfAbrupt(string).
600     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
601     JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
602     if (!thisObj->IsECMAObject()) {
603         // 2. If Type(rx) is not Object, throw a TypeError exception.
604         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
605     }
606     bool isFastPath = IsFastRegExp(thread, thisObj);
607     return RegExpMatch(thread, thisObj, string, isFastPath);
608 }
609 
RegExpMatch(JSThread * thread,const JSHandle<JSTaggedValue> regexp,const JSHandle<JSTaggedValue> string,bool isFastPath)610 JSTaggedValue BuiltinsRegExp::RegExpMatch(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
611                                           const JSHandle<JSTaggedValue> string, bool isFastPath)
612 {
613     bool useCache = true;
614     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
615     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
616     if (!isFastPath || cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) {
617         useCache = false;
618     }
619     bool isGlobal = GetFlag(thread, regexp, RegExpParser::FLAG_GLOBAL, isFastPath);
620     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
621     // 7. If global is false, then
622     if (!isGlobal) {
623         // a. Return RegExpExec(rx, S).
624         if (isFastPath) {
625             return RegExpBuiltinExec(thread, regexp, string, isFastPath, useCache);
626         } else {
627             return RegExpExec(thread, regexp, string, useCache);
628         }
629     }
630 
631     if (useCache) {
632         uint32_t lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, isFastPath));
633         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, string,
634                                                                  RegExpExecResultCache::MATCH_TYPE, regexp,
635                                                                  JSTaggedValue(lastIndex), undefined);
636         if (!cacheResult.IsUndefined()) {
637             return cacheResult;
638         }
639     }
640     bool fullUnicode = GetFlag(thread, regexp, RegExpParser::FLAG_UTF16, isFastPath);
641     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
642     // b. Let setStatus be Set(rx, "lastIndex", 0, true).
643     SetLastIndex(thread, regexp, JSTaggedValue(0), isFastPath);
644     // c. ReturnIfAbrupt(setStatus).
645     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
646     // d. Let A be ArrayCreate(0).
647     JSHandle<JSArray> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0), ArrayMode::LITERAL));
648     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
649     TaggedArray *srcElements = TaggedArray::Cast(array->GetElements().GetTaggedObject());
650     JSMutableHandle<TaggedArray> elements(thread, srcElements);
651     // e. Let n be 0.
652     uint32_t resultNum = 0;
653     uint32_t arrLen = 1;
654     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
655     JSMutableHandle<EcmaString> matchString(thread, JSTaggedValue::Undefined());
656     // f. Repeat,
657     while (true) {
658         if (isFastPath) {
659             uint32_t lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, isFastPath));
660             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
661             result.Update(RegExpBuiltinExecWithoutResult(thread, regexp, string, isFastPath, lastIndex, false));
662             JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
663             uint32_t endIndex = static_cast<uint32_t>(globalTable->GetEndOfCaptureIndex(0).GetInt());
664             if (result->IsNull()) {
665                 // 1. If n=0, return null.
666                 lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, isFastPath));
667                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
668                 if (resultNum == 0) {
669                     RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, string,
670                                                             JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null()),
671                                                             RegExpExecResultCache::MATCH_TYPE,
672                                                             0, 0, undefined);
673                     return JSTaggedValue::Null();
674                 }
675                 RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, string,
676                                                         JSHandle<JSTaggedValue>::Cast(array),
677                                                         RegExpExecResultCache::MATCH_TYPE,
678                                                         0, 0, undefined);
679                 // 2. Else, return A.
680                 return array.GetTaggedValue();
681             }
682             uint32_t startIndex = static_cast<uint32_t>(globalTable->GetStartOfCaptureIndex(0).GetInt());
683             uint32_t len = endIndex - startIndex;
684             matchString.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(
685                 thread->GetEcmaVM(), JSHandle<EcmaString>::Cast(string), startIndex, len)));
686         } else {
687             // i. Let result be RegExpExec(rx, S).
688             result.Update(RegExpExec(thread, regexp, string, useCache));
689             // ii. ReturnIfAbrupt(result).
690             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
691             // iii. If result is null, then
692             if (result->IsNull()) {
693                 // 1. If n=0, return null.
694                 if (resultNum == 0) {
695                     return JSTaggedValue::Null();
696                 }
697                 // 2. Else, return A.
698                 return array.GetTaggedValue();
699             }
700             // iv. Else result is not null,
701             // 1. Let matchStr be ToString(Get(result, "0")).
702             JSHandle<JSTaggedValue> zeroString = thread->GlobalConstants()->GetHandledZeroString();
703             JSTaggedValue matchVal = ObjectFastOperator::FastGetPropertyByValue(
704                 thread, result.GetTaggedValue(), zeroString.GetTaggedValue());
705             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
706             JSHandle<JSTaggedValue> matchStr(thread, matchVal);
707             matchString.Update(JSTaggedValue::ToString(thread, matchStr));
708             // 2. ReturnIfAbrupt(matchStr).
709             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
710         }
711         JSHandle<JSTaggedValue> matchValue = JSHandle<JSTaggedValue>::Cast(matchString);
712         // 3. Let status be CreateDataProperty(A, ToString(n), matchStr).
713         if (arrLen > elements->GetLength()) {
714                 elements.Update(JSObject::GrowElementsCapacity(thread,
715                     JSHandle<JSObject>::Cast(array), elements->GetLength(), true));
716         }
717         elements->Set(thread, arrLen - 1, matchValue);
718         array->SetArrayLength(thread, arrLen);
719         arrLen++;
720         // 5. If matchStr is the empty String, then
721         if (EcmaStringAccessor(matchString).GetLength() == 0) {
722             int64_t lastIndex = GetLastIndex(thread, regexp, isFastPath);
723             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
724             // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
725             // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true).
726             JSTaggedValue nextIndex = JSTaggedValue(AdvanceStringIndex(string, lastIndex, fullUnicode));
727             SetLastIndex(thread, regexp, nextIndex, isFastPath);
728             // e. ReturnIfAbrupt(setStatus).
729             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
730         }
731         // 6. Increase n.
732         resultNum++;
733     }
734 }
735 
MatchAll(EcmaRuntimeCallInfo * argv)736 JSTaggedValue BuiltinsRegExp::MatchAll(EcmaRuntimeCallInfo *argv)
737 {
738     ASSERT(argv);
739     JSThread *thread = argv->GetThread();
740     BUILTINS_API_TRACE(thread, RegExp, MatchAll);
741     [[maybe_unused]] EcmaHandleScope handleScope(thread);
742 
743     // 1. Let R be the this value.
744     // 2. If Type(R) is not Object, throw a TypeError exception.
745     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
746     if (!thisObj->IsECMAObject()) {
747         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
748     }
749 
750     // 3. Let S be ? ToString(string).
751     JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
752     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
753     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
754     bool isFastPath = IsFastRegExp(thread, thisObj);
755     return RegExpMatchAll(thread, thisObj, stringHandle, isFastPath);
756 }
757 
RegExpMatchAll(JSThread * thread,const JSHandle<JSTaggedValue> regexp,const JSHandle<EcmaString> string,bool isFastPath)758 JSTaggedValue BuiltinsRegExp::RegExpMatchAll(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
759                                              const JSHandle<EcmaString> string, bool isFastPath)
760 {
761     JSMutableHandle<JSTaggedValue> matcher(thread, JSTaggedValue::Undefined());
762     bool global = false;
763     bool fullUnicode = false;
764     if (isFastPath) {
765         JSHandle<JSRegExp> jsRegExp = JSHandle<JSRegExp>::Cast(regexp);
766         JSHandle<JSTaggedValue> pattern(thread, jsRegExp->GetOriginalSource());
767         JSHandle<JSTaggedValue> flags(thread, jsRegExp->GetOriginalFlags());
768         matcher.Update(BuiltinsRegExp::RegExpCreate(thread, pattern, flags));
769         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
770         SetLastIndex(thread, matcher,
771             JSHandle<JSObject>::Cast(jsRegExp)->GetPropertyInlinedProps(LAST_INDEX_OFFSET), isFastPath);
772         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
773         global = GetOriginalFlag(thread, matcher, RegExpParser::FLAG_GLOBAL);
774         fullUnicode = GetOriginalFlag(thread, matcher, RegExpParser::FLAG_UTF16);
775     } else {
776         auto ecmaVm = thread->GetEcmaVM();
777         // 4. Let C be ? SpeciesConstructor(R, %RegExp%).
778         JSHandle<JSTaggedValue> defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction();
779         JSHandle<JSObject> objHandle(regexp);
780         JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
781         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
782 
783         const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
784         // 5. Let flags be ? ToString(? Get(R, "flags")).
785         JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
786         JSHandle<JSTaggedValue> getFlags(JSObject::GetProperty(thread, regexp, flagsString).GetValue());
787         JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
788         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
789 
790         // 6. Let matcher be ? Construct(C, « R, flags »).
791         JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
792         EcmaRuntimeCallInfo *runtimeInfo =
793             EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 2); // 2: two args
794         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
795         runtimeInfo->SetCallArg(regexp.GetTaggedValue(), flagsStrHandle.GetTaggedValue());
796         JSTaggedValue taggedMatcher = JSFunction::Construct(runtimeInfo);
797         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
798         matcher.Update(taggedMatcher);
799 
800         // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
801         JSHandle<JSTaggedValue> lastIndexString(globalConstants->GetHandledLastIndexString());
802         JSHandle<JSTaggedValue> getLastIndex(JSObject::GetProperty(thread, regexp, lastIndexString).GetValue());
803         JSTaggedNumber thisLastIndex = JSTaggedValue::ToLength(thread, getLastIndex);
804         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
805 
806         // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
807         ObjectFastOperator::FastSetPropertyByValue(thread, matcher.GetTaggedValue(), lastIndexString.GetTaggedValue(),
808                                                    thisLastIndex);
809         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
810 
811         // 9. If flags contains "g", let global be true.
812         // 10. Else, let global be false.
813         JSHandle<EcmaString> gString(globalConstants->GetHandledGString());
814         if (EcmaStringAccessor::IndexOf(ecmaVm, flagsStrHandle, gString) != -1) {
815             global = true;
816         }
817 
818         // 11. If flags contains "u", let fullUnicode be true.
819         // 12. Else, let fullUnicode be false.
820         JSHandle<EcmaString> uString(globalConstants->GetHandledUString());
821         if (EcmaStringAccessor::IndexOf(ecmaVm, flagsStrHandle, uString) != -1) {
822             fullUnicode = true;
823         }
824     }
825     // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
826     return JSRegExpIterator::CreateRegExpStringIterator(thread, matcher,
827                                                         string, global, fullUnicode).GetTaggedValue();
828 }
829 
RegExpReplaceFast(JSThread * thread,JSHandle<JSTaggedValue> regexp,JSHandle<EcmaString> inputString,uint32_t inputLength)830 JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandle<JSTaggedValue> regexp,
831                                                 JSHandle<EcmaString> inputString, uint32_t inputLength)
832 {
833     ASSERT(regexp->IsJSRegExp());
834     BUILTINS_API_TRACE(thread, RegExp, RegExpReplaceFast);
835     JSHandle<JSRegExp> regexpHandle(regexp);
836     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
837     // get bytecode
838     JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
839     void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
840     // get flags
841     auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
842     uint32_t flags = *reinterpret_cast<uint32_t *>(bytecodeBuffer + RegExpParser::FLAGS_OFFSET);
843     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
844     JSHandle<JSTaggedValue> tagInputString = JSHandle<JSTaggedValue>::Cast(inputString);
845     bool useCache = false;
846     uint32_t lastIndex = 0;
847     GetLastIndex(thread, regexp, lastIndex);
848     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
849 
850     auto globalConst = thread->GlobalConstants();
851     JSHandle<JSTaggedValue> pattern(thread, regexpHandle->GetOriginalSource());
852     JSHandle<JSTaggedValue> flagsBits(thread, regexpHandle->GetOriginalFlags());
853     useCache = ShouldUseCache(thread, inputString);
854     uint32_t lastIndexInput = lastIndex;
855     JSHandle<JSTaggedValue> emptyString(thread, globalConst->GetEmptyString());
856     if (useCache) {
857         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, tagInputString,
858                                                                  RegExpExecResultCache::REPLACE_TYPE, regexp,
859                                                                  JSTaggedValue(lastIndexInput),
860                                                                  emptyString);
861         if (!cacheResult.IsUndefined()) {
862             return cacheResult;
863         }
864     }
865 
866     std::string resultString;
867     MatchAndReplace(thread, regexp, inputString, flags,
868                     lastIndex, inputLength, resultString);
869     auto resultValue = factory->NewFromStdString(resultString);
870     if (useCache) {
871         RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, tagInputString,
872                                                 JSHandle<JSTaggedValue>(resultValue),
873                                                 RegExpExecResultCache::REPLACE_TYPE, lastIndexInput, lastIndex,
874                                                 emptyString);
875     }
876     return resultValue.GetTaggedValue();
877 }
878 
GetLastIndex(JSThread * thread,JSHandle<JSTaggedValue> regexp,uint32_t & lastIndex)879 JSTaggedValue BuiltinsRegExp::GetLastIndex(JSThread *thread, JSHandle<JSTaggedValue> regexp,
880                                            uint32_t &lastIndex)
881 {
882     JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
883     void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
884     auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
885     uint32_t flags = *reinterpret_cast<uint32_t *>(bytecodeBuffer + RegExpParser::FLAGS_OFFSET);
886     JSHandle<JSTaggedValue> lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString());
887     if ((flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) == 0) {
888         lastIndex = 0;
889     } else {
890         JSTaggedValue thisIndex =
891             ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(),
892                                                        lastIndexHandle.GetTaggedValue());
893             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
894         if (thisIndex.IsInt()) {
895             lastIndex = static_cast<uint32_t>(thisIndex.GetInt());
896         } else {
897             JSHandle<JSTaggedValue> thisIndexHandle(thread, thisIndex);
898             auto lengthValue = JSTaggedValue::ToLength(thread, thisIndexHandle);
899             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
900             lastIndex = lengthValue.GetNumber();
901         }
902     }
903     return JSTaggedValue::Undefined();
904 }
905 
ShouldUseCache(JSThread * thread,JSHandle<EcmaString> inputString)906 bool BuiltinsRegExp::ShouldUseCache(JSThread *thread, JSHandle<EcmaString> inputString)
907 {
908     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
909     uint32_t length = EcmaStringAccessor(inputString).GetLength();
910     uint32_t largeStrCount = cacheTable->GetLargeStrCount();
911     if (largeStrCount != 0) {
912         if (length > MIN_REPLACE_STRING_LENGTH) {
913             cacheTable->SetLargeStrCount(thread, --largeStrCount);
914         }
915     } else {
916         cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH);
917     }
918     return length > cacheTable->GetStrLenThreshold();
919 }
920 
MatchAndReplace(JSThread * thread,JSHandle<JSTaggedValue> regexp,JSHandle<EcmaString> inputString,uint32_t & flags,uint32_t lastIndex,uint32_t inputLength,std::string & resultString)921 JSTaggedValue BuiltinsRegExp::MatchAndReplace(JSThread *thread, JSHandle<JSTaggedValue> regexp,
922                                               JSHandle<EcmaString> inputString, uint32_t &flags,
923                                               uint32_t lastIndex, uint32_t inputLength,
924                                               std::string &resultString)
925 {
926     uint32_t nextPosition = 0;
927     JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
928     JSHandle<JSTaggedValue> lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString());
929     JSHandle<JSTaggedValue> tagInputString = JSHandle<JSTaggedValue>::Cast(inputString);
930 
931     // 12. Let done be false.
932     // 13. Repeat, while done is false
933     for (;;) {
934         if (lastIndex > inputLength) {
935             break;
936         }
937         bool matchResult = RegExpExecInternal(thread, regexp, inputString, lastIndex);
938         if (!matchResult) {
939             if (flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) {
940                 lastIndex = 0;
941                 ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
942                                                            lastIndexHandle.GetTaggedValue(), JSTaggedValue(0));
943                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
944             }
945             break;
946         }
947         uint32_t startIndex = static_cast<uint32_t>(globalTable->GetStartOfCaptureIndex(0).GetInt());
948         uint32_t endIndex = static_cast<uint32_t>(globalTable->GetEndIndex().GetInt());
949         lastIndex = endIndex;
950         if (nextPosition < startIndex) {
951             auto substr = EcmaStringAccessor::FastSubString(
952                 thread->GetEcmaVM(), inputString, nextPosition, startIndex - nextPosition);
953             resultString += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
954         }
955         nextPosition = endIndex;
956         if (!(flags & RegExpParser::FLAG_GLOBAL)) {
957             // a. Let setStatus be Set(R, "lastIndex", e, true).
958             ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
959                                                        lastIndexHandle.GetTaggedValue(),
960                                                        JSTaggedValue(lastIndex));
961             // b. ReturnIfAbrupt(setStatus).
962             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
963             break;
964         }
965         if (endIndex == startIndex) {
966             bool unicode = EcmaStringAccessor(inputString).IsUtf16() && (flags & RegExpParser::FLAG_UTF16);
967             endIndex = static_cast<uint32_t>(AdvanceStringIndex(tagInputString, endIndex, unicode));
968         }
969         lastIndex = endIndex;
970     }
971     auto substr = EcmaStringAccessor::FastSubString(
972         thread->GetEcmaVM(), inputString, nextPosition, inputLength - nextPosition);
973     resultString += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
974     return JSTaggedValue::Undefined();
975 }
976 
977 // 21.2.5.8
978 // NOLINTNEXTLINE(readability-function-size)
Replace(EcmaRuntimeCallInfo * argv)979 JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv)
980 {
981     ASSERT(argv);
982     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Replace);
983     JSThread *thread = argv->GetThread();
984     [[maybe_unused]] EcmaHandleScope handleScope(thread);
985     // 1. Let rx be the this value.
986     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
987     if (!thisObj->IsECMAObject()) {
988         // 2. If Type(rx) is not Object, throw a TypeError exception.
989         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
990     }
991     // 3. Let S be ToString(string).
992     JSHandle<JSTaggedValue> string = GetCallArg(argv, 0);
993     JSHandle<JSTaggedValue> inputReplaceValue = GetCallArg(argv, 1);
994     return ReplaceInternal(thread, thisObj, string, inputReplaceValue);
995 }
996 
ReplaceInternal(JSThread * thread,JSHandle<JSTaggedValue> thisObj,JSHandle<JSTaggedValue> string,JSHandle<JSTaggedValue> inputReplaceValue)997 JSTaggedValue BuiltinsRegExp::ReplaceInternal(JSThread *thread,
998                                               JSHandle<JSTaggedValue> thisObj,
999                                               JSHandle<JSTaggedValue> string,
1000                                               JSHandle<JSTaggedValue> inputReplaceValue)
1001 {
1002     JSHandle<EcmaString> srcString = JSTaggedValue::ToString(thread, string);
1003     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1004 
1005     // 4. ReturnIfAbrupt(S).
1006     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1007     JSHandle<JSTaggedValue> inputStr = JSHandle<JSTaggedValue>::Cast(srcString);
1008     // 5. Let lengthS be the number of code unit elements in S.
1009     uint32_t length = EcmaStringAccessor(srcString).GetLength();
1010     // 6. Let functionalReplace be IsCallable(replaceValue).
1011     bool functionalReplace = inputReplaceValue->IsCallable();
1012     JSHandle<EcmaString> replaceValueHandle;
1013     // Add cache for regexp replace
1014     bool useCache = true;
1015     if (!functionalReplace) {
1016         replaceValueHandle = JSTaggedValue::ToString(thread, inputReplaceValue);
1017         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1018     } else {
1019         useCache = false;
1020     }
1021     // 8. Let global be ToBoolean(Get(rx, "global")).
1022     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1023     bool isGlobal = false;
1024     bool fullUnicode = false;
1025     bool isFastPath = IsFastRegExp(thread, thisObj);
1026     if (isFastPath) {
1027         isGlobal = GetOriginalFlag(thread, thisObj, RegExpParser::FLAG_GLOBAL);
1028         fullUnicode = GetOriginalFlag(thread, thisObj, RegExpParser::FLAG_UTF16);
1029         if (isGlobal) {
1030             SetLastIndex(thread, thisObj, JSTaggedValue(0), isFastPath);
1031         }
1032     } else {
1033         // 9. ReturnIfAbrupt(global).
1034         useCache = false;
1035         isGlobal = GetFlag(thread, thisObj, RegExpParser::FLAG_GLOBAL, isFastPath);
1036         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1037         // 10. If global is true, then
1038         if (isGlobal) {
1039             // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")).
1040             fullUnicode = GetFlag(thread, thisObj, RegExpParser::FLAG_UTF16, isFastPath);
1041             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1042             // b. Let setStatus be Set(rx, "lastIndex", 0, true).
1043             SetLastIndex(thread, thisObj, JSTaggedValue(0), isFastPath);
1044             // c. ReturnIfAbrupt(setStatus).
1045             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1046         }
1047     }
1048 
1049     // Add cache for the intermediate result of replace
1050     bool useIntermediateCache = false;
1051     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1052     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1053     if (isFastPath) {
1054         if (isGlobal && !functionalReplace && EcmaStringAccessor(replaceValueHandle).GetLength() == 0) {
1055             return RegExpReplaceFast(thread, thisObj, srcString, length);
1056         }
1057         JSHandle<JSRegExp> regexpHandle(thisObj);
1058         useIntermediateCache = true;
1059         if (!functionalReplace) {
1060             uint32_t strLength = EcmaStringAccessor(replaceValueHandle).GetLength();
1061             uint32_t largeStrCount = cacheTable->GetLargeStrCount();
1062             if (largeStrCount != 0) {
1063                 if (strLength > MIN_REPLACE_STRING_LENGTH) {
1064                     cacheTable->SetLargeStrCount(thread, --largeStrCount);
1065                 }
1066             } else {
1067                 cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH);
1068             }
1069             if (strLength > cacheTable->GetStrLenThreshold()) {
1070                 uint32_t lastIndexInput = static_cast<uint32_t>(GetLastIndex(thread, thisObj, isFastPath));
1071                 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, string,
1072                     RegExpExecResultCache::REPLACE_TYPE, thisObj, JSTaggedValue(lastIndexInput),
1073                     inputReplaceValue);
1074                 if (!cacheResult.IsUndefined()) {
1075                     return cacheResult;
1076                 }
1077             }
1078         }
1079     }
1080 
1081     // 11. Let results be a new empty List.
1082     JSMutableHandle<JSObject> resultsList(thread, JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1083     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1084     int resultsIndex = 0;
1085     JSMutableHandle<JSTaggedValue> nextIndexHandle(thread, JSTaggedValue(0));
1086     JSMutableHandle<JSTaggedValue> execResult(thread, JSTaggedValue(0));
1087     // Add cache for the intermediate result of replace
1088     JSTaggedValue cachedResultsList(JSTaggedValue::VALUE_UNDEFINED);
1089     if (useIntermediateCache) {
1090         uint32_t lastIndexInput = static_cast<uint32_t>(GetLastIndex(thread, thisObj, isFastPath));
1091         cachedResultsList = cacheTable->FindCachedResult(thread, string,
1092             RegExpExecResultCache::INTERMEDIATE_REPLACE_TYPE, thisObj,
1093             JSTaggedValue(lastIndexInput), undefined, true);
1094     }
1095     if (!cachedResultsList.IsUndefined()) {
1096         resultsList.Update(cachedResultsList);
1097         resultsIndex = static_cast<int>(JSArray::Cast(resultsList.GetTaggedValue())->GetArrayLength());
1098     } else {
1099         // 12. Let done be false.
1100         // 13. Repeat, while done is false
1101         for (;;) {
1102             // a. Let result be RegExpExec(rx, S).
1103             execResult.Update(RegExpExec(thread, thisObj, inputStr, useCache, true));
1104             // b. ReturnIfAbrupt(result).
1105             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1106             // c. If result is null, set done to true.
1107             if (execResult->IsNull()) {
1108                 break;
1109             }
1110             // d. Else result is not null, i. Append result to the end of results.
1111             TaggedArray *srcElements = TaggedArray::Cast(resultsList->GetElements().GetTaggedObject());
1112             JSMutableHandle<TaggedArray> elements(thread, srcElements);
1113             if (resultsIndex >= static_cast<int>(elements->GetLength())) {
1114                 elements.Update(JSObject::GrowElementsCapacity(thread, resultsList, elements->GetLength(), true));
1115             }
1116             elements->Set(thread, resultsIndex, execResult);
1117             JSArray::Cast(*resultsList)->SetArrayLength(thread, resultsIndex + 1);
1118             resultsIndex++;
1119             // ii. If global is false, set done to true.
1120             if (!isGlobal) {
1121                 break;
1122             }
1123             // iii. Else, 1. Let matchStr be ToString(Get(result, "0")).
1124             JSHandle<JSTaggedValue> matchedStr = globalConst->GetHandledZeroString();
1125             JSTaggedValue getMatchVal = ObjectFastOperator::FastGetPropertyByValue(
1126                 thread, execResult.GetTaggedValue(), matchedStr.GetTaggedValue());
1127             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1128             JSHandle<JSTaggedValue> getMatch(thread, getMatchVal);
1129             JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, getMatch);
1130             // 2. ReturnIfAbrupt(matchStr).
1131             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1132             // 3. If matchStr is the empty String, then
1133             if (EcmaStringAccessor(matchString).GetLength() == 0) {
1134                 // a. Let thisIndex be ToLength(Get(rx, "lastIndex")).
1135                 uint32_t thisIndex = static_cast<uint32_t>(GetLastIndex(thread, thisObj, isFastPath));
1136                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1137                 // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
1138                 uint32_t nextIndex = static_cast<uint32_t>(AdvanceStringIndex(inputStr, thisIndex, fullUnicode));
1139                 nextIndexHandle.Update(JSTaggedValue(nextIndex));
1140                 // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true).
1141                 SetLastIndex(thread, thisObj, nextIndexHandle.GetTaggedValue(), isFastPath);
1142                 // e. ReturnIfAbrupt(setStatus).
1143                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1144             }
1145         }
1146         if (useIntermediateCache) {
1147             RegExpExecResultCache::AddResultInCache(thread, cacheTable, thisObj, string,
1148                                                     JSHandle<JSTaggedValue>(resultsList),
1149                                                     RegExpExecResultCache::INTERMEDIATE_REPLACE_TYPE, 0, 0,
1150                                                     undefined, true);
1151         }
1152     }
1153     // 14. Let accumulatedResult be the empty String value.
1154     bool isUtf8 = true;
1155     uint32_t resultStrLength = 0;
1156     uint32_t resultArrayLength = (static_cast<uint32_t>(resultsIndex) + 1) * 2;
1157     JSHandle<TaggedArray> resultArray = factory->NewTaggedArray(resultArrayLength);
1158     std::vector<uint64_t> resultLengthArray(resultArrayLength);
1159     // 15. Let nextSourcePosition be 0.
1160     uint32_t nextSourcePosition = 0;
1161     JSMutableHandle<JSTaggedValue> getMatchString(thread, JSTaggedValue::Undefined());
1162     JSMutableHandle<JSTaggedValue> resultValues(thread, JSTaggedValue(0));
1163     JSMutableHandle<JSTaggedValue> ncapturesHandle(thread, JSTaggedValue(0));
1164     JSMutableHandle<JSTaggedValue> capN(thread, JSTaggedValue(0));
1165     // 16. Repeat, for each result in results,
1166     for (int i = 0; i < resultsIndex; i++) {
1167         resultValues.Update(ElementAccessor::Get(resultsList, i));
1168         // a. Let nCaptures be ToLength(Get(result, "length")).
1169         uint32_t ncaptures;
1170         if (isFastPath) {
1171             ncaptures = static_cast<uint32_t>(JSArray::Cast(resultValues.GetTaggedValue())->GetArrayLength());
1172         } else {
1173             JSHandle<JSTaggedValue> lengthHandle = globalConst->GetHandledLengthString();
1174             ncapturesHandle.Update(ObjectFastOperator::FastGetPropertyByValue(
1175                 thread, resultValues.GetTaggedValue(), lengthHandle.GetTaggedValue()));
1176             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1177             ncaptures = JSTaggedValue::ToUint32(thread, ncapturesHandle);
1178         }
1179         // b. ReturnIfAbrupt(nCaptures).
1180         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1181         // c. Let nCaptures be max(nCaptures − 1, 0).
1182         ncaptures = (ncaptures == 0) ? 0 : ncaptures - 1;
1183         // d. Let matched be ToString(Get(result, "0")).
1184         JSTaggedValue value = ObjectFastOperator::GetPropertyByIndex(thread, resultValues.GetTaggedValue(), 0);
1185         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1186         getMatchString.Update(value);
1187         JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, getMatchString);
1188         // e. ReturnIfAbrupt(matched).
1189         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1190         // f. Let matchLength be the number of code units in matched.
1191         uint32_t matchLength = EcmaStringAccessor(matchString).GetLength();
1192         // g. Let position be ToInteger(Get(result, "index")).
1193         JSTaggedValue positionTag = GetExecResultIndex(thread, resultValues, isFastPath);
1194         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1195         JSHandle<JSTaggedValue> positionHandle(thread, positionTag);
1196         uint32_t position = 0;
1197         if (positionHandle->IsInt()) {
1198             position = static_cast<uint32_t>(positionHandle->GetInt());
1199         } else {
1200             position = JSTaggedValue::ToUint32(thread, positionHandle);
1201             // h. ReturnIfAbrupt(position).
1202             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1203         }
1204         // i. Let position be max(min(position, lengthS), 0).
1205         position = std::max<uint32_t>(std::min<uint32_t>(position, length), 0);
1206         // j. Let n be 1.
1207         uint32_t index = 1;
1208         // k. Let captures be an empty List.
1209         JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(ncaptures);
1210         // l. Repeat while n ≤ nCaptures
1211         while (index <= ncaptures) {
1212             // i. Let capN be Get(result, ToString(n)).
1213             capN.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, resultValues.GetTaggedValue(), index));
1214             // ii. ReturnIfAbrupt(capN).
1215             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1216             // iii. If capN is not undefined, then
1217             if (!capN->IsUndefined()) {
1218                 // 1. Let capN be ToString(capN).
1219                 JSHandle<EcmaString> capNStr = JSTaggedValue::ToString(thread, capN);
1220                 // 2. ReturnIfAbrupt(capN).
1221                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1222                 JSHandle<JSTaggedValue> capnStr = JSHandle<JSTaggedValue>::Cast(capNStr);
1223                 capturesList->Set(thread, index - 1, capnStr);
1224             } else {
1225                 // iv. Append capN as the last element of captures.
1226                 capturesList->Set(thread, index - 1, capN);
1227             }
1228             // v. Let n be n+1
1229             ++index;
1230         }
1231 
1232         // j. Let namedCaptures be ? Get(result, "groups").
1233         JSTaggedValue named = GetExecResultGroups(thread, resultValues, isFastPath);
1234         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1235         JSHandle<JSTaggedValue> namedCaptures(thread, named);
1236         // m. If functionalReplace is true, then
1237         JSMutableHandle<EcmaString> replacementString(thread, factory->GetEmptyString());
1238         int emptyArrLength = 0;
1239         if (namedCaptures->IsUndefined()) {
1240             emptyArrLength = 3; // 3: «matched, pos, and string»
1241         } else {
1242             emptyArrLength = 4; // 4: «matched, pos, string, and groups»
1243         }
1244         JSHandle<TaggedArray> replacerArgs =
1245             factory->NewTaggedArray(emptyArrLength + capturesList->GetLength());
1246         if (functionalReplace) {
1247             // i. Let replacerArgs be «matched».
1248             replacerArgs->Set(thread, 0, getMatchString.GetTaggedValue());
1249             // ii. Append in list order the elements of captures to the end of the List replacerArgs.
1250             // iii. Append position and S as the last two elements of replacerArgs.
1251             index = 0;
1252             while (index < capturesList->GetLength()) {
1253                 replacerArgs->Set(thread, index + 1, capturesList->Get(index));
1254                 ++index;
1255             }
1256             replacerArgs->Set(thread, index + 1, JSTaggedValue(position));
1257             replacerArgs->Set(thread, index + 2, inputStr.GetTaggedValue());  // 2: position of string
1258             if (!namedCaptures->IsUndefined()) {
1259                 replacerArgs->Set(thread, index + 3, namedCaptures.GetTaggedValue()); // 3: position of groups
1260             }
1261             // iv. Let replValue be Call(replaceValue, undefined, replacerArgs).
1262             const uint32_t argsLength = replacerArgs->GetLength();
1263             EcmaRuntimeCallInfo *info =
1264                 EcmaInterpreter::NewRuntimeCallInfo(thread, inputReplaceValue, undefined, undefined, argsLength);
1265             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1266             info->SetCallArg(argsLength, replacerArgs);
1267             JSTaggedValue replaceResult = JSFunction::Call(info);
1268             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1269             JSHandle<JSTaggedValue> replValue(thread, replaceResult);
1270             // v. Let replacement be ToString(replValue).
1271             replacementString.Update(JSTaggedValue::ToString(thread, replValue));
1272             // o. ReturnIfAbrupt(replacement).
1273             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1274         } else {
1275             // n. Else,
1276             if (!namedCaptures->IsUndefined()) {
1277                 JSHandle<JSObject> namedCapturesObj = JSTaggedValue::ToObject(thread, namedCaptures);
1278                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1279                 namedCaptures = JSHandle<JSTaggedValue>::Cast(namedCapturesObj);
1280             }
1281             replacementString.Update(BuiltinsString::GetSubstitution(thread, matchString, srcString,
1282                 position, capturesList, namedCaptures, replaceValueHandle));
1283         }
1284         // p. If position ≥ nextSourcePosition, then
1285         if (position >= nextSourcePosition) {
1286             // ii. Let accumulatedResult be the String formed by concatenating the code units of the current value
1287             // of accumulatedResult with the substring of S consisting of the code units from nextSourcePosition
1288             // (inclusive) up to position (exclusive) and with the code units of replacement.
1289             // store undefined in resultArray
1290             resultArray->Set(thread, REPLACE_RESULT_VAL * i, JSTaggedValue::Undefined());
1291             uint64_t bits = 0;
1292             bits |= ReplaceLengthField::Encode(position - nextSourcePosition);
1293             bits |= ReplacePositionField::Encode(nextSourcePosition);
1294             // store position and length bits in resultLengthArray
1295             resultLengthArray[REPLACE_RESULT_VAL * i] = bits;
1296             resultStrLength += (position - nextSourcePosition);
1297             auto subString = EcmaStringAccessor::FastSubString(
1298                 thread->GetEcmaVM(), srcString, nextSourcePosition, position - nextSourcePosition);
1299             isUtf8 &= EcmaStringAccessor(subString).IsUtf8();
1300             // store replacement string in resultArray
1301             resultArray->Set(thread, REPLACE_RESULT_VAL * i + 1, replacementString.GetTaggedValue());
1302             uint32_t replacementLength = EcmaStringAccessor(replacementString).GetLength();
1303             // store length of replacement string in resultLengthArray
1304             resultLengthArray[REPLACE_RESULT_VAL * i + 1] = static_cast<uint64_t>(replacementLength);
1305             resultStrLength += replacementLength;
1306             isUtf8 &= EcmaStringAccessor(replacementString).IsUtf8();
1307             // iii. Let nextSourcePosition be position + matchLength.
1308             nextSourcePosition = position + matchLength;
1309         }
1310     }
1311 
1312     // 17. If nextSourcePosition ≥ lengthS, return accumulatedResult.
1313     if (nextSourcePosition < length) {
1314         // store undefined in resultArray
1315         resultArray->Set(thread, REPLACE_RESULT_VAL * resultsIndex, JSTaggedValue::Undefined());
1316         uint64_t bits = 0;
1317         bits |= ReplaceLengthField::Encode(length - nextSourcePosition);
1318         bits |= ReplacePositionField::Encode(nextSourcePosition);
1319         auto subStringEnd = EcmaStringAccessor::FastSubString(
1320             thread->GetEcmaVM(), srcString, nextSourcePosition, length - nextSourcePosition);
1321         isUtf8 &= EcmaStringAccessor(subStringEnd).IsUtf8();
1322         // store position and length bits in resultLengthArray
1323         resultLengthArray[REPLACE_RESULT_VAL * resultsIndex] = bits;
1324         resultStrLength += (length - nextSourcePosition);
1325     }
1326 
1327     JSHandle<EcmaString> result =
1328         CreateStringFromResultArray(thread, resultArray, resultLengthArray, srcString, resultStrLength, isUtf8);
1329     // 18. Return the String formed by concatenating the code units of accumulatedResult with the substring of S
1330     // consisting of the code units from nextSourcePosition (inclusive) up through the final code unit of S(inclusive).
1331     if (useCache) {
1332         RegExpExecResultCache::AddResultInCache(thread, cacheTable, thisObj, string,
1333                                                 JSHandle<JSTaggedValue>(result),
1334                                                 RegExpExecResultCache::REPLACE_TYPE, 0, nextIndexHandle->GetInt(),
1335                                                 inputReplaceValue);
1336     }
1337     return result.GetTaggedValue();
1338 }
1339 
1340 // 21.2.5.9
Search(EcmaRuntimeCallInfo * argv)1341 JSTaggedValue BuiltinsRegExp::Search(EcmaRuntimeCallInfo *argv)
1342 {
1343     ASSERT(argv);
1344     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Search);
1345     JSThread *thread = argv->GetThread();
1346     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1347     // 1. Let rx be the this value.
1348     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
1349     // 3. Let S be ToString(string).
1350     JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
1351     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
1352 
1353     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1354     JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
1355     if (!thisObj->IsECMAObject()) {
1356         // 2. If Type(rx) is not Object, throw a TypeError exception.
1357         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
1358     }
1359     return RegExpSearch(thread, thisObj, string);
1360 }
1361 
RegExpSearch(JSThread * thread,const JSHandle<JSTaggedValue> regexp,const JSHandle<JSTaggedValue> string)1362 JSTaggedValue BuiltinsRegExp::RegExpSearch(JSThread *thread,
1363                                            const JSHandle<JSTaggedValue> regexp,
1364                                            const JSHandle<JSTaggedValue> string)
1365 {
1366     bool isFastPath = IsFastRegExp(thread, regexp);
1367     if (isFastPath) {
1368         return RegExpSearchFast(thread, regexp, string);
1369     }
1370     // 4. Let previousLastIndex be ? Get(rx, "lastIndex").
1371     JSHandle<JSTaggedValue> lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString());
1372     JSHandle<JSTaggedValue> previousLastIndex = JSObject::GetProperty(thread, regexp, lastIndexString).GetValue();
1373     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1374     // 5. If SameValue(previousLastIndex, 0) is false, then
1375     // Perform ? Set(rx, "lastIndex", 0, true).
1376     if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), JSTaggedValue(0))) {
1377         JSHandle<JSTaggedValue> value(thread, JSTaggedValue(0));
1378         JSObject::SetProperty(thread, regexp, lastIndexString, value, true);
1379         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1380     }
1381     // 6. Let result be ? RegExpExec(rx, S).
1382     JSHandle<JSTaggedValue> result(thread, RegExpExec(thread, regexp, string, false));
1383     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1384     // 7. Let currentLastIndex be ? Get(rx, "lastIndex").
1385     JSHandle<JSTaggedValue> currentLastIndex = JSObject::GetProperty(thread, regexp, lastIndexString).GetValue();
1386     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1387     // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
1388     // Perform ? Set(rx, "lastIndex", previousLastIndex, true).
1389     if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), currentLastIndex.GetTaggedValue())) {
1390         JSObject::SetProperty(thread, regexp, lastIndexString, previousLastIndex, true);
1391         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1392     }
1393     // 9. If result is null, return -1.
1394     if (result->IsNull()) {
1395         return JSTaggedValue(-1);
1396     }
1397     // 10. Return ? Get(result, "index").
1398     JSHandle<JSTaggedValue> index(thread->GlobalConstants()->GetHandledIndexString());
1399     return JSObject::GetProperty(thread, result, index).GetValue().GetTaggedValue();
1400 }
1401 
RegExpSearchFast(JSThread * thread,const JSHandle<JSTaggedValue> regexp,const JSHandle<JSTaggedValue> string)1402 JSTaggedValue BuiltinsRegExp::RegExpSearchFast(JSThread *thread,
1403                                                const JSHandle<JSTaggedValue> regexp,
1404                                                const JSHandle<JSTaggedValue> string)
1405 {
1406     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1407     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1408 
1409     JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, string,
1410                                                              RegExpExecResultCache::SEARCH_TYPE, regexp,
1411                                                              JSTaggedValue(0), undefined);
1412     if (!cacheResult.IsUndefined()) {
1413         return cacheResult;
1414     }
1415     JSHandle<EcmaString> stringHandle = JSHandle<EcmaString>::Cast(string);
1416     bool matchResult = RegExpExecInternal(thread, regexp, stringHandle, 0);
1417     if (!matchResult) {
1418         RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, string,
1419                                                 JSHandle<JSTaggedValue>(thread, JSTaggedValue(-1)),
1420                                                 RegExpExecResultCache::SEARCH_TYPE,
1421                                                 0, 0, undefined);
1422         return JSTaggedValue(-1);
1423     }
1424     JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
1425     JSTaggedValue result = globalTable->GetStartOfCaptureIndex(0);
1426     RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, string,
1427                                             JSHandle<JSTaggedValue>(thread, JSTaggedValue(result)),
1428                                             RegExpExecResultCache::SEARCH_TYPE,
1429                                             0, 0, undefined);
1430     return result;
1431 }
1432 
RegExpSplit(JSThread * thread,const JSHandle<JSTaggedValue> regexp,JSHandle<JSTaggedValue> jsString,JSHandle<JSTaggedValue> limit,bool isFastPath)1433 JSTaggedValue BuiltinsRegExp::RegExpSplit(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1434                                           JSHandle<JSTaggedValue> jsString, JSHandle<JSTaggedValue> limit,
1435                                           bool isFastPath)
1436 {
1437     bool useCache = false;
1438     if (isFastPath) {
1439         if (limit->IsUndefined()) {
1440             useCache = true;
1441             return RegExpSplitFast(thread, regexp, jsString, MAX_SPLIT_LIMIT, useCache);
1442         } else if (limit->IsInt()) {
1443             int64_t lim = limit->GetInt();
1444             if (lim >= 0) {
1445                 return RegExpSplitFast(thread, regexp, jsString, static_cast<uint32_t>(lim), useCache);
1446             }
1447         }
1448     }
1449     auto ecmaVm = thread->GetEcmaVM();
1450     // 5. Let C be SpeciesConstructor(rx, %RegExp%).
1451     JSHandle<JSTaggedValue> defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction();
1452     JSHandle<JSObject> objHandle(regexp);
1453     JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
1454     // 6. ReturnIfAbrupt(C).
1455     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1456     // 7. Let flags be ToString(Get(rx, "flags")).
1457     ObjectFactory *factory = ecmaVm->GetFactory();
1458     const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
1459     JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
1460     JSHandle<JSTaggedValue> taggedFlags = JSObject::GetProperty(thread, regexp, flagsString).GetValue();
1461     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1462     JSHandle<EcmaString> flags;
1463 
1464     if (taggedFlags->IsUndefined()) {
1465         flags = factory->GetEmptyString();
1466     } else {
1467         flags = JSTaggedValue::ToString(thread, taggedFlags);
1468     }
1469     //  8. ReturnIfAbrupt(flags).
1470     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1471     // 9. If flags contains "u", let unicodeMatching be true.
1472     // 10. Else, let unicodeMatching be false.
1473     JSHandle<EcmaString> uStringHandle(globalConstants->GetHandledUString());
1474     bool unicodeMatching = (EcmaStringAccessor::IndexOf(ecmaVm, flags, uStringHandle) != -1);
1475     // 11. If flags contains "y", let newFlags be flags.
1476     JSHandle<EcmaString> newFlagsHandle;
1477     JSHandle<EcmaString> yStringHandle = JSHandle<EcmaString>::Cast(globalConstants->GetHandledYString());
1478     if (EcmaStringAccessor::IndexOf(ecmaVm, flags, yStringHandle) != -1) {
1479         newFlagsHandle = flags;
1480     } else {
1481         // 12. Else, let newFlags be the string that is the concatenation of flags and "y".
1482         JSHandle<EcmaString> yStr = JSHandle<EcmaString>::Cast(globalConstants->GetHandledYString());
1483         newFlagsHandle = factory->ConcatFromString(flags, yStr);
1484     }
1485 
1486     // 13. Let splitter be Construct(C, «rx, newFlags»).
1487     JSHandle<JSObject> globalObject(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject());
1488     JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
1489     EcmaRuntimeCallInfo *runtimeInfo =
1490         EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 2); // 2: two args
1491     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1492     runtimeInfo->SetCallArg(regexp.GetTaggedValue(), newFlagsHandle.GetTaggedValue());
1493     JSTaggedValue taggedSplitter = JSFunction::Construct(runtimeInfo);
1494     // 14. ReturnIfAbrupt(splitter).
1495     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1496 
1497     JSHandle<JSTaggedValue> splitter(thread, taggedSplitter);
1498     // 15. Let A be ArrayCreate(0).
1499     JSHandle<JSObject> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1500     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1501     // 16. Let lengthA be 0.
1502     uint32_t aLength = 0;
1503 
1504     // 17. If limit is undefined, let lim be 2^32–1; else let lim be ToUint32(limit).
1505     uint32_t lim;
1506     if (limit->IsUndefined()) {
1507         lim = MAX_SPLIT_LIMIT;
1508     } else {
1509         lim = JSTaggedValue::ToUint32(thread, limit);
1510         // 18. ReturnIfAbrupt(lim).
1511         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1512     }
1513 
1514     // 19. Let size be the number of elements in S.
1515     uint32_t size = EcmaStringAccessor(jsString->GetTaggedObject()).GetLength();
1516     // 20. Let p be 0.
1517     uint32_t startIndex = 0;
1518     // 21. If lim = 0, return A.
1519     if (lim == 0) {
1520         return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1521     }
1522     // 22. If size = 0, then
1523     if (size == 0) {
1524         // a. Let z be RegExpExec(splitter, S).
1525         JSHandle<JSTaggedValue> execResult(thread, RegExpExec(thread, splitter, jsString, useCache));
1526         // b. ReturnIfAbrupt(z).
1527         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1528         // c. If z is not null, return A.
1529         if (!execResult->IsNull()) {
1530             return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1531         }
1532         // d. Assert: The following call will never result in an abrupt completion.
1533         // e. Perform CreateDataProperty(A, "0", S).
1534         JSObject::CreateDataProperty(thread, array, 0, jsString);
1535         // f. Return A.
1536         return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1537     }
1538     // 23. Let q be p.
1539     uint32_t endIndex = startIndex;
1540     JSMutableHandle<JSTaggedValue> lastIndexvalue(thread, JSTaggedValue(endIndex));
1541     // 24. Repeat, while q < size
1542     JSHandle<JSTaggedValue> lastIndexString = globalConstants->GetHandledLastIndexString();
1543     while (endIndex < size) {
1544         // a. Let setStatus be Set(splitter, "lastIndex", q, true).
1545         lastIndexvalue.Update(JSTaggedValue(endIndex));
1546         JSObject::SetProperty(thread, splitter, lastIndexString, lastIndexvalue, true);
1547         // b. ReturnIfAbrupt(setStatus).
1548         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1549         JSHandle<JSTaggedValue> execResult(thread, RegExpExec(thread, splitter, jsString, useCache));
1550         // d. ReturnIfAbrupt(z).
1551         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1552         // e. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching).
1553         if (execResult->IsNull()) {
1554             endIndex = static_cast<uint32_t>(AdvanceStringIndex(jsString, endIndex, unicodeMatching));
1555         } else {
1556             // f. Else z is not null,
1557             // i. Let e be ToLength(Get(splitter, "lastIndex")).
1558             JSHandle<JSTaggedValue> lastIndexHandle =
1559                 JSObject::GetProperty(thread, splitter, lastIndexString).GetValue();
1560             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1561             JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexHandle);
1562             // ii. ReturnIfAbrupt(e).
1563             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1564             uint32_t lastIndex = lastIndexNumber.GetNumber();
1565             // iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching).
1566             if (lastIndex == startIndex) {
1567                 endIndex = static_cast<uint32_t>(AdvanceStringIndex(jsString, endIndex, unicodeMatching));
1568             } else {
1569                 // iv. Else e != p,
1570                 // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p
1571                 // (inclusive) through q (exclusive).
1572                 auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1573                     JSHandle<EcmaString>::Cast(jsString), startIndex, endIndex - startIndex);
1574                 std::string stdStrT = EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1575                 // 2. Assert: The following call will never result in an abrupt completion.
1576                 // 3. Perform CreateDataProperty(A, ToString(lengthA), T).
1577                 JSHandle<JSTaggedValue> tValue(factory->NewFromStdString(stdStrT));
1578                 JSObject::CreateDataProperty(thread, array, aLength, tValue);
1579                 // 4. Let lengthA be lengthA +1.
1580                 ++aLength;
1581                 // 5. If lengthA = lim, return A.
1582                 if (aLength == lim) {
1583                     return array.GetTaggedValue();
1584                 }
1585                 // 6. Let p be e.
1586                 startIndex = lastIndex;
1587                 // 7. Let numberOfCaptures be ToLength(Get(z, "length")).
1588                 JSHandle<JSTaggedValue> lengthString(thread->GlobalConstants()->GetHandledLengthString());
1589                 JSHandle<JSTaggedValue> capturesHandle =
1590                     JSObject::GetProperty(thread, execResult, lengthString).GetValue();
1591                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1592                 JSTaggedNumber numberOfCapturesNumber = JSTaggedValue::ToLength(thread, capturesHandle);
1593                 // 8. ReturnIfAbrupt(numberOfCaptures).
1594                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1595                 uint32_t numberOfCaptures = numberOfCapturesNumber.GetNumber();
1596                 // 9. Let numberOfCaptures be max(numberOfCaptures-1, 0).
1597                 numberOfCaptures = (numberOfCaptures == 0) ? 0 : numberOfCaptures - 1;
1598                 // 10. Let i be 1.
1599                 uint32_t i = 1;
1600                 // 11. Repeat, while i ≤ numberOfCaptures.
1601                 while (i <= numberOfCaptures) {
1602                     // a. Let nextCapture be Get(z, ToString(i)).
1603                     JSHandle<JSTaggedValue> nextCapture = JSObject::GetProperty(thread, execResult, i).GetValue();
1604                     // b. ReturnIfAbrupt(nextCapture).
1605                     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1606                     // c. Perform CreateDataProperty(A, ToString(lengthA), nextCapture).
1607                     JSObject::CreateDataProperty(thread, array, aLength, nextCapture);
1608                     // d. Let i be i + 1.
1609                     ++i;
1610                     // e. Let lengthA be lengthA +1.
1611                     ++aLength;
1612                     // f. If lengthA = lim, return A.
1613                     if (aLength == lim) {
1614                         return array.GetTaggedValue();
1615                     }
1616                 }
1617                 // 12. Let q be p.
1618                 endIndex = startIndex;
1619             }
1620         }
1621     }
1622     // 25. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive)
1623     // through size (exclusive).
1624     auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1625         JSHandle<EcmaString>::Cast(jsString), startIndex, size - startIndex);
1626     std::string stdStrT = EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1627     // 26. Assert: The following call will never result in an abrupt completion.
1628     // 27. Perform CreateDataProperty(A, ToString(lengthA), t).
1629     JSHandle<JSTaggedValue> tValue(factory->NewFromStdString(stdStrT));
1630     JSObject::CreateDataProperty(thread, array, aLength, tValue);
1631     // 28. Return A.
1632     return array.GetTaggedValue();
1633 }
1634 // 21.2.5.11
1635 // NOLINTNEXTLINE(readability-function-size)
Split(EcmaRuntimeCallInfo * argv)1636 JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv)
1637 {
1638     ASSERT(argv);
1639     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Split);
1640     JSThread *thread = argv->GetThread();
1641     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1642     // 1. Let rx be the this value.
1643     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
1644     // 3. Let S be ToString(string).
1645     JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
1646     JSHandle<JSTaggedValue> limit = GetCallArg(argv, 1);
1647     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
1648 
1649     // 4. ReturnIfAbrupt(string).
1650     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1651     JSHandle<JSTaggedValue> jsString = JSHandle<JSTaggedValue>::Cast(stringHandle);
1652     if (!thisObj->IsECMAObject()) {
1653         // 2. If Type(rx) is not Object, throw a TypeError exception.
1654         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
1655     }
1656     bool isFastPath = IsFastRegExp(thread, thisObj);
1657     return RegExpSplit(thread, thisObj, jsString, limit, isFastPath);
1658 }
1659 
RegExpSplitFast(JSThread * thread,const JSHandle<JSTaggedValue> regexp,JSHandle<JSTaggedValue> jsString,uint32_t limit,bool useCache)1660 JSTaggedValue BuiltinsRegExp::RegExpSplitFast(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1661                                               JSHandle<JSTaggedValue> jsString, uint32_t limit, bool useCache)
1662 {
1663     if (limit == 0) {
1664         return JSArray::ArrayCreate(thread, JSTaggedNumber(0), ArrayMode::LITERAL).GetTaggedValue();
1665     }
1666     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1667     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1668     if (useCache) {
1669         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, jsString,
1670                                                                  RegExpExecResultCache::SPLIT_TYPE, regexp,
1671                                                                  JSTaggedValue(0), undefined);
1672         if (!cacheResult.IsUndefined()) {
1673             return cacheResult;
1674         }
1675     }
1676     uint32_t size = EcmaStringAccessor(jsString->GetTaggedObject()).GetLength();
1677     JSHandle<EcmaString> string = JSHandle<EcmaString>::Cast(jsString);
1678 
1679     if (size == 0) {
1680         bool matchResult = RegExpExecInternal(thread, regexp, string, 0); // 0: lastIndex
1681         if (matchResult) {
1682             JSHandle<JSTaggedValue> res = JSArray::ArrayCreate(thread, JSTaggedNumber(0), ArrayMode::LITERAL);
1683             if (useCache) {
1684                 RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1685                                                         res, RegExpExecResultCache::SPLIT_TYPE, 0, 0, undefined);
1686             }
1687             return res.GetTaggedValue();
1688         }
1689         JSHandle<TaggedArray> element = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); // 1: length
1690         element->Set(thread, 0, jsString);
1691         JSHandle<JSTaggedValue> res = JSHandle<JSTaggedValue>::Cast(JSArray::CreateArrayFromList(thread, element));
1692         if (useCache) {
1693             RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1694                                                     res, RegExpExecResultCache::SPLIT_TYPE, 0, 0, undefined);
1695         }
1696         return res.GetTaggedValue();
1697     }
1698 
1699     bool isUnicode = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_UTF16);
1700     bool isSticky = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_STICKY);
1701 
1702     uint32_t nextMatchFrom = 0;
1703     uint32_t lastMatchEnd = 0;
1704     uint32_t arrLen = 1; // at least one result string
1705     JSHandle<JSArray> splitArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1), ArrayMode::LITERAL));
1706     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1707     TaggedArray *srcElements = TaggedArray::Cast(splitArray->GetElements().GetTaggedObject());
1708     JSMutableHandle<TaggedArray> elements(thread, srcElements);
1709     JSMutableHandle<JSTaggedValue> matchValue(thread, JSTaggedValue::Undefined());
1710     while (nextMatchFrom < size) {
1711         bool matchResult = RegExpExecInternal(thread, regexp, string, nextMatchFrom);
1712         if (!matchResult) {
1713             if (!isSticky) {
1714                 // done match
1715                 break;
1716             }
1717             nextMatchFrom = static_cast<uint32_t>(AdvanceStringIndex(jsString, nextMatchFrom, isUnicode));
1718             continue;
1719         }
1720         // find match result
1721         JSHandle<RegExpGlobalResult> matchResultInfo(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
1722         uint32_t matchStartIndex = static_cast<uint32_t>(matchResultInfo->GetStartOfCaptureIndex(0).GetInt());
1723         uint32_t matchEndIndex = static_cast<uint32_t>(matchResultInfo->GetEndOfCaptureIndex(0).GetInt());
1724         if (matchEndIndex == lastMatchEnd && matchEndIndex == nextMatchFrom) {
1725             // advance index and continue if match result is empty.
1726             nextMatchFrom = static_cast<uint32_t>(AdvanceStringIndex(jsString, nextMatchFrom, isUnicode));
1727         } else {
1728             matchValue.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1729                 string, lastMatchEnd, matchStartIndex - lastMatchEnd)));
1730             if (arrLen > elements->GetLength()) {
1731                 elements.Update(JSObject::GrowElementsCapacity(thread,
1732                     JSHandle<JSObject>::Cast(splitArray), elements->GetLength(), true));
1733             }
1734             elements->Set(thread, arrLen - 1, matchValue);
1735             splitArray->SetArrayLength(thread, arrLen);
1736             if (arrLen == limit) {
1737                 if (useCache) {
1738                     RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1739                                                             JSHandle<JSTaggedValue>(splitArray),
1740                                                             RegExpExecResultCache::SPLIT_TYPE, 0, 0, undefined);
1741                 }
1742                 return JSTaggedValue(splitArray.GetTaggedValue().GetTaggedObject());
1743             }
1744             arrLen++;
1745             uint32_t capturesSize = static_cast<uint32_t>(matchResultInfo->GetTotalCaptureCounts().GetInt());
1746             uint32_t captureIndex = 1;
1747             while (captureIndex < capturesSize) {
1748                 uint32_t captureStartIndex = static_cast<uint32_t>(
1749                     matchResultInfo->GetStartOfCaptureIndex(captureIndex).GetInt());
1750                 uint32_t captureEndIndex = static_cast<uint32_t>(
1751                     matchResultInfo->GetEndOfCaptureIndex(captureIndex).GetInt());
1752                 int32_t subStrLen = static_cast<int32_t>(captureEndIndex - captureStartIndex);
1753                 if (subStrLen < 0) {
1754                     matchValue.Update(JSTaggedValue::Undefined());
1755                 } else {
1756                     matchValue.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1757                         string, captureStartIndex, subStrLen)));
1758                 }
1759                 if (arrLen > elements->GetLength()) {
1760                     elements.Update(JSObject::GrowElementsCapacity(thread,
1761                         JSHandle<JSObject>::Cast(splitArray), arrLen, true));
1762                 }
1763                 elements->Set(thread, arrLen - 1, matchValue);
1764                 splitArray->SetArrayLength(thread, arrLen);
1765                 if (arrLen == limit) {
1766                     if (useCache) {
1767                         RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1768                                                                 JSHandle<JSTaggedValue>(splitArray),
1769                                                                 RegExpExecResultCache::SPLIT_TYPE, 0, 0, undefined);
1770                     }
1771                     return JSTaggedValue(splitArray.GetTaggedValue().GetTaggedObject());
1772                 }
1773                 arrLen++;
1774                 captureIndex++;
1775             }
1776             lastMatchEnd = matchEndIndex;
1777             nextMatchFrom = matchEndIndex;
1778         }
1779     }
1780     matchValue.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1781         JSHandle<EcmaString>::Cast(jsString), lastMatchEnd, size - lastMatchEnd)));
1782     if (arrLen > elements->GetLength()) {
1783         elements.Update(JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(splitArray), arrLen, true));
1784     }
1785     elements->Set(thread, arrLen - 1, matchValue);
1786     splitArray->SetArrayLength(thread, arrLen);
1787     if (limit == MAX_SPLIT_LIMIT) {
1788         RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1789                                                 JSHandle<JSTaggedValue>(splitArray), RegExpExecResultCache::SPLIT_TYPE,
1790                                                 0, 0, undefined);
1791     }
1792     return JSTaggedValue(splitArray.GetTaggedValue().GetTaggedObject());
1793 }
1794 
RegExpExecInternal(JSThread * thread,const JSHandle<JSTaggedValue> regexp,JSHandle<EcmaString> inputString,int32_t lastIndex)1795 bool BuiltinsRegExp::RegExpExecInternal(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1796                                         JSHandle<EcmaString> inputString, int32_t lastIndex)
1797 {
1798     size_t stringLength = EcmaStringAccessor(inputString).GetLength();
1799     bool isUtf16 = EcmaStringAccessor(inputString).IsUtf16();
1800     FlatStringInfo flatStrInfo = EcmaStringAccessor::FlattenAllString(thread->GetEcmaVM(), inputString);
1801     if (EcmaStringAccessor(inputString).IsTreeString()) { // use flattenedString as srcString
1802         inputString = JSHandle<EcmaString>(thread, flatStrInfo.GetString());
1803     }
1804     const uint8_t *strBuffer;
1805     if (isUtf16) {
1806         strBuffer = reinterpret_cast<const uint8_t *>(flatStrInfo.GetDataUtf16());
1807     } else {
1808         strBuffer = flatStrInfo.GetDataUtf8();
1809     }
1810     bool isSuccess = false;
1811     JSTaggedValue regexpSource = JSRegExp::Cast(regexp->GetTaggedObject())->GetOriginalSource();
1812     uint32_t regexpLength = EcmaStringAccessor(regexpSource).GetLength();
1813     if (UNLIKELY(regexpLength > MIN_REGEXP_PATTERN_LENGTH_EXECUTE_WITH_OFFHEAP_STRING && stringLength > 0)) {
1814         size_t utf8Len = LineEcmaString::DataSize(flatStrInfo.GetString());
1815         ASSERT(utf8Len > 0);
1816         uint8_t *offHeapString = new uint8_t[utf8Len];
1817         if (memcpy_s(offHeapString, utf8Len, strBuffer, utf8Len) != EOK) {
1818             LOG_FULL(FATAL) << "memcpy_s failed";
1819             UNREACHABLE();
1820         }
1821         isSuccess = Matcher(thread, regexp, offHeapString, stringLength, lastIndex, isUtf16,
1822             StringSource::OFFHEAP_STRING);
1823         delete[] offHeapString;
1824     } else {
1825         isSuccess = Matcher(thread, regexp, strBuffer, stringLength, lastIndex, isUtf16, StringSource::ONHEAP_STRING);
1826     }
1827     if (isSuccess) {
1828         JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
1829         globalTable->ResetDollar(thread);
1830         globalTable->SetInputString(thread, inputString.GetTaggedValue());
1831     }
1832     return isSuccess;
1833 }
1834 
1835 // NOLINTNEXTLINE(readability-non-const-parameter)
Matcher(JSThread * thread,const JSHandle<JSTaggedValue> regexp,const uint8_t * buffer,size_t length,int32_t lastIndex,bool isUtf16,StringSource source)1836 bool BuiltinsRegExp::Matcher(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1837                              const uint8_t *buffer, size_t length, int32_t lastIndex,
1838                              bool isUtf16, StringSource source)
1839 {
1840     BUILTINS_API_TRACE(thread, RegExp, Matcher);
1841     // get bytecode
1842     JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
1843     void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
1844     auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
1845     // execute
1846     RegExpCachedChunk chunk(thread);
1847     RegExpExecutor executor(&chunk);
1848     if (lastIndex < 0) {
1849         lastIndex = 0;
1850     }
1851     bool ret = false;
1852     if (UNLIKELY(source == StringSource::OFFHEAP_STRING)) {
1853 #ifndef NDEBUG
1854         SharedHeap::GetInstance()->PostGCTaskForTest<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
1855 #endif
1856         ThreadNativeScope scope(thread);
1857         ret = executor.Execute(buffer, lastIndex, static_cast<uint32_t>(length), bytecodeBuffer, isUtf16);
1858     } else {
1859         ret = executor.Execute(buffer, lastIndex, static_cast<uint32_t>(length), bytecodeBuffer, isUtf16);
1860     }
1861     if (ret) {
1862         executor.GetResult(thread);
1863     }
1864     return ret;
1865 }
1866 
AdvanceStringIndex(const JSHandle<JSTaggedValue> & inputStr,int64_t index,bool unicode)1867 int64_t BuiltinsRegExp::AdvanceStringIndex(const JSHandle<JSTaggedValue> &inputStr, int64_t index,
1868                                            bool unicode)
1869 {
1870     // 1. Assert: Type(S) is String.
1871     ASSERT(inputStr->IsString());
1872     // 2. Assert: index is an integer such that 0≤index≤2^53 - 1
1873     ASSERT(0 <= index && index <= pow(2, 53) - 1);
1874     // 3. Assert: Type(unicode) is Boolean.
1875     // 4. If unicode is false, return index+1.
1876     if (!unicode) {
1877         return index + 1;
1878     }
1879     // 5. Let length be the number of code units in S.
1880     uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
1881     // 6. If index+1 ≥ length, return index+1.
1882     if (index + 1 >= length) {
1883         return index + 1;
1884     }
1885     // 7. Let first be the code unit value at index index in S.
1886     uint16_t first = EcmaStringAccessor(inputStr->GetTaggedObject()).Get(index);
1887     // 8. If first < 0xD800 or first > 0xDFFF, return index+1.
1888     if (first < 0xD800 || first > 0xDFFF) {  // NOLINT(readability-magic-numbers)
1889         return index + 1;
1890     }
1891     // 9. Let second be the code unit value at index index+1 in S.
1892     uint16_t second = EcmaStringAccessor(inputStr->GetTaggedObject()).Get(index + 1);
1893     // 10. If second < 0xDC00 or second > 0xDFFF, return index+1.
1894     if (second < 0xDC00 || second > 0xDFFF) {  // NOLINT(readability-magic-numbers)
1895         return index + 1;
1896     }
1897     // 11. Return index + 2.
1898     return index + 2;
1899 }
1900 
GetFlagsInternal(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & constructor,const uint8_t mask)1901 JSTaggedValue BuiltinsRegExp::GetFlagsInternal(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
1902                                                const JSHandle<JSTaggedValue> &constructor, const uint8_t mask)
1903 {
1904     BUILTINS_API_TRACE(thread, RegExp, GetFlagsInternal);
1905     // 1. Let R be the this value.
1906     // 2. If Type(R) is not Object, throw a TypeError exception.
1907     if (!obj->IsECMAObject()) {
1908         // throw a TypeError exception.
1909         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue(false));
1910     }
1911     // 3. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception.
1912     JSHandle<JSObject> patternObj = JSHandle<JSObject>::Cast(obj);
1913     if (!patternObj->IsJSRegExp()) {
1914         // a. If SameValue(R, %RegExp.prototype%) is true, return undefined.
1915         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1916         JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
1917         JSHandle<JSTaggedValue> objConstructor = JSTaggedValue::GetProperty(thread, obj, constructorKey).GetValue();
1918         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(false));
1919         if (objConstructor->IsJSFunction() && constructor->IsJSFunction()) {
1920             JSHandle<GlobalEnv> objRealm = JSObject::GetFunctionRealm(thread, objConstructor);
1921             JSHandle<GlobalEnv> ctorRealm = JSObject::GetFunctionRealm(thread, constructor);
1922             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1923             if (objRealm->GetRegExpPrototype() == obj && *objRealm == *ctorRealm) {
1924                 return JSTaggedValue::Undefined();
1925             }
1926         }
1927         // b. throw a TypeError exception.
1928         THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalFlags]]", JSTaggedValue(false));
1929     }
1930     // 4. Let flags be the value of R’s [[OriginalFlags]] internal slot.
1931     JSHandle<JSRegExp> regexpObj(thread, JSRegExp::Cast(obj->GetTaggedObject()));
1932     // 5. If flags contains the code unit "[flag]", return true.
1933     // 6. Return false.
1934     uint8_t flags = static_cast<uint8_t>(regexpObj->GetOriginalFlags().GetInt());
1935     return GetTaggedBoolean(flags & mask);
1936 }
1937 
1938 // 22.2.7.8
MakeMatchIndicesIndexPairArray(JSThread * thread,const std::vector<std::pair<JSTaggedValue,JSTaggedValue>> & indices,const std::vector<JSHandle<JSTaggedValue>> & groupNames,bool hasGroups)1939 JSHandle<JSTaggedValue> BuiltinsRegExp::MakeMatchIndicesIndexPairArray(JSThread *thread,
1940     const std::vector<std::pair<JSTaggedValue, JSTaggedValue>>& indices,
1941     const std::vector<JSHandle<JSTaggedValue>>& groupNames, bool hasGroups)
1942 {
1943     // 1. Let n be the number of elements in indices.
1944     uint32_t n = indices.size();
1945     // Assert: groupNames has n - 1 elements.
1946     ASSERT(groupNames.size() == n - 1);
1947     // 5. Let A be ! ArrayCreate(n).
1948     JSHandle<JSObject> results(JSArray::ArrayCreate(thread, JSTaggedNumber(n)));
1949     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
1950     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1951     // 6. If hasGroups is true, then
1952     //    a. Let groups be OrdinaryObjectCreate(null).
1953     // 7. Else,
1954     //    a. Let groups be undefined.
1955     JSMutableHandle<JSTaggedValue> groups(thread, JSTaggedValue::Undefined());
1956     if (hasGroups) {
1957         JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
1958         JSHandle<JSObject> nullObj = factory->OrdinaryNewJSObjectCreate(nullHandle);
1959         groups.Update(nullObj.GetTaggedValue());
1960     }
1961     // 8. Perform ! CreateDataPropertyOrThrow(A, "groups", groups).
1962     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1963     JSHandle<JSTaggedValue> groupsKey = globalConst->GetHandledGroupsString();
1964     JSObject::CreateDataProperty(thread, results, groupsKey, groups);
1965     // 9. For each integer i such that 0 ≤ i < n, in ascending order, do
1966     //    a. Let matchIndices be indices[i].
1967     //    b. If matchIndices is not undefined, then
1968     //        i. Let matchIndexPair be GetMatchIndexPair(S, matchIndices).
1969     //    c. Else,
1970     //        i. Let matchIndexPair be undefined.
1971     //    d. Perform ! CreateDataPropertyOrThrow(A, ! ToString(��(i)), matchIndexPair).
1972     //    e. If i > 0 and groupNames[i - 1] is not undefined, then
1973     //        i. Assert: groups is not undefined.
1974     //        ii. Perform ! CreateDataPropertyOrThrow(groups, groupNames[i - 1], matchIndexPair).
1975     JSMutableHandle<JSTaggedValue> matchIndexPair(thread, JSTaggedValue::Undefined());
1976     for (uint32_t i = 0; i < n; i++) {
1977         std::pair<JSTaggedValue, JSTaggedValue> matchIndices = indices[i];
1978         if (!matchIndices.first.IsUndefined()) {
1979             JSHandle<TaggedArray> match = factory->NewTaggedArray(2); // 2 means the length of array
1980             match->Set(thread, 0, matchIndices.first);
1981             match->Set(thread, 1, matchIndices.second);
1982             JSHandle<JSTaggedValue> pair(JSArray::CreateArrayFromList(thread, JSHandle<TaggedArray>::Cast(match)));
1983             matchIndexPair.Update(pair.GetTaggedValue());
1984         } else {
1985             matchIndexPair.Update(JSTaggedValue::Undefined());
1986         }
1987         JSObject::CreateDataProperty(thread, results, i, matchIndexPair);
1988         if (i > 0) {
1989             JSHandle<JSTaggedValue> groupName = groupNames[i - 1];
1990             if (!groupName->IsUndefined()) {
1991                 JSHandle<JSObject> groupObject = JSHandle<JSObject>::Cast(groups);
1992                 JSObject::CreateDataProperty(thread, groupObject, groupName, matchIndexPair);
1993             }
1994         }
1995     }
1996     // 10. Return A.
1997     return JSHandle<JSTaggedValue>::Cast(results);
1998 }
1999 
2000 // 21.2.5.2.2
RegExpBuiltinExec(JSThread * thread,const JSHandle<JSTaggedValue> regexp,const JSHandle<JSTaggedValue> inputStr,bool isFastPath,bool useCache,bool isIntermediateResult)2001 JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
2002                                                 const JSHandle<JSTaggedValue> inputStr,
2003                                                 bool isFastPath, bool useCache, bool isIntermediateResult)
2004 {
2005     ASSERT(regexp->IsJSRegExp());
2006     ASSERT(inputStr->IsString());
2007     BUILTINS_API_TRACE(thread, RegExp, RegExpBuiltinExec);
2008     uint32_t lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, isFastPath));
2009     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2010     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
2011     JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
2012     if (useCache) {
2013         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, inputStr,
2014                                                                  RegExpExecResultCache::EXEC_TYPE, regexp,
2015                                                                  JSTaggedValue(lastIndex), undefined);
2016         if (!cacheResult.IsUndefined()) {
2017             return cacheResult;
2018         }
2019     }
2020     JSTaggedValue result = RegExpBuiltinExecWithoutResult(thread, regexp, inputStr, isFastPath, lastIndex, useCache);
2021     if (result.IsNull()) {
2022         return result;
2023     }
2024     JSHandle<EcmaString> inputString = JSHandle<EcmaString>::Cast(inputStr);
2025     JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
2026     uint32_t capturesSize = static_cast<uint32_t>(globalTable->GetTotalCaptureCounts().GetInt());
2027     JSHandle<JSObject> results(JSArray::ArrayCreate(thread, JSTaggedNumber(capturesSize), ArrayMode::LITERAL));
2028     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2029     auto globalConst = thread->GlobalConstants();
2030     JSHandle<JSTaggedValue> indexValue(thread, globalTable->GetStartOfCaptureIndex(0));
2031     if (isIntermediateResult) {
2032         // inlined intermediate result
2033         results->SetPropertyInlinedPropsWithRep(thread, EXEC_RESULT_INDEX_OFFSET, indexValue.GetTaggedValue());
2034         results->SetPropertyInlinedPropsWithRep(thread, EXEC_RESULT_INPUT_OFFSET, inputStr.GetTaggedValue());
2035     } else {
2036         // 24. Perform CreateDataProperty(A, "index", matchIndex).
2037         JSHandle<JSTaggedValue> indexKey = globalConst->GetHandledIndexString();
2038         JSObject::CreateDataProperty(thread, results, indexKey, indexValue);
2039         // 25. Perform CreateDataProperty(A, "input", S).
2040         JSHandle<JSTaggedValue> inputKey = globalConst->GetHandledInputString();
2041         JSObject::CreateDataProperty(thread, results, inputKey, inputStr);
2042     }
2043 
2044     // 27. Perform CreateDataProperty(A, "0", matched_substr).
2045     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2046     JSHandle<TaggedArray> resultElements = factory->NewTaggedArray(capturesSize);
2047     uint32_t startIndex = static_cast<uint32_t>(globalTable->GetStartOfCaptureIndex(0).GetInt());
2048     uint32_t len = static_cast<uint32_t>(globalTable->GetEndOfCaptureIndex(0).GetInt()) - startIndex;
2049     JSHandle<JSTaggedValue> zeroValue(thread, JSTaggedValue(EcmaStringAccessor::FastSubString(
2050         thread->GetEcmaVM(), inputString, startIndex, len)));
2051     resultElements->Set(thread, 0, zeroValue);
2052     // If R contains any GroupName, then
2053     //   a. Let groups be OrdinaryObjectCreate(null).
2054     //   b. Let hasGroups be true.
2055     // Else,
2056     //   a. Let groups be undefined.
2057     //   b. Let hasGroups be false.
2058     JSHandle<JSTaggedValue> groupName(thread, JSHandle<JSRegExp>::Cast(regexp)->GetGroupName());
2059     JSMutableHandle<JSTaggedValue> groups(thread, JSTaggedValue::Undefined());
2060     bool hasGroups = false;
2061     if (!groupName->IsUndefined()) {
2062         groups.Update(factory->CreateNullJSObject().GetTaggedValue());
2063         hasGroups = true;
2064     }
2065     if (isIntermediateResult) {
2066         // inlined intermediate result
2067         results->SetPropertyInlinedPropsWithRep(thread, EXEC_RESULT_GROUPS_OFFSET, groups.GetTaggedValue());
2068     } else {
2069         // Perform ! CreateDataPropertyOrThrow(A, "groups", groups).
2070         JSHandle<JSTaggedValue> groupsKey = globalConst->GetHandledGroupsString();
2071         JSObject::CreateDataProperty(thread, results, groupsKey, groups);
2072     }
2073     // Append match to indices
2074     uint32_t endIndex = globalTable->GetEndIndex().GetInt();
2075     std::vector<std::pair<JSTaggedValue, JSTaggedValue>> indices;
2076     indices.reserve(capturesSize);
2077     indices.emplace_back(globalTable->GetStartOfCaptureIndex(0), JSTaggedValue(endIndex));
2078     std::vector<JSHandle<JSTaggedValue>> groupNames;
2079     groupNames.reserve(capturesSize);
2080 
2081     // Create a new RegExp on global
2082     uint32_t captureIndex = 1;
2083     JSMutableHandle<JSTaggedValue> iValue(thread, JSTaggedValue::Undefined());
2084     // 28. For each integer i such that i > 0 and i <= n
2085     for (; captureIndex < capturesSize; captureIndex++) {
2086         // a. Let capture_i be ith element of r's captures List
2087         int32_t captureStartIndex = globalTable->GetStartOfCaptureIndex(captureIndex).GetInt();
2088         int32_t captureEndIndex = globalTable->GetEndOfCaptureIndex(captureIndex).GetInt();
2089         int32_t subStrLen = captureEndIndex - captureStartIndex;
2090         if (subStrLen < 0) {
2091             iValue.Update(JSTaggedValue::Undefined());
2092             indices.emplace_back(JSTaggedValue::Undefined(), JSTaggedValue::Undefined());
2093         } else {
2094             iValue.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(
2095                 thread->GetEcmaVM(), inputString, captureStartIndex, subStrLen)));
2096             indices.emplace_back(JSTaggedValue(captureStartIndex), JSTaggedValue(captureEndIndex));
2097         }
2098         // add to RegExp.$i and i must <= 9
2099         if (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) {
2100             globalTable->SetCapture(thread, captureIndex, iValue.GetTaggedValue());
2101         }
2102 
2103         resultElements->Set(thread, captureIndex, iValue);
2104         if (!groupName->IsUndefined()) {
2105             JSHandle<JSObject> groupObject = JSHandle<JSObject>::Cast(groups);
2106             TaggedArray *groupArray = TaggedArray::Cast(groupName->GetTaggedObject());
2107             if (groupArray->GetLength() > captureIndex - 1) {
2108                 JSHandle<JSTaggedValue> skey(thread, groupArray->Get(captureIndex - 1));
2109                 JSObject::CreateDataProperty(thread, groupObject, skey, iValue);
2110                 groupNames.emplace_back(skey);
2111             } else {
2112                 groupNames.emplace_back(undefined);
2113             }
2114         } else {
2115             groupNames.emplace_back(undefined);
2116         }
2117     }
2118     results->SetElements(thread, resultElements);
2119     // If hasIndices is true, then
2120     //   a. Let indicesArray be MakeMatchIndicesIndexPairArray(S, indices, groupNames, hasGroups).
2121     //   b. Perform ! CreateDataPropertyOrThrow(A, "indices", indicesArray).
2122     bool hasIndices = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_HASINDICES);
2123     if (hasIndices) {
2124         auto indicesArray = MakeMatchIndicesIndexPairArray(thread, indices, groupNames, hasGroups);
2125         JSHandle<JSTaggedValue> indicesKey = globalConst->GetHandledIndicesString();
2126         JSObject::CreateDataProperty(thread, results, indicesKey, indicesArray);
2127     }
2128     JSHandle<JSTaggedValue> emptyString = thread->GlobalConstants()->GetHandledEmptyString();
2129     while (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) {
2130         globalTable->SetCapture(thread, captureIndex, emptyString.GetTaggedValue());
2131         ++captureIndex;
2132     }
2133     if (useCache) {
2134         uint32_t newLastIndex = lastIndex;
2135         bool global = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
2136         bool sticky = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_STICKY);
2137         if (global || sticky) {
2138             newLastIndex = static_cast<uint32_t>(globalTable->GetEndIndex().GetInt());
2139         }
2140         RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, inputStr,
2141                                                 JSHandle<JSTaggedValue>(results), RegExpExecResultCache::EXEC_TYPE,
2142                                                 lastIndex, newLastIndex, undefined);
2143     }
2144     // 29. Return A.
2145     return results.GetTaggedValue();
2146 }
2147 
RegExpBuiltinExecWithoutResult(JSThread * thread,const JSHandle<JSTaggedValue> regexp,const JSHandle<JSTaggedValue> inputStr,bool isFastPath,uint32_t lastIndex,bool useCache)2148 JSTaggedValue BuiltinsRegExp::RegExpBuiltinExecWithoutResult(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
2149                                                              const JSHandle<JSTaggedValue> inputStr,
2150                                                              bool isFastPath, uint32_t lastIndex, bool useCache)
2151 {
2152     // check global and sticky flag to determine whether need to update lastIndex
2153     bool global = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
2154     bool sticky = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_STICKY);
2155     bool ifUpdateLastIndex = global || sticky;
2156     if (ifUpdateLastIndex) {
2157         uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
2158         if (lastIndex > length) {
2159             SetLastIndex(thread, regexp, JSTaggedValue(0), isFastPath);
2160             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2161             return JSTaggedValue::Null();
2162         }
2163     } else {
2164         lastIndex = 0;
2165     }
2166     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
2167     JSHandle<EcmaString> inputString = JSHandle<EcmaString>::Cast(inputStr);
2168     bool matchResult = RegExpExecInternal(thread, regexp, inputString, lastIndex);
2169     if (!matchResult) {
2170         uint32_t endIndex = lastIndex;
2171         if (ifUpdateLastIndex) {
2172             endIndex = 0;
2173             SetLastIndex(thread, regexp, JSTaggedValue(0), isFastPath);
2174             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2175         }
2176         if (useCache) {
2177             JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
2178             RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, inputStr,
2179                                                     JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null()),
2180                                                     RegExpExecResultCache::EXEC_TYPE,
2181                                                     lastIndex, endIndex, undefined);
2182         }
2183         return JSTaggedValue::Null();
2184     }
2185     JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
2186     JSTaggedValue endIndex = globalTable->GetEndIndex();
2187     if (ifUpdateLastIndex) {
2188         SetLastIndex(thread, regexp, endIndex, isFastPath);
2189         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2190     }
2191     return JSTaggedValue::True();
2192 }
2193 // 21.2.5.2.1
RegExpExec(JSThread * thread,const JSHandle<JSTaggedValue> & regexp,const JSHandle<JSTaggedValue> & inputString,bool useCache,bool isIntermediateResult)2194 JSTaggedValue BuiltinsRegExp::RegExpExec(JSThread *thread, const JSHandle<JSTaggedValue> &regexp,
2195                                          const JSHandle<JSTaggedValue> &inputString, bool useCache,
2196                                          bool isIntermediateResult)
2197 {
2198     BUILTINS_API_TRACE(thread, RegExp, RegExpExec);
2199     // 1. Assert: Type(R) is Object.
2200     ASSERT(regexp->IsECMAObject());
2201     // 2. Assert: Type(S) is String.
2202     ASSERT(inputString->IsString());
2203     // 3. Let exec be Get(R, "exec").
2204     JSHandle<EcmaString> inputStr = JSTaggedValue::ToString(thread, inputString);
2205     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2206     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
2207     JSHandle<JSTaggedValue> execHandle = globalConst->GetHandledExecString();
2208     JSTaggedValue execVal = ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(),
2209                                                                        execHandle.GetTaggedValue());
2210     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2211 
2212     JSHandle<JSTaggedValue> exec(thread, execVal);
2213     // 4. ReturnIfAbrupt(exec).
2214     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2215     // 5. If IsCallable(exec) is true, then
2216     if (exec->IsCallable()) {
2217         JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
2218         EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, exec, regexp, undefined, 1);
2219         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2220         info->SetCallArg(inputStr.GetTaggedValue());
2221         JSTaggedValue result = JSFunction::Call(info);
2222         // b. ReturnIfAbrupt(result).
2223         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2224         if (!result.IsECMAObject() && !result.IsNull()) {
2225             // throw a TypeError exception.
2226             THROW_TYPE_ERROR_AND_RETURN(thread, "exec result is null or is not Object", JSTaggedValue::Exception());
2227         }
2228         return result;
2229     }
2230     // 6. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
2231     if (!regexp->IsJSRegExp()) {
2232         // throw a TypeError exception.
2233         THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have a [[RegExpMatcher]]", JSTaggedValue::Exception());
2234     }
2235     // 7. Return RegExpBuiltinExec(R, S).
2236     return RegExpBuiltinExec(thread, regexp, inputString, false, useCache, isIntermediateResult);
2237 }
2238 
2239 // 21.2.3.2.1
RegExpAlloc(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget)2240 JSTaggedValue BuiltinsRegExp::RegExpAlloc(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget)
2241 {
2242     BUILTINS_API_TRACE(thread, RegExp, RegExpAlloc);
2243     /**
2244      * 1. Let obj be OrdinaryCreateFromConstructor(newTarget, "%RegExpPrototype%",
2245      * «[[RegExpMatcher]],[[OriginalSource]], [[OriginalFlags]]»).
2246      * */
2247     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2248     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
2249     JSHandle<JSTaggedValue> func = env->GetRegExpFunction();
2250     JSHandle<JSTaggedValue> obj(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(func), newTarget));
2251     // 2. ReturnIfAbrupt(obj).
2252     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2253     // 5. Return obj.
2254     return obj.GetTaggedValue();
2255 }
2256 
UpdateExpressionFlags(JSThread * thread,const CString & checkStr)2257 uint32_t BuiltinsRegExp::UpdateExpressionFlags(JSThread *thread, const CString &checkStr)
2258 {
2259     uint32_t flagsBits = 0;
2260     uint32_t flagsBitsTemp = 0;
2261     for (char i : checkStr) {
2262         switch (i) {
2263             case 'g':
2264                 flagsBitsTemp = RegExpParser::FLAG_GLOBAL;
2265                 break;
2266             case 'i':
2267                 flagsBitsTemp = RegExpParser::FLAG_IGNORECASE;
2268                 break;
2269             case 'm':
2270                 flagsBitsTemp = RegExpParser::FLAG_MULTILINE;
2271                 break;
2272             case 's':
2273                 flagsBitsTemp = RegExpParser::FLAG_DOTALL;
2274                 break;
2275             case 'u':
2276                 flagsBitsTemp = RegExpParser::FLAG_UTF16;
2277                 break;
2278             case 'y':
2279                 flagsBitsTemp = RegExpParser::FLAG_STICKY;
2280                 break;
2281             case 'd':
2282                 flagsBitsTemp = RegExpParser::FLAG_HASINDICES;
2283                 break;
2284             default: {
2285                 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2286                 JSHandle<JSObject> syntaxError = factory->GetJSError(base::ErrorType::SYNTAX_ERROR,
2287                     "invalid regular expression flags", StackCheck::NO);
2288                 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0);
2289             }
2290         }
2291         if ((flagsBits & flagsBitsTemp) != 0) {
2292             ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2293             JSHandle<JSObject> syntaxError =
2294                 factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags", StackCheck::NO);
2295             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0);
2296         }
2297         flagsBits |= flagsBitsTemp;
2298     }
2299     return flagsBits;
2300 }
2301 
FlagsBitsToString(JSThread * thread,uint8_t flags)2302 JSTaggedValue BuiltinsRegExp::FlagsBitsToString(JSThread *thread, uint8_t flags)
2303 {
2304     ASSERT((flags & 0x80) == 0);  // 0x80: first bit of flags must be 0
2305     BUILTINS_API_TRACE(thread, RegExp, FlagsBitsToString);
2306     uint8_t *flagsStr = new uint8_t[RegExpParser::FLAG_NUM + 1];  // FLAG_NUM flags + '\0'
2307     size_t flagsLen = 0;
2308     if (flags & RegExpParser::FLAG_HASINDICES) {
2309         flagsStr[flagsLen] = 'd';
2310         flagsLen++;
2311     }
2312     if (flags & RegExpParser::FLAG_GLOBAL) {
2313         flagsStr[flagsLen] = 'g';
2314         flagsLen++;
2315     }
2316     if (flags & RegExpParser::FLAG_IGNORECASE) {
2317         flagsStr[flagsLen] = 'i';
2318         flagsLen++;
2319     }
2320     if (flags & RegExpParser::FLAG_MULTILINE) {
2321         flagsStr[flagsLen] = 'm';
2322         flagsLen++;
2323     }
2324     if (flags & RegExpParser::FLAG_DOTALL) {
2325         flagsStr[flagsLen] = 's';
2326         flagsLen++;
2327     }
2328     if (flags & RegExpParser::FLAG_UTF16) {
2329         flagsStr[flagsLen] = 'u';
2330         flagsLen++;
2331     }
2332     if (flags & RegExpParser::FLAG_STICKY) {
2333         flagsStr[flagsLen] = 'y';
2334         flagsLen++;
2335     }
2336     flagsStr[flagsLen] = '\0';
2337     JSHandle<EcmaString> flagsString = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(flagsStr, flagsLen);
2338     delete[] flagsStr;
2339 
2340     return flagsString.GetTaggedValue();
2341 }
2342 
2343 // 21.2.3.2.2
RegExpInitialize(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags)2344 JSTaggedValue BuiltinsRegExp::RegExpInitialize(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
2345                                                const JSHandle<JSTaggedValue> &pattern,
2346                                                const JSHandle<JSTaggedValue> &flags)
2347 {
2348     BUILTINS_API_TRACE(thread, RegExp, RegExpInitialize);
2349     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2350     JSHandle<EcmaString> patternStrHandle;
2351     uint8_t flagsBits = 0;
2352     // 1. If pattern is undefined, let P be the empty String.
2353     if (pattern->IsUndefined()) {
2354         patternStrHandle = factory->GetEmptyString();
2355     } else {
2356         // 2. Else, let P be ToString(pattern).
2357         patternStrHandle = JSTaggedValue::ToString(thread, pattern);
2358         // 3. ReturnIfAbrupt(P).
2359         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2360     }
2361     // 4. If flags is undefined, let F be the empty String.
2362     if (flags->IsUndefined()) {
2363         flagsBits = 0;
2364     } else if (flags->IsInt()) {
2365         flagsBits = static_cast<uint8_t>(flags->GetInt());
2366     } else {
2367         // 5. Else, let F be ToString(flags).
2368         JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, flags);
2369         // 6. ReturnIfAbrupt(F).
2370         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2371         /**
2372          * 7. If F contains any code unit other than "d", "g", "i", "m", "u", or "y" or if it contains the same code
2373          * unit more than once, throw a SyntaxError exception.
2374          **/
2375         CString checkStr = ConvertToString(*flagsStrHandle, StringConvertedUsage::LOGICOPERATION);
2376         flagsBits = static_cast<uint8_t>(UpdateExpressionFlags(thread, checkStr));
2377         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2378     }
2379     // 9. 10.
2380     Chunk chunk(thread->GetNativeAreaAllocator());
2381     RegExpParser parser = RegExpParser(thread, &chunk);
2382     RegExpParserCache *regExpParserCache = thread->GetCurrentEcmaContext()->GetRegExpParserCache();
2383     CVector<CString> groupName;
2384     auto getCache = regExpParserCache->GetCache(*patternStrHandle, flagsBits, groupName);
2385     if (getCache.first.IsHole()) {
2386         // String -> CString
2387         bool cesu8 = !(RegExpParser::FLAG_UTF16 & flagsBits);
2388         CString patternStdStr = ConvertToString(*patternStrHandle, StringConvertedUsage::LOGICOPERATION, cesu8);
2389         parser.Init(const_cast<char *>(reinterpret_cast<const char *>(patternStdStr.c_str())), patternStdStr.size(),
2390                     flagsBits);
2391         parser.Parse();
2392         if (parser.IsError()) {
2393             JSHandle<JSObject> syntaxError =
2394                 factory->GetJSError(base::ErrorType::SYNTAX_ERROR, parser.GetErrorMsg().c_str(), StackCheck::NO);
2395             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception());
2396         }
2397         groupName = parser.GetGroupNames();
2398     }
2399     JSHandle<JSRegExp> regexp(thread, JSRegExp::Cast(obj->GetTaggedObject()));
2400     // 11. Set the value of obj’s [[OriginalSource]] internal slot to P.
2401     regexp->SetOriginalSource(thread, patternStrHandle.GetTaggedValue());
2402     // 12. Set the value of obj’s [[OriginalFlags]] internal slot to F.
2403     regexp->SetOriginalFlags(thread, JSTaggedValue(flagsBits));
2404     if (!groupName.empty()) {
2405         JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(groupName.size());
2406         for (size_t i = 0; i < groupName.size(); ++i) {
2407             JSHandle<JSTaggedValue> flagsKey(factory->NewFromStdString(groupName[i].c_str()));
2408             taggedArray->Set(thread, i, flagsKey);
2409         }
2410         regexp->SetGroupName(thread, taggedArray);
2411     }
2412     // 13. Set obj’s [[RegExpMatcher]] internal slot.
2413     if (getCache.first.IsHole()) {
2414         auto bufferSize = parser.GetOriginBufferSize();
2415         auto buffer = parser.GetOriginBuffer();
2416         factory->NewJSRegExpByteCodeData(regexp, buffer, bufferSize);
2417         regExpParserCache->SetCache(*patternStrHandle, flagsBits, regexp->GetByteCodeBuffer(), bufferSize, groupName);
2418     } else {
2419         regexp->SetByteCodeBuffer(thread, getCache.first);
2420         regexp->SetLength(static_cast<uint32_t>(getCache.second));
2421     }
2422     // 14. Let setStatus be Set(obj, "lastIndex", 0, true).
2423     SetLastIndex(thread, obj, JSTaggedValue(0), true);
2424     // 16. Return obj.
2425     return obj.GetTaggedValue();
2426 }
2427 
RegExpCreate(JSThread * thread,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags)2428 JSTaggedValue BuiltinsRegExp::RegExpCreate(JSThread *thread, const JSHandle<JSTaggedValue> &pattern,
2429                                            const JSHandle<JSTaggedValue> &flags)
2430 {
2431     BUILTINS_API_TRACE(thread, RegExp, Create);
2432     auto ecmaVm = thread->GetEcmaVM();
2433     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
2434     JSHandle<JSTaggedValue> newTarget = env->GetRegExpFunction();
2435     // 1. Let obj be RegExpAlloc(%RegExp%).
2436     JSHandle<JSTaggedValue> object(thread, RegExpAlloc(thread, newTarget));
2437     // 2. ReturnIfAbrupt(obj).
2438     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2439     // 3. Return RegExpInitialize(obj, P, F).
2440     return RegExpInitialize(thread, object, pattern, flags);
2441 }
2442 
2443 // 21.2.3.2.4
EscapeRegExpPattern(JSThread * thread,const JSHandle<JSTaggedValue> & src,const JSHandle<JSTaggedValue> & flags)2444 EcmaString *BuiltinsRegExp::EscapeRegExpPattern(JSThread *thread, const JSHandle<JSTaggedValue> &src,
2445                                                 const JSHandle<JSTaggedValue> &flags)
2446 {
2447     BUILTINS_API_TRACE(thread, RegExp, EscapeRegExpPattern);
2448     // String -> CString
2449     JSHandle<EcmaString> srcStr(thread, static_cast<EcmaString *>(src->GetTaggedObject()));
2450     JSHandle<EcmaString> flagsStr(thread, static_cast<EcmaString *>(flags->GetTaggedObject()));
2451     CString srcStdStr = ConvertToString(*srcStr, StringConvertedUsage::LOGICOPERATION);
2452     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2453     // "" -> (?:)
2454     if (srcStdStr.empty()) {
2455         srcStdStr = "(?:)";
2456     }
2457     // "/" -> "\/"
2458     srcStdStr = base::StringHelper::ReplaceAll(srcStdStr, "/", "\\/");
2459     // "\\" -> "\"
2460     srcStdStr = base::StringHelper::ReplaceAll(srcStdStr, "\\", "\\");
2461 
2462     return *factory->NewFromUtf8(srcStdStr);
2463 }
2464 
2465 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2466 #define SET_GET_CAPTURE_IMPL(index)                                                                                   \
2467     JSTaggedValue BuiltinsRegExp::GetCapture##index(JSThread *thread, [[maybe_unused]] const JSHandle<JSObject> &obj) \
2468     {                                                                                                                 \
2469         return RegExpGlobalResult::GetCapture<index>(thread);                                                         \
2470     }                                                                                                                 \
2471     bool BuiltinsRegExp::SetCapture##index([[maybe_unused]] JSThread *thread,                                         \
2472                                            [[maybe_unused]] const JSHandle<JSObject> &obj,                            \
2473                                            [[maybe_unused]] const JSHandle<JSTaggedValue> &value,                     \
2474                                            [[maybe_unused]] bool mayThrow)                                            \
2475     {                                                                                                                 \
2476         return true;                                                                                                  \
2477     }
2478 
2479     SET_GET_CAPTURE_IMPL(1)
2480     SET_GET_CAPTURE_IMPL(2)
2481     SET_GET_CAPTURE_IMPL(3)
2482     SET_GET_CAPTURE_IMPL(4)
2483     SET_GET_CAPTURE_IMPL(5)
2484     SET_GET_CAPTURE_IMPL(6)
2485     SET_GET_CAPTURE_IMPL(7)
2486     SET_GET_CAPTURE_IMPL(8)
2487     SET_GET_CAPTURE_IMPL(9)
2488 #undef SET_GET_CAPTURE_IMPL
2489 
CreateCacheTable(JSThread * thread)2490 JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread)
2491 {
2492     int length = CACHE_TABLE_HEADER_SIZE + INITIAL_CACHE_NUMBER * ENTRY_SIZE;
2493 
2494     auto table = static_cast<RegExpExecResultCache *>(
2495         *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2496     table->SetLargeStrCount(thread, DEFAULT_LARGE_STRING_COUNT);
2497     table->SetConflictCount(thread, DEFAULT_CONFLICT_COUNT);
2498     table->SetStrLenThreshold(thread, 0);
2499     table->SetHitCount(thread, 0);
2500     table->SetCacheCount(thread, 0);
2501     table->SetCacheLength(thread, INITIAL_CACHE_NUMBER);
2502     return JSTaggedValue(table);
2503 }
2504 
FindCachedResult(JSThread * thread,const JSHandle<JSTaggedValue> input,CacheType type,const JSHandle<JSTaggedValue> regexp,JSTaggedValue lastIndexInput,JSHandle<JSTaggedValue> extend,bool isIntermediateResult)2505 JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread,
2506                                                       const JSHandle<JSTaggedValue> input, CacheType type,
2507                                                       const JSHandle<JSTaggedValue> regexp,
2508                                                       JSTaggedValue lastIndexInput, JSHandle<JSTaggedValue> extend,
2509                                                       bool isIntermediateResult)
2510 {
2511     JSHandle<JSRegExp> regexpObj(regexp);
2512     JSTaggedValue pattern = regexpObj->GetOriginalSource();
2513     JSTaggedValue flags = regexpObj->GetOriginalFlags();
2514     JSTaggedValue inputValue = input.GetTaggedValue();
2515     JSTaggedValue extendValue = extend.GetTaggedValue();
2516     if (!pattern.IsString() || !flags.IsInt() || !input->IsString() || !lastIndexInput.IsInt()) {
2517         return JSTaggedValue::Undefined();
2518     }
2519     uint32_t hash = pattern.GetKeyHashCode() + static_cast<uint32_t>(flags.GetInt()) +
2520                     input->GetKeyHashCode() + static_cast<uint32_t>(lastIndexInput.GetInt());
2521     uint32_t entry = hash & static_cast<uint32_t>(GetCacheLength() - 1);
2522     if (!Match(entry, pattern, flags, inputValue, lastIndexInput, extendValue, type)) {
2523         uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(GetCacheLength() - 1);
2524         if (!Match(entry2, pattern, flags, inputValue, lastIndexInput, extendValue, type)) {
2525             return JSTaggedValue::Undefined();
2526         }
2527         entry = entry2;
2528     }
2529     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2530         static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
2531     uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2532     // update cached value if input value is changed
2533     JSTaggedValue cachedStr = Get(index + INPUT_STRING_INDEX);
2534     if (!cachedStr.IsUndefined() && cachedStr != inputValue) {
2535         Set(thread, index + INPUT_STRING_INDEX, inputValue);
2536     }
2537     JSTaggedValue result;
2538     switch (type) {
2539         case REPLACE_TYPE:
2540             result = Get(index + RESULT_REPLACE_INDEX);
2541             break;
2542         case SPLIT_TYPE:
2543             result = Get(index + RESULT_SPLIT_INDEX);
2544             break;
2545         case MATCH_TYPE:
2546             result = Get(index + RESULT_MATCH_INDEX);
2547             break;
2548         case EXEC_TYPE:
2549             result = Get(index + RESULT_EXEC_INDEX);
2550             break;
2551         case INTERMEDIATE_REPLACE_TYPE:
2552             result = Get(index + RESULT_INTERMEDIATE_REPLACE_INDEX);
2553             break;
2554         case TEST_TYPE:
2555             result = Get(index + RESULT_TEST_INDEX);
2556             break;
2557         case SEARCH_TYPE:
2558             result = Get(index + RESULT_SEARCH_INDEX);
2559             break;
2560         default:
2561             LOG_ECMA(FATAL) << "this branch is unreachable";
2562             UNREACHABLE();
2563             break;
2564     }
2565     JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
2566     JSTaggedValue cachedTable = Get(index + CAPTURE_SIZE);
2567     thread->GetCurrentEcmaContext()->SetRegExpGlobalResult(cachedTable);
2568     SetHitCount(thread, GetHitCount() + 1);
2569     if (type != SEARCH_TYPE && type != SPLIT_TYPE) {
2570         BuiltinsRegExp::SetLastIndex(thread, regexp, Get(index + LAST_INDEX_INDEX), true);
2571     }
2572     if (!isIntermediateResult && result.IsJSArray()) {
2573         JSHandle<JSArray> resultHandle(thread, JSArray::Cast(result));
2574         JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()->CloneArrayLiteral(resultHandle);
2575         return copyArray.GetTaggedValue();
2576     }
2577     return result;
2578 }
2579 
AddResultInCache(JSThread * thread,JSHandle<RegExpExecResultCache> cache,const JSHandle<JSTaggedValue> regexp,const JSHandle<JSTaggedValue> input,const JSHandle<JSTaggedValue> resultArray,CacheType type,uint32_t lastIndexInput,uint32_t lastIndex,JSHandle<JSTaggedValue> extend,bool isIntermediateResult)2580 void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache,
2581                                              const JSHandle<JSTaggedValue> regexp,
2582                                              const JSHandle<JSTaggedValue> input,
2583                                              const JSHandle<JSTaggedValue> resultArray, CacheType type,
2584                                              uint32_t lastIndexInput, uint32_t lastIndex,
2585                                              JSHandle<JSTaggedValue> extend, bool isIntermediateResult)
2586 {
2587     JSHandle<JSRegExp> regexpObj(regexp);
2588     JSHandle<JSTaggedValue> pattern(thread, regexpObj->GetOriginalSource());
2589     JSHandle<JSTaggedValue> flags(thread, regexpObj->GetOriginalFlags());
2590     if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) {
2591         return;
2592     }
2593 
2594     JSHandle<JSTaggedValue> resultArrayCopy;
2595     if (!isIntermediateResult && resultArray->IsJSArray()) {
2596         JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()
2597                                             ->CloneArrayLiteral(JSHandle<JSArray>(resultArray));
2598         resultArrayCopy = JSHandle<JSTaggedValue>(copyArray);
2599     } else {
2600         resultArrayCopy = JSHandle<JSTaggedValue>(resultArray);
2601     }
2602     JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
2603     JSHandle<TaggedArray> taggedArray = JSHandle<TaggedArray>::Cast(globalTable);
2604     auto factory = thread->GetEcmaVM()->GetFactory();
2605     uint32_t arrayLength = globalTable->GetLength();
2606     JSHandle<TaggedArray> resTableArray = factory->NewAndCopyTaggedArray(taggedArray, arrayLength, arrayLength);
2607     JSTaggedValue patternValue = pattern.GetTaggedValue();
2608     JSTaggedValue flagsValue = flags.GetTaggedValue();
2609     JSTaggedValue inputValue = input.GetTaggedValue();
2610     JSTaggedValue extendValue = extend.GetTaggedValue();
2611     JSTaggedValue lastIndexInputValue(lastIndexInput);
2612     JSTaggedValue lastIndexValue(lastIndex);
2613     JSTaggedValue resTableArrayValue = resTableArray.GetTaggedValue();
2614 
2615     uint32_t hash = patternValue.GetKeyHashCode() + static_cast<uint32_t>(flagsValue.GetInt()) +
2616                     inputValue.GetKeyHashCode() + lastIndexInput;
2617     uint32_t entry = hash & static_cast<uint32_t>(cache->GetCacheLength() - 1);
2618     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2619         static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
2620     uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2621     if (cache->Get(index).IsUndefined()) {
2622         cache->SetCacheCount(thread, cache->GetCacheCount() + 1);
2623         cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue,
2624                         lastIndexInputValue, lastIndexValue, extendValue, resTableArrayValue);
2625         cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
2626     } else if (cache->Match(entry, patternValue, flagsValue, inputValue, lastIndexInputValue, extendValue, type)) {
2627         cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
2628     } else {
2629         uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(cache->GetCacheLength() - 1);
2630         ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2631             static_cast<size_t>(entry2) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
2632         uint32_t index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE;
2633         if (cache->GetCacheLength() < DEFAULT_CACHE_NUMBER) {
2634             GrowRegexpCache(thread, cache);
2635             // update value after gc.
2636             patternValue = pattern.GetTaggedValue();
2637             flagsValue = flags.GetTaggedValue();
2638             inputValue = input.GetTaggedValue();
2639 
2640             cache->SetCacheLength(thread, DEFAULT_CACHE_NUMBER);
2641             entry2 = hash & static_cast<uint32_t>(cache->GetCacheLength() - 1);
2642             index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE;
2643         }
2644         extendValue = extend.GetTaggedValue();
2645         resTableArrayValue = resTableArray.GetTaggedValue();
2646         if (cache->Get(index2).IsUndefined()) {
2647             cache->SetCacheCount(thread, cache->GetCacheCount() + 1);
2648             cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue,
2649                             lastIndexInputValue, lastIndexValue, extendValue, resTableArrayValue);
2650             cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
2651         } else if (cache->Match(entry2, patternValue, flagsValue, inputValue, lastIndexInputValue, extendValue, type)) {
2652             cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
2653         } else {
2654             cache->SetConflictCount(thread, cache->GetConflictCount() > 1 ? (cache->GetConflictCount() - 1) : 0);
2655             cache->SetCacheCount(thread, cache->GetCacheCount() - 1);
2656             cache->ClearEntry(thread, entry2);
2657             cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue,
2658                             lastIndexInputValue, lastIndexValue, extendValue, resTableArrayValue);
2659             cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
2660         }
2661     }
2662 }
2663 
GrowRegexpCache(JSThread * thread,JSHandle<RegExpExecResultCache> cache)2664 void RegExpExecResultCache::GrowRegexpCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache)
2665 {
2666     int length = CACHE_TABLE_HEADER_SIZE + DEFAULT_CACHE_NUMBER * ENTRY_SIZE;
2667     auto factory = thread->GetEcmaVM()->GetFactory();
2668     auto newCache = factory->ExtendArray(JSHandle<TaggedArray>(cache), length, JSTaggedValue::Undefined());
2669     thread->GetCurrentEcmaContext()->SetRegExpCache(newCache.GetTaggedValue());
2670 }
2671 
SetEntry(JSThread * thread,int entry,JSTaggedValue & pattern,JSTaggedValue & flags,JSTaggedValue & input,JSTaggedValue & lastIndexInputValue,JSTaggedValue & lastIndexValue,JSTaggedValue & extendValue,JSTaggedValue & resTableArray)2672 void RegExpExecResultCache::SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags,
2673                                      JSTaggedValue &input, JSTaggedValue &lastIndexInputValue,
2674                                      JSTaggedValue &lastIndexValue, JSTaggedValue &extendValue,
2675                                      JSTaggedValue &resTableArray)
2676 {
2677     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2678             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2679     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2680     Set(thread, index + PATTERN_INDEX, pattern);
2681     Set(thread, index + FLAG_INDEX, flags);
2682     Set(thread, index + INPUT_STRING_INDEX, input);
2683     Set(thread, index + LAST_INDEX_INPUT_INDEX, lastIndexInputValue);
2684     Set(thread, index + LAST_INDEX_INDEX, lastIndexValue);
2685     Set(thread, index + EXTEND_INDEX, extendValue);
2686     Set(thread, index + CAPTURE_SIZE, resTableArray);
2687 }
2688 
UpdateResultArray(JSThread * thread,int entry,JSTaggedValue resultArray,CacheType type)2689 void RegExpExecResultCache::UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type)
2690 {
2691     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2692             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2693     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2694     switch (type) {
2695         case REPLACE_TYPE:
2696             Set(thread, index + RESULT_REPLACE_INDEX, resultArray);
2697             break;
2698         case SPLIT_TYPE:
2699             Set(thread, index + RESULT_SPLIT_INDEX, resultArray);
2700             break;
2701         case MATCH_TYPE:
2702             Set(thread, index + RESULT_MATCH_INDEX, resultArray);
2703             break;
2704         case EXEC_TYPE:
2705             Set(thread, index + RESULT_EXEC_INDEX, resultArray);
2706             break;
2707         case INTERMEDIATE_REPLACE_TYPE:
2708             Set(thread, index + RESULT_INTERMEDIATE_REPLACE_INDEX, resultArray);
2709             break;
2710         case TEST_TYPE:
2711             Set(thread, index + RESULT_TEST_INDEX, resultArray);
2712             break;
2713         case SEARCH_TYPE:
2714             Set(thread, index + RESULT_SEARCH_INDEX, resultArray);
2715             break;
2716         default:
2717             LOG_ECMA(FATAL) << "this branch is unreachable";
2718             UNREACHABLE();
2719             break;
2720     }
2721 }
2722 
ClearEntry(JSThread * thread,int entry)2723 void RegExpExecResultCache::ClearEntry(JSThread *thread, int entry)
2724 {
2725     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2726             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2727     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2728     JSTaggedValue undefined = JSTaggedValue::Undefined();
2729     for (int i = 0; i < ENTRY_SIZE; i++) {
2730         Set(thread, index + i, undefined);
2731     }
2732 }
2733 
Match(int entry,JSTaggedValue & pattern,JSTaggedValue & flags,JSTaggedValue & input,JSTaggedValue & lastIndexInputValue,JSTaggedValue & extend,CacheType type)2734 bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input,
2735                                   JSTaggedValue &lastIndexInputValue, JSTaggedValue &extend, CacheType type)
2736 {
2737     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2738             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2739     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2740 
2741     JSTaggedValue typeKey = Get(index + RESULT_REPLACE_INDEX + type);
2742     if (typeKey.IsUndefined()) {
2743         return false;
2744     }
2745 
2746     JSTaggedValue keyPattern = Get(index + PATTERN_INDEX);
2747     if (keyPattern.IsUndefined()) {
2748         return false;
2749     }
2750 
2751     uint8_t flagsBits = static_cast<uint8_t>(flags.GetInt());
2752     JSTaggedValue keyFlags = Get(index + FLAG_INDEX);
2753     uint8_t keyFlagsBits = static_cast<uint8_t>(keyFlags.GetInt());
2754     if (flagsBits != keyFlagsBits) {
2755         return false;
2756     }
2757 
2758     uint32_t lastIndexInputInt = static_cast<uint32_t>(lastIndexInputValue.GetInt());
2759     JSTaggedValue keyLastIndexInput = Get(index + LAST_INDEX_INPUT_INDEX);
2760     uint32_t keyLastIndexInputInt = static_cast<uint32_t>(keyLastIndexInput.GetInt());
2761     if (lastIndexInputInt != keyLastIndexInputInt) {
2762         return false;
2763     }
2764 
2765     JSTaggedValue keyInput = Get(index + INPUT_STRING_INDEX);
2766     JSTaggedValue keyExtend = Get(index + EXTEND_INDEX);
2767     EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject());
2768     EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject());
2769     EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject());
2770     EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject());
2771     bool extendEqual = false;
2772     if (extend.IsString() && keyExtend.IsString()) {
2773         EcmaString *extendStr = EcmaString::Cast(extend.GetTaggedObject());
2774         EcmaString *keyExtendStr = EcmaString::Cast(keyExtend.GetTaggedObject());
2775         extendEqual = EcmaStringAccessor::StringsAreEqual(extendStr, keyExtendStr);
2776     } else if (extend.IsUndefined() && keyExtend.IsUndefined()) {
2777         extendEqual = true;
2778     } else {
2779         return false;
2780     }
2781     return extendEqual &&
2782            EcmaStringAccessor::StringsAreEqual(patternStr, keyPatternStr) &&
2783            EcmaStringAccessor::StringsAreEqual(inputStr, keyInputStr);
2784 }
2785 
CreateGlobalResultTable(JSThread * thread)2786 JSTaggedValue RegExpGlobalResult::CreateGlobalResultTable(JSThread *thread)
2787 {
2788     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2789     uint32_t initialLength = GLOBAL_TABLE_SIZE + INITIAL_CAPTURE_INDICES;
2790     auto table = static_cast<RegExpGlobalResult *>(
2791         *factory->NewTaggedArray(initialLength, JSTaggedValue::Undefined()));
2792     // initialize dollars with empty string
2793     JSTaggedValue emptyString = factory->GetEmptyString().GetTaggedValue();
2794     for (uint32_t i = 1; i <= DOLLAR_NUMBER; i++) {
2795         table->SetCapture(thread, CAPTURE_START_INDEX + i, emptyString);
2796     }
2797     // initialize match info
2798     table->SetTotalCaptureCounts(thread, JSTaggedValue(0));
2799     table->SetInputString(thread, emptyString);
2800     for (uint32_t i = 0; i < INITIAL_CAPTURE_INDICES / 2; i++) { // 2: capture pair
2801         table->SetStartOfCaptureIndex(thread, i, JSTaggedValue(0));
2802         table->SetEndOfCaptureIndex(thread, i, JSTaggedValue(0));
2803     }
2804     return JSTaggedValue(table);
2805 }
2806 
GrowCapturesCapacity(JSThread * thread,JSHandle<RegExpGlobalResult> result,uint32_t length)2807 JSHandle<RegExpGlobalResult> RegExpGlobalResult::GrowCapturesCapacity(JSThread *thread,
2808     JSHandle<RegExpGlobalResult>result, uint32_t length)
2809 {
2810     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2811     JSHandle<TaggedArray> newResult = factory->ExtendArray(
2812         JSHandle<TaggedArray>(result), length, JSTaggedValue(0));
2813     thread->GetCurrentEcmaContext()->SetRegExpGlobalResult(newResult.GetTaggedValue());
2814     return JSHandle<RegExpGlobalResult>(newResult);
2815 }
2816 
GetFlag(JSThread * thread,const JSHandle<JSTaggedValue> regexp,uint32_t flag,bool isFastPath)2817 bool BuiltinsRegExp::GetFlag(JSThread *thread, const JSHandle<JSTaggedValue> regexp, uint32_t flag, bool isFastPath)
2818 {
2819     if (isFastPath) {
2820         uint8_t flagsBits = static_cast<uint8_t>(JSHandle<JSRegExp>::Cast(regexp)->GetOriginalFlags().GetInt());
2821         return (flagsBits & flag) != 0;
2822     } else {
2823         JSMutableHandle<JSTaggedValue> flagStr(thread, JSTaggedValue::Undefined());
2824         switch (flag) {
2825             case RegExpParser::FLAG_GLOBAL:
2826                 flagStr.Update(thread->GlobalConstants()->GetHandledGlobalString());
2827                 break;
2828             case RegExpParser::FLAG_UTF16:
2829                 flagStr.Update(thread->GlobalConstants()->GetHandledUnicodeString());
2830                 break;
2831             case RegExpParser::FLAG_STICKY:
2832                 flagStr.Update(thread->GlobalConstants()->GetHandledStickyString());
2833                 break;
2834             case RegExpParser::FLAG_MULTILINE:
2835                 flagStr.Update(thread->GlobalConstants()->GetHandledMultilineString());
2836                 break;
2837             case RegExpParser::FLAG_IGNORECASE:
2838                 flagStr.Update(thread->GlobalConstants()->GetHandledIgnoreCaseString());
2839                 break;
2840             case RegExpParser::FLAG_HASINDICES:
2841                 flagStr.Update(thread->GlobalConstants()->GetHandledHasIndicesString());
2842                 break;
2843             case RegExpParser::FLAG_DOTALL:
2844                 UNREACHABLE();
2845             default:
2846                 break;
2847         }
2848         JSTaggedValue globalValue =
2849             ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(), flagStr.GetTaggedValue());
2850         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
2851         return globalValue.ToBoolean();
2852     }
2853 }
2854 
GetOriginalFlag(JSThread * thread,const JSHandle<JSTaggedValue> regexp,uint32_t flag)2855 bool BuiltinsRegExp::GetOriginalFlag(JSThread *thread, const JSHandle<JSTaggedValue> regexp, uint32_t flag)
2856 {
2857     return GetFlag(thread, regexp, flag, true);
2858 }
2859 
SetLastIndex(JSThread * thread,const JSHandle<JSTaggedValue> regexp,JSTaggedValue lastIndex,bool isFastPath)2860 void BuiltinsRegExp::SetLastIndex(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
2861                                   JSTaggedValue lastIndex, bool isFastPath)
2862 {
2863     if (isFastPath) {
2864         JSHandle<JSObject>::Cast(regexp)->SetPropertyInlinedPropsWithRep(thread, LAST_INDEX_OFFSET, lastIndex);
2865         return;
2866     }
2867     ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
2868         thread->GlobalConstants()->GetHandledLastIndexString().GetTaggedValue(), lastIndex);
2869 }
2870 
GetLastIndex(JSThread * thread,const JSHandle<JSTaggedValue> regexp,bool isFastPath)2871 int64_t BuiltinsRegExp::GetLastIndex(JSThread *thread, const JSHandle<JSTaggedValue> regexp, bool isFastPath)
2872 {
2873     if (isFastPath) {
2874         return JSHandle<JSObject>::Cast(regexp)->GetPropertyInlinedProps(LAST_INDEX_OFFSET).GetInt();
2875     }
2876     JSHandle<JSTaggedValue> lastIndexHandle(thread, ObjectFastOperator::FastGetPropertyByValue(
2877         thread, regexp.GetTaggedValue(), thread->GlobalConstants()->GetHandledLastIndexString().GetTaggedValue()));
2878     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
2879     JSTaggedNumber thisIndex = JSTaggedValue::ToLength(thread, lastIndexHandle);
2880     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
2881     return thisIndex.GetNumber();
2882 }
2883 
GetExecResultIndex(JSThread * thread,const JSHandle<JSTaggedValue> & execResults,bool isFastPath)2884 JSTaggedValue BuiltinsRegExp::GetExecResultIndex(JSThread *thread, const JSHandle<JSTaggedValue> &execResults,
2885                                                  bool isFastPath)
2886 {
2887     if (isFastPath) {
2888         return JSHandle<JSObject>::Cast(execResults)->GetPropertyInlinedProps(EXEC_RESULT_INDEX_OFFSET);
2889     }
2890     JSHandle<JSTaggedValue> resultIndex = thread->GlobalConstants()->GetHandledIndexString();
2891     JSTaggedValue index = ObjectFastOperator::FastGetPropertyByValue(
2892         thread, execResults.GetTaggedValue(), resultIndex.GetTaggedValue());
2893     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2894     return index;
2895 }
2896 
GetExecResultGroups(JSThread * thread,const JSHandle<JSTaggedValue> & execResults,bool isFastPath)2897 JSTaggedValue BuiltinsRegExp::GetExecResultGroups(JSThread *thread, const JSHandle<JSTaggedValue> &execResults,
2898                                                   bool isFastPath)
2899 {
2900     if (isFastPath) {
2901         return JSHandle<JSObject>::Cast(execResults)->GetPropertyInlinedProps(EXEC_RESULT_GROUPS_OFFSET);
2902     }
2903     JSHandle<JSTaggedValue> groupKey = thread->GlobalConstants()->GetHandledGroupsString();
2904     JSTaggedValue groups = ObjectFastOperator::FastGetPropertyByValue(
2905         thread, execResults.GetTaggedValue(), groupKey.GetTaggedValue());
2906     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2907     return groups;
2908 }
2909 
CreateStringFromResultArray(JSThread * thread,const JSHandle<TaggedArray> resultArray,const std::vector<uint64_t> & resultLengthArray,JSHandle<EcmaString> srcString,uint32_t resultStrLength,bool isUtf8)2910 JSHandle<EcmaString> BuiltinsRegExp::CreateStringFromResultArray(JSThread *thread,
2911     const JSHandle<TaggedArray> resultArray, const std::vector<uint64_t> &resultLengthArray,
2912     JSHandle<EcmaString> srcString, uint32_t resultStrLength, bool isUtf8)
2913 {
2914     JSHandle<EcmaString> result = JSHandle<EcmaString>(thread,
2915         EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), resultStrLength, isUtf8));
2916     FlatStringInfo flatStrInfo = EcmaStringAccessor::FlattenAllString(thread->GetEcmaVM(), srcString);
2917     if (EcmaStringAccessor(srcString).IsTreeString()) { // use flattenedString as srcString
2918         srcString = JSHandle<EcmaString>(thread, flatStrInfo.GetString());
2919     }
2920     FlatStringInfo resultInfo = FlatStringInfo(*result, 0, resultStrLength);
2921     uint32_t nextPos = 0;
2922     uint32_t resultArrayLength = resultArray->GetLength();
2923     for (int i = 0; i < static_cast<int>(resultArrayLength); i++) {
2924         JSTaggedValue substrValue = resultArray->Get(thread, i);
2925         if (substrValue.IsHole()) {
2926             continue;
2927         }
2928         resultInfo.SetStartIndex(nextPos);
2929         if (substrValue.IsUndefined()) {
2930             uint64_t bits = resultLengthArray[i];
2931             uint32_t subLength = ReplaceLengthField::Decode(bits);
2932             uint32_t subPosition = ReplacePositionField::Decode(bits);
2933             if (isUtf8) {
2934                 EcmaStringAccessor::WriteToFlatWithPos<uint8_t>(*srcString, resultInfo.GetDataUtf8Writable(),
2935                                                                 subLength, subPosition);
2936             } else {
2937                 EcmaStringAccessor::WriteToFlatWithPos<uint16_t>(*srcString, resultInfo.GetDataUtf16Writable(),
2938                                                                  subLength, subPosition);
2939             }
2940             nextPos += subLength;
2941         } else {
2942             EcmaString *replacementStr = EcmaString::Cast(substrValue.GetTaggedObject());
2943             uint32_t replaceLength = static_cast<uint32_t>(resultLengthArray[i]);
2944             if (isUtf8) {
2945                 EcmaStringAccessor::WriteToFlat(replacementStr, resultInfo.GetDataUtf8Writable(), replaceLength);
2946             } else {
2947                 EcmaStringAccessor::WriteToFlat(replacementStr, resultInfo.GetDataUtf16Writable(), replaceLength);
2948             }
2949             nextPos += replaceLength;
2950         }
2951     }
2952     return result;
2953 }
2954 }  // namespace panda::ecmascript::builtins
2955