* 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 {
template <int N>
JSTaggedValue RegExpGlobalResult::GetCapture(JSThread *thread)
{
JSHandle<JSTaggedValue> table = JSHandle<JSTaggedValue>(thread, RegExpExecResultCache::GetGlobalTable(thread));
JSHandle<RegExpGlobalResult> globalTable = JSHandle<RegExpGlobalResult>::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<EcmaString>(thread, EcmaString::Cast(globalTable->GetInputString(thread))),
static_cast<uint32_t>(startIndex), static_cast<uint32_t>(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 <RBMode mode>
JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread,
const JSHandle<JSTaggedValue> input, CacheType type,
const JSHandle<JSTaggedValue> regexp,
JSTaggedValue lastIndexInput, JSHandle<JSTaggedValue> extend,
bool isIntermediateResult)
{
JSHandle<JSRegExp> regexpObj(regexp);
JSTaggedValue pattern = regexpObj->GetOriginalSource<mode>(thread);
JSTaggedValue flags = regexpObj->GetOriginalFlags<mode>(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<uint32_t>(flags.GetInt()) +
input->GetKeyHashCode(thread) + static_cast<uint32_t>(lastIndexInput.GetInt());
uint32_t entry = hash & static_cast<uint32_t>(GetCacheLength() - 1);
if (!Match<mode>(thread, entry, pattern, flags, inputValue, lastIndexInput, extendValue, type)) {
uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(GetCacheLength() - 1);
if (!Match<mode>(thread, entry2, pattern, flags, inputValue, lastIndexInput, extendValue, type)) {
return JSTaggedValue::Undefined();
}
entry = entry2;
}
ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
JSTaggedValue cachedStr = Get<mode>(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<mode>(thread, index + RESULT_REPLACE_INDEX);
break;
case SPLIT_TYPE:
result = Get<mode>(thread, index + RESULT_SPLIT_INDEX);
break;
case MATCH_TYPE:
result = Get<mode>(thread, index + RESULT_MATCH_INDEX);
break;
case EXEC_TYPE:
result = Get<mode>(thread, index + RESULT_EXEC_INDEX);
break;
case INTERMEDIATE_REPLACE_TYPE:
result = Get<mode>(thread, index + RESULT_INTERMEDIATE_REPLACE_INDEX);
break;
case TEST_TYPE:
result = Get<mode>(thread, index + RESULT_TEST_INDEX);
break;
case SEARCH_TYPE:
result = Get<mode>(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<RBMode::FAST_NO_RB>(thread, index + LAST_INDEX_INDEX), true);
}
if (!isIntermediateResult && result.IsJSArray()) {
JSHandle<JSArray> resultHandle(thread, JSArray::Cast(result));
JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()->CloneArrayLiteral(resultHandle);
return copyArray.GetTaggedValue();
}
return result;
}
template <RBMode mode>
bool RegExpExecResultCache::Match(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags,
JSTaggedValue &input, JSTaggedValue &lastIndexInputValue, JSTaggedValue &extend,
CacheType type)
{
ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
JSTaggedValue typeKey = Get<mode>(thread, index + RESULT_REPLACE_INDEX + type);
if (typeKey.IsUndefined()) {
return false;
}
JSTaggedValue keyPattern = Get<mode>(thread, index + PATTERN_INDEX);
if (keyPattern.IsUndefined()) {
return false;
}
uint8_t flagsBits = static_cast<uint8_t>(flags.GetInt());
JSTaggedValue keyFlags = Get<RBMode::FAST_NO_RB>(thread, index + FLAG_INDEX);
uint8_t keyFlagsBits = static_cast<uint8_t>(keyFlags.GetInt());
if (flagsBits != keyFlagsBits) {
return false;
}
uint32_t lastIndexInputInt = static_cast<uint32_t>(lastIndexInputValue.GetInt());
JSTaggedValue keyLastIndexInput = Get<RBMode::FAST_NO_RB>(thread, index + LAST_INDEX_INPUT_INDEX);
uint32_t keyLastIndexInputInt = static_cast<uint32_t>(keyLastIndexInput.GetInt());
if (lastIndexInputInt != keyLastIndexInputInt) {
return false;
}
JSTaggedValue keyInput = Get<mode>(thread, index + INPUT_STRING_INDEX);
JSTaggedValue keyExtend = Get<mode>(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<mode>(thread, extendStr, keyExtendStr);
} else if (extend.IsUndefined() && keyExtend.IsUndefined()) {
extendEqual = true;
} else {
return false;
}
return extendEqual &&
EcmaStringAccessor::StringsAreEqual<mode>(thread, patternStr, keyPatternStr) &&
EcmaStringAccessor::StringsAreEqual<mode>(thread, inputStr, keyInputStr);
}
}
#endif