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