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