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