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