/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_INL_H #define ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_INL_H #include "ecmascript/global_env.h" #include "ecmascript/js_regexp.h" #include "ecmascript/js_array.h" namespace panda::ecmascript::builtins { /* static */ template JSTaggedValue RegExpGlobalResult::GetCapture(JSThread *thread) { JSHandle table = JSHandle(thread, RegExpExecResultCache::GetGlobalTable(thread)); JSHandle globalTable = JSHandle::Cast(table); JSTaggedValue res = globalTable->Get(thread, CAPTURE_START_INDEX + N - 1); int captureNum = globalTable->GetTotalCaptureCounts().GetInt(); if (res.IsHole() && (N < captureNum)) { int startIndex = globalTable->GetStartOfCaptureIndex(N).GetInt(); int endIndex = globalTable->GetEndOfCaptureIndex(N).GetInt(); int len = endIndex - startIndex; if (len < 0) { res = JSTaggedValue::Undefined(); } else { res = JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), JSHandle(thread, EcmaString::Cast(globalTable->GetInputString(thread))), static_cast(startIndex), static_cast(len))); } globalTable->Set(thread, CAPTURE_START_INDEX + N - 1, res); } else if (res.IsHole()) { res = thread->GetEcmaVM()->GetFactory()->GetEmptyString().GetTaggedValue(); globalTable->Set(thread, CAPTURE_START_INDEX + N - 1, res); } return res; } template JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JSHandle input, CacheType type, const JSHandle regexp, JSTaggedValue lastIndexInput, JSHandle extend, bool isIntermediateResult) { JSHandle regexpObj(regexp); JSTaggedValue pattern = regexpObj->GetOriginalSource(thread); JSTaggedValue flags = regexpObj->GetOriginalFlags(thread); JSTaggedValue inputValue = input.GetTaggedValue(); JSTaggedValue extendValue = extend.GetTaggedValue(); if (!pattern.IsString() || !flags.IsInt() || !input->IsString() || !lastIndexInput.IsInt()) { return JSTaggedValue::Undefined(); } uint32_t hash = pattern.GetKeyHashCode(thread) + static_cast(flags.GetInt()) + input->GetKeyHashCode(thread) + static_cast(lastIndexInput.GetInt()); uint32_t entry = hash & static_cast(GetCacheLength() - 1); if (!Match(thread, entry, pattern, flags, inputValue, lastIndexInput, extendValue, type)) { uint32_t entry2 = (entry + 1) & static_cast(GetCacheLength() - 1); if (!Match(thread, entry2, pattern, flags, inputValue, lastIndexInput, extendValue, type)) { return JSTaggedValue::Undefined(); } entry = entry2; } ASSERT((static_cast(CACHE_TABLE_HEADER_SIZE) + static_cast(entry) * static_cast(ENTRY_SIZE)) <= static_cast(UINT32_MAX)); uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; // update cached value if input value is changed JSTaggedValue cachedStr = Get(thread, index + INPUT_STRING_INDEX); if (!cachedStr.IsUndefined() && cachedStr != inputValue) { Set(thread, index + INPUT_STRING_INDEX, inputValue); } JSTaggedValue result; switch (type) { case REPLACE_TYPE: result = Get(thread, index + RESULT_REPLACE_INDEX); break; case SPLIT_TYPE: result = Get(thread, index + RESULT_SPLIT_INDEX); break; case MATCH_TYPE: result = Get(thread, index + RESULT_MATCH_INDEX); break; case EXEC_TYPE: result = Get(thread, index + RESULT_EXEC_INDEX); break; case INTERMEDIATE_REPLACE_TYPE: result = Get(thread, index + RESULT_INTERMEDIATE_REPLACE_INDEX); break; case TEST_TYPE: result = Get(thread, index + RESULT_TEST_INDEX); break; case SEARCH_TYPE: result = Get(thread, index + RESULT_SEARCH_INDEX); break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); break; } SetLastMatchGlobalTableIndex(thread, index); SetUseLastMatch(thread, true); SetNeedUpdateGlobal(thread, true); SetHitCount(thread, GetHitCount() + 1); if (type != SEARCH_TYPE && type != SPLIT_TYPE) { BuiltinsRegExp::SetLastIndex(thread, regexp, Get(thread, index + LAST_INDEX_INDEX), true); } if (!isIntermediateResult && result.IsJSArray()) { JSHandle resultHandle(thread, JSArray::Cast(result)); JSHandle copyArray = thread->GetEcmaVM()->GetFactory()->CloneArrayLiteral(resultHandle); return copyArray.GetTaggedValue(); } return result; } template bool RegExpExecResultCache::Match(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input, JSTaggedValue &lastIndexInputValue, JSTaggedValue &extend, CacheType type) { ASSERT((static_cast(CACHE_TABLE_HEADER_SIZE) + static_cast(entry) * static_cast(ENTRY_SIZE)) <= static_cast(INT_MAX)); int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; JSTaggedValue typeKey = Get(thread, index + RESULT_REPLACE_INDEX + type); if (typeKey.IsUndefined()) { return false; } JSTaggedValue keyPattern = Get(thread, index + PATTERN_INDEX); if (keyPattern.IsUndefined()) { return false; } uint8_t flagsBits = static_cast(flags.GetInt()); JSTaggedValue keyFlags = Get(thread, index + FLAG_INDEX); uint8_t keyFlagsBits = static_cast(keyFlags.GetInt()); if (flagsBits != keyFlagsBits) { return false; } uint32_t lastIndexInputInt = static_cast(lastIndexInputValue.GetInt()); JSTaggedValue keyLastIndexInput = Get(thread, index + LAST_INDEX_INPUT_INDEX); uint32_t keyLastIndexInputInt = static_cast(keyLastIndexInput.GetInt()); if (lastIndexInputInt != keyLastIndexInputInt) { return false; } JSTaggedValue keyInput = Get(thread, index + INPUT_STRING_INDEX); JSTaggedValue keyExtend = Get(thread, index + EXTEND_INDEX); EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject()); EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject()); EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject()); EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject()); bool extendEqual = false; if (extend.IsString() && keyExtend.IsString()) { EcmaString *extendStr = EcmaString::Cast(extend.GetTaggedObject()); EcmaString *keyExtendStr = EcmaString::Cast(keyExtend.GetTaggedObject()); extendEqual = EcmaStringAccessor::StringsAreEqual(thread, extendStr, keyExtendStr); } else if (extend.IsUndefined() && keyExtend.IsUndefined()) { extendEqual = true; } else { return false; } return extendEqual && EcmaStringAccessor::StringsAreEqual(thread, patternStr, keyPatternStr) && EcmaStringAccessor::StringsAreEqual(thread, inputStr, keyInputStr); } } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_INL_H