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