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