1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/builtins/builtins_regexp.h"
17
18 #include <cmath>
19
20 #include "ecmascript/ecma_string-inl.h"
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/interpreter/interpreter.h"
24 #include "ecmascript/js_array.h"
25 #include "ecmascript/js_function.h"
26 #include "ecmascript/js_hclass.h"
27 #include "ecmascript/js_object-inl.h"
28 #include "ecmascript/js_regexp.h"
29 #include "ecmascript/js_regexp_iterator.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/mem/assert_scope.h"
32 #include "ecmascript/mem/c_containers.h"
33 #include "ecmascript/object_factory.h"
34 #include "ecmascript/object_fast_operator-inl.h"
35 #include "ecmascript/regexp/regexp_parser_cache.h"
36 #include "ecmascript/tagged_array-inl.h"
37
38 namespace panda::ecmascript::builtins {
39 // 21.2.3.1
RegExpConstructor(EcmaRuntimeCallInfo * argv)40 JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv)
41 {
42 ASSERT(argv);
43 BUILTINS_API_TRACE(argv->GetThread(), RegExp, Constructor);
44 JSThread *thread = argv->GetThread();
45 [[maybe_unused]] EcmaHandleScope handleScope(thread);
46 JSHandle<JSTaggedValue> newTargetTemp = GetNewTarget(argv);
47 JSHandle<JSTaggedValue> pattern = GetCallArg(argv, 0);
48 JSHandle<JSTaggedValue> flags = GetCallArg(argv, 1);
49 // 1. Let patternIsRegExp be IsRegExp(pattern).
50 bool patternIsRegExp = JSObject::IsRegExp(thread, pattern);
51 // 2. ReturnIfAbrupt(patternIsRegExp).
52 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
53 // 3. If NewTarget is not undefined, let newTarget be NewTarget.
54 JSHandle<JSTaggedValue> newTarget;
55 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
56 if (!newTargetTemp->IsUndefined()) {
57 newTarget = newTargetTemp;
58 } else {
59 auto ecmaVm = thread->GetEcmaVM();
60 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
61 // disable gc
62 [[maybe_unused]] DisallowGarbageCollection no_gc;
63 // 4.a Let newTarget be the active function object.
64 newTarget = env->GetRegExpFunction();
65 JSHandle<JSTaggedValue> constructorString = globalConst->GetHandledConstructorString();
66 // 4.b If patternIsRegExp is true and flags is undefined
67 if (patternIsRegExp && flags->IsUndefined()) {
68 // 4.b.i Let patternConstructor be Get(pattern, "constructor").
69 JSTaggedValue patternConstructor = ObjectFastOperator::FastGetPropertyByValue(
70 thread, pattern.GetTaggedValue(), constructorString.GetTaggedValue());
71 // 4.b.ii ReturnIfAbrupt(patternConstructor).
72 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
73 // 4.b.iii If SameValue(newTarget, patternConstructor) is true, return pattern.
74 if (JSTaggedValue::SameValue(newTarget.GetTaggedValue(), patternConstructor)) {
75 return pattern.GetTaggedValue();
76 }
77 }
78 }
79 // 5. If Type(pattern) is Object and pattern has a [[RegExpMatcher]] internal slot
80 bool isJsReg = false;
81 if (pattern->IsECMAObject()) {
82 JSHandle<JSObject> patternObj = JSHandle<JSObject>::Cast(pattern);
83 isJsReg = patternObj->IsJSRegExp();
84 }
85 JSHandle<JSTaggedValue> patternTemp;
86 JSHandle<JSTaggedValue> flagsTemp;
87 if (isJsReg) {
88 JSHandle<JSRegExp> patternReg(thread, JSRegExp::Cast(pattern->GetTaggedObject()));
89 // 5.a Let P be the value of pattern’s [[OriginalSource]] internal slot.
90 patternTemp = JSHandle<JSTaggedValue>(thread, patternReg->GetOriginalSource());
91 if (flags->IsUndefined()) {
92 // 5.b If flags is undefined, let F be the value of pattern’s [[OriginalFlags]] internal slot.
93 flagsTemp = JSHandle<JSTaggedValue>(thread, patternReg->GetOriginalFlags());
94 } else {
95 // 5.c Else, let F be flags.
96 flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
97 }
98 // 6. Else if patternIsRegExp is true
99 } else if (patternIsRegExp) {
100 JSHandle<JSTaggedValue> sourceString(globalConst->GetHandledSourceString());
101 JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
102 // disable gc
103 [[maybe_unused]] DisallowGarbageCollection noGc;
104 // 6.a Let P be Get(pattern, "source").
105 patternTemp = JSObject::GetProperty(thread, pattern, sourceString).GetValue();
106 // 6.b ReturnIfAbrupt(P).
107 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
108 // 6.c If flags is undefined
109 if (flags->IsUndefined()) {
110 // 6.c.i Let F be Get(pattern, "flags").
111 flagsTemp = JSObject::GetProperty(thread, pattern, flagsString).GetValue();
112 // 6.c.ii ReturnIfAbrupt(F).
113 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
114 } else {
115 // 6.d Else, let F be flags.
116 flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
117 }
118 } else {
119 // 7.a Let P be pattern.
120 patternTemp = pattern;
121 // 7.b Let F be flags.
122 if (flags->IsUndefined()) {
123 flagsTemp = flags;
124 } else {
125 flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
126 }
127 }
128 // 8. Let O be RegExpAlloc(newTarget).
129 JSHandle<JSTaggedValue> object(thread, RegExpAlloc(thread, newTarget));
130 // 9. ReturnIfAbrupt(O).
131 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
132 // 10. Return RegExpInitialize(O, P, F).
133 JSTaggedValue result = RegExpInitialize(thread, object, patternTemp, flagsTemp);
134 return JSTaggedValue(result);
135 }
136
137 // prototype
138 // 20.2.5.2
Exec(EcmaRuntimeCallInfo * argv)139 JSTaggedValue BuiltinsRegExp::Exec(EcmaRuntimeCallInfo *argv)
140 {
141 ASSERT(argv);
142 BUILTINS_API_TRACE(argv->GetThread(), RegExp, Exec);
143 JSThread *thread = argv->GetThread();
144 [[maybe_unused]] EcmaHandleScope handleScope(thread);
145 // 1. Let R be the this value.
146 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
147 // 4. Let S be ToString(string).
148 JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
149 JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
150 // 5. ReturnIfAbrupt(S).
151 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
152 JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
153 // 2. If Type(R) is not Object, throw a TypeError exception.
154 if (!thisObj->IsECMAObject()) {
155 // throw a TypeError exception.
156 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
157 }
158 // 3. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
159 if (!thisObj->IsJSRegExp()) {
160 // throw a TypeError exception.
161 THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[RegExpMatcher]]", JSTaggedValue::Exception());
162 }
163
164 bool useCache = true;
165 JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
166 if (cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) {
167 useCache = false;
168 }
169
170 // 6. Return RegExpBuiltinExec(R, S).
171 JSTaggedValue result = RegExpBuiltinExec(thread, thisObj, string, useCache);
172 return JSTaggedValue(result);
173 }
174
175 // 20.2.5.13
Test(EcmaRuntimeCallInfo * argv)176 JSTaggedValue BuiltinsRegExp::Test(EcmaRuntimeCallInfo *argv)
177 {
178 ASSERT(argv);
179 BUILTINS_API_TRACE(argv->GetThread(), RegExp, Test);
180 JSThread *thread = argv->GetThread();
181 [[maybe_unused]] EcmaHandleScope handleScope(thread);
182 // 1. Let R be the this value.
183 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
184 JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
185 // 3. Let string be ToString(S).
186 // 4. ReturnIfAbrupt(string).
187 JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
188 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
189 JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
190 // 2. If Type(R) is not Object, throw a TypeError exception.
191 if (!thisObj->IsECMAObject()) {
192 // throw a TypeError exception.
193 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
194 }
195
196 // 5. Let match be RegExpExec(R, string).
197 JSTaggedValue matchResult = RegExpExec(thread, thisObj, string, false);
198 // 6. ReturnIfAbrupt(match).
199 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
200 // 7. If match is not null, return true; else return false.
201 return GetTaggedBoolean(!matchResult.IsNull());
202 }
203
204 // 20.2.5.14
ToString(EcmaRuntimeCallInfo * argv)205 JSTaggedValue BuiltinsRegExp::ToString(EcmaRuntimeCallInfo *argv)
206 {
207 ASSERT(argv);
208 BUILTINS_API_TRACE(argv->GetThread(), RegExp, ToString);
209 JSThread *thread = argv->GetThread();
210 [[maybe_unused]] EcmaHandleScope handleScope(thread);
211 // 1. Let R be the this value.
212 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
213 auto ecmaVm = thread->GetEcmaVM();
214 // 2. If Type(R) is not Object, throw a TypeError exception.
215 if (!thisObj->IsECMAObject()) {
216 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
217 }
218 ObjectFactory *factory = ecmaVm->GetFactory();
219 const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
220 JSHandle<JSTaggedValue> sourceString(globalConstants->GetHandledSourceString());
221 JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
222 // 3. Let pattern be ToString(Get(R, "source")).
223 JSHandle<JSTaggedValue> getSource(JSObject::GetProperty(thread, thisObj, sourceString).GetValue());
224 JSHandle<JSTaggedValue> getFlags(JSObject::GetProperty(thread, thisObj, flagsString).GetValue());
225 JSHandle<EcmaString> sourceStrHandle = JSTaggedValue::ToString(thread, getSource);
226 // 4. ReturnIfAbrupt(pattern).
227 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
228 // 5. Let flags be ToString(Get(R, "flags")).
229 JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
230 // 4. ReturnIfAbrupt(flags).
231 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
232 JSHandle<EcmaString> slashStr = JSHandle<EcmaString>::Cast(globalConstants->GetHandledBackslashString());
233 // 7. Let result be the String value formed by concatenating "/", pattern, and "/", and flags.
234 JSHandle<EcmaString> tempStr = factory->ConcatFromString(slashStr, sourceStrHandle);
235 JSHandle<EcmaString> resultTemp = factory->ConcatFromString(tempStr, slashStr);
236 return factory->ConcatFromString(resultTemp, flagsStrHandle).GetTaggedValue();
237 }
238
239 // 20.2.5.3
GetFlags(EcmaRuntimeCallInfo * argv)240 JSTaggedValue BuiltinsRegExp::GetFlags(EcmaRuntimeCallInfo *argv)
241 {
242 ASSERT(argv);
243 BUILTINS_API_TRACE(argv->GetThread(), RegExp, GetFlags);
244 JSThread *thread = argv->GetThread();
245 [[maybe_unused]] EcmaHandleScope handleScope(thread);
246 // 1. Let R be the this value.
247 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
248 // 2. If Type(R) is not Object, throw a TypeError exception.
249 if (!thisObj->IsECMAObject()) {
250 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
251 }
252 // 3. Let result be the empty String.
253 // 4. ~ 19.
254 ASSERT(JSHandle<JSObject>::Cast(thisObj)->IsJSRegExp());
255 uint8_t flagsBits = static_cast<uint8_t>(JSRegExp::Cast(thisObj->GetTaggedObject())->GetOriginalFlags().GetInt());
256 return FlagsBitsToString(thread, flagsBits);
257 }
258
259 // 20.2.5.4
GetGlobal(EcmaRuntimeCallInfo * argv)260 JSTaggedValue BuiltinsRegExp::GetGlobal(EcmaRuntimeCallInfo *argv)
261 {
262 ASSERT(argv);
263 JSThread *thread = argv->GetThread();
264 [[maybe_unused]] EcmaHandleScope handleScope(thread);
265 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
266 bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_GLOBAL);
267 return GetTaggedBoolean(result);
268 }
269
270 // 20.2.5.5
GetIgnoreCase(EcmaRuntimeCallInfo * argv)271 JSTaggedValue BuiltinsRegExp::GetIgnoreCase(EcmaRuntimeCallInfo *argv)
272 {
273 ASSERT(argv);
274 JSThread *thread = argv->GetThread();
275 [[maybe_unused]] EcmaHandleScope handleScope(thread);
276 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
277 bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_IGNORECASE);
278 return GetTaggedBoolean(result);
279 }
280
281 // 20.2.5.7
GetMultiline(EcmaRuntimeCallInfo * argv)282 JSTaggedValue BuiltinsRegExp::GetMultiline(EcmaRuntimeCallInfo *argv)
283 {
284 ASSERT(argv);
285 JSThread *thread = argv->GetThread();
286 [[maybe_unused]] EcmaHandleScope handleScope(thread);
287 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
288 bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_MULTILINE);
289 return GetTaggedBoolean(result);
290 }
291
GetDotAll(EcmaRuntimeCallInfo * argv)292 JSTaggedValue BuiltinsRegExp::GetDotAll(EcmaRuntimeCallInfo *argv)
293 {
294 ASSERT(argv);
295 JSThread *thread = argv->GetThread();
296 [[maybe_unused]] EcmaHandleScope handleScope(thread);
297 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
298 bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_DOTALL);
299 return GetTaggedBoolean(result);
300 }
301
302 // 20.2.5.10
GetSource(EcmaRuntimeCallInfo * argv)303 JSTaggedValue BuiltinsRegExp::GetSource(EcmaRuntimeCallInfo *argv)
304 {
305 ASSERT(argv);
306 JSThread *thread = argv->GetThread();
307 [[maybe_unused]] EcmaHandleScope handleScope(thread);
308 // 1. Let R be the this value.
309 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
310 // 2. If Type(R) is not Object, throw a TypeError exception.
311 // 3. If R does not have an [[OriginalSource]] internal slot, throw a TypeError exception.
312 // 4. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception.
313 if (!thisObj->IsECMAObject()) {
314 // throw a TypeError exception.
315 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
316 }
317 if (!thisObj->IsJSRegExp()) {
318 // throw a TypeError exception.
319 THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalSource]]", JSTaggedValue::Exception());
320 }
321 // 5. Let src be the value of R’s [[OriginalSource]] internal slot.
322 JSHandle<JSRegExp> regexpObj(thread, JSRegExp::Cast(thisObj->GetTaggedObject()));
323 JSHandle<JSTaggedValue> source(thread, regexpObj->GetOriginalSource());
324 // 6. Let flags be the value of R’s [[OriginalFlags]] internal slot.
325 uint8_t flagsBits = static_cast<uint8_t>(regexpObj->GetOriginalFlags().GetInt());
326 JSHandle<JSTaggedValue> flags(thread, FlagsBitsToString(thread, flagsBits));
327 // 7. Return EscapeRegExpPattern(src, flags).
328 return JSTaggedValue(EscapeRegExpPattern(thread, source, flags));
329 }
330
331 // 20.2.5.12
GetSticky(EcmaRuntimeCallInfo * argv)332 JSTaggedValue BuiltinsRegExp::GetSticky(EcmaRuntimeCallInfo *argv)
333 {
334 ASSERT(argv);
335 JSThread *thread = argv->GetThread();
336 [[maybe_unused]] EcmaHandleScope handleScope(thread);
337 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
338 bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_STICKY);
339 return GetTaggedBoolean(result);
340 }
341
342 // 20.2.5.15
GetUnicode(EcmaRuntimeCallInfo * argv)343 JSTaggedValue BuiltinsRegExp::GetUnicode(EcmaRuntimeCallInfo *argv)
344 {
345 ASSERT(argv);
346 JSThread *thread = argv->GetThread();
347 [[maybe_unused]] EcmaHandleScope handleScope(thread);
348 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
349 bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_UTF16);
350 return GetTaggedBoolean(result);
351 }
352
353 // 21.2.4.2
GetSpecies(EcmaRuntimeCallInfo * argv)354 JSTaggedValue BuiltinsRegExp::GetSpecies(EcmaRuntimeCallInfo *argv)
355 {
356 ASSERT(argv);
357 return GetThis(argv).GetTaggedValue();
358 }
359
360 // 21.2.5.6
Match(EcmaRuntimeCallInfo * argv)361 JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv)
362 {
363 ASSERT(argv);
364 BUILTINS_API_TRACE(argv->GetThread(), RegExp, Match);
365 JSThread *thread = argv->GetThread();
366 [[maybe_unused]] EcmaHandleScope handleScope(thread);
367 // 1. Let rx be the this value.
368 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
369 // 3. Let S be ToString(string)
370 JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
371 JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
372 bool useCache = true;
373 JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
374 if (cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) {
375 useCache = false;
376 }
377 // 4. ReturnIfAbrupt(string).
378 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
379 JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
380 if (!thisObj->IsECMAObject()) {
381 // 2. If Type(rx) is not Object, throw a TypeError exception.
382 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
383 }
384 // 5. Let global be ToBoolean(Get(rx, "global")).
385 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
386 JSHandle<JSTaggedValue> global = globalConst->GetHandledGlobalString();
387 JSTaggedValue globalValue =
388 ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(), global.GetTaggedValue());
389 // 6. ReturnIfAbrupt(global).
390 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
391
392 JSHandle<JSRegExp> regexpObj(thisObj);
393 JSMutableHandle<JSTaggedValue> pattern(thread, JSTaggedValue::Undefined());
394 JSMutableHandle<JSTaggedValue> flags(thread, JSTaggedValue::Undefined());
395 if (thisObj->IsJSRegExp()) {
396 pattern.Update(regexpObj->GetOriginalSource());
397 flags.Update(regexpObj->GetOriginalFlags());
398 }
399 bool isGlobal = globalValue.ToBoolean();
400 // 7. If global is false, then
401 if (!isGlobal) {
402 // a. Return RegExpExec(rx, S).
403 if (useCache) {
404 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, inputString,
405 RegExpExecResultCache::EXEC_TYPE, thisObj);
406 if (cacheResult != JSTaggedValue::Undefined()) {
407 return cacheResult;
408 }
409 }
410 JSTaggedValue result = RegExpExec(thread, thisObj, string, useCache);
411 return JSTaggedValue(result);
412 }
413
414 if (useCache) {
415 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, inputString,
416 RegExpExecResultCache::MATCH_TYPE, thisObj);
417 if (cacheResult != JSTaggedValue::Undefined()) {
418 return cacheResult;
419 }
420 }
421
422 // 8. Else global is true
423 // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")).
424 JSHandle<JSTaggedValue> unicode = globalConst->GetHandledUnicodeString();
425 JSTaggedValue uincodeValue =
426 ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(), unicode.GetTaggedValue());
427 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
428 bool fullUnicode = uincodeValue.ToBoolean();
429 // b. ReturnIfAbrupt(fullUnicode)
430 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
431 // c. Let setStatus be Set(rx, "lastIndex", 0, true).
432 JSHandle<JSTaggedValue> lastIndexString(globalConst->GetHandledLastIndexString());
433 ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(), lastIndexString.GetTaggedValue(),
434 JSTaggedValue(0));
435 // d. ReturnIfAbrupt(setStatus).
436 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
437 // e. Let A be ArrayCreate(0).
438 JSHandle<JSObject> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
439 // f. Let n be 0.
440 int resultNum = 0;
441 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue(0));
442 // g. Repeat,
443 while (true) {
444 // i. Let result be RegExpExec(rx, S).
445 result.Update(RegExpExec(thread, thisObj, string, useCache));
446
447 // ii. ReturnIfAbrupt(result).
448 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
449 // iii. If result is null, then
450 if (result->IsNull()) {
451 // 1. If n=0, return null.
452 if (resultNum == 0) {
453 return JSTaggedValue::Null();
454 }
455 if (useCache) {
456 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputString,
457 JSHandle<JSTaggedValue>(array),
458 RegExpExecResultCache::MATCH_TYPE, 0);
459 }
460 // 2. Else, return A.
461 return array.GetTaggedValue();
462 }
463 // iv. Else result is not null,
464 // 1. Let matchStr be ToString(Get(result, "0")).
465 JSHandle<JSTaggedValue> zeroString = globalConst->GetHandledZeroString();
466 JSTaggedValue matchVal = ObjectFastOperator::FastGetPropertyByValue(
467 thread, result.GetTaggedValue(), zeroString.GetTaggedValue());
468 JSHandle<JSTaggedValue> matchStr(thread, matchVal);
469 JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, matchStr);
470 // 2. ReturnIfAbrupt(matchStr).
471 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
472 JSHandle<JSTaggedValue> matchValue = JSHandle<JSTaggedValue>::Cast(matchString);
473 // 3. Let status be CreateDataProperty(A, ToString(n), matchStr).
474 JSObject::CreateDataProperty(thread, array, resultNum, matchValue);
475 // 5. If matchStr is the empty String, then
476 if (EcmaStringAccessor(JSTaggedValue::ToString(thread, matchValue)).GetLength() == 0) {
477 // a. Let thisIndex be ToLength(Get(rx, "lastIndex")).
478 JSTaggedValue lastIndex = ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(),
479 lastIndexString.GetTaggedValue());
480 JSHandle<JSTaggedValue> lastIndexHandle(thread, lastIndex);
481 JSTaggedNumber thisIndex = JSTaggedValue::ToLength(thread, lastIndexHandle);
482 // b. ReturnIfAbrupt(thisIndex).
483 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
484 // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
485 // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true).
486 JSTaggedValue nextIndex =
487 JSTaggedValue(AdvanceStringIndex(string, thisIndex.GetNumber(), fullUnicode));
488 ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(),
489 lastIndexString.GetTaggedValue(), nextIndex);
490 // e. ReturnIfAbrupt(setStatus).
491 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
492 }
493 // 6. Increase n.
494 resultNum++;
495 }
496 }
497
MatchAll(EcmaRuntimeCallInfo * argv)498 JSTaggedValue BuiltinsRegExp::MatchAll(EcmaRuntimeCallInfo *argv)
499 {
500 ASSERT(argv);
501 JSThread *thread = argv->GetThread();
502 BUILTINS_API_TRACE(thread, RegExp, MatchAll);
503 [[maybe_unused]] EcmaHandleScope handleScope(thread);
504
505 // 1. Let R be the this value.
506 // 2. If Type(R) is not Object, throw a TypeError exception.
507 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
508 auto ecmaVm = thread->GetEcmaVM();
509 if (!thisObj->IsECMAObject()) {
510 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
511 }
512
513 // 3. Let S be ? ToString(string).
514 JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
515 JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
516 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
517
518 // 4. Let C be ? SpeciesConstructor(R, %RegExp%).
519 JSHandle<JSTaggedValue> defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction();
520 JSHandle<JSObject> objHandle(thisObj);
521 JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
522 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
523
524 const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
525 // 5. Let flags be ? ToString(? Get(R, "flags")).
526 JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
527 JSHandle<JSTaggedValue> getFlags(JSObject::GetProperty(thread, thisObj, flagsString).GetValue());
528 JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
529 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
530
531 // 6. Let matcher be ? Construct(C, « R, flags »).
532 JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
533 EcmaRuntimeCallInfo *runtimeInfo =
534 EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 2); // 2: two args
535 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
536 runtimeInfo->SetCallArg(thisObj.GetTaggedValue(), flagsStrHandle.GetTaggedValue());
537 JSTaggedValue taggedMatcher = JSFunction::Construct(runtimeInfo);
538 JSHandle<JSTaggedValue> matcherHandle(thread, taggedMatcher);
539 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
540
541 // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
542 JSHandle<JSTaggedValue> lastIndexString(globalConstants->GetHandledLastIndexString());
543 JSHandle<JSTaggedValue> getLastIndex(JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue());
544 JSTaggedNumber thisLastIndex = JSTaggedValue::ToLength(thread, getLastIndex);
545 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
546
547 // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
548 ObjectFastOperator::FastSetPropertyByValue(thread, matcherHandle.GetTaggedValue(), lastIndexString.GetTaggedValue(),
549 thisLastIndex);
550 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
551
552 // 9. If flags contains "g", let global be true.
553 // 10. Else, let global be false.
554 JSHandle<EcmaString> gString(globalConstants->GetHandledGString());
555 bool global = false;
556 if (EcmaStringAccessor::IndexOf(*flagsStrHandle, *gString) != -1) {
557 global = true;
558 }
559
560 // 11. If flags contains "u", let fullUnicode be true.
561 // 12. Else, let fullUnicode be false.
562 JSHandle<EcmaString> uString(globalConstants->GetHandledUString());
563 bool fullUnicode = false;
564 if (EcmaStringAccessor::IndexOf(*flagsStrHandle, *uString) != -1) {
565 fullUnicode = true;
566 }
567
568 // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
569 return JSRegExpIterator::CreateRegExpStringIterator(thread, matcherHandle,
570 stringHandle, global, fullUnicode).GetTaggedValue();
571 }
572
RegExpReplaceFast(JSThread * thread,JSHandle<JSTaggedValue> & regexp,JSHandle<EcmaString> inputString,uint32_t inputLength)573 JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandle<JSTaggedValue> ®exp,
574 JSHandle<EcmaString> inputString, uint32_t inputLength)
575 {
576 ASSERT(regexp->IsJSRegExp());
577 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
578 // get bytecode
579 JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
580 void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
581 // get flags
582 auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
583 uint32_t flags = *reinterpret_cast<uint32_t *>(bytecodeBuffer + RegExpParser::FLAGS_OFFSET);
584 JSHandle<JSTaggedValue> lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString());
585 uint32_t lastIndex = 0;
586 JSHandle<JSRegExp> regexpHandle(regexp);
587 bool useCache = false;
588 if ((flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) == 0) {
589 lastIndex = 0;
590 } else {
591 JSTaggedValue thisIndex = ObjectFastOperator::FastGetPropertyByValue(thread,
592 regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue());
593 if (thisIndex.IsInt()) {
594 lastIndex = static_cast<uint32_t>(thisIndex.GetInt());
595 } else {
596 JSHandle<JSTaggedValue> thisIndexHandle(thread, thisIndex);
597 lastIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber();
598 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
599 }
600 }
601
602 auto globalConst = thread->GlobalConstants();
603 JSHandle<JSTaggedValue> tagInputString = JSHandle<JSTaggedValue>::Cast(inputString);
604 JSHandle<JSTaggedValue> pattern(thread, regexpHandle->GetOriginalSource());
605 JSHandle<JSTaggedValue> flagsBits(thread, regexpHandle->GetOriginalFlags());
606
607 JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
608 uint32_t length = EcmaStringAccessor(inputString).GetLength();
609 uint32_t largeStrCount = cacheTable->GetLargeStrCount();
610 if (largeStrCount != 0) {
611 if (length > MIN_REPLACE_STRING_LENGTH) {
612 cacheTable->SetLargeStrCount(thread, --largeStrCount);
613 }
614 } else {
615 cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH);
616 }
617 if (length > cacheTable->GetStrLenThreshold()) {
618 useCache = true;
619 }
620 if (useCache) {
621 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, tagInputString,
622 RegExpExecResultCache::REPLACE_TYPE, regexp,
623 globalConst->GetEmptyString());
624 if (cacheResult != JSTaggedValue::Undefined()) {
625 return cacheResult;
626 }
627 }
628
629 std::string resultString;
630 uint32_t nextPosition = 0;
631
632 // 12. Let done be false.
633 // 13. Repeat, while done is false
634 for (;;) {
635 if (lastIndex > inputLength) {
636 break;
637 }
638
639 bool isUtf16 = EcmaStringAccessor(inputString).IsUtf16();
640 auto inputPtr = EcmaStringAccessor(inputString).ToOneByteDataForced();
641 const uint8_t *strBuffer = inputPtr.get();
642
643 RegExpExecutor::MatchResult matchResult = Matcher(thread, regexp, strBuffer, inputLength, lastIndex, isUtf16);
644 if (!matchResult.isSuccess_) {
645 if (flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) {
646 lastIndex = 0;
647 ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
648 lastIndexHandle.GetTaggedValue(), JSTaggedValue(0));
649 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
650 }
651 break;
652 }
653 uint32_t startIndex = matchResult.index_;
654 uint32_t endIndex = matchResult.endIndex_;
655 lastIndex = endIndex;
656 if (nextPosition < startIndex) {
657 auto substr = EcmaStringAccessor::FastSubString(
658 thread->GetEcmaVM(), inputString, nextPosition, startIndex - nextPosition);
659 resultString += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
660 }
661 nextPosition = endIndex;
662 if (!(flags & RegExpParser::FLAG_GLOBAL)) {
663 // a. Let setStatus be Set(R, "lastIndex", e, true).
664 ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
665 lastIndexHandle.GetTaggedValue(), JSTaggedValue(lastIndex));
666 // b. ReturnIfAbrupt(setStatus).
667 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
668 break;
669 }
670 if (endIndex == startIndex) {
671 bool unicode = EcmaStringAccessor(inputString).IsUtf16() && (flags & RegExpParser::FLAG_UTF16);
672 endIndex = AdvanceStringIndex(tagInputString, endIndex, unicode);
673 }
674 lastIndex = endIndex;
675 }
676 auto substr = EcmaStringAccessor::FastSubString(
677 thread->GetEcmaVM(), inputString, nextPosition, inputLength - nextPosition);
678 resultString += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
679 auto resultValue = factory->NewFromStdString(resultString);
680 if (useCache) {
681 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, tagInputString,
682 JSHandle<JSTaggedValue>(resultValue),
683 RegExpExecResultCache::REPLACE_TYPE, lastIndex,
684 globalConst->GetEmptyString());
685 }
686 return resultValue.GetTaggedValue();
687 }
688
689 // 21.2.5.8
690 // NOLINTNEXTLINE(readability-function-size)
Replace(EcmaRuntimeCallInfo * argv)691 JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv)
692 {
693 ASSERT(argv);
694 BUILTINS_API_TRACE(argv->GetThread(), RegExp, Replace);
695 JSThread *thread = argv->GetThread();
696 [[maybe_unused]] EcmaHandleScope handleScope(thread);
697 // 1. Let rx be the this value.
698 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
699 if (!thisObj->IsECMAObject()) {
700 // 2. If Type(rx) is not Object, throw a TypeError exception.
701 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
702 }
703 // 3. Let S be ToString(string).
704 JSHandle<JSTaggedValue> string = GetCallArg(argv, 0);
705 JSHandle<JSTaggedValue> inputReplaceValue = GetCallArg(argv, 1);
706 JSHandle<EcmaString> srcString = JSTaggedValue::ToString(thread, string);
707 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
708
709 // 4. ReturnIfAbrupt(S).
710 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
711 JSHandle<JSTaggedValue> inputStr = JSHandle<JSTaggedValue>::Cast(srcString);
712 // 5. Let lengthS be the number of code unit elements in S.
713 uint32_t length = EcmaStringAccessor(srcString).GetLength();
714 // 6. Let functionalReplace be IsCallable(replaceValue).
715 bool functionalReplace = inputReplaceValue->IsCallable();
716 JSHandle<EcmaString> replaceValueHandle;
717 if (!functionalReplace) {
718 replaceValueHandle = JSTaggedValue::ToString(thread, inputReplaceValue);
719 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
720 }
721 JSHandle<JSTaggedValue> lastIndex = globalConst->GetHandledLastIndexString();
722 // 8. Let global be ToBoolean(Get(rx, "global")).
723 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
724 JSHandle<JSTaggedValue> global = globalConst->GetHandledGlobalString();
725 JSTaggedValue globalValue =
726 ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(), global.GetTaggedValue());
727 // 9. ReturnIfAbrupt(global).
728 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
729 bool isGlobal = globalValue.ToBoolean();
730
731 // 10. If global is true, then
732 bool fullUnicode = false;
733 if (isGlobal) {
734 // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")).
735 JSHandle<JSTaggedValue> unicode = globalConst->GetHandledUnicodeString();
736 JSTaggedValue fullUnicodeTag =
737 ObjectFastOperator::FastGetPropertyByValue(thread, thisObj.GetTaggedValue(), unicode.GetTaggedValue());
738 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
739 fullUnicode = fullUnicodeTag.ToBoolean();
740 // b. ReturnIfAbrupt(fullUnicode).
741 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
742 // c. Let setStatus be Set(rx, "lastIndex", 0, true).
743 ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(),
744 lastIndex.GetTaggedValue(), JSTaggedValue(0));
745 // d. ReturnIfAbrupt(setStatus).
746 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
747 }
748
749 // Add cache for regexp replace
750 bool useCache = false;
751 JSMutableHandle<JSTaggedValue> pattern(thread, JSTaggedValue::Undefined());
752 JSMutableHandle<JSTaggedValue> flagsBits(thread, JSTaggedValue::Undefined());
753 JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
754 if (isGlobal && !functionalReplace && thisObj->IsJSRegExp()) {
755 JSHClass *hclass = JSHandle<JSObject>::Cast(thisObj)->GetJSHClass();
756 JSHClass *originHClass = JSHClass::Cast(globalConst->GetJSRegExpClass().GetTaggedObject());
757 if (hclass == originHClass) {
758 if (EcmaStringAccessor(replaceValueHandle).GetLength() == 0) {
759 return RegExpReplaceFast(thread, thisObj, srcString, length);
760 } else {
761 JSHandle<JSRegExp> regexpHandle(thisObj);
762 if (regexpHandle->IsJSRegExp()) {
763 pattern.Update(regexpHandle->GetOriginalSource());
764 flagsBits.Update(regexpHandle->GetOriginalFlags());
765 }
766 uint32_t strLength = EcmaStringAccessor(replaceValueHandle).GetLength();
767 uint32_t largeStrCount = cacheTable->GetLargeStrCount();
768 if (largeStrCount != 0) {
769 if (strLength > MIN_REPLACE_STRING_LENGTH) {
770 cacheTable->SetLargeStrCount(thread, --largeStrCount);
771 }
772 } else {
773 cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH);
774 }
775 if (strLength > cacheTable->GetStrLenThreshold()) {
776 useCache = true;
777 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, string,
778 RegExpExecResultCache::REPLACE_TYPE,
779 thisObj,
780 inputReplaceValue.GetTaggedValue());
781 if (cacheResult != JSTaggedValue::Undefined()) {
782 return cacheResult;
783 }
784 }
785 }
786 }
787 }
788
789 JSHandle<JSTaggedValue> matchedStr = globalConst->GetHandledZeroString();
790 // 11. Let results be a new empty List.
791 JSHandle<JSObject> resultsList(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
792 int resultsIndex = 0;
793 // 12. Let done be false.
794 // 13. Repeat, while done is false
795 JSMutableHandle<JSTaggedValue> nextIndexHandle(thread, JSTaggedValue(0));
796 JSMutableHandle<JSTaggedValue> execResult(thread, JSTaggedValue(0));
797 for (;;) {
798 // a. Let result be RegExpExec(rx, S).
799 execResult.Update(RegExpExec(thread, thisObj, inputStr, useCache));
800 // b. ReturnIfAbrupt(result).
801 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
802 // c. If result is null, set done to true.
803 if (execResult->IsNull()) {
804 break;
805 }
806 // d. Else result is not null, i. Append result to the end of results.
807 JSObject::CreateDataProperty(thread, resultsList, resultsIndex, execResult);
808 resultsIndex++;
809 // ii. If global is false, set done to true.
810 if (!isGlobal) {
811 break;
812 }
813 // iii. Else, 1. Let matchStr be ToString(Get(result, "0")).
814 JSTaggedValue getMatchVal = ObjectFastOperator::FastGetPropertyByValue(
815 thread, execResult.GetTaggedValue(), matchedStr.GetTaggedValue());
816 JSHandle<JSTaggedValue> getMatch(thread, getMatchVal);
817 JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, getMatch);
818 // 2. ReturnIfAbrupt(matchStr).
819 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
820 // 3. If matchStr is the empty String, then
821 if (EcmaStringAccessor(matchString).GetLength() == 0) {
822 // a. Let thisIndex be ToLength(Get(rx, "lastIndex")).
823 JSTaggedValue thisIndexVal = ObjectFastOperator::FastGetPropertyByValue(
824 thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue());
825 JSHandle<JSTaggedValue> thisIndexHandle(thread, thisIndexVal);
826 uint32_t thisIndex = 0;
827 if (thisIndexHandle->IsInt()) {
828 thisIndex = static_cast<uint32_t>(thisIndexHandle->GetInt());
829 } else {
830 thisIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber();
831 // b. ReturnIfAbrupt(thisIndex).
832 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
833 }
834 // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
835 uint32_t nextIndex = AdvanceStringIndex(inputStr, thisIndex, fullUnicode);
836 nextIndexHandle.Update(JSTaggedValue(nextIndex));
837 // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true).
838 ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue(),
839 nextIndexHandle.GetTaggedValue());
840 // e. ReturnIfAbrupt(setStatus).
841 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
842 }
843 }
844 // 14. Let accumulatedResult be the empty String value.
845 std::string accumulatedResult;
846 // 15. Let nextSourcePosition be 0.
847 uint32_t nextSourcePosition = 0;
848 JSHandle<JSTaggedValue> getMatchString;
849 JSMutableHandle<JSTaggedValue> resultValues(thread, JSTaggedValue(0));
850 JSMutableHandle<JSTaggedValue> ncapturesHandle(thread, JSTaggedValue(0));
851 JSMutableHandle<JSTaggedValue> capN(thread, JSTaggedValue(0));
852 // 16. Repeat, for each result in results,
853 for (int i = 0; i < resultsIndex; i++) {
854 resultValues.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, resultsList.GetTaggedValue(), i));
855 // a. Let nCaptures be ToLength(Get(result, "length")).
856 JSHandle<JSTaggedValue> lengthHandle = globalConst->GetHandledLengthString();
857 ncapturesHandle.Update(ObjectFastOperator::FastGetPropertyByValue(
858 thread, resultValues.GetTaggedValue(), lengthHandle.GetTaggedValue()));
859 uint32_t ncaptures = JSTaggedValue::ToUint32(thread, ncapturesHandle);
860 // b. ReturnIfAbrupt(nCaptures).
861 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
862 // c. Let nCaptures be max(nCaptures − 1, 0).
863 ncaptures = std::max<uint32_t>((ncaptures - 1), 0);
864 // d. Let matched be ToString(Get(result, "0")).
865 JSTaggedValue value = ObjectFastOperator::GetPropertyByIndex(thread, resultValues.GetTaggedValue(), 0);
866 getMatchString = JSHandle<JSTaggedValue>(thread, value);
867 JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, getMatchString);
868 // e. ReturnIfAbrupt(matched).
869 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
870 // f. Let matchLength be the number of code units in matched.
871 uint32_t matchLength = EcmaStringAccessor(matchString).GetLength();
872 // g. Let position be ToInteger(Get(result, "index")).
873 JSHandle<JSTaggedValue> resultIndex = globalConst->GetHandledIndexString();
874 JSTaggedValue positionTag = ObjectFastOperator::FastGetPropertyByValue(
875 thread, resultValues.GetTaggedValue(), resultIndex.GetTaggedValue());
876 JSHandle<JSTaggedValue> positionHandle(thread, positionTag);
877 uint32_t position = 0;
878 if (positionHandle->IsInt()) {
879 position = static_cast<uint32_t>(positionHandle->GetInt());
880 } else {
881 position = JSTaggedValue::ToUint32(thread, positionHandle);
882 // h. ReturnIfAbrupt(position).
883 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
884 }
885 // i. Let position be max(min(position, lengthS), 0).
886 position = std::max<uint32_t>(std::min<uint32_t>(position, length), 0);
887 // j. Let n be 1.
888 uint32_t index = 1;
889 // k. Let captures be an empty List.
890 JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(ncaptures);
891 // l. Repeat while n ≤ nCaptures
892 while (index <= ncaptures) {
893 // i. Let capN be Get(result, ToString(n)).
894 capN.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, resultValues.GetTaggedValue(), index));
895 // ii. ReturnIfAbrupt(capN).
896 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
897 // iii. If capN is not undefined, then
898 if (!capN->IsUndefined()) {
899 // 1. Let capN be ToString(capN).
900 JSHandle<EcmaString> capNStr = JSTaggedValue::ToString(thread, capN);
901 // 2. ReturnIfAbrupt(capN).
902 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
903 JSHandle<JSTaggedValue> capnStr = JSHandle<JSTaggedValue>::Cast(capNStr);
904 capturesList->Set(thread, index - 1, capnStr);
905 } else {
906 // iv. Append capN as the last element of captures.
907 capturesList->Set(thread, index - 1, capN);
908 }
909 // v. Let n be n+1
910 ++index;
911 }
912
913 // j. Let namedCaptures be ? Get(result, "groups").
914 JSHandle<JSTaggedValue> groupsKey = globalConst->GetHandledGroupsString();
915 JSTaggedValue named = ObjectFastOperator::FastGetPropertyByValue(thread,
916 resultValues.GetTaggedValue(), groupsKey.GetTaggedValue());
917 JSHandle<JSTaggedValue> namedCaptures(thread, named);
918 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
919 // m. If functionalReplace is true, then
920 CString replacement;
921 int emptyArrLength = 0;
922 if (namedCaptures->IsUndefined()) {
923 emptyArrLength = 3; // 3: «matched, pos, and string»
924 } else {
925 emptyArrLength = 4; // 4: «matched, pos, string, and groups»
926 }
927 JSHandle<TaggedArray> replacerArgs =
928 factory->NewTaggedArray(emptyArrLength + capturesList->GetLength());
929 if (functionalReplace) {
930 // i. Let replacerArgs be «matched».
931 replacerArgs->Set(thread, 0, getMatchString.GetTaggedValue());
932 // ii. Append in list order the elements of captures to the end of the List replacerArgs.
933 // iii. Append position and S as the last two elements of replacerArgs.
934 index = 0;
935 while (index < capturesList->GetLength()) {
936 replacerArgs->Set(thread, index + 1, capturesList->Get(index));
937 ++index;
938 }
939 replacerArgs->Set(thread, index + 1, JSTaggedValue(position));
940 replacerArgs->Set(thread, index + 2, inputStr.GetTaggedValue()); // 2: position of string
941 if (!namedCaptures->IsUndefined()) {
942 replacerArgs->Set(thread, index + 3, namedCaptures.GetTaggedValue()); // 3: position of groups
943 }
944 // iv. Let replValue be Call(replaceValue, undefined, replacerArgs).
945 const int32_t argsLength = static_cast<int32_t>(replacerArgs->GetLength());
946 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
947 EcmaRuntimeCallInfo *info =
948 EcmaInterpreter::NewRuntimeCallInfo(thread, inputReplaceValue, undefined, undefined, argsLength);
949 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
950 info->SetCallArg(argsLength, replacerArgs);
951 JSTaggedValue replaceResult = JSFunction::Call(info);
952 JSHandle<JSTaggedValue> replValue(thread, replaceResult);
953 // v. Let replacement be ToString(replValue).
954 JSHandle<EcmaString> replacementString = JSTaggedValue::ToString(thread, replValue);
955 // o. ReturnIfAbrupt(replacement).
956 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
957 replacement = ConvertToString(*replacementString, StringConvertedUsage::LOGICOPERATION);
958 } else {
959 // n. Else,
960 if (!namedCaptures->IsUndefined()) {
961 JSHandle<JSObject> namedCapturesObj = JSTaggedValue::ToObject(thread, namedCaptures);
962 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
963 namedCaptures = JSHandle<JSTaggedValue>::Cast(namedCapturesObj);
964 }
965 JSHandle<JSTaggedValue> replacementHandle(
966 thread, BuiltinsString::GetSubstitution(thread, matchString, srcString,
967 position, capturesList, namedCaptures,
968 replaceValueHandle));
969 replacement = ConvertToString(EcmaString::Cast(replacementHandle->GetTaggedObject()),
970 StringConvertedUsage::LOGICOPERATION);
971 }
972 // p. If position ≥ nextSourcePosition, then
973 if (position >= nextSourcePosition) {
974 // ii. Let accumulatedResult be the String formed by concatenating the code units of the current value
975 // of accumulatedResult with the substring of S consisting of the code units from nextSourcePosition
976 // (inclusive) up to position (exclusive) and with the code units of replacement.
977 auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
978 JSHandle<EcmaString>::Cast(inputStr), nextSourcePosition, position - nextSourcePosition);
979 accumulatedResult += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
980 accumulatedResult += replacement;
981 // iii. Let nextSourcePosition be position + matchLength.
982 nextSourcePosition = position + matchLength;
983 }
984 }
985 // 17. If nextSourcePosition ≥ lengthS, return accumulatedResult.
986 if (nextSourcePosition >= length) {
987 JSHandle<EcmaString> resultValue = factory->NewFromStdString(accumulatedResult);
988 if (useCache) {
989 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, string,
990 JSHandle<JSTaggedValue>(resultValue),
991 RegExpExecResultCache::REPLACE_TYPE, nextIndexHandle->GetInt(),
992 inputReplaceValue.GetTaggedValue());
993 }
994 return resultValue.GetTaggedValue();
995 }
996 // 18. Return the String formed by concatenating the code units of accumulatedResult with the substring of S
997 // consisting of the code units from nextSourcePosition (inclusive) up through the final code unit of S(inclusive).
998 auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
999 JSHandle<EcmaString>::Cast(inputStr), nextSourcePosition, length - nextSourcePosition);
1000 accumulatedResult += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1001 JSHandle<EcmaString> resultValue = factory->NewFromStdString(accumulatedResult);
1002 if (useCache) {
1003 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, string,
1004 JSHandle<JSTaggedValue>(resultValue),
1005 RegExpExecResultCache::REPLACE_TYPE, nextIndexHandle->GetInt(),
1006 inputReplaceValue.GetTaggedValue());
1007 }
1008 return resultValue.GetTaggedValue();
1009 }
1010
1011 // 21.2.5.9
Search(EcmaRuntimeCallInfo * argv)1012 JSTaggedValue BuiltinsRegExp::Search(EcmaRuntimeCallInfo *argv)
1013 {
1014 ASSERT(argv);
1015 BUILTINS_API_TRACE(argv->GetThread(), RegExp, Search);
1016 JSThread *thread = argv->GetThread();
1017 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1018 // 1. Let rx be the this value.
1019 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
1020 // 3. Let S be ToString(string).
1021 JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
1022 JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
1023
1024 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1025 JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
1026 if (!thisObj->IsECMAObject()) {
1027 // 2. If Type(rx) is not Object, throw a TypeError exception.
1028 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
1029 }
1030 // 4. Let previousLastIndex be ? Get(rx, "lastIndex").
1031 JSHandle<JSTaggedValue> lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString());
1032 JSHandle<JSTaggedValue> previousLastIndex = JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue();
1033 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1034 // 5. If SameValue(previousLastIndex, 0) is false, then
1035 // Perform ? Set(rx, "lastIndex", 0, true).
1036 if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), JSTaggedValue(0))) {
1037 JSHandle<JSTaggedValue> value(thread, JSTaggedValue(0));
1038 JSObject::SetProperty(thread, thisObj, lastIndexString, value, true);
1039 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1040 }
1041 // 6. Let result be ? RegExpExec(rx, S).
1042 JSHandle<JSTaggedValue> result(thread, RegExpExec(thread, thisObj, string, false));
1043 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1044 // 7. Let currentLastIndex be ? Get(rx, "lastIndex").
1045 JSHandle<JSTaggedValue> currentLastIndex = JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue();
1046 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1047 // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
1048 // Perform ? Set(rx, "lastIndex", previousLastIndex, true).
1049 if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), currentLastIndex.GetTaggedValue())) {
1050 JSObject::SetProperty(thread, thisObj, lastIndexString, previousLastIndex, true);
1051 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1052 }
1053 // 9. If result is null, return -1.
1054 if (result->IsNull()) {
1055 return JSTaggedValue(-1);
1056 }
1057 // 10. Return ? Get(result, "index").
1058 JSHandle<JSTaggedValue> index(thread->GlobalConstants()->GetHandledIndexString());
1059 return JSObject::GetProperty(thread, result, index).GetValue().GetTaggedValue();
1060 }
1061
1062 // 21.2.5.11
1063 // NOLINTNEXTLINE(readability-function-size)
Split(EcmaRuntimeCallInfo * argv)1064 JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv)
1065 {
1066 ASSERT(argv);
1067 BUILTINS_API_TRACE(argv->GetThread(), RegExp, Split);
1068 JSThread *thread = argv->GetThread();
1069 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1070 bool useCache = false;
1071 // 1. Let rx be the this value.
1072 JSHandle<JSTaggedValue> thisObj = GetThis(argv);
1073 auto ecmaVm = thread->GetEcmaVM();
1074 // 3. Let S be ToString(string).
1075 JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
1076 JSHandle<JSTaggedValue> limit = GetCallArg(argv, 1);
1077 JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
1078
1079 // 4. ReturnIfAbrupt(string).
1080 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1081 JSHandle<JSTaggedValue> jsString = JSHandle<JSTaggedValue>::Cast(stringHandle);
1082 if (!thisObj->IsECMAObject()) {
1083 // 2. If Type(rx) is not Object, throw a TypeError exception.
1084 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
1085 }
1086 // 5. Let C be SpeciesConstructor(rx, %RegExp%).
1087 JSHandle<JSTaggedValue> defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction();
1088 JSHandle<JSObject> objHandle(thisObj);
1089 JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
1090 // 6. ReturnIfAbrupt(C).
1091 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1092 // 7. Let flags be ToString(Get(rx, "flags")).
1093 ObjectFactory *factory = ecmaVm->GetFactory();
1094 const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
1095 JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
1096 JSHandle<JSTaggedValue> taggedFlags = JSObject::GetProperty(thread, thisObj, flagsString).GetValue();
1097 JSHandle<EcmaString> flags;
1098
1099 if (taggedFlags->IsUndefined()) {
1100 flags = factory->GetEmptyString();
1101 } else {
1102 flags = JSTaggedValue::ToString(thread, taggedFlags);
1103 }
1104 // 8. ReturnIfAbrupt(flags).
1105 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1106 // 9. If flags contains "u", let unicodeMatching be true.
1107 // 10. Else, let unicodeMatching be false.
1108 JSHandle<EcmaString> uStringHandle(globalConstants->GetHandledUString());
1109 bool unicodeMatching = (EcmaStringAccessor::IndexOf(*flags, *uStringHandle) != -1);
1110 // 11. If flags contains "y", let newFlags be flags.
1111 JSHandle<EcmaString> newFlagsHandle;
1112 JSHandle<EcmaString> yStringHandle = JSHandle<EcmaString>::Cast(globalConstants->GetHandledYString());
1113 if (EcmaStringAccessor::IndexOf(*flags, *yStringHandle) != -1) {
1114 newFlagsHandle = flags;
1115 } else {
1116 // 12. Else, let newFlags be the string that is the concatenation of flags and "y".
1117 JSHandle<EcmaString> yStr = JSHandle<EcmaString>::Cast(globalConstants->GetHandledYString());
1118 newFlagsHandle = factory->ConcatFromString(flags, yStr);
1119 }
1120
1121 // 17. If limit is undefined, let lim be 2^32–1; else let lim be ToUint32(limit).
1122 uint32_t lim;
1123 if (limit->IsUndefined()) {
1124 lim = MAX_SPLIT_LIMIT;
1125 } else {
1126 lim = JSTaggedValue::ToUint32(thread, limit);
1127 // 18. ReturnIfAbrupt(lim).
1128 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1129 }
1130
1131 if (lim == MAX_SPLIT_LIMIT) {
1132 useCache = true;
1133 }
1134
1135 JSHandle<JSRegExp> regexpHandle(thisObj);
1136 JSMutableHandle<JSTaggedValue> pattern(thread, JSTaggedValue::Undefined());
1137 JSMutableHandle<JSTaggedValue> flagsBits(thread, JSTaggedValue::Undefined());
1138 if (thisObj->IsJSRegExp()) {
1139 pattern.Update(regexpHandle->GetOriginalSource());
1140 flagsBits.Update(regexpHandle->GetOriginalFlags());
1141 }
1142 JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
1143 if (useCache) {
1144 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, inputString,
1145 RegExpExecResultCache::SPLIT_TYPE, thisObj);
1146 if (cacheResult != JSTaggedValue::Undefined()) {
1147 return cacheResult;
1148 }
1149 }
1150
1151 // 13. Let splitter be Construct(C, «rx, newFlags»).
1152 JSHandle<JSObject> globalObject(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject());
1153 JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
1154 EcmaRuntimeCallInfo *runtimeInfo =
1155 EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 2); // 2: two args
1156 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1157 runtimeInfo->SetCallArg(thisObj.GetTaggedValue(), newFlagsHandle.GetTaggedValue());
1158 JSTaggedValue taggedSplitter = JSFunction::Construct(runtimeInfo);
1159 // 14. ReturnIfAbrupt(splitter).
1160 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1161
1162 JSHandle<JSTaggedValue> splitter(thread, taggedSplitter);
1163 // 15. Let A be ArrayCreate(0).
1164 JSHandle<JSObject> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1165 // 16. Let lengthA be 0.
1166 uint32_t aLength = 0;
1167
1168 // 19. Let size be the number of elements in S.
1169 uint32_t size = EcmaStringAccessor(jsString->GetTaggedObject()).GetLength();
1170 // 20. Let p be 0.
1171 uint32_t startIndex = 0;
1172 // 21. If lim = 0, return A.
1173 if (lim == 0) {
1174 return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1175 }
1176 // 22. If size = 0, then
1177 if (size == 0) {
1178 // a. Let z be RegExpExec(splitter, S).
1179 JSHandle<JSTaggedValue> execResult(thread, RegExpExec(thread, splitter, jsString, useCache));
1180 // b. ReturnIfAbrupt(z).
1181 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1182 // c. If z is not null, return A.
1183 if (!execResult->IsNull()) {
1184 return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1185 }
1186 // d. Assert: The following call will never result in an abrupt completion.
1187 // e. Perform CreateDataProperty(A, "0", S).
1188 JSObject::CreateDataProperty(thread, array, 0, jsString);
1189 // f. Return A.
1190 return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1191 }
1192 // 23. Let q be p.
1193 uint32_t endIndex = startIndex;
1194 JSMutableHandle<JSTaggedValue> lastIndexvalue(thread, JSTaggedValue(endIndex));
1195 // 24. Repeat, while q < size
1196 JSHandle<JSTaggedValue> lastIndexString = globalConstants->GetHandledLastIndexString();
1197 while (endIndex < size) {
1198 // a. Let setStatus be Set(splitter, "lastIndex", q, true).
1199 lastIndexvalue.Update(JSTaggedValue(endIndex));
1200 JSObject::SetProperty(thread, splitter, lastIndexString, lastIndexvalue, true);
1201 // b. ReturnIfAbrupt(setStatus).
1202 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1203 JSHandle<JSTaggedValue> execResult(thread, RegExpExec(thread, splitter, jsString, useCache));
1204 // d. ReturnIfAbrupt(z).
1205 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1206 // e. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching).
1207 if (execResult->IsNull()) {
1208 endIndex = AdvanceStringIndex(jsString, endIndex, unicodeMatching);
1209 } else {
1210 // f. Else z is not null,
1211 // i. Let e be ToLength(Get(splitter, "lastIndex")).
1212 JSHandle<JSTaggedValue> lastIndexHandle =
1213 JSObject::GetProperty(thread, splitter, lastIndexString).GetValue();
1214 JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexHandle);
1215 // ii. ReturnIfAbrupt(e).
1216 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1217 uint32_t lastIndex = lastIndexNumber.GetNumber();
1218 // iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching).
1219 if (lastIndex == startIndex) {
1220 endIndex = AdvanceStringIndex(jsString, endIndex, unicodeMatching);
1221 } else {
1222 // iv. Else e != p,
1223 // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p
1224 // (inclusive) through q (exclusive).
1225 auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1226 JSHandle<EcmaString>::Cast(jsString), startIndex, endIndex - startIndex);
1227 std::string stdStrT = EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1228 // 2. Assert: The following call will never result in an abrupt completion.
1229 // 3. Perform CreateDataProperty(A, ToString(lengthA), T).
1230 JSHandle<JSTaggedValue> tValue(factory->NewFromStdString(stdStrT));
1231 JSObject::CreateDataProperty(thread, array, aLength, tValue);
1232 // 4. Let lengthA be lengthA +1.
1233 ++aLength;
1234 // 5. If lengthA = lim, return A.
1235 if (aLength == lim) {
1236 if (useCache) {
1237 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString,
1238 JSHandle<JSTaggedValue>(array),
1239 RegExpExecResultCache::SPLIT_TYPE, lastIndex);
1240 }
1241 return array.GetTaggedValue();
1242 }
1243 // 6. Let p be e.
1244 startIndex = lastIndex;
1245 // 7. Let numberOfCaptures be ToLength(Get(z, "length")).
1246 JSHandle<JSTaggedValue> lengthString(thread->GlobalConstants()->GetHandledLengthString());
1247 JSHandle<JSTaggedValue> capturesHandle =
1248 JSObject::GetProperty(thread, execResult, lengthString).GetValue();
1249 JSTaggedNumber numberOfCapturesNumber = JSTaggedValue::ToLength(thread, capturesHandle);
1250 // 8. ReturnIfAbrupt(numberOfCaptures).
1251 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1252 uint32_t numberOfCaptures = numberOfCapturesNumber.GetNumber();
1253 // 9. Let numberOfCaptures be max(numberOfCaptures-1, 0).
1254 numberOfCaptures = (numberOfCaptures == 0) ? 0 : numberOfCaptures - 1;
1255 // 10. Let i be 1.
1256 uint32_t i = 1;
1257 // 11. Repeat, while i ≤ numberOfCaptures.
1258 while (i <= numberOfCaptures) {
1259 // a. Let nextCapture be Get(z, ToString(i)).
1260 JSHandle<JSTaggedValue> nextCapture = JSObject::GetProperty(thread, execResult, i).GetValue();
1261 // b. ReturnIfAbrupt(nextCapture).
1262 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1263 // c. Perform CreateDataProperty(A, ToString(lengthA), nextCapture).
1264 JSObject::CreateDataProperty(thread, array, aLength, nextCapture);
1265 // d. Let i be i + 1.
1266 ++i;
1267 // e. Let lengthA be lengthA +1.
1268 ++aLength;
1269 // f. If lengthA = lim, return A.
1270 if (aLength == lim) {
1271 if (useCache) {
1272 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString,
1273 JSHandle<JSTaggedValue>(array),
1274 RegExpExecResultCache::SPLIT_TYPE, lastIndex);
1275 }
1276 return array.GetTaggedValue();
1277 }
1278 }
1279 // 12. Let q be p.
1280 endIndex = startIndex;
1281 }
1282 }
1283 }
1284 // 25. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive)
1285 // through size (exclusive).
1286 auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1287 JSHandle<EcmaString>::Cast(jsString), startIndex, size - startIndex);
1288 std::string stdStrT = EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1289 // 26. Assert: The following call will never result in an abrupt completion.
1290 // 27. Perform CreateDataProperty(A, ToString(lengthA), t).
1291 JSHandle<JSTaggedValue> tValue(factory->NewFromStdString(stdStrT));
1292 JSObject::CreateDataProperty(thread, array, aLength, tValue);
1293 if (lim == MAX_SPLIT_LIMIT) {
1294 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString,
1295 JSHandle<JSTaggedValue>(array), RegExpExecResultCache::SPLIT_TYPE,
1296 endIndex);
1297 }
1298 // 28. Return A.
1299 return array.GetTaggedValue();
1300 }
1301
1302 // NOLINTNEXTLINE(readability-non-const-parameter)
Matcher(JSThread * thread,const JSHandle<JSTaggedValue> & regexp,const uint8_t * buffer,size_t length,int32_t lastIndex,bool isUtf16)1303 RegExpExecutor::MatchResult BuiltinsRegExp::Matcher(JSThread *thread, const JSHandle<JSTaggedValue> ®exp,
1304 const uint8_t *buffer, size_t length, int32_t lastIndex,
1305 bool isUtf16)
1306 {
1307 // get bytecode
1308 JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
1309 void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
1310 auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
1311 // execute
1312 Chunk chunk(thread->GetNativeAreaAllocator());
1313 RegExpExecutor executor(&chunk);
1314 if (lastIndex < 0) {
1315 lastIndex = 0;
1316 }
1317 bool ret = executor.Execute(buffer, lastIndex, static_cast<uint32_t>(length), bytecodeBuffer, isUtf16);
1318 RegExpExecutor::MatchResult result = executor.GetResult(thread, ret);
1319 return result;
1320 }
1321
AdvanceStringIndex(const JSHandle<JSTaggedValue> & inputStr,uint32_t index,bool unicode)1322 uint32_t BuiltinsRegExp::AdvanceStringIndex(const JSHandle<JSTaggedValue> &inputStr, uint32_t index,
1323 bool unicode)
1324 {
1325 // 1. Assert: Type(S) is String.
1326 ASSERT(inputStr->IsString());
1327 // 2. Assert: index is an integer such that 0≤index≤2^53 - 1
1328 ASSERT(index <= pow(2, 53) - 1);
1329 // 3. Assert: Type(unicode) is Boolean.
1330 // 4. If unicode is false, return index+1.
1331 if (!unicode) {
1332 return index + 1;
1333 }
1334 // 5. Let length be the number of code units in S.
1335 uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
1336 // 6. If index+1 ≥ length, return index+1.
1337 if (index + 1 >= length) {
1338 return index + 1;
1339 }
1340 // 7. Let first be the code unit value at index index in S.
1341 uint16_t first = EcmaStringAccessor(inputStr->GetTaggedObject()).Get(index);
1342 // 8. If first < 0xD800 or first > 0xDFFF, return index+1.
1343 if (first < 0xD800 || first > 0xDFFF) { // NOLINT(readability-magic-numbers)
1344 return index + 1;
1345 }
1346 // 9. Let second be the code unit value at index index+1 in S.
1347 uint16_t second = EcmaStringAccessor(inputStr->GetTaggedObject()).Get(index + 1);
1348 // 10. If second < 0xDC00 or second > 0xDFFF, return index+1.
1349 if (second < 0xDC00 || second > 0xDFFF) { // NOLINT(readability-magic-numbers)
1350 return index + 1;
1351 }
1352 // 11. Return index + 2.
1353 return index + 2;
1354 }
1355
GetFlagsInternal(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const uint8_t mask)1356 bool BuiltinsRegExp::GetFlagsInternal(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const uint8_t mask)
1357 {
1358 // 1. Let R be the this value.
1359 // 2. If Type(R) is not Object, throw a TypeError exception.
1360 if (!obj->IsECMAObject()) {
1361 // throw a TypeError exception.
1362 THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", false);
1363 }
1364 // 3. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception.
1365 JSHandle<JSObject> patternObj = JSHandle<JSObject>::Cast(obj);
1366 if (!patternObj->IsJSRegExp()) {
1367 // throw a TypeError exception.
1368 THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalFlags]]", false);
1369 }
1370 // 4. Let flags be the value of R’s [[OriginalFlags]] internal slot.
1371 JSHandle<JSRegExp> regexpObj(thread, JSRegExp::Cast(obj->GetTaggedObject()));
1372 // 5. If flags contains the code unit "[flag]", return true.
1373 // 6. Return false.
1374 uint8_t flags = static_cast<uint8_t>(regexpObj->GetOriginalFlags().GetInt());
1375 return flags & mask;
1376 }
1377 // 21.2.5.2.2
RegExpBuiltinExec(JSThread * thread,const JSHandle<JSTaggedValue> & regexp,const JSHandle<JSTaggedValue> & inputStr,bool useCache)1378 JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle<JSTaggedValue> ®exp,
1379 const JSHandle<JSTaggedValue> &inputStr, bool useCache)
1380 {
1381 ASSERT(JSObject::IsRegExp(thread, regexp));
1382 ASSERT(inputStr->IsString());
1383
1384 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1385 JSHandle<JSTaggedValue> lastIndexHandle = globalConst->GetHandledLastIndexString();
1386 JSTaggedValue result =
1387 ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue());
1388 int32_t lastIndex = 0;
1389 if (result.IsInt()) {
1390 lastIndex = result.GetInt();
1391 } else {
1392 JSHandle<JSTaggedValue> lastIndexResult(thread, result);
1393 JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexResult);
1394 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1395 lastIndex = lastIndexNumber.GetNumber();
1396 }
1397
1398 JSHandle<JSTaggedValue> globalHandle = globalConst->GetHandledGlobalString();
1399 bool global = ObjectFastOperator::FastGetPropertyByValue(
1400 thread, regexp.GetTaggedValue(), globalHandle.GetTaggedValue()).ToBoolean();
1401 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1402 JSHandle<JSTaggedValue> stickyHandle = globalConst->GetHandledStickyString();
1403 bool sticky = ObjectFastOperator::FastGetPropertyByValue(
1404 thread, regexp.GetTaggedValue(), stickyHandle.GetTaggedValue()).ToBoolean();
1405 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1406 if (!global && !sticky) {
1407 lastIndex = 0;
1408 }
1409
1410 JSHandle<JSRegExp> regexpObj(regexp);
1411 JSMutableHandle<JSTaggedValue> pattern(thread, regexpObj->GetOriginalSource());
1412 JSMutableHandle<JSTaggedValue> flags(thread, regexpObj->GetOriginalFlags());
1413
1414 JSHandle<RegExpExecResultCache> cacheTable(thread->GetEcmaVM()->GetRegExpCache());
1415 uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
1416 if (lastIndex > static_cast<int32_t>(length)) {
1417 ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(),
1418 JSTaggedValue(0));
1419 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1420 return JSTaggedValue::Null();
1421 }
1422 JSHandle<EcmaString> inputString = JSTaggedValue::ToString(thread, inputStr);
1423 bool isUtf16 = EcmaStringAccessor(inputString).IsUtf16();
1424 auto inputPtr = EcmaStringAccessor(inputString).ToOneByteDataForced();
1425 const uint8_t *strBuffer = inputPtr.get();
1426 size_t stringLength = EcmaStringAccessor(inputString).GetLength();
1427 RegExpExecutor::MatchResult matchResult = Matcher(thread, regexp, strBuffer, stringLength, lastIndex, isUtf16);
1428 if (!matchResult.isSuccess_) {
1429 if (global || sticky) {
1430 JSHandle<JSTaggedValue> lastIndexValue(thread, JSTaggedValue(0));
1431 ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
1432 lastIndexHandle.GetTaggedValue(), JSTaggedValue(0));
1433 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1434 }
1435 return JSTaggedValue::Null();
1436 }
1437 uint32_t endIndex = matchResult.endIndex_;
1438 if (global || sticky) {
1439 // a. Let setStatus be Set(R, "lastIndex", e, true).
1440 ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(),
1441 JSTaggedValue(endIndex));
1442 // b. ReturnIfAbrupt(setStatus).
1443 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1444 }
1445 uint32_t capturesSize = matchResult.captures_.size();
1446 JSHandle<JSObject> results(JSArray::ArrayCreate(thread, JSTaggedNumber(capturesSize)));
1447 uint32_t matchIndex = matchResult.index_;
1448 // 24. Perform CreateDataProperty(A, "index", matchIndex).
1449 JSHandle<JSTaggedValue> indexKey = globalConst->GetHandledIndexString();
1450 JSHandle<JSTaggedValue> indexValue(thread, JSTaggedValue(matchIndex));
1451 JSObject::CreateDataProperty(thread, results, indexKey, indexValue);
1452 // 25. Perform CreateDataProperty(A, "input", S).
1453 JSHandle<JSTaggedValue> inputKey = globalConst->GetHandledInputString();
1454
1455 JSHandle<JSTaggedValue> inputValue(thread, static_cast<EcmaString *>(inputStr->GetTaggedObject()));
1456 JSObject::CreateDataProperty(thread, results, inputKey, inputValue);
1457 // 27. Perform CreateDataProperty(A, "0", matched_substr).
1458 JSHandle<JSTaggedValue> zeroValue(matchResult.captures_[0].second);
1459 JSObject::CreateDataProperty(thread, results, 0, zeroValue);
1460 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1461 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1462
1463 JSHandle<JSTaggedValue> groupName(thread, regexpObj->GetGroupName());
1464 JSMutableHandle<JSTaggedValue> groups(thread, JSTaggedValue::Undefined());
1465 if (!groupName->IsUndefined()) {
1466 JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
1467 JSHandle<JSObject> nullObj = factory->OrdinaryNewJSObjectCreate(nullHandle);
1468 groups.Update(nullObj.GetTaggedValue());
1469 }
1470 JSHandle<JSTaggedValue> groupsKey = globalConst->GetHandledGroupsString();
1471 JSObject::CreateDataProperty(thread, results, groupsKey, groups);
1472 // Create a new RegExp on global
1473 JSHandle<JSObject> globalRegExp = JSHandle<JSObject>(env->GetRegExpFunction());
1474 JSMutableHandle<JSTaggedValue> keyString(thread, JSTaggedValue::Undefined());
1475 uint32_t captureIndex = 1;
1476 // 28. For each integer i such that i > 0 and i <= n
1477 for (; captureIndex < capturesSize; captureIndex++) {
1478 // a. Let capture_i be ith element of r's captures List
1479 JSTaggedValue capturedValue;
1480 if (matchResult.captures_[captureIndex].first) {
1481 capturedValue = JSTaggedValue::Undefined();
1482 } else {
1483 capturedValue = matchResult.captures_[captureIndex].second.GetTaggedValue();
1484 }
1485 JSHandle<JSTaggedValue> iValue(thread, capturedValue);
1486 // add to RegExp.$i and i must <= 9
1487 if (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) {
1488 keyString.Update(GetDollarString(thread, static_cast<RegExpGlobalArrayIndex>(captureIndex)));
1489 ObjectOperator op(thread, globalRegExp, keyString);
1490 PropertyBox *cell = PropertyBox::Cast(op.GetValue().GetTaggedObject());
1491 cell->SetValue(thread, iValue);
1492 }
1493
1494 JSObject::CreateDataProperty(thread, results, captureIndex, iValue);
1495 if (!groupName->IsUndefined()) {
1496 JSHandle<JSObject> groupObject = JSHandle<JSObject>::Cast(groups);
1497 TaggedArray *groupArray = TaggedArray::Cast(regexpObj->GetGroupName().GetTaggedObject());
1498 if (groupArray->GetLength() > captureIndex - 1) {
1499 JSHandle<JSTaggedValue> skey(thread, groupArray->Get(captureIndex - 1));
1500 JSObject::CreateDataProperty(thread, groupObject, skey, iValue);
1501 }
1502 }
1503 }
1504 JSHandle<JSTaggedValue> emptyString = thread->GlobalConstants()->GetHandledEmptyString();
1505 while (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) {
1506 keyString.Update(GetDollarString(thread, static_cast<RegExpGlobalArrayIndex>(captureIndex)));
1507 ObjectOperator op(thread, globalRegExp, keyString);
1508 PropertyBox *cell = PropertyBox::Cast(op.GetValue().GetTaggedObject());
1509 cell->SetValue(thread, emptyString);
1510 ++captureIndex;
1511 }
1512 if (lastIndex == 0 && useCache) {
1513 RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputStr,
1514 JSHandle<JSTaggedValue>(results), RegExpExecResultCache::EXEC_TYPE,
1515 endIndex);
1516 }
1517 // 29. Return A.
1518 return results.GetTaggedValue();
1519 }
1520
1521 // 21.2.5.2.1
RegExpExec(JSThread * thread,const JSHandle<JSTaggedValue> & regexp,const JSHandle<JSTaggedValue> & inputString,bool useCache)1522 JSTaggedValue BuiltinsRegExp::RegExpExec(JSThread *thread, const JSHandle<JSTaggedValue> ®exp,
1523 const JSHandle<JSTaggedValue> &inputString, bool useCache)
1524 {
1525 // 1. Assert: Type(R) is Object.
1526 ASSERT(regexp->IsECMAObject());
1527 // 2. Assert: Type(S) is String.
1528 ASSERT(inputString->IsString());
1529 // 3. Let exec be Get(R, "exec").
1530 JSHandle<EcmaString> inputStr = JSTaggedValue::ToString(thread, inputString);
1531
1532 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1533 JSHandle<JSTaggedValue> execHandle = globalConst->GetHandledExecString();
1534 JSTaggedValue execVal = ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(),
1535 execHandle.GetTaggedValue());
1536 JSHandle<JSTaggedValue> exec(thread, execVal);
1537 // 4. ReturnIfAbrupt(exec).
1538 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1539 // 5. If IsCallable(exec) is true, then
1540 if (exec->IsCallable()) {
1541 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1542 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, exec, regexp, undefined, 1);
1543 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1544 info->SetCallArg(inputStr.GetTaggedValue());
1545 JSTaggedValue result = JSFunction::Call(info);
1546 // b. ReturnIfAbrupt(result).
1547 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1548 if (!result.IsECMAObject() && !result.IsNull()) {
1549 // throw a TypeError exception.
1550 THROW_TYPE_ERROR_AND_RETURN(thread, "exec result is null or is not Object", JSTaggedValue::Exception());
1551 }
1552 return result;
1553 }
1554 // 6. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
1555 if (!regexp->IsJSRegExp()) {
1556 // throw a TypeError exception.
1557 THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have a [[RegExpMatcher]]", JSTaggedValue::Exception());
1558 }
1559 // 7. Return RegExpBuiltinExec(R, S).
1560 return RegExpBuiltinExec(thread, regexp, inputString, useCache);
1561 }
1562
1563 // 21.2.3.2.1
RegExpAlloc(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget)1564 JSTaggedValue BuiltinsRegExp::RegExpAlloc(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget)
1565 {
1566 /**
1567 * 1. Let obj be OrdinaryCreateFromConstructor(newTarget, "%RegExpPrototype%",
1568 * «[[RegExpMatcher]],[[OriginalSource]], [[OriginalFlags]]»).
1569 * */
1570 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1571 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
1572 JSHandle<JSTaggedValue> func = env->GetRegExpFunction();
1573 JSHandle<JSTaggedValue> obj(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(func), newTarget));
1574 // 2. ReturnIfAbrupt(obj).
1575 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1576 // 5. Return obj.
1577 return obj.GetTaggedValue();
1578 }
1579
UpdateExpressionFlags(JSThread * thread,const CString & checkStr)1580 uint32_t BuiltinsRegExp::UpdateExpressionFlags(JSThread *thread, const CString &checkStr)
1581 {
1582 uint32_t flagsBits = 0;
1583 uint32_t flagsBitsTemp = 0;
1584 for (char i : checkStr) {
1585 switch (i) {
1586 case 'g':
1587 flagsBitsTemp = RegExpParser::FLAG_GLOBAL;
1588 break;
1589 case 'i':
1590 flagsBitsTemp = RegExpParser::FLAG_IGNORECASE;
1591 break;
1592 case 'm':
1593 flagsBitsTemp = RegExpParser::FLAG_MULTILINE;
1594 break;
1595 case 's':
1596 flagsBitsTemp = RegExpParser::FLAG_DOTALL;
1597 break;
1598 case 'u':
1599 flagsBitsTemp = RegExpParser::FLAG_UTF16;
1600 break;
1601 case 'y':
1602 flagsBitsTemp = RegExpParser::FLAG_STICKY;
1603 break;
1604 default: {
1605 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1606 JSHandle<JSObject> syntaxError =
1607 factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags");
1608 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0);
1609 }
1610 }
1611 if ((flagsBits & flagsBitsTemp) != 0) {
1612 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1613 JSHandle<JSObject> syntaxError =
1614 factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags");
1615 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0);
1616 }
1617 flagsBits |= flagsBitsTemp;
1618 }
1619 return flagsBits;
1620 }
1621
GetDollarString(JSThread * thread,RegExpGlobalArrayIndex index)1622 JSHandle<JSTaggedValue> BuiltinsRegExp::GetDollarString(JSThread *thread, RegExpGlobalArrayIndex index)
1623 {
1624 switch (index) {
1625 case DOLLAR_ONE:
1626 return thread->GlobalConstants()->GetHandledDollarStringOne();
1627 case DOLLAR_TWO:
1628 return thread->GlobalConstants()->GetHandledDollarStringTwo();
1629 case DOLLAR_THREE:
1630 return thread->GlobalConstants()->GetHandledDollarStringThree();
1631 case DOLLAR_FOUR:
1632 return thread->GlobalConstants()->GetHandledDollarStringFour();
1633 case DOLLAR_FIVE:
1634 return thread->GlobalConstants()->GetHandledDollarStringFive();
1635 case DOLLAR_SIX:
1636 return thread->GlobalConstants()->GetHandledDollarStringSix();
1637 case DOLLAR_SEVEN:
1638 return thread->GlobalConstants()->GetHandledDollarStringSeven();
1639 case DOLLAR_EIGHT:
1640 return thread->GlobalConstants()->GetHandledDollarStringEight();
1641 case DOLLAR_NINE:
1642 return thread->GlobalConstants()->GetHandledDollarStringNine();
1643 default:
1644 return thread->GlobalConstants()->GetHandledEmptyString();
1645 }
1646 }
1647
FlagsBitsToString(JSThread * thread,uint8_t flags)1648 JSTaggedValue BuiltinsRegExp::FlagsBitsToString(JSThread *thread, uint8_t flags)
1649 {
1650 ASSERT((flags & 0xC0) == 0); // 0xC0: first 2 bits of flags must be 0
1651
1652 uint8_t *flagsStr = new uint8_t[7]; // 7: maximum 6 flags + '\0'
1653 size_t flagsLen = 0;
1654 if (flags & RegExpParser::FLAG_GLOBAL) {
1655 flagsStr[flagsLen] = 'g';
1656 flagsLen++;
1657 }
1658 if (flags & RegExpParser::FLAG_IGNORECASE) {
1659 flagsStr[flagsLen] = 'i';
1660 flagsLen++;
1661 }
1662 if (flags & RegExpParser::FLAG_MULTILINE) {
1663 flagsStr[flagsLen] = 'm';
1664 flagsLen++;
1665 }
1666 if (flags & RegExpParser::FLAG_DOTALL) {
1667 flagsStr[flagsLen] = 's';
1668 flagsLen++;
1669 }
1670 if (flags & RegExpParser::FLAG_UTF16) {
1671 flagsStr[flagsLen] = 'u';
1672 flagsLen++;
1673 }
1674 if (flags & RegExpParser::FLAG_STICKY) {
1675 flagsStr[flagsLen] = 'y';
1676 flagsLen++;
1677 }
1678 flagsStr[flagsLen] = '\0';
1679 JSHandle<EcmaString> flagsString = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(flagsStr, flagsLen);
1680 delete[] flagsStr;
1681
1682 return flagsString.GetTaggedValue();
1683 }
1684
1685 // 21.2.3.2.2
RegExpInitialize(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags)1686 JSTaggedValue BuiltinsRegExp::RegExpInitialize(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
1687 const JSHandle<JSTaggedValue> &pattern,
1688 const JSHandle<JSTaggedValue> &flags)
1689 {
1690 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1691 JSHandle<EcmaString> patternStrHandle;
1692 uint8_t flagsBits = 0;
1693 // 1. If pattern is undefined, let P be the empty String.
1694 if (pattern->IsUndefined()) {
1695 patternStrHandle = factory->GetEmptyString();
1696 } else {
1697 // 2. Else, let P be ToString(pattern).
1698 patternStrHandle = JSTaggedValue::ToString(thread, pattern);
1699 // 3. ReturnIfAbrupt(P).
1700 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1701 }
1702 // 4. If flags is undefined, let F be the empty String.
1703 if (flags->IsUndefined()) {
1704 flagsBits = 0;
1705 } else if (flags->IsInt()) {
1706 flagsBits = static_cast<uint8_t>(flags->GetInt());
1707 } else {
1708 // 5. Else, let F be ToString(flags).
1709 JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, flags);
1710 // 6. ReturnIfAbrupt(F).
1711 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1712 /**
1713 * 7. If F contains any code unit other than "g", "i", "m", "u", or "y" or if it contains the same code
1714 * unit more than once, throw a SyntaxError exception.
1715 **/
1716 CString checkStr = ConvertToString(*flagsStrHandle, StringConvertedUsage::LOGICOPERATION);
1717 flagsBits = static_cast<uint8_t>(UpdateExpressionFlags(thread, checkStr));
1718 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1719 }
1720 // String -> CString
1721 CString patternStdStr = ConvertToString(*patternStrHandle, StringConvertedUsage::LOGICOPERATION);
1722 // 9. 10.
1723 Chunk chunk(thread->GetNativeAreaAllocator());
1724 RegExpParser parser = RegExpParser(&chunk);
1725 RegExpParserCache *regExpParserCache = thread->GetEcmaVM()->GetRegExpParserCache();
1726 CVector<CString> groupName;
1727 auto getCache = regExpParserCache->GetCache(*patternStrHandle, flagsBits, groupName);
1728 if (getCache.first == JSTaggedValue::Hole()) {
1729 parser.Init(const_cast<char *>(reinterpret_cast<const char *>(patternStdStr.c_str())), patternStdStr.size(),
1730 flagsBits);
1731 parser.Parse();
1732 if (parser.IsError()) {
1733 JSHandle<JSObject> syntaxError =
1734 factory->GetJSError(base::ErrorType::SYNTAX_ERROR, parser.GetErrorMsg().c_str());
1735 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception());
1736 }
1737 groupName = parser.GetGroupNames();
1738 }
1739 JSHandle<JSRegExp> regexp(thread, JSRegExp::Cast(obj->GetTaggedObject()));
1740 // 11. Set the value of obj’s [[OriginalSource]] internal slot to P.
1741 regexp->SetOriginalSource(thread, patternStrHandle.GetTaggedValue());
1742 // 12. Set the value of obj’s [[OriginalFlags]] internal slot to F.
1743 regexp->SetOriginalFlags(thread, JSTaggedValue(flagsBits));
1744 if (!groupName.empty()) {
1745 JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(groupName.size());
1746 for (size_t i = 0; i < groupName.size(); ++i) {
1747 JSHandle<JSTaggedValue> flagsKey(factory->NewFromStdString(groupName[i].c_str()));
1748 taggedArray->Set(thread, i, flagsKey);
1749 }
1750 regexp->SetGroupName(thread, taggedArray);
1751 }
1752 // 13. Set obj’s [[RegExpMatcher]] internal slot.
1753 if (getCache.first == JSTaggedValue::Hole()) {
1754 auto bufferSize = parser.GetOriginBufferSize();
1755 auto buffer = parser.GetOriginBuffer();
1756 factory->NewJSRegExpByteCodeData(regexp, buffer, bufferSize);
1757 regExpParserCache->SetCache(*patternStrHandle, flagsBits, regexp->GetByteCodeBuffer(), bufferSize, groupName);
1758 } else {
1759 regexp->SetByteCodeBuffer(thread, getCache.first);
1760 regexp->SetLength(static_cast<uint32_t>(getCache.second));
1761 }
1762 // 14. Let setStatus be Set(obj, "lastIndex", 0, true).
1763 JSHandle<JSTaggedValue> lastIndexString = thread->GlobalConstants()->GetHandledLastIndexString();
1764 ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(),
1765 lastIndexString.GetTaggedValue(), JSTaggedValue(0));
1766 // 15. ReturnIfAbrupt(setStatus).
1767 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1768 // 16. Return obj.
1769 return obj.GetTaggedValue();
1770 }
1771
RegExpCreate(JSThread * thread,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags)1772 JSTaggedValue BuiltinsRegExp::RegExpCreate(JSThread *thread, const JSHandle<JSTaggedValue> &pattern,
1773 const JSHandle<JSTaggedValue> &flags)
1774 {
1775 BUILTINS_API_TRACE(thread, RegExp, Create);
1776 auto ecmaVm = thread->GetEcmaVM();
1777 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1778 JSHandle<JSTaggedValue> newTarget = env->GetRegExpFunction();
1779 // 1. Let obj be RegExpAlloc(%RegExp%).
1780 JSHandle<JSTaggedValue> object(thread, RegExpAlloc(thread, newTarget));
1781 // 2. ReturnIfAbrupt(obj).
1782 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1783 // 3. Return RegExpInitialize(obj, P, F).
1784 return RegExpInitialize(thread, object, pattern, flags);
1785 }
1786
1787 // 21.2.3.2.4
EscapeRegExpPattern(JSThread * thread,const JSHandle<JSTaggedValue> & src,const JSHandle<JSTaggedValue> & flags)1788 EcmaString *BuiltinsRegExp::EscapeRegExpPattern(JSThread *thread, const JSHandle<JSTaggedValue> &src,
1789 const JSHandle<JSTaggedValue> &flags)
1790 {
1791 // String -> CString
1792 JSHandle<EcmaString> srcStr(thread, static_cast<EcmaString *>(src->GetTaggedObject()));
1793 JSHandle<EcmaString> flagsStr(thread, static_cast<EcmaString *>(flags->GetTaggedObject()));
1794 CString srcStdStr = ConvertToString(*srcStr, StringConvertedUsage::LOGICOPERATION);
1795 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1796 // "" -> (?:)
1797 if (srcStdStr.empty()) {
1798 srcStdStr = "(?:)";
1799 }
1800 // "/" -> "\/"
1801 srcStdStr = base::StringHelper::RepalceAll(srcStdStr, "/", "\\/");
1802 // "\\" -> "\"
1803 srcStdStr = base::StringHelper::RepalceAll(srcStdStr, "\\", "\\");
1804
1805 return *factory->NewFromUtf8(srcStdStr);
1806 }
1807
CreateCacheTable(JSThread * thread)1808 JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread)
1809 {
1810 int length = CACHE_TABLE_HEADER_SIZE + INITIAL_CACHE_NUMBER * ENTRY_SIZE;
1811
1812 auto table = static_cast<RegExpExecResultCache *>(
1813 *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
1814 table->SetLargeStrCount(thread, DEFAULT_LARGE_STRING_COUNT);
1815 table->SetConflictCount(thread, DEFAULT_CONFLICT_COUNT);
1816 table->SetStrLenThreshold(thread, 0);
1817 table->SetHitCount(thread, 0);
1818 table->SetCacheCount(thread, 0);
1819 table->SetCacheLength(thread, INITIAL_CACHE_NUMBER);
1820 return JSTaggedValue(table);
1821 }
1822
FindCachedResult(JSThread * thread,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags,const JSHandle<JSTaggedValue> & input,CacheType type,const JSHandle<JSTaggedValue> & regexp,JSTaggedValue extend)1823 JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JSHandle<JSTaggedValue> &pattern,
1824 const JSHandle<JSTaggedValue> &flags,
1825 const JSHandle<JSTaggedValue> &input, CacheType type,
1826 const JSHandle<JSTaggedValue> ®exp, JSTaggedValue extend)
1827 {
1828 JSTaggedValue patternValue = pattern.GetTaggedValue();
1829 JSTaggedValue flagsValue = flags.GetTaggedValue();
1830 JSTaggedValue inputValue = input.GetTaggedValue();
1831
1832 if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) {
1833 return JSTaggedValue::Undefined();
1834 }
1835
1836 uint32_t hash = pattern->GetKeyHashCode() + static_cast<uint32_t>(flags->GetInt()) + input->GetKeyHashCode();
1837 uint32_t entry = hash & static_cast<uint32_t>(GetCacheLength() - 1);
1838 if (!Match(entry, patternValue, flagsValue, inputValue, extend)) {
1839 uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(GetCacheLength() - 1);
1840 if (!Match(entry2, patternValue, flagsValue, inputValue, extend)) {
1841 return JSTaggedValue::Undefined();
1842 }
1843 entry = entry2;
1844 }
1845 ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1846 static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
1847 uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1848 JSTaggedValue result;
1849 switch (type) {
1850 case REPLACE_TYPE:
1851 result = Get(index + RESULT_REPLACE_INDEX);
1852 break;
1853 case SPLIT_TYPE:
1854 result = Get(index + RESULT_SPLIT_INDEX);
1855 break;
1856 case MATCH_TYPE:
1857 result = Get(index + RESULT_MATCH_INDEX);
1858 break;
1859 case EXEC_TYPE:
1860 result = Get(index + RESULT_EXEC_INDEX);
1861 break;
1862 default:
1863 UNREACHABLE();
1864 break;
1865 }
1866 SetHitCount(thread, GetHitCount() + 1);
1867 JSHandle<JSTaggedValue> lastIndexHandle = thread->GlobalConstants()->GetHandledLastIndexString();
1868 ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(),
1869 Get(index + LAST_INDEX_INDEX));
1870 if (result.IsJSArray()) {
1871 JSHandle<JSArray> resultHandle(thread, JSArray::Cast(result));
1872 JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()->CloneArrayLiteral(resultHandle);
1873 return copyArray.GetTaggedValue();
1874 }
1875 return result;
1876 }
1877
AddResultInCache(JSThread * thread,JSHandle<RegExpExecResultCache> cache,const JSHandle<JSTaggedValue> & pattern,const JSHandle<JSTaggedValue> & flags,const JSHandle<JSTaggedValue> & input,const JSHandle<JSTaggedValue> & resultArray,CacheType type,uint32_t lastIndex,JSTaggedValue extend)1878 void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache,
1879 const JSHandle<JSTaggedValue> &pattern,
1880 const JSHandle<JSTaggedValue> &flags, const JSHandle<JSTaggedValue> &input,
1881 const JSHandle<JSTaggedValue> &resultArray, CacheType type,
1882 uint32_t lastIndex, JSTaggedValue extend)
1883 {
1884 if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) {
1885 return;
1886 }
1887
1888 JSHandle<JSTaggedValue> resultArrayCopy;
1889 if (resultArray->IsJSArray()) {
1890 JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()
1891 ->CloneArrayLiteral(JSHandle<JSArray>(resultArray));
1892 resultArrayCopy = JSHandle<JSTaggedValue>(copyArray);
1893 } else {
1894 resultArrayCopy = JSHandle<JSTaggedValue>(resultArray);
1895 }
1896
1897 JSTaggedValue patternValue = pattern.GetTaggedValue();
1898 JSTaggedValue flagsValue = flags.GetTaggedValue();
1899 JSTaggedValue inputValue = input.GetTaggedValue();
1900 JSTaggedValue lastIndexValue(lastIndex);
1901
1902 uint32_t hash = patternValue.GetKeyHashCode() + static_cast<uint32_t>(flagsValue.GetInt()) +
1903 inputValue.GetKeyHashCode();
1904 uint32_t entry = hash & static_cast<uint32_t>(cache->GetCacheLength() - 1);
1905 ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1906 static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
1907 uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1908 if (cache->Get(index) == JSTaggedValue::Undefined()) {
1909 cache->SetCacheCount(thread, cache->GetCacheCount() + 1);
1910 cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue, extend);
1911 cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
1912 } else if (cache->Match(entry, patternValue, flagsValue, inputValue, extend)) {
1913 cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
1914 } else {
1915 uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(cache->GetCacheLength() - 1);
1916 ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1917 static_cast<size_t>(entry2) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
1918 uint32_t index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE;
1919 JSHandle<JSTaggedValue> extendHandle(thread, extend);
1920 if (cache->GetCacheLength() < DEFAULT_CACHE_NUMBER) {
1921 GrowRegexpCache(thread, cache);
1922 // update value after gc.
1923 patternValue = pattern.GetTaggedValue();
1924 flagsValue = flags.GetTaggedValue();
1925 inputValue = input.GetTaggedValue();
1926
1927 cache->SetCacheLength(thread, DEFAULT_CACHE_NUMBER);
1928 entry2 = hash & static_cast<uint32_t>(cache->GetCacheLength() - 1);
1929 index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE;
1930 }
1931 JSTaggedValue extendValue = extendHandle.GetTaggedValue();
1932 if (cache->Get(index2) == JSTaggedValue::Undefined()) {
1933 cache->SetCacheCount(thread, cache->GetCacheCount() + 1);
1934 cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue, lastIndexValue, extendValue);
1935 cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
1936 } else if (cache->Match(entry2, patternValue, flagsValue, inputValue, extendValue)) {
1937 cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
1938 } else {
1939 cache->SetConflictCount(thread, cache->GetConflictCount() > 1 ? (cache->GetConflictCount() - 1) : 0);
1940 cache->SetCacheCount(thread, cache->GetCacheCount() - 1);
1941 cache->ClearEntry(thread, entry2);
1942 cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue, extendValue);
1943 cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
1944 }
1945 }
1946 }
1947
GrowRegexpCache(JSThread * thread,JSHandle<RegExpExecResultCache> cache)1948 void RegExpExecResultCache::GrowRegexpCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache)
1949 {
1950 int length = CACHE_TABLE_HEADER_SIZE + DEFAULT_CACHE_NUMBER * ENTRY_SIZE;
1951 auto factory = thread->GetEcmaVM()->GetFactory();
1952 auto newCache = factory->ExtendArray(JSHandle<TaggedArray>(cache), length, JSTaggedValue::Undefined());
1953 thread->GetEcmaVM()->SetRegExpCache(newCache.GetTaggedValue());
1954 }
1955
SetEntry(JSThread * thread,int entry,JSTaggedValue & pattern,JSTaggedValue & flags,JSTaggedValue & input,JSTaggedValue & lastIndexValue,JSTaggedValue & extendValue)1956 void RegExpExecResultCache::SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags,
1957 JSTaggedValue &input, JSTaggedValue &lastIndexValue, JSTaggedValue &extendValue)
1958 {
1959 ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1960 static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
1961 int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1962 Set(thread, index + PATTERN_INDEX, pattern);
1963 Set(thread, index + FLAG_INDEX, flags);
1964 Set(thread, index + INPUT_STRING_INDEX, input);
1965 Set(thread, index + LAST_INDEX_INDEX, lastIndexValue);
1966 Set(thread, index + EXTEND_INDEX, extendValue);
1967 }
1968
UpdateResultArray(JSThread * thread,int entry,JSTaggedValue resultArray,CacheType type)1969 void RegExpExecResultCache::UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type)
1970 {
1971 ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1972 static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
1973 int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1974 switch (type) {
1975 break;
1976 case REPLACE_TYPE:
1977 Set(thread, index + RESULT_REPLACE_INDEX, resultArray);
1978 break;
1979 case SPLIT_TYPE:
1980 Set(thread, index + RESULT_SPLIT_INDEX, resultArray);
1981 break;
1982 case MATCH_TYPE:
1983 Set(thread, index + RESULT_MATCH_INDEX, resultArray);
1984 break;
1985 case EXEC_TYPE:
1986 Set(thread, index + RESULT_EXEC_INDEX, resultArray);
1987 break;
1988 default:
1989 UNREACHABLE();
1990 break;
1991 }
1992 }
1993
ClearEntry(JSThread * thread,int entry)1994 void RegExpExecResultCache::ClearEntry(JSThread *thread, int entry)
1995 {
1996 ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
1997 static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
1998 int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
1999 JSTaggedValue undefined = JSTaggedValue::Undefined();
2000 for (int i = 0; i < ENTRY_SIZE; i++) {
2001 Set(thread, index + i, undefined);
2002 }
2003 }
2004
Match(int entry,JSTaggedValue & pattern,JSTaggedValue & flags,JSTaggedValue & input,JSTaggedValue & extend)2005 bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input,
2006 JSTaggedValue &extend)
2007 {
2008 ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2009 static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2010 int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2011 JSTaggedValue keyPattern = Get(index + PATTERN_INDEX);
2012 JSTaggedValue keyFlags = Get(index + FLAG_INDEX);
2013 JSTaggedValue keyInput = Get(index + INPUT_STRING_INDEX);
2014 JSTaggedValue keyExtend = Get(index + EXTEND_INDEX);
2015
2016 if (keyPattern == JSTaggedValue::Undefined()) {
2017 return false;
2018 }
2019
2020 EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject());
2021 uint8_t flagsBits = static_cast<uint8_t>(flags.GetInt());
2022 EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject());
2023 EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject());
2024 uint8_t keyFlagsBits = static_cast<uint8_t>(keyFlags.GetInt());
2025 EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject());
2026 bool extendEqual = false;
2027 if (extend.IsString() && keyExtend.IsString()) {
2028 EcmaString *extendStr = EcmaString::Cast(extend.GetTaggedObject());
2029 EcmaString *keyExtendStr = EcmaString::Cast(keyExtend.GetTaggedObject());
2030 extendEqual = EcmaStringAccessor::StringsAreEqual(extendStr, keyExtendStr);
2031 } else if (extend.IsUndefined() && keyExtend.IsUndefined()) {
2032 extendEqual = true;
2033 } else {
2034 return false;
2035 }
2036 return EcmaStringAccessor::StringsAreEqual(patternStr, keyPatternStr) && flagsBits == keyFlagsBits &&
2037 EcmaStringAccessor::StringsAreEqual(inputStr, keyInputStr) && extendEqual;
2038 }
2039 } // namespace panda::ecmascript::builtins
2040