1/*
2 * Copyright (c) 2021 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_GLOBAL_DICTIONARY_INL_H
17#define ECMASCRIPT_GLOBAL_DICTIONARY_INL_H
18
19#include "ecmascript/filter_helper.h"
20#include "ecmascript/js_symbol.h"
21#include "ecmascript/global_dictionary.h"
22#include "ecmascript/ic/property_box.h"
23#include "ecmascript/mem/c_containers.h"
24#include "ecmascript/tagged_hash_table.h"
25
26namespace panda {
27namespace ecmascript {
28int GlobalDictionary::Hash(const JSTaggedValue &key)
29{
30    if (key.IsHeapObject()) {
31        if (key.IsSymbol()) {
32            auto symbolString = JSSymbol::Cast(key.GetTaggedObject());
33            return symbolString->GetHashField();
34        }
35        if (key.IsString()) {
36            auto keyString = EcmaString::Cast(key.GetTaggedObject());
37            return EcmaStringAccessor(keyString).GetHashcode();
38        }
39    }
40    // key must be object
41    LOG_ECMA(FATAL) << "this branch is unreachable";
42    UNREACHABLE();
43}
44
45bool GlobalDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other)
46{
47    return key == other;
48}
49
50PropertyBox *GlobalDictionary::GetBox(int entry) const
51{
52    int index = GetEntryIndex(entry) + ENTRY_VALUE_INDEX;
53    return PropertyBox::Cast(Get(index).GetTaggedObject());
54}
55
56JSTaggedValue GlobalDictionary::GetValue(int entry) const
57{
58    return GetBox(entry)->GetValue();
59}
60
61PropertyAttributes GlobalDictionary::GetAttributes(int entry) const
62{
63    int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX;
64    return PropertyAttributes(Get(index));
65}
66
67void GlobalDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value,
68                                const PropertyAttributes &attributes)
69{
70    SetKey(thread, entry, key);
71    SetAttributes(thread, entry, attributes);
72    UpdateValueAndAttributes(thread, entry, value, attributes);
73}
74
75void GlobalDictionary::ClearEntry(const JSThread *thread, int entry)
76{
77    JSTaggedValue hole = JSTaggedValue::Hole();
78    PropertyAttributes metaData;
79    SetEntry(thread, entry, hole, hole, metaData);
80}
81
82void GlobalDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value,
83                                                const PropertyAttributes &metaData)
84{
85    UpdateValue(thread, entry, value);
86    SetAttributes(thread, entry, metaData);
87}
88
89void GlobalDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData)
90{
91    int index = static_cast<int>(GetEntryIndex(entry) + ENTRY_DETAILS_INDEX);
92    Set(thread, index, metaData.GetTaggedValue());
93}
94
95void GlobalDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value)
96{
97    SetValue(thread, entry, value);
98}
99
100void GlobalDictionary::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const
101{
102    ASSERT_PRINT(offset + EntriesCount() <= static_cast<int>(keyArray->GetLength()),
103                 "keyArray capacity is not enough for dictionary");
104    int arrayIndex = 0;
105    int size = Size();
106
107    CVector<std::pair<JSTaggedValue, uint32_t>> sortArr;
108    for (int hashIndex = 0; hashIndex < size; hashIndex++) {
109        JSTaggedValue key = GetKey(hashIndex);
110        if (!key.IsUndefined() && !key.IsHole()) {
111            PropertyAttributes attr = GetAttributes(hashIndex);
112            std::pair<JSTaggedValue, uint32_t> pair(key, attr.GetOffset());
113            sortArr.push_back(pair);
114        }
115    }
116    std::sort(sortArr.begin(), sortArr.end(), CompKey);
117    for (auto entry : sortArr) {
118        JSTaggedValue nameKey = entry.first;
119        keyArray->Set(thread, arrayIndex + offset, nameKey);
120        arrayIndex++;
121    }
122}
123
124void GlobalDictionary::GetAllKeysByFilter(const JSThread *thread,
125    uint32_t &keyArrayEffectivelength, TaggedArray *keyArray, uint32_t filter) const
126{
127    ASSERT_PRINT(keyArrayEffectivelength + static_cast<uint32_t>(EntriesCount()) <= keyArray->GetLength(),
128                 "keyArray capacity is not enough for dictionary");
129    int size = Size();
130
131    CVector<std::pair<JSTaggedValue, uint32_t>> sortArr;
132    for (int hashIndex = 0; hashIndex < size; hashIndex++) {
133        JSTaggedValue key = GetKey(hashIndex);
134        if (!key.IsUndefined() && !key.IsHole()) {
135            PropertyAttributes attr = GetAttributes(hashIndex);
136            bool bIgnore = FilterHelper::IgnoreKeyByFilter<PropertyAttributes>(attr, filter);
137            if (bIgnore) {
138                continue;
139            }
140            if (key.IsString() && (filter & NATIVE_KEY_SKIP_STRINGS)) {
141                continue;
142            }
143            if (key.IsSymbol() && (filter & NATIVE_KEY_SKIP_SYMBOLS)) {
144                continue;
145            }
146            std::pair<JSTaggedValue, uint32_t> pair(key, attr.GetOffset());
147            sortArr.push_back(pair);
148        }
149    }
150    std::sort(sortArr.begin(), sortArr.end(), CompKey);
151    for (auto entry : sortArr) {
152        JSTaggedValue nameKey = entry.first;
153        keyArray->Set(thread, keyArrayEffectivelength, nameKey);
154        keyArrayEffectivelength++;
155    }
156}
157
158std::pair<uint32_t, uint32_t> GlobalDictionary::GetNumOfEnumKeys() const
159{
160    uint32_t enumKeys = 0;
161    uint32_t shadowKeys = 0;
162    int size = Size();
163    for (int hashIndex = 0; hashIndex < size; hashIndex++) {
164        JSTaggedValue key = GetKey(hashIndex);
165        if (key.IsString()) {
166            PropertyAttributes attr = GetAttributes(hashIndex);
167            if (attr.IsEnumerable()) {
168                enumKeys++;
169            } else {
170                shadowKeys++;
171            }
172        }
173    }
174    return std::make_pair(enumKeys, shadowKeys);
175}
176
177void GlobalDictionary::GetEnumAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray,
178                                      uint32_t *keys) const
179{
180    ASSERT_PRINT(offset + GetNumOfEnumKeys().first <= static_cast<unsigned int>(keyArray->GetLength()),
181                 "keyArray capacity is not enough for dictionary");
182    int arrayIndex = 0;
183    int size = Size();
184
185    CVector<std::pair<JSTaggedValue, uint32_t>> sortArr;
186    for (int hashIndex = 0; hashIndex < size; hashIndex++) {
187        JSTaggedValue key = GetKey(hashIndex);
188        if (key.IsString()) {
189            PropertyAttributes attr = GetAttributes(hashIndex);
190            if (attr.IsEnumerable()) {
191                std::pair<JSTaggedValue, uint32_t> pair(key, attr.GetOffset());
192                sortArr.push_back(pair);
193            }
194        }
195    }
196    std::sort(sortArr.begin(), sortArr.end(), CompKey);
197    for (const auto &entry : sortArr) {
198        JSTaggedValue nameKey = entry.first;
199        keyArray->Set(thread, arrayIndex + offset, nameKey);
200        arrayIndex++;
201    }
202    *keys += arrayIndex;
203}
204
205bool GlobalDictionary::CompKey(const std::pair<JSTaggedValue, uint32_t> &a, const std::pair<JSTaggedValue, uint32_t> &b)
206{
207    return a.second < b.second;
208}
209
210void GlobalDictionary::InvalidatePropertyBox(JSThread *thread, const JSHandle<GlobalDictionary> &dictHandle, int entry)
211{
212    PropertyBox *box = dictHandle->GetBox(entry);
213
214    ASSERT(!box->GetValue().IsHole());
215    JSHandle<JSTaggedValue> oldValue(thread, box->GetValue());
216    GlobalDictionary::InvalidateAndReplaceEntry(thread, dictHandle, entry, oldValue);
217}
218
219void GlobalDictionary::InvalidateAndReplaceEntry(JSThread *thread, const JSHandle<GlobalDictionary> &dictHandle,
220                                                 int entry, const JSHandle<JSTaggedValue> &oldValue)
221{
222    if (!dictHandle->IsValidateBox(entry)) {
223        return;
224    }
225    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
226    // Swap with a copy.
227    JSHandle<PropertyBox> newBox = factory->NewPropertyBox(oldValue);
228    PropertyBox *box = dictHandle->GetBox(entry);
229    PropertyAttributes attr = dictHandle->GetAttributes(entry);
230    if (!attr.IsConfigurable() || box->GetValue().IsHole()) {
231        return;
232    }
233    ASSERT_PRINT(attr.IsConfigurable(), "property must be configurable");
234    ASSERT_PRINT(!box->GetValue().IsHole(), "value must not be hole");
235
236    // Cell is officially mutable henceforth.
237    attr.SetBoxType(PropertyBoxType::MUTABLE);
238    dictHandle->SetAttributes(thread, entry, attr);
239    dictHandle->UpdateValue(thread, entry, newBox.GetTaggedValue());
240    box->Clear(thread);
241}
242
243bool GlobalDictionary::IsValidateBox(int entry) const
244{
245    int index = GetEntryIndex(entry) + ENTRY_VALUE_INDEX;
246    return !Get(index).IsUndefined();
247}
248}  // namespace ecmascript
249}  // namespace panda
250
251#endif
252