1/*
2 * Copyright (c) 2022 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#ifndef ECMASCRIPT_REQUIRE_JS_CJS_MODULE_CACHE_H
17#define ECMASCRIPT_REQUIRE_JS_CJS_MODULE_CACHE_H
18
19#include "ecmascript/ecma_vm.h"
20#include "ecmascript/object_factory.h"
21#include "ecmascript/tagged_hash_table.h"
22#include "ecmascript/ecma_string.h"
23#include "ecmascript/require/js_cjs_module.h"
24#include "ecmascript/js_thread.h"
25
26namespace panda::ecmascript {
27class CjsModuleCache : public TaggedHashTable<CjsModuleCache> {
28public:
29    using HashTable = TaggedHashTable<CjsModuleCache>;
30
31    static CjsModuleCache *Cast(TaggedObject *object)
32    {
33        ASSERT(JSTaggedValue(object).IsTaggedArray());
34        return reinterpret_cast<CjsModuleCache *>(object);
35    }
36
37    inline static int GetKeyIndex(int entry)
38    {
39        return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX;
40    }
41
42    inline static int GetValueIndex(int entry)
43    {
44        return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX;
45    }
46
47    inline static int GetEntryIndex(int entry)
48    {
49        return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize();
50    }
51
52    inline static int GetEntrySize()
53    {
54        return ENTRY_SIZE;
55    }
56
57    static inline bool IsMatch(const JSTaggedValue &fileName, const JSTaggedValue &other)
58    {
59        if (fileName.IsHole() || fileName.IsUndefined()) {
60            return false;
61        }
62
63        auto *nameString = static_cast<EcmaString *>(fileName.GetTaggedObject());
64        auto *otherString = static_cast<EcmaString *>(other.GetTaggedObject());
65        return EcmaStringAccessor::StringsAreEqual(nameString, otherString);
66    }
67
68    static inline uint32_t Hash(const JSTaggedValue &key)
69    {
70        ASSERT(key.IsString());
71        EcmaString *nameStr = static_cast<EcmaString *>(key.GetTaggedObject());
72        return EcmaStringAccessor(nameStr).GetHashcode();
73    }
74
75    static const int DEFAULT_ELEMENTS_NUMBER = 64;
76
77    static JSHandle<CjsModuleCache> Create(JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER)
78    {
79        return HashTable::Create(thread, numberOfElements);
80    }
81
82    inline int FindEntry(const JSTaggedValue &key)
83    {
84        int size = Size();
85        int count = 1;
86        JSTaggedValue keyValue;
87        uint32_t hash = Hash(key);
88
89        for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) {
90            keyValue = GetKey(entry);
91            if (keyValue.IsHole()) {
92                continue;
93            }
94            if (keyValue.IsUndefined()) {
95                return -1;
96            }
97            if (IsMatch(key, keyValue)) {
98                return entry;
99            }
100        }
101        return -1;
102    }
103
104    inline bool ContainsModule(const JSTaggedValue &key)
105    {
106        int entry = FindEntry(key);
107        return entry != -1;
108    }
109
110    inline JSTaggedValue GetModule(const JSTaggedValue &key)
111    {
112        int entry = FindEntry(key);
113        ASSERT(entry != -1);
114        return GetValue(entry);
115    }
116
117    inline void SetEntry(const JSThread *thread, int entry,
118                         const JSHandle<JSTaggedValue> &key,
119                         const JSHandle<JSTaggedValue> &value)
120    {
121        JSTaggedValue keyValue = key.GetTaggedValue();
122        JSTaggedValue valueValue = value.GetTaggedValue();
123        SetKey(thread, entry, keyValue);
124        SetValue(thread, entry, valueValue);
125    }
126
127    static JSHandle<CjsModuleCache> PutIfAbsentAndReset(const JSThread *thread,
128                                                        const JSHandle<CjsModuleCache> &dictionary,
129                                                        const JSHandle<JSTaggedValue> &key,
130                                                        const JSHandle<JSTaggedValue> &value);
131    static JSHandle<CjsModuleCache> ResetModule(const JSThread *thread,
132                                                const JSHandle<CjsModuleCache> &dictionary,
133                                                const JSHandle<JSTaggedValue> &key,
134                                                const JSHandle<JSTaggedValue> &value);
135    static int ComputeCompactSize([[maybe_unused]] const JSHandle<CjsModuleCache> &table, int computeHashTableSize,
136        [[maybe_unused]] int tableSize, [[maybe_unused]] int addedElements)
137    {
138        return computeHashTableSize;
139    }
140    static constexpr int ENTRY_KEY_INDEX = 0;
141    static constexpr int ENTRY_VALUE_INDEX = 1;
142    static constexpr int ENTRY_SIZE = 2;
143    static constexpr int DEAULT_DICTIONART_CAPACITY = 4;
144};
145}  // namespace panda::ecmascript
146#endif  // ECMASCRIPT_REQUIRE_JS_CJS_MODULE_CACHE_H