diff --git a/BUILD.gn b/BUILD.gn index fbbfe4760bc54ece6c9e6d01728eaaf190f776bf..cb1c324403b421ff899d8b11fbb9596e36e9c1fa 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -313,6 +313,7 @@ ecma_source = [ "ecmascript/js_primitive_ref.cpp", "ecmascript/js_promise.cpp", "ecmascript/js_proxy.cpp", + "ecmascript/js_regexp.cpp", "ecmascript/js_serializer.cpp", "ecmascript/js_set.cpp", "ecmascript/js_set_iterator.cpp", diff --git a/ecmascript/builtins/builtins_regexp.cpp b/ecmascript/builtins/builtins_regexp.cpp index fd7c25e6e91085f8d4fd6daacde90d0e4ea55072..27582fe6e435e98c61779b4ca49ff641a7af37b7 100644 --- a/ecmascript/builtins/builtins_regexp.cpp +++ b/ecmascript/builtins/builtins_regexp.cpp @@ -400,8 +400,8 @@ JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) JSHandle flags(thread, regexpObj->GetOriginalFlags()); if (isCached) { JSTaggedValue cacheResult = - cacheTable->FindCachedResult(thread, pattern, flags, inputString, - RegExpExecResultCache::MATCH_TYPE, thisObj); + RegExpExecResultCache::FindCachedResult(thread, cacheTable, pattern, flags, inputString, + RegExpExecResultCache::MATCH_TYPE, thisObj); if (cacheResult != JSTaggedValue::Undefined()) { return cacheResult; } @@ -441,8 +441,8 @@ JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) return JSTaggedValue::Null(); } if (isCached) { - cacheTable->AddResultInCache(thread, pattern, flags, inputString, array.GetTaggedValue(), - RegExpExecResultCache::MATCH_TYPE, 0); + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputString, + array.GetTaggedValue(), RegExpExecResultCache::MATCH_TYPE, 0); } // 2. Else, return A. return array.GetTaggedValue(); @@ -527,8 +527,8 @@ JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandleFindCachedResult(thread, pattern, flagsBits, tagInputString, - RegExpExecResultCache::REPLACE_TYPE, regexp); + RegExpExecResultCache::FindCachedResult(thread, cacheTable, pattern, flagsBits, tagInputString, + RegExpExecResultCache::REPLACE_TYPE, regexp); if (cacheResult != JSTaggedValue::Undefined()) { return cacheResult; } @@ -593,7 +593,7 @@ JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandleNewFromStdString(resultString).GetTaggedValue(); if (isCached) { - cacheTable->AddResultInCache(thread, pattern, flagsBits, tagInputString, resultValue, + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, tagInputString, resultValue, RegExpExecResultCache::REPLACE_TYPE, lastIndex); } return resultValue; @@ -973,8 +973,8 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); if (isCached) { JSTaggedValue cacheResult = - cacheTable->FindCachedResult(thread, pattern, flagsBits, inputString, - RegExpExecResultCache::SPLIT_TYPE, thisObj); + RegExpExecResultCache::FindCachedResult(thread, cacheTable, pattern, flagsBits, inputString, + RegExpExecResultCache::SPLIT_TYPE, thisObj); if (cacheResult != JSTaggedValue::Undefined()) { return cacheResult; } @@ -1064,8 +1064,9 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) // 5. If lengthA = lim, return A. if (aLength == lim) { if (isCached) { - cacheTable->AddResultInCache(thread, pattern, flagsBits, inputString, array.GetTaggedValue(), - RegExpExecResultCache::SPLIT_TYPE, lastIndex); + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, + array.GetTaggedValue(), RegExpExecResultCache::SPLIT_TYPE, + lastIndex); } return array.GetTaggedValue(); } @@ -1098,9 +1099,9 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) // f. If lengthA = lim, return A. if (aLength == lim) { if (isCached) { - cacheTable->AddResultInCache(thread, pattern, flagsBits, inputString, - array.GetTaggedValue(), - RegExpExecResultCache::SPLIT_TYPE, lastIndex); + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, + array.GetTaggedValue(), + RegExpExecResultCache::SPLIT_TYPE, lastIndex); } return array.GetTaggedValue(); } @@ -1119,8 +1120,8 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) JSHandle tValue(factory->NewFromStdString(stdStrT)); JSObject::CreateDataProperty(thread, array, aLength, tValue); if (lim == MAX_SPLIT_LIMIT) { - cacheTable->AddResultInCache(thread, pattern, flagsBits, inputString, array.GetTaggedValue(), - RegExpExecResultCache::SPLIT_TYPE, endIndex); + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, array.GetTaggedValue(), + RegExpExecResultCache::SPLIT_TYPE, endIndex); } // 28. Return A. return array.GetTaggedValue(); @@ -1241,8 +1242,8 @@ JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); if (lastIndex == 0 && isCached) { JSTaggedValue cacheResult = - cacheTable->FindCachedResult(thread, pattern, flags, inputStr, - RegExpExecResultCache::EXEC_TYPE, regexp); + RegExpExecResultCache::FindCachedResult(thread, cacheTable, pattern, flags, inputStr, + RegExpExecResultCache::EXEC_TYPE, regexp); if (cacheResult != JSTaggedValue::Undefined()) { return cacheResult; } @@ -1319,8 +1320,8 @@ JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle JSObject::CreateDataProperty(thread, results, i, iValue); } if (lastIndex == 0 && isCached) { - cacheTable->AddResultInCache(thread, pattern, flags, inputStr, results.GetTaggedValue(), - RegExpExecResultCache::EXEC_TYPE, endIndex); + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputStr, results.GetTaggedValue(), + RegExpExecResultCache::EXEC_TYPE, endIndex); } // 29. Return A. return results.GetTaggedValue(); @@ -1583,166 +1584,110 @@ EcmaString *BuiltinsRegExp::EscapeRegExpPattern(JSThread *thread, const JSHandle JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread) { - int length = CACHE_TABLE_HEADER_SIZE + DEFAULT_CACHE_NUMBER * ENTRY_SIZE; - - auto table = static_cast(*thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length)); - table->SetLargeStrCount(thread, DEFAULT_LARGE_STRING_COUNT); - table->SetConflictCount(thread, DEFAULT_CONFLICT_COUNT); - table->SetStrLenThreshold(thread, 0); - table->SetHitCount(thread, 0); - table->SetCacheCount(thread, 0); - - return JSTaggedValue(table); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array = factory->NewTaggedArray(CACHE_TABLE_HEADER_SIZE, + JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE); + auto table = RegexpTable::Create(thread); + auto cache = static_cast(*array); + cache->SetLargeStrCount(thread, DEFAULT_LARGE_STRING_COUNT); + cache->SetConflictCount(thread, DEFAULT_CONFLICT_COUNT); + cache->SetStrLenThreshold(thread, 0); + cache->SetHitCount(thread, 0); + cache->SetCacheCount(thread, 0); + cache->SetRegexpTable(thread, JSTaggedValue(table)); + return JSTaggedValue(cache); } -JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JSHandle &pattern, +JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, JSHandle cache, + const JSHandle &pattern, const JSHandle &flags, const JSHandle &input, CacheType type, const JSHandle ®exp) { - JSTaggedValue patternValue = pattern.GetTaggedValue(); - JSTaggedValue flagsValue = flags.GetTaggedValue(); - JSTaggedValue inputValue = input.GetTaggedValue(); - if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) { return JSTaggedValue::Undefined(); } - uint32_t hash = pattern->GetKeyHashCode() + flags->GetInt() + input->GetKeyHashCode(); - uint32_t entry = hash & (RegExpExecResultCache::DEFAULT_CACHE_NUMBER - 1); - if (!Match(entry, patternValue, flagsValue, inputValue)) { - uint32_t entry2 = (entry + 1) & (DEFAULT_CACHE_NUMBER - 1); - if (!Match(entry2, patternValue, flagsValue, inputValue)) { - return JSTaggedValue::Undefined(); - } - entry = entry2; + JSHandle key = thread->GetEcmaVM()->GetFactory()->NewKeyBox(); + key->SetPattern(thread, pattern); + key->SetFlag(thread, flags); + key->SetInputString(thread, input); + + RegexpTable* table = RegexpTable::Cast(cache->GetRegexpTable().GetTaggedObject()); + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry == -1) { + return JSTaggedValue::Undefined(); } - uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + + KeyBox* resultKey = KeyBox::Cast(table->GetKey(entry).GetTaggedObject()); + ResultBox* resultBox = ResultBox::Cast(table->GetValue(entry).GetTaggedObject()); JSTaggedValue result; switch (type) { case REPLACE_TYPE: - result = Get(index + RESULT_REPLACE_INDEX); + result = resultBox->GetResultReplace(); break; case SPLIT_TYPE: - result = Get(index + RESULT_SPLIT_INDEX); + result = resultBox->GetResultSplit(); break; case MATCH_TYPE: - result = Get(index + RESULT_MATCH_INDEX); + result = resultBox->GetResultMatch(); break; case EXEC_TYPE: - result = Get(index + RESULT_EXEC_INDEX); + result = resultBox->GetResultExec(); break; default: UNREACHABLE(); break; } - SetHitCount(thread, GetHitCount() + 1); + cache->SetHitCount(thread, cache->GetHitCount() + 1); JSHandle lastIndexHandle = thread->GlobalConstants()->GetHandledLastIndexString(); FastRuntimeStub::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(), - Get(index + LAST_INDEX_INDEX)); + resultKey->GetLastIndex()); return result; } -void RegExpExecResultCache::AddResultInCache(JSThread *thread, const JSHandle &pattern, - const JSHandle &flags, const JSHandle &input, - JSTaggedValue resultArray, CacheType type, uint32_t lastIndex) +void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandle cache, + const JSHandle &pattern, const JSHandle &flags, + const JSHandle &input, JSTaggedValue resultArray, + CacheType type, uint32_t lastIndex) { if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) { return; } - JSTaggedValue patternValue = pattern.GetTaggedValue(); - JSTaggedValue flagsValue = flags.GetTaggedValue(); - JSTaggedValue inputValue = input.GetTaggedValue(); + JSHandle resultHandle(thread, resultArray); JSTaggedValue lastIndexValue(lastIndex); - - uint32_t hash = pattern->GetKeyHashCode() + flags->GetInt() + input->GetKeyHashCode(); - uint32_t entry = hash & (DEFAULT_CACHE_NUMBER - 1); - uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; - if (Get(index) == JSTaggedValue::Undefined()) { - SetCacheCount(thread, GetCacheCount() + 1); - SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue); - UpdateResultArray(thread, entry, resultArray, type); - } else if (Match(entry, patternValue, flagsValue, inputValue)) { - UpdateResultArray(thread, entry, resultArray, type); - } else { - uint32_t entry2 = (entry + 1) & (DEFAULT_CACHE_NUMBER - 1); - uint32_t index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE; - if (Get(index2) == JSTaggedValue::Undefined()) { - SetCacheCount(thread, GetCacheCount() + 1); - SetEntry(thread, entry2, patternValue, flagsValue, inputValue, lastIndexValue); - UpdateResultArray(thread, entry2, resultArray, type); - } else if (Match(entry2, patternValue, flagsValue, inputValue)) { - UpdateResultArray(thread, entry2, resultArray, type); - } else { - SetConflictCount(thread, GetConflictCount() - 1); - SetCacheCount(thread, GetCacheCount() - 1); - ClearEntry(thread, entry2); - SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue); - UpdateResultArray(thread, entry, resultArray, type); - } - } -} - -void RegExpExecResultCache::SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags, - JSTaggedValue &input, JSTaggedValue &lastIndexValue) -{ - int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; - Set(thread, index + PATTERN_INDEX, pattern); - Set(thread, index + FLAG_INDEX, flags); - Set(thread, index + INPUT_STRING_INDEX, input); - Set(thread, index + LAST_INDEX_INDEX, lastIndexValue); -} - -void RegExpExecResultCache::UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type) -{ - int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + + JSHandle key = thread->GetEcmaVM()->GetFactory()->NewKeyBox(); + JSHandle result = thread->GetEcmaVM()->GetFactory()->NewResultBox(); + key->SetPattern(thread, pattern); + key->SetFlag(thread, flags); + key->SetInputString(thread, input); + key->SetLastIndex(thread, lastIndexValue); + switch (type) { - break; case REPLACE_TYPE: - Set(thread, index + RESULT_REPLACE_INDEX, resultArray); + result->SetResultReplace(thread, resultHandle); break; case SPLIT_TYPE: - Set(thread, index + RESULT_SPLIT_INDEX, resultArray); + result->SetResultSplit(thread, resultHandle); break; case MATCH_TYPE: - Set(thread, index + RESULT_MATCH_INDEX, resultArray); + result->SetResultMatch(thread, resultHandle); break; case EXEC_TYPE: - Set(thread, index + RESULT_EXEC_INDEX, resultArray); + result->SetResultExec(thread, resultHandle); break; default: UNREACHABLE(); break; } -} - -void RegExpExecResultCache::ClearEntry(JSThread *thread, int entry) -{ - int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; - JSTaggedValue undefined = JSTaggedValue::Undefined(); - for (int i = 0; i < ENTRY_SIZE; i++) { - Set(thread, index + i, undefined); - } -} -bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input) -{ - int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; - JSTaggedValue keyPattern = Get(index + PATTERN_INDEX); - JSTaggedValue keyFlags = Get(index + FLAG_INDEX); - JSTaggedValue keyInput = Get(index + INPUT_STRING_INDEX); - - if (keyPattern == JSTaggedValue::Undefined()) { - return false; - } + JSHandle table(thread, RegexpTable::Cast(cache->GetRegexpTable().GetTaggedObject())); + JSHandle keyValue(thread, key.GetTaggedValue()); + JSHandle resultValue(thread, result.GetTaggedValue()); - EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject()); - uint8_t flagsBits = static_cast(flags.GetInt()); - EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject()); - EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject()); - uint8_t keyFlagsBits = static_cast(keyFlags.GetInt()); - EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject()); - return EcmaString::StringsAreEqual(patternStr, keyPatternStr) && flagsBits == keyFlagsBits && - EcmaString::StringsAreEqual(inputStr, keyInputStr); + RegexpTable* newTable = RegexpTable::Insert(thread, table, keyValue, resultValue); + cache->SetRegexpTable(thread, JSTaggedValue(newTable)); + cache->SetCacheCount(thread, table->Size()); } } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_regexp.h b/ecmascript/builtins/builtins_regexp.h index 16391de2e5cc8a55ce67621e5b56c5f0987c8c01..863e3578ab04637dbcf6b7130a421f3d5b53133a 100644 --- a/ecmascript/builtins/builtins_regexp.h +++ b/ecmascript/builtins/builtins_regexp.h @@ -112,18 +112,13 @@ public: return reinterpret_cast(object); } static JSTaggedValue CreateCacheTable(JSThread *thread); - JSTaggedValue FindCachedResult(JSThread *thread, const JSHandle &patten, + static JSTaggedValue FindCachedResult(JSThread *thread, JSHandle cache, const JSHandle &patten, const JSHandle &flags, const JSHandle &input, CacheType type, const JSHandle ®exp); - void AddResultInCache(JSThread *thread, const JSHandle &patten, const JSHandle &flags, + static void AddResultInCache(JSThread *thread, JSHandle cache, const JSHandle &patten, const JSHandle &flags, const JSHandle &input, JSTaggedValue resultArray, CacheType type, uint32_t lastIndex); - void ClearEntry(JSThread *thread, int entry); - void SetEntry(JSThread *thread, int entry, JSTaggedValue &patten, JSTaggedValue &flags, JSTaggedValue &input, - JSTaggedValue &lastIndexValue); - void UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type); - bool Match(int entry, JSTaggedValue &pattenStr, JSTaggedValue &flagsStr, JSTaggedValue &inputStr); inline void SetHitCount(JSThread *thread, int hitCount) { Set(thread, CACHE_HIT_COUNT_INDEX, JSTaggedValue(hitCount)); @@ -165,6 +160,11 @@ public: Set(thread, STRING_LENGTH_THRESHOLD_INDEX, JSTaggedValue(newThreshold)); } + inline void SetRegexpTable(JSThread *thread, JSTaggedValue table) + { + Set(thread, REGEXP_TABLE_INDEX, table); + } + inline uint32_t GetLargeStrCount() { return Get(LARGE_STRING_COUNT_INDEX).GetInt(); @@ -180,25 +180,21 @@ public: return Get(STRING_LENGTH_THRESHOLD_INDEX).GetInt(); } + inline JSTaggedValue GetRegexpTable() + { + return Get(REGEXP_TABLE_INDEX); + } + private: static constexpr int DEFAULT_LARGE_STRING_COUNT = 10; static constexpr int DEFAULT_CONFLICT_COUNT = 100; - static constexpr int DEFAULT_CACHE_NUMBER = 0x1000; static constexpr int CACHE_COUNT_INDEX = 0; static constexpr int CACHE_HIT_COUNT_INDEX = 1; static constexpr int LARGE_STRING_COUNT_INDEX = 2; static constexpr int CONFLICT_COUNT_INDEX = 3; static constexpr int STRING_LENGTH_THRESHOLD_INDEX = 4; - static constexpr int CACHE_TABLE_HEADER_SIZE = 5; - static constexpr int PATTERN_INDEX = 0; - static constexpr int FLAG_INDEX = 1; - static constexpr int INPUT_STRING_INDEX = 2; - static constexpr int LAST_INDEX_INDEX = 3; - static constexpr int RESULT_REPLACE_INDEX = 4; - static constexpr int RESULT_SPLIT_INDEX = 5; - static constexpr int RESULT_MATCH_INDEX = 6; - static constexpr int RESULT_EXEC_INDEX = 7; - static constexpr int ENTRY_SIZE = 8; + static constexpr int REGEXP_TABLE_INDEX = 5; + static constexpr int CACHE_TABLE_HEADER_SIZE = 6; }; } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_H diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp index a639335a944fab5c0384361637e84b804e4e95e2..9288c8b1afa7135009cc256fb7ce172acfab5e39 100644 --- a/ecmascript/global_env_constants.cpp +++ b/ecmascript/global_env_constants.cpp @@ -127,6 +127,12 @@ void GlobalEnvConstants::InitRootsClass([[maybe_unused]] JSThread *thread, JSHCl factory->NewEcmaDynClass(dynClassClass, TransitionHandler::SIZE, JSType::TRANSITION_HANDLER).GetTaggedValue()); SetConstant(ConstantIndex::PROPERTY_BOX_CLASS_INDEX, factory->NewEcmaDynClass(dynClassClass, PropertyBox::SIZE, JSType::PROPERTY_BOX).GetTaggedValue()); + SetConstant(ConstantIndex::KEY_BOX_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, KeyBox::SIZE, JSType::KEY_BOX).GetTaggedValue()); + SetConstant(ConstantIndex::RESULT_BOX_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ResultBox::SIZE, JSType::RESULT_BOX).GetTaggedValue()); + SetConstant(ConstantIndex::REGEXP_TABLE_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::REGEXP_TABLE).GetTaggedValue()); SetConstant(ConstantIndex::FUNCTION_EXTRA_INFO_INDEX, factory->NewEcmaDynClass(dynClassClass, JSFunctionExtraInfo::SIZE, JSType::FUNCTION_EXTRA_INFO) .GetTaggedValue()); diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index 5a36e2845af09b29cc41c9ef9133dfbd36c96584..75aa93ad869aae08cf7b8d0f087fe99c07d4e496 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -59,6 +59,9 @@ class JSThread; V(JSTaggedValue, PrototypeHandlerClass, PROTOTYPE_HANDLER_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, TransitionHandlerClass, TRANSITION_HANDLER_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, PropertyBoxClass, PROPERTY_BOX_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, KeyBoxClass, KEY_BOX_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ResultBoxClass, RESULT_BOX_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, RegexpTableClass, REGEXP_TABLE_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, FunctionExtraInfoClass, FUNCTION_EXTRA_INFO_INDEX, ecma_roots_class) \ V(JSTaggedValue, ProgramClass, PROGRAM_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, EcmaModuleClass, ECMA_MODULE_CLASS_INDEX, ecma_roots_class) \ diff --git a/ecmascript/js_hclass-inl.h b/ecmascript/js_hclass-inl.h index 2eeccd944ccb4c876ce040b134c19d046ce15d6a..522588082917a634c923ad0c6fe47abba90fa991 100644 --- a/ecmascript/js_hclass-inl.h +++ b/ecmascript/js_hclass-inl.h @@ -152,7 +152,7 @@ inline bool JSHClass::HasReferenceField(JSType type) inline size_t JSHClass::SizeFromJSHClass(JSType type, TaggedObject *header) { - if (type == JSType::TAGGED_ARRAY || type == JSType::TAGGED_DICTIONARY) { + if (type == JSType::TAGGED_ARRAY || type == JSType::TAGGED_DICTIONARY || type == JSType::REGEXP_TABLE) { auto length = reinterpret_cast(header)->GetLength(); return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); } diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index 7f29a4976a82fc03ea09532c689d0f1a5d39347e..d61a4c32f380492118f36356dc8cf72442475642 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -98,6 +98,9 @@ class ProtoChangeDetails; HCLASS, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ STRING, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ TAGGED_ARRAY, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + KEY_BOX, /* //////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + RESULT_BOX, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + REGEXP_TABLE, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ TAGGED_DICTIONARY, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ FREE_OBJECT_WITH_ONE_FIELD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ FREE_OBJECT_WITH_NONE_FIELD, /* ///////////////////////////////////////////////////////////////////-PADDING */ \ @@ -322,7 +325,7 @@ public: inline bool IsTaggedArray() const { JSType jsType = GetObjectType(); - return jsType == JSType::TAGGED_ARRAY || jsType == JSType::TAGGED_DICTIONARY; + return jsType == JSType::TAGGED_ARRAY || jsType == JSType::TAGGED_DICTIONARY || jsType == JSType::REGEXP_TABLE; } inline bool IsDictionary() const @@ -627,6 +630,22 @@ public: { return GetObjectType() == JSType::PROPERTY_BOX; } + + inline bool IsKeyBox() const + { + return GetObjectType() == JSType::KEY_BOX; + } + + inline bool IsResultBox() const + { + return GetObjectType() == JSType::RESULT_BOX; + } + + inline bool IsRegexpTable() const + { + return GetObjectType() == JSType::REGEXP_TABLE || GetObjectType() == JSType::TAGGED_DICTIONARY; + } + inline bool IsProtoChangeMarker() const { return GetObjectType() == JSType::PROTO_CHANGE_MARKER; diff --git a/ecmascript/js_regexp.cpp b/ecmascript/js_regexp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dab155e18f028533238880d69b2ddd39dc1b96ae --- /dev/null +++ b/ecmascript/js_regexp.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 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. + */ + +#include "js_regexp.h" +#include "ecmascript/ecma_macros.h" + +namespace panda::ecmascript { +int RegexpTable::Hash(const JSTaggedValue &key) { + if (key.IsHeapObject() && key.IsKeyBox()) { + KeyBox* keyBox = KeyBox::Cast(key.GetTaggedObject()); + JSTaggedValue patternValue = keyBox->GetPattern(); + JSTaggedValue flagValue = keyBox->GetFlag(); + JSTaggedValue inputValue = keyBox->GetInputString(); + int hash = patternValue.GetKeyHashCode() + flagValue.GetInt() + inputValue.GetKeyHashCode(); + // hash & size + return hash; + } else { + LOG(ERROR, RUNTIME) << "invalie key" << key.GetRawData(); + if (key.IsHeapObject()) { + JSType jsType = key.GetTaggedObject()->GetClass()->GetObjectType(); + LOG(ERROR, RUNTIME) << "invalie key type " << static_cast(jsType); + } + return -1; + } + UNREACHABLE(); +} + +bool RegexpTable::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) { + if (key.IsHeapObject() || other.IsHeapObject()) { + return false; + } + KeyBox* keyBox = KeyBox::Cast(key.GetTaggedObject()); + KeyBox* otherBox = KeyBox::Cast(other.GetTaggedObject()); + + EcmaString *patternString = EcmaString::Cast(keyBox->GetPattern().GetTaggedObject()); + uint8_t flagBits = static_cast(keyBox->GetFlag().GetInt()); + EcmaString *inputString = EcmaString::Cast(keyBox->GetInputString().GetTaggedObject()); + + EcmaString *keyPatternString = EcmaString::Cast(otherBox->GetPattern().GetTaggedObject()); + uint8_t keyflagBits = static_cast(otherBox->GetFlag().GetInt()); + EcmaString *keyInputString = EcmaString::Cast(otherBox->GetInputString().GetTaggedObject()); + + return EcmaString::StringsAreEqual(patternString, keyPatternString) && flagBits == keyflagBits && + EcmaString::StringsAreEqual(inputString, keyInputString); +} + +RegexpTable *RegexpTable::Create(const JSThread *thread, int numberOfElements) +{ + ASSERT_PRINT((numberOfElements > 0), "the size must be greater than zero"); + auto size = static_cast(numberOfElements); + ASSERT_PRINT(helpers::math::IsPowerOfTwo(static_cast(numberOfElements)), "the size must be power of two"); + + int length = RegexpTable::GetEntryIndex(numberOfElements); + + auto table = static_cast(*thread->GetEcmaVM()->GetFactory()->NewRegexpTable(length)); + table->SetEntriesCount(thread, 0); + table->SetHoleEntriesCount(thread, 0); + table->SetHashTableSize(thread, size); + return table; +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/js_regexp.h b/ecmascript/js_regexp.h index ef8560df12adc4f4e87c50e44ab2a2034e84a2ea..fe1056328d4c4fe4999e43f27e0b7048a6387ccf 100644 --- a/ecmascript/js_regexp.h +++ b/ecmascript/js_regexp.h @@ -20,6 +20,7 @@ #include "ecmascript/js_hclass.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/ecma_macros.h" +#include "ecmascript/tagged_hash_table.h" namespace panda::ecmascript { class JSRegExp : public JSObject { @@ -41,6 +42,84 @@ public: DECL_DUMP() }; + +class KeyBox : public TaggedObject { +public: + static KeyBox *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsKeyBox()); + return static_cast(object); + } + + void Clear(const JSThread *thread); + + inline bool IsInvalid() const; + + static constexpr size_t FLAG_INDEX= TaggedObjectSize(); + ACCESSORS(Flag, FLAG_INDEX, LAST_INDEX_INDEX) + ACCESSORS(LastIndex, LAST_INDEX_INDEX, PATTERN_INDEX) + ACCESSORS(Pattern, PATTERN_INDEX, INPUT_STRING_INDEX) + ACCESSORS(InputString, INPUT_STRING_INDEX, SIZE) + + DECL_VISIT_OBJECT(FLAG_INDEX, SIZE) +}; + +class ResultBox : public TaggedObject { +public: + static ResultBox *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsResultBox()); + return static_cast(object); + } + + void Clear(const JSThread *thread); + + inline bool IsInvalid() const; + + static constexpr size_t RESULT_REPLACE_INDEX = TaggedObjectSize(); + + ACCESSORS(ResultReplace, RESULT_REPLACE_INDEX, RESULT_SPLIT_INDEX) + ACCESSORS(ResultSplit, RESULT_SPLIT_INDEX, RESULT_MATCH_INDEX) + ACCESSORS(ResultMatch, RESULT_MATCH_INDEX, RESULT_EXEC_INDEX) + ACCESSORS(ResultExec, RESULT_EXEC_INDEX, SIZE) + + DECL_VISIT_OBJECT(RESULT_REPLACE_INDEX, SIZE) +}; + +class RegexpTable : public TaggedHashTable { +public: + using RegexpTableT = TaggedHashTable; + inline static int GetKeyIndex(int entry) + { + return RegexpTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return RegexpTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + RESULT_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return RegexpTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static RegexpTable *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsRegexpTable()); + return static_cast(object); + } + static int Hash(const JSTaggedValue &key); + + static bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other); + + static RegexpTable *Create(const JSThread *thread, int numberOfElements = DEFAULT_CACHE_NUMBER); + static constexpr int KEY_INDEX = 0; + static constexpr int RESULT_INDEX = 1; + static constexpr int ENTRY_SIZE = 2; + static constexpr int DEFAULT_CACHE_NUMBER = 0x1000; +}; } // namespace panda::ecmascript #endif // ECMASCRIPT_JSPRIMITIVEREF_H diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index 27d6f86f29bebe0707ff1c7341510edef4681a8b..9b5ebd35177e1e5d3f7490e9938379bd87e8fe7b 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -746,6 +746,21 @@ inline bool JSTaggedValue::IsPropertyBox() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsPropertyBox(); } +inline bool JSTaggedValue::IsKeyBox() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsKeyBox(); +} + +inline bool JSTaggedValue::IsResultBox() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsResultBox(); +} + +inline bool JSTaggedValue::IsRegexpTable() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsRegexpTable(); +} + inline bool JSTaggedValue::IsProtoChangeDetails() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsProtoChangeDetails(); diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index 1cd220490dd6fc7d75ad3c795751300579a5a017..5b1fb3a530e89c72563835487f255e25e1b2f258 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -316,6 +316,9 @@ public: bool IsPrototypeHandler() const; bool IsTransitionHandler() const; bool IsPropertyBox() const; + bool IsKeyBox() const; + bool IsResultBox() const; + bool IsRegexpTable() const; bool IsProtoChangeMarker() const; bool IsProtoChangeDetails() const; bool IsMachineCodeObject() const; diff --git a/ecmascript/mem/heap_roots-inl.h b/ecmascript/mem/heap_roots-inl.h index e05ed383b470743aea354038745ac5ebdcdabced..49704ca4c893a9774f1f09a37f0638ffc69c1b20 100644 --- a/ecmascript/mem/heap_roots-inl.h +++ b/ecmascript/mem/heap_roots-inl.h @@ -197,6 +197,7 @@ void HeapRootManager::MarkObjectBody(TaggedObject *object, JSHClass *klass, cons break; case JSType::TAGGED_ARRAY: case JSType::TAGGED_DICTIONARY: + case JSType::REGEXP_TABLE: TaggedArray::Cast(object)->VisitRangeSlot(visitor); break; case JSType::GLOBAL_ENV: @@ -286,6 +287,12 @@ void HeapRootManager::MarkObjectBody(TaggedObject *object, JSHClass *klass, cons case JSType::JS_NATIVE_OBJECT: JSNativeObject::Cast(object)->VisitRangeSlot(visitor); break; + case JSType::KEY_BOX: + KeyBox::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::RESULT_BOX: + ResultBox::Cast(object)->VisitRangeSlot(visitor); + break; default: UNREACHABLE(); } diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index 554f77f1adc7a646cba828eb632f9ae75493b661..add016df67a44eb90c2736a5cfab7384f0bc4466 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -131,6 +131,9 @@ void ObjectFactory::ObtainRootClass([[maybe_unused]] const JSHandle & jsProxyCallableClass_ = JSHClass::Cast(globalConst->GetJSProxyCallableClass().GetTaggedObject()); jsProxyConstructClass_ = JSHClass::Cast(globalConst->GetJSProxyConstructClass().GetTaggedObject()); PropertyBoxClass_ = JSHClass::Cast(globalConst->GetPropertyBoxClass().GetTaggedObject()); + keyBoxClass_ = JSHClass::Cast(globalConst->GetKeyBoxClass().GetTaggedObject()); + resultBoxClass_ = JSHClass::Cast(globalConst->GetResultBoxClass().GetTaggedObject()); + regexpTableClass_ = JSHClass::Cast(globalConst->GetRegexpTableClass().GetTaggedObject()); protoChangeMarkerClass_ = JSHClass::Cast(globalConst->GetProtoChangeMarkerClass().GetTaggedObject()); protoChangeDetailsClass_ = JSHClass::Cast(globalConst->GetProtoChangeDetailsClass().GetTaggedObject()); promiseRecordClass_ = JSHClass::Cast(globalConst->GetPromiseRecordClass().GetTaggedObject()); @@ -1528,6 +1531,17 @@ JSHandle ObjectFactory::NewDictionaryArray(array_size_t length) return array; } +JSHandle ObjectFactory::NewRegexpTable(array_size_t length) +{ + NewObjectHook(); + ASSERT(length > 0); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(regexpTableClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), length); + return array; +} + JSHandle ObjectFactory::ExtendArray(const JSHandle &old, array_size_t length, JSTaggedValue initVal) { @@ -1743,6 +1757,30 @@ JSHandle ObjectFactory::NewPropertyBox(const JSHandle ObjectFactory::NewKeyBox() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(keyBoxClass_); + JSHandle box(thread_, header); + box->SetPattern(thread_, JSTaggedValue::Undefined()); + box->SetFlag(thread_, JSTaggedValue::Undefined()); + box->SetInputString(thread_, JSTaggedValue::Undefined()); + box->SetLastIndex(thread_, JSTaggedValue::Undefined()); + return box; +} + +JSHandle ObjectFactory::NewResultBox() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(resultBoxClass_); + JSHandle box(thread_, header); + box->SetResultReplace(thread_, JSTaggedValue::Undefined()); + box->SetResultSplit(thread_, JSTaggedValue::Undefined()); + box->SetResultMatch(thread_, JSTaggedValue::Undefined()); + box->SetResultExec(thread_, JSTaggedValue::Undefined()); + return box; +} + JSHandle ObjectFactory::NewProtoChangeMarker() { NewObjectHook(); diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index e4ac085292ef667de9fc544372fb5113c305c62e..c8dc3a8a40ec2a044765c8852b9b210e1dbca17a 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -84,6 +84,9 @@ class LayoutInfo; class JSIntlBoundFunction; class FreeObject; class JSNativePointer; +class KeyBox; +class ResultBox; +class RegexpTable; namespace job { class MicroJobQueue; @@ -224,9 +227,13 @@ public: JSHandle NewTaggedArray(array_size_t length, JSTaggedValue initVal, bool nonMovable); JSHandle NewTaggedArray(array_size_t length, JSTaggedValue initVal, MemSpaceType spaceType); JSHandle NewDictionaryArray(array_size_t length); + JSHandle NewRegexpTable(array_size_t length); + JSHandle NewJSForinIterator(const JSHandle &obj); JSHandle NewPropertyBox(const JSHandle &name); + JSHandle NewKeyBox(); + JSHandle NewResultBox(); JSHandle NewProtoChangeMarker(); @@ -408,6 +415,9 @@ private: JSHClass *programClass_ {nullptr}; JSHClass *machineCodeClass_ {nullptr}; JSHClass *ecmaModuleClass_ {nullptr}; + JSHClass *keyBoxClass_ {nullptr}; + JSHClass *resultBoxClass_ {nullptr}; + JSHClass *regexpTableClass_ {nullptr}; EcmaVM *vm_ {nullptr}; Heap *heap_ {nullptr}; diff --git a/ecmascript/tagged_array-inl.h b/ecmascript/tagged_array-inl.h index a340d85726b5de5df385643bd1027d78cf64bd78..18afbd30e90188e7967149c1ab2f20600970f0c9 100644 --- a/ecmascript/tagged_array-inl.h +++ b/ecmascript/tagged_array-inl.h @@ -29,6 +29,7 @@ inline array_size_t TaggedArray::GetLength() const inline JSTaggedValue TaggedArray::Get(array_size_t idx) const { ASSERT(idx < GetLength()); + // Note: Here we can't statically decide the element type is a primitive or heap object, especially for // dynamically-typed languages like JavaScript. So we simply skip the read-barrier. size_t offset = JSTaggedValue::TaggedTypeSize() * idx;