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