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