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