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