• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 
18 #include <cmath>
19 
20 #include "ecmascript/ecma_string-inl.h"
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/interpreter/interpreter.h"
24 #include "ecmascript/js_array.h"
25 #include "ecmascript/js_function.h"
26 #include "ecmascript/js_hclass.h"
27 #include "ecmascript/js_object-inl.h"
28 #include "ecmascript/js_regexp.h"
29 #include "ecmascript/js_regexp_iterator.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/mem/assert_scope.h"
32 #include "ecmascript/mem/c_containers.h"
33 #include "ecmascript/object_factory.h"
34 #include "ecmascript/object_fast_operator-inl.h"
35 #include "ecmascript/regexp/regexp_parser_cache.h"
36 #include "ecmascript/tagged_array-inl.h"
37 
38 namespace panda::ecmascript::builtins {
39 // 21.2.3.1
RegExpConstructor(EcmaRuntimeCallInfo * argv)40 JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv)
41 {
42     ASSERT(argv);
43     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Constructor);
44     JSThread *thread = argv->GetThread();
45     [[maybe_unused]] EcmaHandleScope handleScope(thread);
46     JSHandle<JSTaggedValue> newTargetTemp = GetNewTarget(argv);
47     JSHandle<JSTaggedValue> pattern = GetCallArg(argv, 0);
48     JSHandle<JSTaggedValue> flags = GetCallArg(argv, 1);
49     // 1. Let patternIsRegExp be IsRegExp(pattern).
50     bool patternIsRegExp = JSObject::IsRegExp(thread, pattern);
51     // 2. ReturnIfAbrupt(patternIsRegExp).
52     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
53     // 3. If NewTarget is not undefined, let newTarget be NewTarget.
54     JSHandle<JSTaggedValue> newTarget;
55     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
56     if (!newTargetTemp->IsUndefined()) {
57         newTarget = newTargetTemp;
58     } else {
59         auto ecmaVm = thread->GetEcmaVM();
60         JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
61         // disable gc
62         [[maybe_unused]] DisallowGarbageCollection no_gc;
63         // 4.a Let newTarget be the active function object.
64         newTarget = env->GetRegExpFunction();
65         JSHandle<JSTaggedValue> constructorString = globalConst->GetHandledConstructorString();
66         // 4.b If patternIsRegExp is true and flags is undefined
67         if (patternIsRegExp && flags->IsUndefined()) {
68             // 4.b.i Let patternConstructor be Get(pattern, "constructor").
69             JSTaggedValue patternConstructor = ObjectFastOperator::FastGetPropertyByValue(
70                 thread, pattern.GetTaggedValue(), constructorString.GetTaggedValue());
71             // 4.b.ii ReturnIfAbrupt(patternConstructor).
72             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
73             // 4.b.iii If SameValue(newTarget, patternConstructor) is true, return pattern.
74             if (JSTaggedValue::SameValue(newTarget.GetTaggedValue(), patternConstructor)) {
75                 return pattern.GetTaggedValue();
76             }
77         }
78     }
79     // 5. If Type(pattern) is Object and pattern has a [[RegExpMatcher]] internal slot
80     bool isJsReg = false;
81     if (pattern->IsECMAObject()) {
82         JSHandle<JSObject> patternObj = JSHandle<JSObject>::Cast(pattern);
83         isJsReg = patternObj->IsJSRegExp();
84     }
85     JSHandle<JSTaggedValue> patternTemp;
86     JSHandle<JSTaggedValue> flagsTemp;
87     if (isJsReg) {
88         JSHandle<JSRegExp> patternReg(thread, JSRegExp::Cast(pattern->GetTaggedObject()));
89         // 5.a Let P be the value of pattern’s [[OriginalSource]] internal slot.
90         patternTemp = JSHandle<JSTaggedValue>(thread, patternReg->GetOriginalSource());
91         if (flags->IsUndefined()) {
92             // 5.b If flags is undefined, let F be the value of pattern’s [[OriginalFlags]] internal slot.
93             flagsTemp = JSHandle<JSTaggedValue>(thread, patternReg->GetOriginalFlags());
94         } else {
95             // 5.c Else, let F be flags.
96             flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
97         }
98         // 6. Else if patternIsRegExp is true
99     } else if (patternIsRegExp) {
100         JSHandle<JSTaggedValue> sourceString(globalConst->GetHandledSourceString());
101         JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
102         // disable gc
103         [[maybe_unused]] DisallowGarbageCollection noGc;
104         // 6.a Let P be Get(pattern, "source").
105         patternTemp = JSObject::GetProperty(thread, pattern, sourceString).GetValue();
106         // 6.b ReturnIfAbrupt(P).
107         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
108         // 6.c If flags is undefined
109         if (flags->IsUndefined()) {
110             // 6.c.i Let F be Get(pattern, "flags").
111             flagsTemp = JSObject::GetProperty(thread, pattern, flagsString).GetValue();
112             // 6.c.ii ReturnIfAbrupt(F).
113             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
114         } else {
115             // 6.d Else, let F be flags.
116             flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
117         }
118     } else {
119         // 7.a Let P be pattern.
120         patternTemp = pattern;
121         // 7.b Let F be flags.
122         if (flags->IsUndefined()) {
123             flagsTemp = flags;
124         } else {
125             flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
126         }
127     }
128     // 8. Let O be RegExpAlloc(newTarget).
129     JSHandle<JSTaggedValue> object(thread, RegExpAlloc(thread, newTarget));
130     // 9. ReturnIfAbrupt(O).
131     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
132     // 10. Return RegExpInitialize(O, P, F).
133     JSTaggedValue result = RegExpInitialize(thread, object, patternTemp, flagsTemp);
134     return JSTaggedValue(result);
135 }
136 
137 // prototype
138 // 20.2.5.2
Exec(EcmaRuntimeCallInfo * argv)139 JSTaggedValue BuiltinsRegExp::Exec(EcmaRuntimeCallInfo *argv)
140 {
141     ASSERT(argv);
142     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Exec);
143     JSThread *thread = argv->GetThread();
144     [[maybe_unused]] EcmaHandleScope handleScope(thread);
145     // 1. Let R be the this value.
146     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
147     // 4. Let S be ToString(string).
148     JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
149     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
150     // 5. ReturnIfAbrupt(S).
151     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
152     JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
153     // 2. If Type(R) is not Object, throw a TypeError exception.
154     if (!thisObj->IsECMAObject()) {
155         // throw a TypeError exception.
156         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
157     }
158     // 3. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
159     if (!thisObj->IsJSRegExp()) {
160         // throw a TypeError exception.
161         THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[RegExpMatcher]]", JSTaggedValue::Exception());
162     }
163 
164     bool useCache = true;
165     JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
166     if (cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) {
167         useCache = false;
168     }
169 
170     // 6. Return RegExpBuiltinExec(R, S).
171     JSTaggedValue result = RegExpBuiltinExec(thread, thisObj, string, useCache);
172     return JSTaggedValue(result);
173 }
174 
175 // 20.2.5.13
Test(EcmaRuntimeCallInfo * argv)176 JSTaggedValue BuiltinsRegExp::Test(EcmaRuntimeCallInfo *argv)
177 {
178     ASSERT(argv);
179     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Test);
180     JSThread *thread = argv->GetThread();
181     [[maybe_unused]] EcmaHandleScope handleScope(thread);
182     // 1. Let R be the this value.
183     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
184     JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
185     // 3. Let string be ToString(S).
186     // 4. ReturnIfAbrupt(string).
187     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
188     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
189     JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
190     // 2. If Type(R) is not Object, throw a TypeError exception.
191     if (!thisObj->IsECMAObject()) {
192         // throw a TypeError exception.
193         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
194     }
195 
196     // 5. Let match be RegExpExec(R, string).
197     JSTaggedValue matchResult = RegExpExec(thread, thisObj, string, false);
198     // 6. ReturnIfAbrupt(match).
199     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
200     // 7. If match is not null, return true; else return false.
201     return GetTaggedBoolean(!matchResult.IsNull());
202 }
203 
204 // 20.2.5.14
ToString(EcmaRuntimeCallInfo * argv)205 JSTaggedValue BuiltinsRegExp::ToString(EcmaRuntimeCallInfo *argv)
206 {
207     ASSERT(argv);
208     BUILTINS_API_TRACE(argv->GetThread(), RegExp, ToString);
209     JSThread *thread = argv->GetThread();
210     [[maybe_unused]] EcmaHandleScope handleScope(thread);
211     // 1. Let R be the this value.
212     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
213     auto ecmaVm = thread->GetEcmaVM();
214     // 2. If Type(R) is not Object, throw a TypeError exception.
215     if (!thisObj->IsECMAObject()) {
216         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
217     }
218     ObjectFactory *factory = ecmaVm->GetFactory();
219     const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
220     JSHandle<JSTaggedValue> sourceString(globalConstants->GetHandledSourceString());
221     JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
222     // 3. Let pattern be ToString(Get(R, "source")).
223     JSHandle<JSTaggedValue> getSource(JSObject::GetProperty(thread, thisObj, sourceString).GetValue());
224     JSHandle<JSTaggedValue> getFlags(JSObject::GetProperty(thread, thisObj, flagsString).GetValue());
225     JSHandle<EcmaString> sourceStrHandle = JSTaggedValue::ToString(thread, getSource);
226     // 4. ReturnIfAbrupt(pattern).
227     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
228     // 5. Let flags be ToString(Get(R, "flags")).
229     JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
230     // 4. ReturnIfAbrupt(flags).
231     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
232     JSHandle<EcmaString> slashStr = JSHandle<EcmaString>::Cast(globalConstants->GetHandledBackslashString());
233     // 7. Let result be the String value formed by concatenating "/", pattern, and "/", and flags.
234     JSHandle<EcmaString> tempStr = factory->ConcatFromString(slashStr, sourceStrHandle);
235     JSHandle<EcmaString> resultTemp = factory->ConcatFromString(tempStr, slashStr);
236     return factory->ConcatFromString(resultTemp, flagsStrHandle).GetTaggedValue();
237 }
238 
239 // 20.2.5.3
GetFlags(EcmaRuntimeCallInfo * argv)240 JSTaggedValue BuiltinsRegExp::GetFlags(EcmaRuntimeCallInfo *argv)
241 {
242     ASSERT(argv);
243     BUILTINS_API_TRACE(argv->GetThread(), RegExp, GetFlags);
244     JSThread *thread = argv->GetThread();
245     [[maybe_unused]] EcmaHandleScope handleScope(thread);
246     // 1. Let R be the this value.
247     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
248     // 2. If Type(R) is not Object, throw a TypeError exception.
249     if (!thisObj->IsECMAObject()) {
250         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
251     }
252     // 3. Let result be the empty String.
253     // 4. ~ 19.
254     ASSERT(JSHandle<JSObject>::Cast(thisObj)->IsJSRegExp());
255     uint8_t flagsBits = static_cast<uint8_t>(JSRegExp::Cast(thisObj->GetTaggedObject())->GetOriginalFlags().GetInt());
256     return FlagsBitsToString(thread, flagsBits);
257 }
258 
259 // 20.2.5.4
GetGlobal(EcmaRuntimeCallInfo * argv)260 JSTaggedValue BuiltinsRegExp::GetGlobal(EcmaRuntimeCallInfo *argv)
261 {
262     ASSERT(argv);
263     JSThread *thread = argv->GetThread();
264     [[maybe_unused]] EcmaHandleScope handleScope(thread);
265     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
266     bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_GLOBAL);
267     return GetTaggedBoolean(result);
268 }
269 
270 // 20.2.5.5
GetIgnoreCase(EcmaRuntimeCallInfo * argv)271 JSTaggedValue BuiltinsRegExp::GetIgnoreCase(EcmaRuntimeCallInfo *argv)
272 {
273     ASSERT(argv);
274     JSThread *thread = argv->GetThread();
275     [[maybe_unused]] EcmaHandleScope handleScope(thread);
276     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
277     bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_IGNORECASE);
278     return GetTaggedBoolean(result);
279 }
280 
281 // 20.2.5.7
GetMultiline(EcmaRuntimeCallInfo * argv)282 JSTaggedValue BuiltinsRegExp::GetMultiline(EcmaRuntimeCallInfo *argv)
283 {
284     ASSERT(argv);
285     JSThread *thread = argv->GetThread();
286     [[maybe_unused]] EcmaHandleScope handleScope(thread);
287     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
288     bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_MULTILINE);
289     return GetTaggedBoolean(result);
290 }
291 
GetDotAll(EcmaRuntimeCallInfo * argv)292 JSTaggedValue BuiltinsRegExp::GetDotAll(EcmaRuntimeCallInfo *argv)
293 {
294     ASSERT(argv);
295     JSThread *thread = argv->GetThread();
296     [[maybe_unused]] EcmaHandleScope handleScope(thread);
297     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
298     bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_DOTALL);
299     return GetTaggedBoolean(result);
300 }
301 
302 // 20.2.5.10
GetSource(EcmaRuntimeCallInfo * argv)303 JSTaggedValue BuiltinsRegExp::GetSource(EcmaRuntimeCallInfo *argv)
304 {
305     ASSERT(argv);
306     JSThread *thread = argv->GetThread();
307     [[maybe_unused]] EcmaHandleScope handleScope(thread);
308     // 1. Let R be the this value.
309     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
310     // 2. If Type(R) is not Object, throw a TypeError exception.
311     // 3. If R does not have an [[OriginalSource]] internal slot, throw a TypeError exception.
312     // 4. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception.
313     if (!thisObj->IsECMAObject()) {
314         // throw a TypeError exception.
315         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
316     }
317     if (!thisObj->IsJSRegExp()) {
318         // throw a TypeError exception.
319         THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalSource]]", JSTaggedValue::Exception());
320     }
321     // 5. Let src be the value of R’s [[OriginalSource]] internal slot.
322     JSHandle<JSRegExp> regexpObj(thread, JSRegExp::Cast(thisObj->GetTaggedObject()));
323     JSHandle<JSTaggedValue> source(thread, regexpObj->GetOriginalSource());
324     // 6. Let flags be the value of R’s [[OriginalFlags]] internal slot.
325     uint8_t flagsBits = static_cast<uint8_t>(regexpObj->GetOriginalFlags().GetInt());
326     JSHandle<JSTaggedValue> flags(thread, FlagsBitsToString(thread, flagsBits));
327     // 7. Return EscapeRegExpPattern(src, flags).
328     return JSTaggedValue(EscapeRegExpPattern(thread, source, flags));
329 }
330 
331 // 20.2.5.12
GetSticky(EcmaRuntimeCallInfo * argv)332 JSTaggedValue BuiltinsRegExp::GetSticky(EcmaRuntimeCallInfo *argv)
333 {
334     ASSERT(argv);
335     JSThread *thread = argv->GetThread();
336     [[maybe_unused]] EcmaHandleScope handleScope(thread);
337     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
338     bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_STICKY);
339     return GetTaggedBoolean(result);
340 }
341 
342 // 20.2.5.15
GetUnicode(EcmaRuntimeCallInfo * argv)343 JSTaggedValue BuiltinsRegExp::GetUnicode(EcmaRuntimeCallInfo *argv)
344 {
345     ASSERT(argv);
346     JSThread *thread = argv->GetThread();
347     [[maybe_unused]] EcmaHandleScope handleScope(thread);
348     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
349     bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_UTF16);
350     return GetTaggedBoolean(result);
351 }
352 
353 // 21.2.4.2
GetSpecies(EcmaRuntimeCallInfo * argv)354 JSTaggedValue BuiltinsRegExp::GetSpecies(EcmaRuntimeCallInfo *argv)
355 {
356     ASSERT(argv);
357     return GetThis(argv).GetTaggedValue();
358 }
359 
360 // 21.2.5.6
Match(EcmaRuntimeCallInfo * argv)361 JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv)
362 {
363     ASSERT(argv);
364     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Match);
365     JSThread *thread = argv->GetThread();
366     [[maybe_unused]] EcmaHandleScope handleScope(thread);
367     // 1. Let rx be the this value.
368     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
369     // 3. Let S be ToString(string)
370     JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
371     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
372     bool useCache = true;
373     JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
374     if (cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) {
375         useCache = false;
376     }
377     // 4. ReturnIfAbrupt(string).
378     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
379     JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
380     if (!thisObj->IsECMAObject()) {
381         // 2. If Type(rx) is not Object, throw a TypeError exception.
382         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
383     }
384     // 5. Let global be ToBoolean(Get(rx, "global")).
385     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
386     JSHandle<JSTaggedValue> global = globalConst->GetHandledGlobalString();
387     JSTaggedValue globalValue =
388         ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(), global.GetTaggedValue());
389     // 6. ReturnIfAbrupt(global).
390     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
391 
392     JSHandle<JSRegExp> regexpObj(thisObj);
393     JSMutableHandle<JSTaggedValue> pattern(thread, JSTaggedValue::Undefined());
394     JSMutableHandle<JSTaggedValue> flags(thread, JSTaggedValue::Undefined());
395     if (thisObj->IsJSRegExp()) {
396         pattern.Update(regexpObj->GetOriginalSource());
397         flags.Update(regexpObj->GetOriginalFlags());
398     }
399     bool isGlobal = globalValue.ToBoolean();
400     // 7. If global is false, then
401     if (!isGlobal) {
402         // a. Return RegExpExec(rx, S).
403         if (useCache) {
404             JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, inputString,
405                                                                      RegExpExecResultCache::EXEC_TYPE, thisObj);
406             if (cacheResult != JSTaggedValue::Undefined()) {
407                 return cacheResult;
408             }
409         }
410         JSTaggedValue result = RegExpExec(thread, thisObj, string, useCache);
411         return JSTaggedValue(result);
412     }
413 
414     if (useCache) {
415         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, inputString,
416                                                                  RegExpExecResultCache::MATCH_TYPE, thisObj);
417         if (cacheResult != JSTaggedValue::Undefined()) {
418             return cacheResult;
419         }
420     }
421 
422     // 8. Else global is true
423     // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")).
424     JSHandle<JSTaggedValue> unicode = globalConst->GetHandledUnicodeString();
425     JSTaggedValue uincodeValue =
426         ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(), unicode.GetTaggedValue());
427     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
428     bool fullUnicode = uincodeValue.ToBoolean();
429     // b. ReturnIfAbrupt(fullUnicode)
430     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
431     // c. Let setStatus be Set(rx, "lastIndex", 0, true).
432     JSHandle<JSTaggedValue> lastIndexString(globalConst->GetHandledLastIndexString());
433     ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(), lastIndexString.GetTaggedValue(),
434                                                JSTaggedValue(0));
435     // d. ReturnIfAbrupt(setStatus).
436     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
437     // e. Let A be ArrayCreate(0).
438     JSHandle<JSObject> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
439     // f. Let n be 0.
440     int resultNum = 0;
441     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue(0));
442     // g. Repeat,
443     while (true) {
444         // i. Let result be RegExpExec(rx, S).
445         result.Update(RegExpExec(thread, thisObj, string, useCache));
446 
447         // ii. ReturnIfAbrupt(result).
448         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
449         // iii. If result is null, then
450         if (result->IsNull()) {
451             // 1. If n=0, return null.
452             if (resultNum == 0) {
453                 return JSTaggedValue::Null();
454             }
455             if (useCache) {
456                 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputString,
457                                                         JSHandle<JSTaggedValue>(array),
458                                                         RegExpExecResultCache::MATCH_TYPE, 0);
459             }
460             // 2. Else, return A.
461             return array.GetTaggedValue();
462         }
463         // iv. Else result is not null,
464         // 1. Let matchStr be ToString(Get(result, "0")).
465         JSHandle<JSTaggedValue> zeroString = globalConst->GetHandledZeroString();
466         JSTaggedValue matchVal = ObjectFastOperator::FastGetPropertyByValue(
467             thread, result.GetTaggedValue(), zeroString.GetTaggedValue());
468         JSHandle<JSTaggedValue> matchStr(thread, matchVal);
469         JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, matchStr);
470         // 2. ReturnIfAbrupt(matchStr).
471         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
472         JSHandle<JSTaggedValue> matchValue = JSHandle<JSTaggedValue>::Cast(matchString);
473         // 3. Let status be CreateDataProperty(A, ToString(n), matchStr).
474         JSObject::CreateDataProperty(thread, array, resultNum, matchValue);
475         // 5. If matchStr is the empty String, then
476         if (EcmaStringAccessor(JSTaggedValue::ToString(thread, matchValue)).GetLength() == 0) {
477             // a. Let thisIndex be ToLength(Get(rx, "lastIndex")).
478             JSTaggedValue lastIndex = ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(),
479                                                                                  lastIndexString.GetTaggedValue());
480             JSHandle<JSTaggedValue> lastIndexHandle(thread, lastIndex);
481             JSTaggedNumber thisIndex = JSTaggedValue::ToLength(thread, lastIndexHandle);
482             // b. ReturnIfAbrupt(thisIndex).
483             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
484             // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
485             // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true).
486             JSTaggedValue nextIndex =
487                 JSTaggedValue(AdvanceStringIndex(string, thisIndex.GetNumber(), fullUnicode));
488             ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(),
489                 lastIndexString.GetTaggedValue(), nextIndex);
490             // e. ReturnIfAbrupt(setStatus).
491             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
492         }
493         // 6. Increase n.
494         resultNum++;
495     }
496 }
497 
MatchAll(EcmaRuntimeCallInfo * argv)498 JSTaggedValue BuiltinsRegExp::MatchAll(EcmaRuntimeCallInfo *argv)
499 {
500     ASSERT(argv);
501     JSThread *thread = argv->GetThread();
502     BUILTINS_API_TRACE(thread, RegExp, MatchAll);
503     [[maybe_unused]] EcmaHandleScope handleScope(thread);
504 
505     // 1. Let R be the this value.
506     // 2. If Type(R) is not Object, throw a TypeError exception.
507     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
508     auto ecmaVm = thread->GetEcmaVM();
509     if (!thisObj->IsECMAObject()) {
510         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
511     }
512 
513     // 3. Let S be ? ToString(string).
514     JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
515     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
516     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
517 
518     // 4. Let C be ? SpeciesConstructor(R, %RegExp%).
519     JSHandle<JSTaggedValue> defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction();
520     JSHandle<JSObject> objHandle(thisObj);
521     JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
522     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
523 
524     const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
525     // 5. Let flags be ? ToString(? Get(R, "flags")).
526     JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
527     JSHandle<JSTaggedValue> getFlags(JSObject::GetProperty(thread, thisObj, flagsString).GetValue());
528     JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
529     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
530 
531     // 6. Let matcher be ? Construct(C, « R, flags »).
532     JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
533     EcmaRuntimeCallInfo *runtimeInfo =
534         EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 2); // 2: two args
535     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
536     runtimeInfo->SetCallArg(thisObj.GetTaggedValue(), flagsStrHandle.GetTaggedValue());
537     JSTaggedValue taggedMatcher = JSFunction::Construct(runtimeInfo);
538     JSHandle<JSTaggedValue> matcherHandle(thread, taggedMatcher);
539     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
540 
541     // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
542     JSHandle<JSTaggedValue> lastIndexString(globalConstants->GetHandledLastIndexString());
543     JSHandle<JSTaggedValue> getLastIndex(JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue());
544     JSTaggedNumber thisLastIndex = JSTaggedValue::ToLength(thread, getLastIndex);
545     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
546 
547     // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
548     ObjectFastOperator::FastSetPropertyByValue(thread, matcherHandle.GetTaggedValue(), lastIndexString.GetTaggedValue(),
549                                                thisLastIndex);
550     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
551 
552     // 9. If flags contains "g", let global be true.
553     // 10. Else, let global be false.
554     JSHandle<EcmaString> gString(globalConstants->GetHandledGString());
555     bool global = false;
556     if (EcmaStringAccessor::IndexOf(*flagsStrHandle, *gString) != -1) {
557         global = true;
558     }
559 
560     // 11. If flags contains "u", let fullUnicode be true.
561     // 12. Else, let fullUnicode be false.
562     JSHandle<EcmaString> uString(globalConstants->GetHandledUString());
563     bool fullUnicode = false;
564     if (EcmaStringAccessor::IndexOf(*flagsStrHandle, *uString) != -1) {
565         fullUnicode = true;
566     }
567 
568     // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
569     return JSRegExpIterator::CreateRegExpStringIterator(thread, matcherHandle,
570                                                         stringHandle, global, fullUnicode).GetTaggedValue();
571 }
572 
RegExpReplaceFast(JSThread * thread,JSHandle<JSTaggedValue> & regexp,JSHandle<EcmaString> inputString,uint32_t inputLength)573 JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandle<JSTaggedValue> &regexp,
574                                                 JSHandle<EcmaString> inputString, uint32_t inputLength)
575 {
576     ASSERT(regexp->IsJSRegExp());
577     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
578     // get bytecode
579     JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
580     void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
581     // get flags
582     auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
583     uint32_t flags = *reinterpret_cast<uint32_t *>(bytecodeBuffer + RegExpParser::FLAGS_OFFSET);
584     JSHandle<JSTaggedValue> lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString());
585     uint32_t lastIndex = 0;
586     JSHandle<JSRegExp> regexpHandle(regexp);
587     bool useCache = false;
588     if ((flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) == 0) {
589         lastIndex = 0;
590     } else {
591         JSTaggedValue thisIndex = ObjectFastOperator::FastGetPropertyByValue(thread,
592             regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue());
593         if (thisIndex.IsInt()) {
594             lastIndex = static_cast<uint32_t>(thisIndex.GetInt());
595         } else {
596             JSHandle<JSTaggedValue> thisIndexHandle(thread, thisIndex);
597             lastIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber();
598             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
599         }
600     }
601 
602     auto globalConst = thread->GlobalConstants();
603     JSHandle<JSTaggedValue> tagInputString = JSHandle<JSTaggedValue>::Cast(inputString);
604     JSHandle<JSTaggedValue> pattern(thread, regexpHandle->GetOriginalSource());
605     JSHandle<JSTaggedValue> flagsBits(thread, regexpHandle->GetOriginalFlags());
606 
607     JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
608     uint32_t length = EcmaStringAccessor(inputString).GetLength();
609     uint32_t largeStrCount = cacheTable->GetLargeStrCount();
610     if (largeStrCount != 0) {
611         if (length > MIN_REPLACE_STRING_LENGTH) {
612             cacheTable->SetLargeStrCount(thread, --largeStrCount);
613         }
614     } else {
615         cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH);
616     }
617     if (length > cacheTable->GetStrLenThreshold()) {
618         useCache = true;
619     }
620     if (useCache) {
621         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, tagInputString,
622                                                                  RegExpExecResultCache::REPLACE_TYPE, regexp,
623                                                                  globalConst->GetEmptyString());
624         if (cacheResult != JSTaggedValue::Undefined()) {
625             return cacheResult;
626         }
627     }
628 
629     std::string resultString;
630     uint32_t nextPosition = 0;
631 
632     // 12. Let done be false.
633     // 13. Repeat, while done is false
634     for (;;) {
635         if (lastIndex > inputLength) {
636             break;
637         }
638 
639         bool isUtf16 = EcmaStringAccessor(inputString).IsUtf16();
640         auto inputPtr = EcmaStringAccessor(inputString).ToOneByteDataForced();
641         const uint8_t *strBuffer = inputPtr.get();
642 
643         RegExpExecutor::MatchResult matchResult = Matcher(thread, regexp, strBuffer, inputLength, lastIndex, isUtf16);
644         if (!matchResult.isSuccess_) {
645             if (flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) {
646                 lastIndex = 0;
647                 ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
648                                                            lastIndexHandle.GetTaggedValue(), JSTaggedValue(0));
649                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
650             }
651             break;
652         }
653         uint32_t startIndex = matchResult.index_;
654         uint32_t endIndex = matchResult.endIndex_;
655         lastIndex = endIndex;
656         if (nextPosition < startIndex) {
657             auto substr = EcmaStringAccessor::FastSubString(
658                 thread->GetEcmaVM(), inputString, nextPosition, startIndex - nextPosition);
659             resultString += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
660         }
661         nextPosition = endIndex;
662         if (!(flags & RegExpParser::FLAG_GLOBAL)) {
663             // a. Let setStatus be Set(R, "lastIndex", e, true).
664             ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
665                 lastIndexHandle.GetTaggedValue(), JSTaggedValue(lastIndex));
666             // b. ReturnIfAbrupt(setStatus).
667             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
668             break;
669         }
670         if (endIndex == startIndex) {
671             bool unicode = EcmaStringAccessor(inputString).IsUtf16() && (flags & RegExpParser::FLAG_UTF16);
672             endIndex = AdvanceStringIndex(tagInputString, endIndex, unicode);
673         }
674         lastIndex = endIndex;
675     }
676     auto substr = EcmaStringAccessor::FastSubString(
677         thread->GetEcmaVM(), inputString, nextPosition, inputLength - nextPosition);
678     resultString += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
679     auto resultValue = factory->NewFromStdString(resultString);
680     if (useCache) {
681         RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, tagInputString,
682                                                 JSHandle<JSTaggedValue>(resultValue),
683                                                 RegExpExecResultCache::REPLACE_TYPE, lastIndex,
684                                                 globalConst->GetEmptyString());
685     }
686     return resultValue.GetTaggedValue();
687 }
688 
689 // 21.2.5.8
690 // NOLINTNEXTLINE(readability-function-size)
Replace(EcmaRuntimeCallInfo * argv)691 JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv)
692 {
693     ASSERT(argv);
694     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Replace);
695     JSThread *thread = argv->GetThread();
696     [[maybe_unused]] EcmaHandleScope handleScope(thread);
697     // 1. Let rx be the this value.
698     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
699     if (!thisObj->IsECMAObject()) {
700         // 2. If Type(rx) is not Object, throw a TypeError exception.
701         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
702     }
703     // 3. Let S be ToString(string).
704     JSHandle<JSTaggedValue> string = GetCallArg(argv, 0);
705     JSHandle<JSTaggedValue> inputReplaceValue = GetCallArg(argv, 1);
706     JSHandle<EcmaString> srcString = JSTaggedValue::ToString(thread, string);
707     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
708 
709     // 4. ReturnIfAbrupt(S).
710     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
711     JSHandle<JSTaggedValue> inputStr = JSHandle<JSTaggedValue>::Cast(srcString);
712     // 5. Let lengthS be the number of code unit elements in S.
713     uint32_t length = EcmaStringAccessor(srcString).GetLength();
714     // 6. Let functionalReplace be IsCallable(replaceValue).
715     bool functionalReplace = inputReplaceValue->IsCallable();
716     JSHandle<EcmaString> replaceValueHandle;
717     if (!functionalReplace) {
718         replaceValueHandle = JSTaggedValue::ToString(thread, inputReplaceValue);
719         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
720     }
721     JSHandle<JSTaggedValue> lastIndex = globalConst->GetHandledLastIndexString();
722     // 8. Let global be ToBoolean(Get(rx, "global")).
723     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
724     JSHandle<JSTaggedValue> global = globalConst->GetHandledGlobalString();
725     JSTaggedValue globalValue =
726         ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(), global.GetTaggedValue());
727     // 9. ReturnIfAbrupt(global).
728     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
729     bool isGlobal = globalValue.ToBoolean();
730 
731     // 10. If global is true, then
732     bool fullUnicode = false;
733     if (isGlobal) {
734         // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")).
735         JSHandle<JSTaggedValue> unicode = globalConst->GetHandledUnicodeString();
736         JSTaggedValue fullUnicodeTag =
737             ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(), unicode.GetTaggedValue());
738         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
739         fullUnicode = fullUnicodeTag.ToBoolean();
740         // b. ReturnIfAbrupt(fullUnicode).
741         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
742         // c. Let setStatus be Set(rx, "lastIndex", 0, true).
743         ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(),
744                                                    lastIndex.GetTaggedValue(), JSTaggedValue(0));
745         // d. ReturnIfAbrupt(setStatus).
746         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
747     }
748 
749     // Add cache for regexp replace
750     bool useCache = false;
751     JSMutableHandle<JSTaggedValue> pattern(thread, JSTaggedValue::Undefined());
752     JSMutableHandle<JSTaggedValue> flagsBits(thread, JSTaggedValue::Undefined());
753     JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
754     if (isGlobal && !functionalReplace && thisObj->IsJSRegExp()) {
755         JSHClass *hclass = JSHandle<JSObject>::Cast(thisObj)->GetJSHClass();
756         JSHClass *originHClass = JSHClass::Cast(globalConst->GetJSRegExpClass().GetTaggedObject());
757         if (hclass == originHClass) {
758             if (EcmaStringAccessor(replaceValueHandle).GetLength() == 0) {
759                 return RegExpReplaceFast(thread, thisObj, srcString, length);
760             } else {
761                 JSHandle<JSRegExp> regexpHandle(thisObj);
762                 if (regexpHandle->IsJSRegExp()) {
763                     pattern.Update(regexpHandle->GetOriginalSource());
764                     flagsBits.Update(regexpHandle->GetOriginalFlags());
765                 }
766                 uint32_t strLength = EcmaStringAccessor(replaceValueHandle).GetLength();
767                 uint32_t largeStrCount = cacheTable->GetLargeStrCount();
768                 if (largeStrCount != 0) {
769                     if (strLength > MIN_REPLACE_STRING_LENGTH) {
770                         cacheTable->SetLargeStrCount(thread, --largeStrCount);
771                     }
772                 } else {
773                     cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH);
774                 }
775                 if (strLength > cacheTable->GetStrLenThreshold()) {
776                     useCache = true;
777                     JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, string,
778                                                                              RegExpExecResultCache::REPLACE_TYPE,
779                                                                              thisObj,
780                                                                              inputReplaceValue.GetTaggedValue());
781                     if (cacheResult != JSTaggedValue::Undefined()) {
782                         return cacheResult;
783                     }
784                 }
785             }
786         }
787     }
788 
789     JSHandle<JSTaggedValue> matchedStr = globalConst->GetHandledZeroString();
790     // 11. Let results be a new empty List.
791     JSHandle<JSObject> resultsList(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
792     int resultsIndex = 0;
793     // 12. Let done be false.
794     // 13. Repeat, while done is false
795     JSMutableHandle<JSTaggedValue> nextIndexHandle(thread, JSTaggedValue(0));
796     JSMutableHandle<JSTaggedValue> execResult(thread, JSTaggedValue(0));
797     for (;;) {
798         // a. Let result be RegExpExec(rx, S).
799         execResult.Update(RegExpExec(thread, thisObj, inputStr, useCache));
800         // b. ReturnIfAbrupt(result).
801         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
802         // c. If result is null, set done to true.
803         if (execResult->IsNull()) {
804             break;
805         }
806         // d. Else result is not null, i. Append result to the end of results.
807         JSObject::CreateDataProperty(thread, resultsList, resultsIndex, execResult);
808         resultsIndex++;
809         // ii. If global is false, set done to true.
810         if (!isGlobal) {
811             break;
812         }
813         // iii. Else, 1. Let matchStr be ToString(Get(result, "0")).
814         JSTaggedValue getMatchVal = ObjectFastOperator::FastGetPropertyByValue(
815             thread, execResult.GetTaggedValue(), matchedStr.GetTaggedValue());
816         JSHandle<JSTaggedValue> getMatch(thread, getMatchVal);
817         JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, getMatch);
818         // 2. ReturnIfAbrupt(matchStr).
819         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
820         // 3. If matchStr is the empty String, then
821         if (EcmaStringAccessor(matchString).GetLength() == 0) {
822             // a. Let thisIndex be ToLength(Get(rx, "lastIndex")).
823             JSTaggedValue thisIndexVal = ObjectFastOperator::FastGetPropertyByValue(
824                 thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue());
825             JSHandle<JSTaggedValue> thisIndexHandle(thread, thisIndexVal);
826             uint32_t thisIndex = 0;
827             if (thisIndexHandle->IsInt()) {
828                 thisIndex = static_cast<uint32_t>(thisIndexHandle->GetInt());
829             } else {
830                 thisIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber();
831                 // b. ReturnIfAbrupt(thisIndex).
832                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
833             }
834             // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
835             uint32_t nextIndex = AdvanceStringIndex(inputStr, thisIndex, fullUnicode);
836             nextIndexHandle.Update(JSTaggedValue(nextIndex));
837             // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true).
838             ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue(),
839                                                        nextIndexHandle.GetTaggedValue());
840             // e. ReturnIfAbrupt(setStatus).
841             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
842         }
843     }
844     // 14. Let accumulatedResult be the empty String value.
845     std::string accumulatedResult;
846     // 15. Let nextSourcePosition be 0.
847     uint32_t nextSourcePosition = 0;
848     JSHandle<JSTaggedValue> getMatchString;
849     JSMutableHandle<JSTaggedValue> resultValues(thread, JSTaggedValue(0));
850     JSMutableHandle<JSTaggedValue> ncapturesHandle(thread, JSTaggedValue(0));
851     JSMutableHandle<JSTaggedValue> capN(thread, JSTaggedValue(0));
852     // 16. Repeat, for each result in results,
853     for (int i = 0; i < resultsIndex; i++) {
854         resultValues.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, resultsList.GetTaggedValue(), i));
855         // a. Let nCaptures be ToLength(Get(result, "length")).
856         JSHandle<JSTaggedValue> lengthHandle = globalConst->GetHandledLengthString();
857         ncapturesHandle.Update(ObjectFastOperator::FastGetPropertyByValue(
858             thread, resultValues.GetTaggedValue(), lengthHandle.GetTaggedValue()));
859         uint32_t ncaptures = JSTaggedValue::ToUint32(thread, ncapturesHandle);
860         // b. ReturnIfAbrupt(nCaptures).
861         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
862         // c. Let nCaptures be max(nCaptures − 1, 0).
863         ncaptures = std::max<uint32_t>((ncaptures - 1), 0);
864         // d. Let matched be ToString(Get(result, "0")).
865         JSTaggedValue value = ObjectFastOperator::GetPropertyByIndex(thread, resultValues.GetTaggedValue(), 0);
866         getMatchString = JSHandle<JSTaggedValue>(thread, value);
867         JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, getMatchString);
868         // e. ReturnIfAbrupt(matched).
869         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
870         // f. Let matchLength be the number of code units in matched.
871         uint32_t matchLength = EcmaStringAccessor(matchString).GetLength();
872         // g. Let position be ToInteger(Get(result, "index")).
873         JSHandle<JSTaggedValue> resultIndex = globalConst->GetHandledIndexString();
874         JSTaggedValue positionTag = ObjectFastOperator::FastGetPropertyByValue(
875             thread, resultValues.GetTaggedValue(), resultIndex.GetTaggedValue());
876         JSHandle<JSTaggedValue> positionHandle(thread, positionTag);
877         uint32_t position = 0;
878         if (positionHandle->IsInt()) {
879             position = static_cast<uint32_t>(positionHandle->GetInt());
880         } else {
881             position = JSTaggedValue::ToUint32(thread, positionHandle);
882             // h. ReturnIfAbrupt(position).
883             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
884         }
885         // i. Let position be max(min(position, lengthS), 0).
886         position = std::max<uint32_t>(std::min<uint32_t>(position, length), 0);
887         // j. Let n be 1.
888         uint32_t index = 1;
889         // k. Let captures be an empty List.
890         JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(ncaptures);
891         // l. Repeat while n ≤ nCaptures
892         while (index <= ncaptures) {
893             // i. Let capN be Get(result, ToString(n)).
894             capN.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, resultValues.GetTaggedValue(), index));
895             // ii. ReturnIfAbrupt(capN).
896             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
897             // iii. If capN is not undefined, then
898             if (!capN->IsUndefined()) {
899                 // 1. Let capN be ToString(capN).
900                 JSHandle<EcmaString> capNStr = JSTaggedValue::ToString(thread, capN);
901                 // 2. ReturnIfAbrupt(capN).
902                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
903                 JSHandle<JSTaggedValue> capnStr = JSHandle<JSTaggedValue>::Cast(capNStr);
904                 capturesList->Set(thread, index - 1, capnStr);
905             } else {
906                 // iv. Append capN as the last element of captures.
907                 capturesList->Set(thread, index - 1, capN);
908             }
909             // v. Let n be n+1
910             ++index;
911         }
912 
913         // j. Let namedCaptures be ? Get(result, "groups").
914         JSHandle<JSTaggedValue> groupsKey = globalConst->GetHandledGroupsString();
915         JSTaggedValue named = ObjectFastOperator::FastGetPropertyByValue(thread,
916             resultValues.GetTaggedValue(), groupsKey.GetTaggedValue());
917         JSHandle<JSTaggedValue> namedCaptures(thread, named);
918         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
919         // m. If functionalReplace is true, then
920         CString replacement;
921         int emptyArrLength = 0;
922         if (namedCaptures->IsUndefined()) {
923             emptyArrLength = 3; // 3: «matched, pos, and string»
924         } else {
925             emptyArrLength = 4; // 4: «matched, pos, string, and groups»
926         }
927         JSHandle<TaggedArray> replacerArgs =
928             factory->NewTaggedArray(emptyArrLength + capturesList->GetLength());
929         if (functionalReplace) {
930             // i. Let replacerArgs be «matched».
931             replacerArgs->Set(thread, 0, getMatchString.GetTaggedValue());
932             // ii. Append in list order the elements of captures to the end of the List replacerArgs.
933             // iii. Append position and S as the last two elements of replacerArgs.
934             index = 0;
935             while (index < capturesList->GetLength()) {
936                 replacerArgs->Set(thread, index + 1, capturesList->Get(index));
937                 ++index;
938             }
939             replacerArgs->Set(thread, index + 1, JSTaggedValue(position));
940             replacerArgs->Set(thread, index + 2, inputStr.GetTaggedValue());  // 2: position of string
941             if (!namedCaptures->IsUndefined()) {
942                 replacerArgs->Set(thread, index + 3, namedCaptures.GetTaggedValue()); // 3: position of groups
943             }
944             // iv. Let replValue be Call(replaceValue, undefined, replacerArgs).
945             const int32_t argsLength = static_cast<int32_t>(replacerArgs->GetLength());
946             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
947             EcmaRuntimeCallInfo *info =
948                 EcmaInterpreter::NewRuntimeCallInfo(thread, inputReplaceValue, undefined, undefined, argsLength);
949             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
950             info->SetCallArg(argsLength, replacerArgs);
951             JSTaggedValue replaceResult = JSFunction::Call(info);
952             JSHandle<JSTaggedValue> replValue(thread, replaceResult);
953             // v. Let replacement be ToString(replValue).
954             JSHandle<EcmaString> replacementString = JSTaggedValue::ToString(thread, replValue);
955             // o. ReturnIfAbrupt(replacement).
956             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
957             replacement = ConvertToString(*replacementString, StringConvertedUsage::LOGICOPERATION);
958         } else {
959             // n. Else,
960             if (!namedCaptures->IsUndefined()) {
961                 JSHandle<JSObject> namedCapturesObj = JSTaggedValue::ToObject(thread, namedCaptures);
962                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
963                 namedCaptures = JSHandle<JSTaggedValue>::Cast(namedCapturesObj);
964             }
965             JSHandle<JSTaggedValue> replacementHandle(
966                 thread, BuiltinsString::GetSubstitution(thread, matchString, srcString,
967                                                         position, capturesList, namedCaptures,
968                                                         replaceValueHandle));
969             replacement = ConvertToString(EcmaString::Cast(replacementHandle->GetTaggedObject()),
970                                           StringConvertedUsage::LOGICOPERATION);
971         }
972         // p. If position ≥ nextSourcePosition, then
973         if (position >= nextSourcePosition) {
974             // ii. Let accumulatedResult be the String formed by concatenating the code units of the current value
975             // of accumulatedResult with the substring of S consisting of the code units from nextSourcePosition
976             // (inclusive) up to position (exclusive) and with the code units of replacement.
977             auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
978                 JSHandle<EcmaString>::Cast(inputStr), nextSourcePosition, position - nextSourcePosition);
979             accumulatedResult += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
980             accumulatedResult += replacement;
981             // iii. Let nextSourcePosition be position + matchLength.
982             nextSourcePosition = position + matchLength;
983         }
984     }
985     // 17. If nextSourcePosition ≥ lengthS, return accumulatedResult.
986     if (nextSourcePosition >= length) {
987         JSHandle<EcmaString> resultValue = factory->NewFromStdString(accumulatedResult);
988         if (useCache) {
989             RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, string,
990                                                     JSHandle<JSTaggedValue>(resultValue),
991                                                     RegExpExecResultCache::REPLACE_TYPE, nextIndexHandle->GetInt(),
992                                                     inputReplaceValue.GetTaggedValue());
993         }
994         return resultValue.GetTaggedValue();
995     }
996     // 18. Return the String formed by concatenating the code units of accumulatedResult with the substring of S
997     // consisting of the code units from nextSourcePosition (inclusive) up through the final code unit of S(inclusive).
998     auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
999         JSHandle<EcmaString>::Cast(inputStr), nextSourcePosition, length - nextSourcePosition);
1000     accumulatedResult += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1001     JSHandle<EcmaString> resultValue = factory->NewFromStdString(accumulatedResult);
1002     if (useCache) {
1003         RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, string,
1004                                                 JSHandle<JSTaggedValue>(resultValue),
1005                                                 RegExpExecResultCache::REPLACE_TYPE, nextIndexHandle->GetInt(),
1006                                                 inputReplaceValue.GetTaggedValue());
1007     }
1008     return resultValue.GetTaggedValue();
1009 }
1010 
1011 // 21.2.5.9
Search(EcmaRuntimeCallInfo * argv)1012 JSTaggedValue BuiltinsRegExp::Search(EcmaRuntimeCallInfo *argv)
1013 {
1014     ASSERT(argv);
1015     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Search);
1016     JSThread *thread = argv->GetThread();
1017     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1018     // 1. Let rx be the this value.
1019     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
1020     // 3. Let S be ToString(string).
1021     JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
1022     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
1023 
1024     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1025     JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
1026     if (!thisObj->IsECMAObject()) {
1027         // 2. If Type(rx) is not Object, throw a TypeError exception.
1028         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
1029     }
1030     // 4. Let previousLastIndex be ? Get(rx, "lastIndex").
1031     JSHandle<JSTaggedValue> lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString());
1032     JSHandle<JSTaggedValue> previousLastIndex = JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue();
1033     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1034     // 5. If SameValue(previousLastIndex, 0) is false, then
1035     // Perform ? Set(rx, "lastIndex", 0, true).
1036     if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), JSTaggedValue(0))) {
1037         JSHandle<JSTaggedValue> value(thread, JSTaggedValue(0));
1038         JSObject::SetProperty(thread, thisObj, lastIndexString, value, true);
1039         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1040     }
1041     // 6. Let result be ? RegExpExec(rx, S).
1042     JSHandle<JSTaggedValue> result(thread, RegExpExec(thread, thisObj, string, false));
1043     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1044     // 7. Let currentLastIndex be ? Get(rx, "lastIndex").
1045     JSHandle<JSTaggedValue> currentLastIndex = JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue();
1046     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1047     // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
1048     // Perform ? Set(rx, "lastIndex", previousLastIndex, true).
1049     if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), currentLastIndex.GetTaggedValue())) {
1050         JSObject::SetProperty(thread, thisObj, lastIndexString, previousLastIndex, true);
1051         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1052     }
1053     // 9. If result is null, return -1.
1054     if (result->IsNull()) {
1055         return JSTaggedValue(-1);
1056     }
1057     // 10. Return ? Get(result, "index").
1058     JSHandle<JSTaggedValue> index(thread->GlobalConstants()->GetHandledIndexString());
1059     return JSObject::GetProperty(thread, result, index).GetValue().GetTaggedValue();
1060 }
1061 
1062 // 21.2.5.11
1063 // NOLINTNEXTLINE(readability-function-size)
Split(EcmaRuntimeCallInfo * argv)1064 JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv)
1065 {
1066     ASSERT(argv);
1067     BUILTINS_API_TRACE(argv->GetThread(), RegExp, Split);
1068     JSThread *thread = argv->GetThread();
1069     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1070     bool useCache = false;
1071     // 1. Let rx be the this value.
1072     JSHandle<JSTaggedValue> thisObj = GetThis(argv);
1073     auto ecmaVm = thread->GetEcmaVM();
1074     // 3. Let S be ToString(string).
1075     JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
1076     JSHandle<JSTaggedValue> limit = GetCallArg(argv, 1);
1077     JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
1078 
1079     // 4. ReturnIfAbrupt(string).
1080     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1081     JSHandle<JSTaggedValue> jsString = JSHandle<JSTaggedValue>::Cast(stringHandle);
1082     if (!thisObj->IsECMAObject()) {
1083         // 2. If Type(rx) is not Object, throw a TypeError exception.
1084         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
1085     }
1086     // 5. Let C be SpeciesConstructor(rx, %RegExp%).
1087     JSHandle<JSTaggedValue> defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction();
1088     JSHandle<JSObject> objHandle(thisObj);
1089     JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
1090     // 6. ReturnIfAbrupt(C).
1091     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1092     // 7. Let flags be ToString(Get(rx, "flags")).
1093     ObjectFactory *factory = ecmaVm->GetFactory();
1094     const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
1095     JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
1096     JSHandle<JSTaggedValue> taggedFlags = JSObject::GetProperty(thread, thisObj, flagsString).GetValue();
1097     JSHandle<EcmaString> flags;
1098 
1099     if (taggedFlags->IsUndefined()) {
1100         flags = factory->GetEmptyString();
1101     } else {
1102         flags = JSTaggedValue::ToString(thread, taggedFlags);
1103     }
1104     //  8. ReturnIfAbrupt(flags).
1105     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1106     // 9. If flags contains "u", let unicodeMatching be true.
1107     // 10. Else, let unicodeMatching be false.
1108     JSHandle<EcmaString> uStringHandle(globalConstants->GetHandledUString());
1109     bool unicodeMatching = (EcmaStringAccessor::IndexOf(*flags, *uStringHandle) != -1);
1110     // 11. If flags contains "y", let newFlags be flags.
1111     JSHandle<EcmaString> newFlagsHandle;
1112     JSHandle<EcmaString> yStringHandle = JSHandle<EcmaString>::Cast(globalConstants->GetHandledYString());
1113     if (EcmaStringAccessor::IndexOf(*flags, *yStringHandle) != -1) {
1114         newFlagsHandle = flags;
1115     } else {
1116         // 12. Else, let newFlags be the string that is the concatenation of flags and "y".
1117         JSHandle<EcmaString> yStr = JSHandle<EcmaString>::Cast(globalConstants->GetHandledYString());
1118         newFlagsHandle = factory->ConcatFromString(flags, yStr);
1119     }
1120 
1121     // 17. If limit is undefined, let lim be 2^32–1; else let lim be ToUint32(limit).
1122     uint32_t lim;
1123     if (limit->IsUndefined()) {
1124         lim = MAX_SPLIT_LIMIT;
1125     } else {
1126         lim = JSTaggedValue::ToUint32(thread, limit);
1127         // 18. ReturnIfAbrupt(lim).
1128         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1129     }
1130 
1131     if (lim == MAX_SPLIT_LIMIT) {
1132         useCache = true;
1133     }
1134 
1135     JSHandle<JSRegExp> regexpHandle(thisObj);
1136     JSMutableHandle<JSTaggedValue> pattern(thread, JSTaggedValue::Undefined());
1137     JSMutableHandle<JSTaggedValue> flagsBits(thread, JSTaggedValue::Undefined());
1138     if (thisObj->IsJSRegExp()) {
1139         pattern.Update(regexpHandle->GetOriginalSource());
1140         flagsBits.Update(regexpHandle->GetOriginalFlags());
1141     }
1142     JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
1143     if (useCache) {
1144         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, inputString,
1145                                                                  RegExpExecResultCache::SPLIT_TYPE, thisObj);
1146         if (cacheResult != JSTaggedValue::Undefined()) {
1147             return cacheResult;
1148         }
1149     }
1150 
1151     // 13. Let splitter be Construct(C, «rx, newFlags»).
1152     JSHandle<JSObject> globalObject(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject());
1153     JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
1154     EcmaRuntimeCallInfo *runtimeInfo =
1155         EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 2); // 2: two args
1156     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1157     runtimeInfo->SetCallArg(thisObj.GetTaggedValue(), newFlagsHandle.GetTaggedValue());
1158     JSTaggedValue taggedSplitter = JSFunction::Construct(runtimeInfo);
1159     // 14. ReturnIfAbrupt(splitter).
1160     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1161 
1162     JSHandle<JSTaggedValue> splitter(thread, taggedSplitter);
1163     // 15. Let A be ArrayCreate(0).
1164     JSHandle<JSObject> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1165     // 16. Let lengthA be 0.
1166     uint32_t aLength = 0;
1167 
1168     // 19. Let size be the number of elements in S.
1169     uint32_t size = EcmaStringAccessor(jsString->GetTaggedObject()).GetLength();
1170     // 20. Let p be 0.
1171     uint32_t startIndex = 0;
1172     // 21. If lim = 0, return A.
1173     if (lim == 0) {
1174         return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1175     }
1176     // 22. If size = 0, then
1177     if (size == 0) {
1178         // a. Let z be RegExpExec(splitter, S).
1179         JSHandle<JSTaggedValue> execResult(thread, RegExpExec(thread, splitter, jsString, useCache));
1180         // b. ReturnIfAbrupt(z).
1181         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1182         // c. If z is not null, return A.
1183         if (!execResult->IsNull()) {
1184             return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1185         }
1186         // d. Assert: The following call will never result in an abrupt completion.
1187         // e. Perform CreateDataProperty(A, "0", S).
1188         JSObject::CreateDataProperty(thread, array, 0, jsString);
1189         // f. Return A.
1190         return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1191     }
1192     // 23. Let q be p.
1193     uint32_t endIndex = startIndex;
1194     JSMutableHandle<JSTaggedValue> lastIndexvalue(thread, JSTaggedValue(endIndex));
1195     // 24. Repeat, while q < size
1196     JSHandle<JSTaggedValue> lastIndexString = globalConstants->GetHandledLastIndexString();
1197     while (endIndex < size) {
1198         // a. Let setStatus be Set(splitter, "lastIndex", q, true).
1199         lastIndexvalue.Update(JSTaggedValue(endIndex));
1200         JSObject::SetProperty(thread, splitter, lastIndexString, lastIndexvalue, true);
1201         // b. ReturnIfAbrupt(setStatus).
1202         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1203         JSHandle<JSTaggedValue> execResult(thread, RegExpExec(thread, splitter, jsString, useCache));
1204         // d. ReturnIfAbrupt(z).
1205         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1206         // e. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching).
1207         if (execResult->IsNull()) {
1208             endIndex = AdvanceStringIndex(jsString, endIndex, unicodeMatching);
1209         } else {
1210             // f. Else z is not null,
1211             // i. Let e be ToLength(Get(splitter, "lastIndex")).
1212             JSHandle<JSTaggedValue> lastIndexHandle =
1213                 JSObject::GetProperty(thread, splitter, lastIndexString).GetValue();
1214             JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexHandle);
1215             // ii. ReturnIfAbrupt(e).
1216             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1217             uint32_t lastIndex = lastIndexNumber.GetNumber();
1218             // iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching).
1219             if (lastIndex == startIndex) {
1220                 endIndex = AdvanceStringIndex(jsString, endIndex, unicodeMatching);
1221             } else {
1222                 // iv. Else e != p,
1223                 // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p
1224                 // (inclusive) through q (exclusive).
1225                 auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1226                     JSHandle<EcmaString>::Cast(jsString), startIndex, endIndex - startIndex);
1227                 std::string stdStrT = EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1228                 // 2. Assert: The following call will never result in an abrupt completion.
1229                 // 3. Perform CreateDataProperty(A, ToString(lengthA), T).
1230                 JSHandle<JSTaggedValue> tValue(factory->NewFromStdString(stdStrT));
1231                 JSObject::CreateDataProperty(thread, array, aLength, tValue);
1232                 // 4. Let lengthA be lengthA +1.
1233                 ++aLength;
1234                 // 5. If lengthA = lim, return A.
1235                 if (aLength == lim) {
1236                     if (useCache) {
1237                         RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString,
1238                                                                 JSHandle<JSTaggedValue>(array),
1239                                                                 RegExpExecResultCache::SPLIT_TYPE, lastIndex);
1240                     }
1241                     return array.GetTaggedValue();
1242                 }
1243                 // 6. Let p be e.
1244                 startIndex = lastIndex;
1245                 // 7. Let numberOfCaptures be ToLength(Get(z, "length")).
1246                 JSHandle<JSTaggedValue> lengthString(thread->GlobalConstants()->GetHandledLengthString());
1247                 JSHandle<JSTaggedValue> capturesHandle =
1248                     JSObject::GetProperty(thread, execResult, lengthString).GetValue();
1249                 JSTaggedNumber numberOfCapturesNumber = JSTaggedValue::ToLength(thread, capturesHandle);
1250                 // 8. ReturnIfAbrupt(numberOfCaptures).
1251                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1252                 uint32_t numberOfCaptures = numberOfCapturesNumber.GetNumber();
1253                 // 9. Let numberOfCaptures be max(numberOfCaptures-1, 0).
1254                 numberOfCaptures = (numberOfCaptures == 0) ? 0 : numberOfCaptures - 1;
1255                 // 10. Let i be 1.
1256                 uint32_t i = 1;
1257                 // 11. Repeat, while i ≤ numberOfCaptures.
1258                 while (i <= numberOfCaptures) {
1259                     // a. Let nextCapture be Get(z, ToString(i)).
1260                     JSHandle<JSTaggedValue> nextCapture = JSObject::GetProperty(thread, execResult, i).GetValue();
1261                     // b. ReturnIfAbrupt(nextCapture).
1262                     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1263                     // c. Perform CreateDataProperty(A, ToString(lengthA), nextCapture).
1264                     JSObject::CreateDataProperty(thread, array, aLength, nextCapture);
1265                     // d. Let i be i + 1.
1266                     ++i;
1267                     // e. Let lengthA be lengthA +1.
1268                     ++aLength;
1269                     // f. If lengthA = lim, return A.
1270                     if (aLength == lim) {
1271                         if (useCache) {
1272                             RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString,
1273                                                                     JSHandle<JSTaggedValue>(array),
1274                                                                     RegExpExecResultCache::SPLIT_TYPE, lastIndex);
1275                         }
1276                         return array.GetTaggedValue();
1277                     }
1278                 }
1279                 // 12. Let q be p.
1280                 endIndex = startIndex;
1281             }
1282         }
1283     }
1284     // 25. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive)
1285     // through size (exclusive).
1286     auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1287         JSHandle<EcmaString>::Cast(jsString), startIndex, size - startIndex);
1288     std::string stdStrT = EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1289     // 26. Assert: The following call will never result in an abrupt completion.
1290     // 27. Perform CreateDataProperty(A, ToString(lengthA), t).
1291     JSHandle<JSTaggedValue> tValue(factory->NewFromStdString(stdStrT));
1292     JSObject::CreateDataProperty(thread, array, aLength, tValue);
1293     if (lim == MAX_SPLIT_LIMIT) {
1294         RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString,
1295                                                 JSHandle<JSTaggedValue>(array), RegExpExecResultCache::SPLIT_TYPE,
1296                                                 endIndex);
1297     }
1298     // 28. Return A.
1299     return array.GetTaggedValue();
1300 }
1301 
1302 // NOLINTNEXTLINE(readability-non-const-parameter)
Matcher(JSThread * thread,const JSHandle<JSTaggedValue> & regexp,const uint8_t * buffer,size_t length,int32_t lastIndex,bool isUtf16)1303 RegExpExecutor::MatchResult BuiltinsRegExp::Matcher(JSThread *thread, const JSHandle<JSTaggedValue> &regexp,
1304                                                     const uint8_t *buffer, size_t length, int32_t lastIndex,
1305                                                     bool isUtf16)
1306 {
1307     // get bytecode
1308     JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
1309     void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
1310     auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
1311     // execute
1312     Chunk chunk(thread->GetNativeAreaAllocator());
1313     RegExpExecutor executor(&chunk);
1314     if (lastIndex < 0) {
1315         lastIndex = 0;
1316     }
1317     bool ret = executor.Execute(buffer, lastIndex, static_cast<uint32_t>(length), bytecodeBuffer, isUtf16);
1318     RegExpExecutor::MatchResult result = executor.GetResult(thread, ret);
1319     return result;
1320 }
1321 
AdvanceStringIndex(const JSHandle<JSTaggedValue> & inputStr,uint32_t index,bool unicode)1322 uint32_t BuiltinsRegExp::AdvanceStringIndex(const JSHandle<JSTaggedValue> &inputStr, uint32_t index,
1323                                             bool unicode)
1324 {
1325     // 1. Assert: Type(S) is String.
1326     ASSERT(inputStr->IsString());
1327     // 2. Assert: index is an integer such that 0≤index≤2^53 - 1
1328     ASSERT(index <= pow(2, 53) - 1);
1329     // 3. Assert: Type(unicode) is Boolean.
1330     // 4. If unicode is false, return index+1.
1331     if (!unicode) {
1332         return index + 1;
1333     }
1334     // 5. Let length be the number of code units in S.
1335     uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
1336     // 6. If index+1 ≥ length, return index+1.
1337     if (index + 1 >= length) {
1338         return index + 1;
1339     }
1340     // 7. Let first be the code unit value at index index in S.
1341     uint16_t first = EcmaStringAccessor(inputStr->GetTaggedObject()).Get(index);
1342     // 8. If first < 0xD800 or first > 0xDFFF, return index+1.
1343     if (first < 0xD800 || first > 0xDFFF) {  // NOLINT(readability-magic-numbers)
1344         return index + 1;
1345     }
1346     // 9. Let second be the code unit value at index index+1 in S.
1347     uint16_t second = EcmaStringAccessor(inputStr->GetTaggedObject()).Get(index + 1);
1348     // 10. If second < 0xDC00 or second > 0xDFFF, return index+1.
1349     if (second < 0xDC00 || second > 0xDFFF) {  // NOLINT(readability-magic-numbers)
1350         return index + 1;
1351     }
1352     // 11. Return index + 2.
1353     return index + 2;
1354 }
1355 
GetFlagsInternal(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const uint8_t mask)1356 bool BuiltinsRegExp::GetFlagsInternal(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const uint8_t mask)
1357 {
1358     // 1. Let R be the this value.
1359     // 2. If Type(R) is not Object, throw a TypeError exception.
1360     if (!obj->IsECMAObject()) {
1361         // throw a TypeError exception.
1362         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", false);
1363     }
1364     // 3. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception.
1365     JSHandle<JSObject> patternObj = JSHandle<JSObject>::Cast(obj);
1366     if (!patternObj->IsJSRegExp()) {
1367         // throw a TypeError exception.
1368         THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalFlags]]", false);
1369     }
1370     // 4. Let flags be the value of R’s [[OriginalFlags]] internal slot.
1371     JSHandle<JSRegExp> regexpObj(thread, JSRegExp::Cast(obj->GetTaggedObject()));
1372     // 5. If flags contains the code unit "[flag]", return true.
1373     // 6. Return false.
1374     uint8_t flags = static_cast<uint8_t>(regexpObj->GetOriginalFlags().GetInt());
1375     return flags & mask;
1376 }
1377 // 21.2.5.2.2
RegExpBuiltinExec(JSThread * thread,const JSHandle<JSTaggedValue> & regexp,const JSHandle<JSTaggedValue> & inputStr,bool useCache)1378 JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle<JSTaggedValue> &regexp,
1379                                                 const JSHandle<JSTaggedValue> &inputStr, bool useCache)
1380 {
1381     ASSERT(JSObject::IsRegExp(thread, regexp));
1382     ASSERT(inputStr->IsString());
1383 
1384     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1385     JSHandle<JSTaggedValue> lastIndexHandle = globalConst->GetHandledLastIndexString();
1386     JSTaggedValue result =
1387         ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue());
1388     int32_t lastIndex = 0;
1389     if (result.IsInt()) {
1390         lastIndex = result.GetInt();
1391     } else {
1392         JSHandle<JSTaggedValue> lastIndexResult(thread, result);
1393         JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexResult);
1394         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1395         lastIndex = lastIndexNumber.GetNumber();
1396     }
1397 
1398     JSHandle<JSTaggedValue> globalHandle = globalConst->GetHandledGlobalString();
1399     bool global = ObjectFastOperator::FastGetPropertyByValue(
1400         thread, regexp.GetTaggedValue(), globalHandle.GetTaggedValue()).ToBoolean();
1401     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1402     JSHandle<JSTaggedValue> stickyHandle = globalConst->GetHandledStickyString();
1403     bool sticky = ObjectFastOperator::FastGetPropertyByValue(
1404         thread, regexp.GetTaggedValue(), stickyHandle.GetTaggedValue()).ToBoolean();
1405     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1406     if (!global && !sticky) {
1407         lastIndex = 0;
1408     }
1409 
1410     JSHandle<JSRegExp> regexpObj(regexp);
1411     JSMutableHandle<JSTaggedValue> pattern(thread, regexpObj->GetOriginalSource());
1412     JSMutableHandle<JSTaggedValue> flags(thread, regexpObj->GetOriginalFlags());
1413 
1414     JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
1415     uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
1416     if (lastIndex > static_cast<int32_t>(length)) {
1417         ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(),
1418                                                    JSTaggedValue(0));
1419         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1420         return JSTaggedValue::Null();
1421     }
1422     JSHandle<EcmaString> inputString = JSTaggedValue::ToString(thread, inputStr);
1423     bool isUtf16 = EcmaStringAccessor(inputString).IsUtf16();
1424     auto inputPtr = EcmaStringAccessor(inputString).ToOneByteDataForced();
1425     const uint8_t *strBuffer = inputPtr.get();
1426     size_t stringLength = EcmaStringAccessor(inputString).GetLength();
1427     RegExpExecutor::MatchResult matchResult = Matcher(thread, regexp, strBuffer, stringLength, lastIndex, isUtf16);
1428     if (!matchResult.isSuccess_) {
1429         if (global || sticky) {
1430             JSHandle<JSTaggedValue> lastIndexValue(thread, JSTaggedValue(0));
1431             ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
1432                 lastIndexHandle.GetTaggedValue(), JSTaggedValue(0));
1433             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1434         }
1435         return JSTaggedValue::Null();
1436     }
1437     uint32_t endIndex = matchResult.endIndex_;
1438     if (global || sticky) {
1439         // a. Let setStatus be Set(R, "lastIndex", e, true).
1440         ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(),
1441                                                    JSTaggedValue(endIndex));
1442         // b. ReturnIfAbrupt(setStatus).
1443         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1444     }
1445     uint32_t capturesSize = matchResult.captures_.size();
1446     JSHandle<JSObject> results(JSArray::ArrayCreate(thread, JSTaggedNumber(capturesSize)));
1447     uint32_t matchIndex = matchResult.index_;
1448     // 24. Perform CreateDataProperty(A, "index", matchIndex).
1449     JSHandle<JSTaggedValue> indexKey = globalConst->GetHandledIndexString();
1450     JSHandle<JSTaggedValue> indexValue(thread, JSTaggedValue(matchIndex));
1451     JSObject::CreateDataProperty(thread, results, indexKey, indexValue);
1452     // 25. Perform CreateDataProperty(A, "input", S).
1453     JSHandle<JSTaggedValue> inputKey = globalConst->GetHandledInputString();
1454 
1455     JSHandle<JSTaggedValue> inputValue(thread, static_cast<EcmaString *>(inputStr->GetTaggedObject()));
1456     JSObject::CreateDataProperty(thread, results, inputKey, inputValue);
1457     // 27. Perform CreateDataProperty(A, "0", matched_substr).
1458     JSHandle<JSTaggedValue> zeroValue(matchResult.captures_[0].second);
1459     JSObject::CreateDataProperty(thread, results, 0, zeroValue);
1460     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1461     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1462 
1463     JSHandle<JSTaggedValue> groupName(thread, regexpObj->GetGroupName());
1464     JSMutableHandle<JSTaggedValue> groups(thread, JSTaggedValue::Undefined());
1465     if (!groupName->IsUndefined()) {
1466         JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
1467         JSHandle<JSObject> nullObj = factory->OrdinaryNewJSObjectCreate(nullHandle);
1468         groups.Update(nullObj.GetTaggedValue());
1469     }
1470     JSHandle<JSTaggedValue> groupsKey = globalConst->GetHandledGroupsString();
1471     JSObject::CreateDataProperty(thread, results, groupsKey, groups);
1472     // Create a new RegExp on global
1473     JSHandle<JSObject> globalRegExp = JSHandle<JSObject>(env->GetRegExpFunction());
1474     JSMutableHandle<JSTaggedValue> keyString(thread, JSTaggedValue::Undefined());
1475     uint32_t captureIndex = 1;
1476     // 28. For each integer i such that i > 0 and i <= n
1477     for (; captureIndex < capturesSize; captureIndex++) {
1478         // a. Let capture_i be ith element of r's captures List
1479         JSTaggedValue capturedValue;
1480         if (matchResult.captures_[captureIndex].first) {
1481             capturedValue = JSTaggedValue::Undefined();
1482         } else {
1483             capturedValue = matchResult.captures_[captureIndex].second.GetTaggedValue();
1484         }
1485         JSHandle<JSTaggedValue> iValue(thread, capturedValue);
1486         // add to RegExp.$i and i must <= 9
1487         if (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) {
1488             keyString.Update(GetDollarString(thread, static_cast<RegExpGlobalArrayIndex>(captureIndex)));
1489             ObjectOperator op(thread, globalRegExp, keyString);
1490             PropertyBox *cell = PropertyBox::Cast(op.GetValue().GetTaggedObject());
1491             cell->SetValue(thread, iValue);
1492         }
1493 
1494         JSObject::CreateDataProperty(thread, results, captureIndex, iValue);
1495         if (!groupName->IsUndefined()) {
1496             JSHandle<JSObject> groupObject = JSHandle<JSObject>::Cast(groups);
1497             TaggedArray *groupArray = TaggedArray::Cast(regexpObj->GetGroupName().GetTaggedObject());
1498             if (groupArray->GetLength() > captureIndex - 1) {
1499                 JSHandle<JSTaggedValue> skey(thread, groupArray->Get(captureIndex - 1));
1500                 JSObject::CreateDataProperty(thread, groupObject, skey, iValue);
1501             }
1502         }
1503     }
1504     JSHandle<JSTaggedValue> emptyString = thread->GlobalConstants()->GetHandledEmptyString();
1505     while (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) {
1506         keyString.Update(GetDollarString(thread, static_cast<RegExpGlobalArrayIndex>(captureIndex)));
1507         ObjectOperator op(thread, globalRegExp, keyString);
1508         PropertyBox *cell = PropertyBox::Cast(op.GetValue().GetTaggedObject());
1509         cell->SetValue(thread, emptyString);
1510         ++captureIndex;
1511     }
1512     if (lastIndex == 0 && useCache) {
1513         RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputStr,
1514                                                 JSHandle<JSTaggedValue>(results), RegExpExecResultCache::EXEC_TYPE,
1515                                                 endIndex);
1516     }
1517     // 29. Return A.
1518     return results.GetTaggedValue();
1519 }
1520 
1521 // 21.2.5.2.1
RegExpExec(JSThread * thread,const JSHandle<JSTaggedValue> & regexp,const JSHandle<JSTaggedValue> & inputString,bool useCache)1522 JSTaggedValue BuiltinsRegExp::RegExpExec(JSThread *thread, const JSHandle<JSTaggedValue> &regexp,
1523                                          const JSHandle<JSTaggedValue> &inputString, bool useCache)
1524 {
1525     // 1. Assert: Type(R) is Object.
1526     ASSERT(regexp->IsECMAObject());
1527     // 2. Assert: Type(S) is String.
1528     ASSERT(inputString->IsString());
1529     // 3. Let exec be Get(R, "exec").
1530     JSHandle<EcmaString> inputStr = JSTaggedValue::ToString(thread, inputString);
1531 
1532     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1533     JSHandle<JSTaggedValue> execHandle = globalConst->GetHandledExecString();
1534     JSTaggedValue execVal = ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(),
1535                                                                        execHandle.GetTaggedValue());
1536     JSHandle<JSTaggedValue> exec(thread, execVal);
1537     // 4. ReturnIfAbrupt(exec).
1538     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1539     // 5. If IsCallable(exec) is true, then
1540     if (exec->IsCallable()) {
1541         JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1542         EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, exec, regexp, undefined, 1);
1543         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1544         info->SetCallArg(inputStr.GetTaggedValue());
1545         JSTaggedValue result = JSFunction::Call(info);
1546         // b. ReturnIfAbrupt(result).
1547         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1548         if (!result.IsECMAObject() && !result.IsNull()) {
1549             // throw a TypeError exception.
1550             THROW_TYPE_ERROR_AND_RETURN(thread, "exec result is null or is not Object", JSTaggedValue::Exception());
1551         }
1552         return result;
1553     }
1554     // 6. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
1555     if (!regexp->IsJSRegExp()) {
1556         // throw a TypeError exception.
1557         THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have a [[RegExpMatcher]]", JSTaggedValue::Exception());
1558     }
1559     // 7. Return RegExpBuiltinExec(R, S).
1560     return RegExpBuiltinExec(thread, regexp, inputString, useCache);
1561 }
1562 
1563 // 21.2.3.2.1
RegExpAlloc(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget)1564 JSTaggedValue BuiltinsRegExp::RegExpAlloc(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget)
1565 {
1566     /**
1567      * 1. Let obj be OrdinaryCreateFromConstructor(newTarget, "%RegExpPrototype%",
1568      * «[[RegExpMatcher]],[[OriginalSource]], [[OriginalFlags]]»).
1569      * */
1570     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1571     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1572     JSHandle<JSTaggedValue> func = env->GetRegExpFunction();
1573     JSHandle<JSTaggedValue> obj(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(func), newTarget));
1574     // 2. ReturnIfAbrupt(obj).
1575     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1576     // 5. Return obj.
1577     return obj.GetTaggedValue();
1578 }
1579 
UpdateExpressionFlags(JSThread * thread,const CString & checkStr)1580 uint32_t BuiltinsRegExp::UpdateExpressionFlags(JSThread *thread, const CString &checkStr)
1581 {
1582     uint32_t flagsBits = 0;
1583     uint32_t flagsBitsTemp = 0;
1584     for (char i : checkStr) {
1585         switch (i) {
1586             case 'g':
1587                 flagsBitsTemp = RegExpParser::FLAG_GLOBAL;
1588                 break;
1589             case 'i':
1590                 flagsBitsTemp = RegExpParser::FLAG_IGNORECASE;
1591                 break;
1592             case 'm':
1593                 flagsBitsTemp = RegExpParser::FLAG_MULTILINE;
1594                 break;
1595             case 's':
1596                 flagsBitsTemp = RegExpParser::FLAG_DOTALL;
1597                 break;
1598             case 'u':
1599                 flagsBitsTemp = RegExpParser::FLAG_UTF16;
1600                 break;
1601             case 'y':
1602                 flagsBitsTemp = RegExpParser::FLAG_STICKY;
1603                 break;
1604             default: {
1605                 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1606                 JSHandle<JSObject> syntaxError =
1607                     factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags");
1608                 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0);
1609             }
1610         }
1611         if ((flagsBits & flagsBitsTemp) != 0) {
1612             ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1613             JSHandle<JSObject> syntaxError =
1614                 factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags");
1615             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0);
1616         }
1617         flagsBits |= flagsBitsTemp;
1618     }
1619     return flagsBits;
1620 }
1621 
GetDollarString(JSThread * thread,RegExpGlobalArrayIndex index)1622 JSHandle<JSTaggedValue> BuiltinsRegExp::GetDollarString(JSThread *thread, RegExpGlobalArrayIndex index)
1623 {
1624     switch (index) {
1625         case DOLLAR_ONE:
1626             return thread->GlobalConstants()->GetHandledDollarStringOne();
1627         case DOLLAR_TWO:
1628             return thread->GlobalConstants()->GetHandledDollarStringTwo();
1629         case DOLLAR_THREE:
1630             return thread->GlobalConstants()->GetHandledDollarStringThree();
1631         case DOLLAR_FOUR:
1632             return thread->GlobalConstants()->GetHandledDollarStringFour();
1633         case DOLLAR_FIVE:
1634             return thread->GlobalConstants()->GetHandledDollarStringFive();
1635         case DOLLAR_SIX:
1636             return thread->GlobalConstants()->GetHandledDollarStringSix();
1637         case DOLLAR_SEVEN:
1638             return thread->GlobalConstants()->GetHandledDollarStringSeven();
1639         case DOLLAR_EIGHT:
1640             return thread->GlobalConstants()->GetHandledDollarStringEight();
1641         case DOLLAR_NINE:
1642             return thread->GlobalConstants()->GetHandledDollarStringNine();
1643         default:
1644             return thread->GlobalConstants()->GetHandledEmptyString();
1645     }
1646 }
1647 
FlagsBitsToString(JSThread * thread,uint8_t flags)1648 JSTaggedValue BuiltinsRegExp::FlagsBitsToString(JSThread *thread, uint8_t flags)
1649 {
1650     ASSERT((flags & 0xC0) == 0);  // 0xC0: first 2 bits of flags must be 0
1651 
1652     uint8_t *flagsStr = new uint8_t[7];  // 7: maximum 6 flags + '\0'
1653     size_t flagsLen = 0;
1654     if (flags & RegExpParser::FLAG_GLOBAL) {
1655         flagsStr[flagsLen] = 'g';
1656         flagsLen++;
1657     }
1658     if (flags & RegExpParser::FLAG_IGNORECASE) {
1659         flagsStr[flagsLen] = 'i';
1660         flagsLen++;
1661     }
1662     if (flags & RegExpParser::FLAG_MULTILINE) {
1663         flagsStr[flagsLen] = 'm';
1664         flagsLen++;
1665     }
1666     if (flags & RegExpParser::FLAG_DOTALL) {
1667         flagsStr[flagsLen] = 's';
1668         flagsLen++;
1669     }
1670     if (flags & RegExpParser::FLAG_UTF16) {
1671         flagsStr[flagsLen] = 'u';
1672         flagsLen++;
1673     }
1674     if (flags & RegExpParser::FLAG_STICKY) {
1675         flagsStr[flagsLen] = 'y';
1676         flagsLen++;
1677     }
1678     flagsStr[flagsLen] = '\0';
1679     JSHandle<EcmaString> flagsString = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(flagsStr, flagsLen);
1680     delete[] flagsStr;
1681 
1682     return flagsString.GetTaggedValue();
1683 }
1684 
1685 // 21.2.3.2.2
RegExpInitialize(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags)1686 JSTaggedValue BuiltinsRegExp::RegExpInitialize(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
1687                                                const JSHandle<JSTaggedValue> &pattern,
1688                                                const JSHandle<JSTaggedValue> &flags)
1689 {
1690     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1691     JSHandle<EcmaString> patternStrHandle;
1692     uint8_t flagsBits = 0;
1693     // 1. If pattern is undefined, let P be the empty String.
1694     if (pattern->IsUndefined()) {
1695         patternStrHandle = factory->GetEmptyString();
1696     } else {
1697         // 2. Else, let P be ToString(pattern).
1698         patternStrHandle = JSTaggedValue::ToString(thread, pattern);
1699         // 3. ReturnIfAbrupt(P).
1700         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1701     }
1702     // 4. If flags is undefined, let F be the empty String.
1703     if (flags->IsUndefined()) {
1704         flagsBits = 0;
1705     } else if (flags->IsInt()) {
1706         flagsBits = static_cast<uint8_t>(flags->GetInt());
1707     } else {
1708         // 5. Else, let F be ToString(flags).
1709         JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, flags);
1710         // 6. ReturnIfAbrupt(F).
1711         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1712         /**
1713          * 7. If F contains any code unit other than "g", "i", "m", "u", or "y" or if it contains the same code
1714          * unit more than once, throw a SyntaxError exception.
1715          **/
1716         CString checkStr = ConvertToString(*flagsStrHandle, StringConvertedUsage::LOGICOPERATION);
1717         flagsBits = static_cast<uint8_t>(UpdateExpressionFlags(thread, checkStr));
1718         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1719     }
1720     // String -> CString
1721     CString patternStdStr = ConvertToString(*patternStrHandle, StringConvertedUsage::LOGICOPERATION);
1722     // 9. 10.
1723     Chunk chunk(thread->GetNativeAreaAllocator());
1724     RegExpParser parser = RegExpParser(&chunk);
1725     RegExpParserCache *regExpParserCache = thread->GetEcmaVM()->GetRegExpParserCache();
1726     CVector<CString> groupName;
1727     auto getCache = regExpParserCache->GetCache(*patternStrHandle, flagsBits, groupName);
1728     if (getCache.first == JSTaggedValue::Hole()) {
1729         parser.Init(const_cast<char *>(reinterpret_cast<const char *>(patternStdStr.c_str())), patternStdStr.size(),
1730                     flagsBits);
1731         parser.Parse();
1732         if (parser.IsError()) {
1733             JSHandle<JSObject> syntaxError =
1734                 factory->GetJSError(base::ErrorType::SYNTAX_ERROR, parser.GetErrorMsg().c_str());
1735             THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception());
1736         }
1737         groupName = parser.GetGroupNames();
1738     }
1739     JSHandle<JSRegExp> regexp(thread, JSRegExp::Cast(obj->GetTaggedObject()));
1740     // 11. Set the value of obj’s [[OriginalSource]] internal slot to P.
1741     regexp->SetOriginalSource(thread, patternStrHandle.GetTaggedValue());
1742     // 12. Set the value of obj’s [[OriginalFlags]] internal slot to F.
1743     regexp->SetOriginalFlags(thread, JSTaggedValue(flagsBits));
1744     if (!groupName.empty()) {
1745         JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(groupName.size());
1746         for (size_t i = 0; i < groupName.size(); ++i) {
1747             JSHandle<JSTaggedValue> flagsKey(factory->NewFromStdString(groupName[i].c_str()));
1748             taggedArray->Set(thread, i, flagsKey);
1749         }
1750         regexp->SetGroupName(thread, taggedArray);
1751     }
1752     // 13. Set obj’s [[RegExpMatcher]] internal slot.
1753     if (getCache.first == JSTaggedValue::Hole()) {
1754         auto bufferSize = parser.GetOriginBufferSize();
1755         auto buffer = parser.GetOriginBuffer();
1756         factory->NewJSRegExpByteCodeData(regexp, buffer, bufferSize);
1757         regExpParserCache->SetCache(*patternStrHandle, flagsBits, regexp->GetByteCodeBuffer(), bufferSize, groupName);
1758     } else {
1759         regexp->SetByteCodeBuffer(thread, getCache.first);
1760         regexp->SetLength(static_cast<uint32_t>(getCache.second));
1761     }
1762     // 14. Let setStatus be Set(obj, "lastIndex", 0, true).
1763     JSHandle<JSTaggedValue> lastIndexString = thread->GlobalConstants()->GetHandledLastIndexString();
1764     ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(),
1765                                                lastIndexString.GetTaggedValue(), JSTaggedValue(0));
1766     // 15. ReturnIfAbrupt(setStatus).
1767     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1768     // 16. Return obj.
1769     return obj.GetTaggedValue();
1770 }
1771 
RegExpCreate(JSThread * thread,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags)1772 JSTaggedValue BuiltinsRegExp::RegExpCreate(JSThread *thread, const JSHandle<JSTaggedValue> &pattern,
1773                                            const JSHandle<JSTaggedValue> &flags)
1774 {
1775     BUILTINS_API_TRACE(thread, RegExp, Create);
1776     auto ecmaVm = thread->GetEcmaVM();
1777     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1778     JSHandle<JSTaggedValue> newTarget = env->GetRegExpFunction();
1779     // 1. Let obj be RegExpAlloc(%RegExp%).
1780     JSHandle<JSTaggedValue> object(thread, RegExpAlloc(thread, newTarget));
1781     // 2. ReturnIfAbrupt(obj).
1782     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1783     // 3. Return RegExpInitialize(obj, P, F).
1784     return RegExpInitialize(thread, object, pattern, flags);
1785 }
1786 
1787 // 21.2.3.2.4
EscapeRegExpPattern(JSThread * thread,const JSHandle<JSTaggedValue> & src,const JSHandle<JSTaggedValue> & flags)1788 EcmaString *BuiltinsRegExp::EscapeRegExpPattern(JSThread *thread, const JSHandle<JSTaggedValue> &src,
1789                                                 const JSHandle<JSTaggedValue> &flags)
1790 {
1791     // String -> CString
1792     JSHandle<EcmaString> srcStr(thread, static_cast<EcmaString *>(src->GetTaggedObject()));
1793     JSHandle<EcmaString> flagsStr(thread, static_cast<EcmaString *>(flags->GetTaggedObject()));
1794     CString srcStdStr = ConvertToString(*srcStr, StringConvertedUsage::LOGICOPERATION);
1795     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1796     // "" -> (?:)
1797     if (srcStdStr.empty()) {
1798         srcStdStr = "(?:)";
1799     }
1800     // "/" -> "\/"
1801     srcStdStr = base::StringHelper::RepalceAll(srcStdStr, "/", "\\/");
1802     // "\\" -> "\"
1803     srcStdStr = base::StringHelper::RepalceAll(srcStdStr, "\\", "\\");
1804 
1805     return *factory->NewFromUtf8(srcStdStr);
1806 }
1807 
CreateCacheTable(JSThread * thread)1808 JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread)
1809 {
1810     int length = CACHE_TABLE_HEADER_SIZE + INITIAL_CACHE_NUMBER * ENTRY_SIZE;
1811 
1812     auto table = static_cast<RegExpExecResultCache *>(
1813         *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
1814     table->SetLargeStrCount(thread, DEFAULT_LARGE_STRING_COUNT);
1815     table->SetConflictCount(thread, DEFAULT_CONFLICT_COUNT);
1816     table->SetStrLenThreshold(thread, 0);
1817     table->SetHitCount(thread, 0);
1818     table->SetCacheCount(thread, 0);
1819     table->SetCacheLength(thread, INITIAL_CACHE_NUMBER);
1820     return JSTaggedValue(table);
1821 }
1822 
FindCachedResult(JSThread * thread,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags,const JSHandle<JSTaggedValue> & input,CacheType type,const JSHandle<JSTaggedValue> & regexp,JSTaggedValue extend)1823 JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JSHandle<JSTaggedValue> &pattern,
1824                                                       const JSHandle<JSTaggedValue> &flags,
1825                                                       const JSHandle<JSTaggedValue> &input, CacheType type,
1826                                                       const JSHandle<JSTaggedValue> &regexp, JSTaggedValue extend)
1827 {
1828     JSTaggedValue patternValue = pattern.GetTaggedValue();
1829     JSTaggedValue flagsValue = flags.GetTaggedValue();
1830     JSTaggedValue inputValue = input.GetTaggedValue();
1831 
1832     if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) {
1833         return JSTaggedValue::Undefined();
1834     }
1835 
1836     uint32_t hash = pattern->GetKeyHashCode() + static_cast<uint32_t>(flags->GetInt()) + input->GetKeyHashCode();
1837     uint32_t entry = hash & static_cast<uint32_t>(GetCacheLength() - 1);
1838     if (!Match(entry, patternValue, flagsValue, inputValue, extend)) {
1839         uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(GetCacheLength() - 1);
1840         if (!Match(entry2, patternValue, flagsValue, inputValue, extend)) {
1841             return JSTaggedValue::Undefined();
1842         }
1843         entry = entry2;
1844     }
1845     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1846         static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
1847     uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1848     JSTaggedValue result;
1849     switch (type) {
1850         case REPLACE_TYPE:
1851             result = Get(index + RESULT_REPLACE_INDEX);
1852             break;
1853         case SPLIT_TYPE:
1854             result = Get(index + RESULT_SPLIT_INDEX);
1855             break;
1856         case MATCH_TYPE:
1857             result = Get(index + RESULT_MATCH_INDEX);
1858             break;
1859         case EXEC_TYPE:
1860             result = Get(index + RESULT_EXEC_INDEX);
1861             break;
1862         default:
1863             UNREACHABLE();
1864             break;
1865     }
1866     SetHitCount(thread, GetHitCount() + 1);
1867     JSHandle<JSTaggedValue> lastIndexHandle = thread->GlobalConstants()->GetHandledLastIndexString();
1868     ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(),
1869                                                Get(index + LAST_INDEX_INDEX));
1870     if (result.IsJSArray()) {
1871         JSHandle<JSArray> resultHandle(thread, JSArray::Cast(result));
1872         JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()->CloneArrayLiteral(resultHandle);
1873         return copyArray.GetTaggedValue();
1874     }
1875     return result;
1876 }
1877 
AddResultInCache(JSThread * thread,JSHandle<RegExpExecResultCache> cache,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags,const JSHandle<JSTaggedValue> & input,const JSHandle<JSTaggedValue> & resultArray,CacheType type,uint32_t lastIndex,JSTaggedValue extend)1878 void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache,
1879                                              const JSHandle<JSTaggedValue> &pattern,
1880                                              const JSHandle<JSTaggedValue> &flags, const JSHandle<JSTaggedValue> &input,
1881                                              const JSHandle<JSTaggedValue> &resultArray, CacheType type,
1882                                              uint32_t lastIndex, JSTaggedValue extend)
1883 {
1884     if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) {
1885         return;
1886     }
1887 
1888     JSHandle<JSTaggedValue> resultArrayCopy;
1889     if (resultArray->IsJSArray()) {
1890         JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()
1891                                             ->CloneArrayLiteral(JSHandle<JSArray>(resultArray));
1892         resultArrayCopy = JSHandle<JSTaggedValue>(copyArray);
1893     } else {
1894         resultArrayCopy = JSHandle<JSTaggedValue>(resultArray);
1895     }
1896 
1897     JSTaggedValue patternValue = pattern.GetTaggedValue();
1898     JSTaggedValue flagsValue = flags.GetTaggedValue();
1899     JSTaggedValue inputValue = input.GetTaggedValue();
1900     JSTaggedValue lastIndexValue(lastIndex);
1901 
1902     uint32_t hash = patternValue.GetKeyHashCode() + static_cast<uint32_t>(flagsValue.GetInt()) +
1903                     inputValue.GetKeyHashCode();
1904     uint32_t entry = hash & static_cast<uint32_t>(cache->GetCacheLength() - 1);
1905     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1906         static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
1907     uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1908     if (cache->Get(index) == JSTaggedValue::Undefined()) {
1909         cache->SetCacheCount(thread, cache->GetCacheCount() + 1);
1910         cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue, extend);
1911         cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
1912     } else if (cache->Match(entry, patternValue, flagsValue, inputValue, extend)) {
1913         cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
1914     } else {
1915         uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(cache->GetCacheLength() - 1);
1916         ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1917             static_cast<size_t>(entry2) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
1918         uint32_t index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE;
1919         JSHandle<JSTaggedValue> extendHandle(thread, extend);
1920         if (cache->GetCacheLength() < DEFAULT_CACHE_NUMBER) {
1921             GrowRegexpCache(thread, cache);
1922             // update value after gc.
1923             patternValue = pattern.GetTaggedValue();
1924             flagsValue = flags.GetTaggedValue();
1925             inputValue = input.GetTaggedValue();
1926 
1927             cache->SetCacheLength(thread, DEFAULT_CACHE_NUMBER);
1928             entry2 = hash & static_cast<uint32_t>(cache->GetCacheLength() - 1);
1929             index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE;
1930         }
1931         JSTaggedValue extendValue = extendHandle.GetTaggedValue();
1932         if (cache->Get(index2) == JSTaggedValue::Undefined()) {
1933             cache->SetCacheCount(thread, cache->GetCacheCount() + 1);
1934             cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue, lastIndexValue, extendValue);
1935             cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
1936         } else if (cache->Match(entry2, patternValue, flagsValue, inputValue, extendValue)) {
1937             cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
1938         } else {
1939             cache->SetConflictCount(thread, cache->GetConflictCount() > 1 ? (cache->GetConflictCount() - 1) : 0);
1940             cache->SetCacheCount(thread, cache->GetCacheCount() - 1);
1941             cache->ClearEntry(thread, entry2);
1942             cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue, extendValue);
1943             cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
1944         }
1945     }
1946 }
1947 
GrowRegexpCache(JSThread * thread,JSHandle<RegExpExecResultCache> cache)1948 void RegExpExecResultCache::GrowRegexpCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache)
1949 {
1950     int length = CACHE_TABLE_HEADER_SIZE + DEFAULT_CACHE_NUMBER * ENTRY_SIZE;
1951     auto factory = thread->GetEcmaVM()->GetFactory();
1952     auto newCache = factory->ExtendArray(JSHandle<TaggedArray>(cache), length, JSTaggedValue::Undefined());
1953     thread->GetEcmaVM()->SetRegExpCache(newCache.GetTaggedValue());
1954 }
1955 
SetEntry(JSThread * thread,int entry,JSTaggedValue & pattern,JSTaggedValue & flags,JSTaggedValue & input,JSTaggedValue & lastIndexValue,JSTaggedValue & extendValue)1956 void RegExpExecResultCache::SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags,
1957                                      JSTaggedValue &input, JSTaggedValue &lastIndexValue, JSTaggedValue &extendValue)
1958 {
1959     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1960             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
1961     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1962     Set(thread, index + PATTERN_INDEX, pattern);
1963     Set(thread, index + FLAG_INDEX, flags);
1964     Set(thread, index + INPUT_STRING_INDEX, input);
1965     Set(thread, index + LAST_INDEX_INDEX, lastIndexValue);
1966     Set(thread, index + EXTEND_INDEX, extendValue);
1967 }
1968 
UpdateResultArray(JSThread * thread,int entry,JSTaggedValue resultArray,CacheType type)1969 void RegExpExecResultCache::UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type)
1970 {
1971     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1972             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
1973     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1974     switch (type) {
1975         break;
1976         case REPLACE_TYPE:
1977             Set(thread, index + RESULT_REPLACE_INDEX, resultArray);
1978             break;
1979         case SPLIT_TYPE:
1980             Set(thread, index + RESULT_SPLIT_INDEX, resultArray);
1981             break;
1982         case MATCH_TYPE:
1983             Set(thread, index + RESULT_MATCH_INDEX, resultArray);
1984             break;
1985         case EXEC_TYPE:
1986             Set(thread, index + RESULT_EXEC_INDEX, resultArray);
1987             break;
1988         default:
1989             UNREACHABLE();
1990             break;
1991     }
1992 }
1993 
ClearEntry(JSThread * thread,int entry)1994 void RegExpExecResultCache::ClearEntry(JSThread *thread, int entry)
1995 {
1996     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1997             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
1998     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1999     JSTaggedValue undefined = JSTaggedValue::Undefined();
2000     for (int i = 0; i < ENTRY_SIZE; i++) {
2001         Set(thread, index + i, undefined);
2002     }
2003 }
2004 
Match(int entry,JSTaggedValue & pattern,JSTaggedValue & flags,JSTaggedValue & input,JSTaggedValue & extend)2005 bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input,
2006                                   JSTaggedValue &extend)
2007 {
2008     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2009             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2010     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2011     JSTaggedValue keyPattern = Get(index + PATTERN_INDEX);
2012     JSTaggedValue keyFlags = Get(index + FLAG_INDEX);
2013     JSTaggedValue keyInput = Get(index + INPUT_STRING_INDEX);
2014     JSTaggedValue keyExtend = Get(index + EXTEND_INDEX);
2015 
2016     if (keyPattern == JSTaggedValue::Undefined()) {
2017         return false;
2018     }
2019 
2020     EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject());
2021     uint8_t flagsBits = static_cast<uint8_t>(flags.GetInt());
2022     EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject());
2023     EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject());
2024     uint8_t keyFlagsBits = static_cast<uint8_t>(keyFlags.GetInt());
2025     EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject());
2026     bool extendEqual = false;
2027     if (extend.IsString() && keyExtend.IsString()) {
2028         EcmaString *extendStr = EcmaString::Cast(extend.GetTaggedObject());
2029         EcmaString *keyExtendStr = EcmaString::Cast(keyExtend.GetTaggedObject());
2030         extendEqual = EcmaStringAccessor::StringsAreEqual(extendStr, keyExtendStr);
2031     } else if (extend.IsUndefined() && keyExtend.IsUndefined()) {
2032         extendEqual = true;
2033     } else {
2034         return false;
2035     }
2036     return EcmaStringAccessor::StringsAreEqual(patternStr, keyPatternStr) && flagsBits == keyFlagsBits &&
2037            EcmaStringAccessor::StringsAreEqual(inputStr, keyInputStr) && extendEqual;
2038 }
2039 }  // namespace panda::ecmascript::builtins
2040