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