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