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