/* * 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. */ #ifndef ECMASCRIPT_TRANSITIONS_DICTIONARY_H #define ECMASCRIPT_TRANSITIONS_DICTIONARY_H #include "ecmascript/accessor_data.h" #include "ecmascript/js_symbol.h" #include "ecmascript/js_tagged_number.h" #include "ecmascript/tagged_hash_table.h" namespace panda::ecmascript { class TransitionsDictionary : public TaggedHashTable { public: using HashTableT = TaggedHashTable; static inline bool IsMatch([[maybe_unused]] const JSTaggedValue &key, [[maybe_unused]] const JSTaggedValue &otherKey) { UNREACHABLE(); } static inline int Hash([[maybe_unused]] const JSTaggedValue &key) { UNREACHABLE(); } static inline bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &metaData, const JSTaggedValue &otherKey, const JSTaggedValue &otherDetails) { return key == otherKey && metaData == otherDetails; } static inline int Hash(const JSTaggedValue &key, const JSTaggedValue &metaData) { ASSERT(key.IsStringOrSymbol()); uint32_t hash = 0; if (key.IsString()) { hash = EcmaStringAccessor(key).GetHashcode(); } else if (key.IsSymbol()) { hash = JSSymbol::Cast(key.GetTaggedObject())->GetHashField(); } int metaDataHash = metaData.IsInt() ? metaData.GetInt() : static_cast(metaData.GetRawData()); return static_cast(hash) + metaDataHash; } inline static int GetKeyIndex(int entry) { return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; } inline static int GetValueIndex(int entry) { return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; } inline static int GetEntryIndex(int entry) { return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); } inline static int GetEntrySize() { return ENTRY_SIZE; } static TransitionsDictionary *Cast(TaggedObject *object) { return reinterpret_cast(object); } static constexpr int DEFAULT_ELEMENTS_NUMBER = 16; static JSHandle Create(const JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER) { return HashTableT::Create(thread, numberOfElements); } // Attempt to shrink the dictionary after deletion of key. inline static JSHandle Shrink(const JSThread *thread, const JSHandle &dictionary) { return HashTableT::Shrink(thread, dictionary, 0); } inline JSTaggedValue GetAttributes(int entry) const { int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; return HashTableT::Get(index); } inline void SetAttributes(const JSThread *thread, int entry, JSTaggedValue metaData) { int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; HashTableT::Set(thread, index, metaData); } inline void SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, const JSTaggedValue &metaData) { SetKey(thread, entry, key); JSTaggedValue weakValue = JSTaggedValue(value.CreateAndGetWeakRef()); SetValue(thread, entry, weakValue); if (!metaData.IsHeapObject()) { SetAttributes(thread, entry, metaData); return; } JSTaggedValue weakMetaData = JSTaggedValue(metaData.CreateAndGetWeakRef()); SetAttributes(thread, entry, weakMetaData); } inline void RemoveElement(const JSThread *thread, int entry) { SetKey(thread, entry, JSTaggedValue::Hole()); SetValue(thread, entry, JSTaggedValue::Hole()); SetAttributes(thread, entry, JSTaggedValue::Hole()); IncreaseHoleEntriesCount(thread); } int FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData); static JSHandle PutIfAbsent(const JSThread *thread, const JSHandle &dictionary, const JSHandle &key, const JSHandle &value, const JSHandle &metaData); static JSHandle Remove(const JSThread *thread, const JSHandle &table, const JSHandle &key, const JSTaggedValue &metaData); void Rehash(const JSThread *thread, TransitionsDictionary *newTable); static bool CheckWeakExist(const JSTaggedValue &value) { if (value == JSTaggedValue::Undefined()) { return false; } return true; } static int ComputeCompactSize(const JSHandle &table, int computeHashTableSize, int tableSize, int addedElements) { int realEntryCount = 0; for (int i = 0; i < tableSize; i++) { // value is weak reference, if not use will be set undefined. if (TransitionsDictionary::CheckWeakExist(table->GetValue(i))) { realEntryCount++; } } return std::min(computeHashTableSize, static_cast(helpers::math::GetPowerOfTwoValue32(realEntryCount + addedElements) * HASH_TABLE_BUFFER)); } static constexpr int ENTRY_SIZE = 3; static constexpr int ENTRY_KEY_INDEX = 0; static constexpr int ENTRY_VALUE_INDEX = 1; static constexpr int ENTRY_DETAILS_INDEX = 2; static constexpr int HASH_TABLE_BUFFER = 2; DECL_DUMP() }; } // namespace panda::ecmascript #endif