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