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