• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "ecmascript/js_api/js_api_hashmap.h"
17 
18 #include "ecmascript/containers/containers_errors.h"
19 #include "ecmascript/tagged_hash_array.h"
20 
21 namespace panda::ecmascript {
22 using ContainerError = containers::ContainerError;
23 using ErrorFlag = containers::ErrorFlag;
IsEmpty()24 JSTaggedValue JSAPIHashMap::IsEmpty()
25 {
26     return JSTaggedValue(GetSize() == 0);
27 }
28 
HasKey(JSThread * thread,JSTaggedValue key)29 JSTaggedValue JSAPIHashMap::HasKey(JSThread *thread, JSTaggedValue key)
30 {
31     TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable(thread).GetTaggedObject());
32     int hash = TaggedNode::Hash(thread, key);
33     return JSTaggedValue(!(hashArray->GetNode(thread, hash, key).IsHole()));
34 }
35 
HasValue(JSThread * thread,JSHandle<JSAPIHashMap> hashMap,JSHandle<JSTaggedValue> value)36 JSTaggedValue JSAPIHashMap::HasValue(JSThread *thread, JSHandle<JSAPIHashMap> hashMap,
37                                      JSHandle<JSTaggedValue> value)
38 {
39     JSHandle<TaggedHashArray> hashArray(thread, hashMap->GetTable(thread));
40     uint32_t tabLength = hashArray->GetLength();
41     JSTaggedValue taggedValue = value.GetTaggedValue();
42     for (uint32_t index = 0; index < tabLength; index++) {
43         JSTaggedValue node = hashArray->Get(thread, index);
44         if (node.IsHole()) {
45             continue;
46         }
47         if (node.IsLinkedNode()) {
48             if (HasValueLinkedNode(thread, node, taggedValue)) {
49                 return JSTaggedValue::True();
50             }
51         } else {
52             if (HasValueRBTreeNode(thread, node, taggedValue)) {
53                 return JSTaggedValue::True();
54             }
55         }
56     }
57     return JSTaggedValue::False();
58 }
59 
HasValueLinkedNode(JSThread * thread,JSTaggedValue node,JSTaggedValue value)60 bool JSAPIHashMap::HasValueLinkedNode(JSThread *thread, JSTaggedValue node, JSTaggedValue value)
61 {
62     ASSERT(node.IsLinkedNode());
63     while (!node.IsHole()) {
64         LinkedNode *p = LinkedNode::Cast(node.GetTaggedObject());
65         if (JSTaggedValue::SameValue(thread, p->GetValue(thread), value)) {
66             return true;
67         }
68         node = p->GetNext(thread);
69     }
70     return false;
71 }
72 
HasValueRBTreeNode(JSThread * thread,JSTaggedValue node,JSTaggedValue value)73 bool JSAPIHashMap::HasValueRBTreeNode(JSThread *thread, JSTaggedValue node, JSTaggedValue value)
74 {
75     ASSERT(node.IsRBTreeNode());
76     RBTreeNode *p = RBTreeNode::Cast(node.GetTaggedObject());
77     if (JSTaggedValue::SameValue(thread, p->GetValue(thread), value)) {
78         return true;
79     }
80     JSTaggedValue left = p->GetLeft(thread);
81     if (!left.IsHole() && HasValueRBTreeNode(thread, left, value)) {
82         return true;
83     }
84     JSTaggedValue right = p->GetRight(thread);
85     if (!right.IsHole() && HasValueRBTreeNode(thread, right, value)) {
86         return true;
87     }
88     return false;
89 }
90 
Replace(JSThread * thread,JSTaggedValue key,JSTaggedValue newValue)91 bool JSAPIHashMap::Replace(JSThread *thread, JSTaggedValue key, JSTaggedValue newValue)
92 {
93     TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable(thread).GetTaggedObject());
94     int hash = TaggedNode::Hash(thread, key);
95     JSTaggedValue nodeVa = hashArray->GetNode(thread, hash, key);
96     if (nodeVa.IsHole()) {
97         return false;
98     }
99     if (nodeVa.IsLinkedNode()) {
100         LinkedNode::Cast(nodeVa.GetTaggedObject())->SetValue(thread, newValue);
101     } else {
102         RBTreeNode::Cast(nodeVa.GetTaggedObject())->SetValue(thread, newValue);
103     }
104     return true;
105 }
106 
Set(JSThread * thread,JSHandle<JSAPIHashMap> hashMap,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> value)107 void JSAPIHashMap::Set(JSThread *thread, JSHandle<JSAPIHashMap> hashMap,
108                        JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value)
109 {
110     if (key.GetTaggedValue().IsUndefined()) {
111         JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, key.GetTaggedValue());
112         CString errorMsg =
113             "The type of \"key\" must be Key of JS. Received value is: " + ConvertToString(thread, *result);
114         JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
115         THROW_NEW_ERROR_AND_RETURN(thread, error);
116     }
117     JSHandle<TaggedHashArray> hashArray(thread, hashMap->GetTable(thread));
118     int hash = TaggedNode::Hash(thread, key.GetTaggedValue());
119     JSTaggedValue setValue = TaggedHashArray::SetVal(thread, hashArray, hash, key, value);
120     uint32_t nodeNum = hashMap->GetSize();
121     if (!setValue.IsUndefined()) {
122         hashMap->SetSize(++nodeNum);
123     }
124     uint32_t tableLength = (hashArray->GetLength()) * TaggedHashArray::DEFAULT_LOAD_FACTOR;
125     if (nodeNum > tableLength) {
126         hashArray = TaggedHashArray::Resize(thread, hashArray, hashArray->GetLength());
127         hashMap->SetTable(thread, hashArray);
128     }
129 }
130 
Get(JSThread * thread,JSTaggedValue key)131 JSTaggedValue JSAPIHashMap::Get(JSThread *thread, JSTaggedValue key)
132 {
133     TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable(thread).GetTaggedObject());
134     int hash = TaggedNode::Hash(thread, key);
135     JSTaggedValue node = hashArray->GetNode(thread, hash, key);
136     if (node.IsHole()) {
137         return JSTaggedValue::Undefined();
138     } else if (node.IsRBTreeNode()) {
139         return RBTreeNode::Cast(node.GetTaggedObject())->GetValue(thread);
140     } else {
141         return LinkedNode::Cast(node.GetTaggedObject())->GetValue(thread);
142     }
143 }
144 
SetAll(JSThread * thread,JSHandle<JSAPIHashMap> dst,JSHandle<JSAPIHashMap> src)145 void JSAPIHashMap::SetAll(JSThread *thread, JSHandle<JSAPIHashMap> dst, JSHandle<JSAPIHashMap> src)
146 {
147     JSHandle<TaggedHashArray> hashArray(thread, src->GetTable(thread));
148     uint32_t srcTabLength = hashArray->GetLength();
149     JSMutableHandle<JSTaggedValue> node(thread, JSTaggedValue::Hole());
150     for (uint32_t index = 0; index < srcTabLength; index++) {
151         node.Update(hashArray->Get(thread, index));
152         if (node->IsHole()) {
153             continue;
154         }
155         if (node->IsLinkedNode()) {
156             SetAllLinkedNode(thread, dst, JSMutableHandle<LinkedNode>::Cast(node));
157         } else {
158             SetAllRBTreeNode(thread, dst, JSHandle<RBTreeNode>(node));
159         }
160     }
161 }
162 
SetAllLinkedNode(JSThread * thread,JSHandle<JSAPIHashMap> hashMap,JSMutableHandle<LinkedNode> node)163 void JSAPIHashMap::SetAllLinkedNode(JSThread *thread, JSHandle<JSAPIHashMap> hashMap, JSMutableHandle<LinkedNode> node)
164 {
165     ASSERT(node.GetTaggedValue().IsLinkedNode());
166     while (!node.GetTaggedValue().IsHole()) {
167         if (!hashMap->Replace(thread, node->GetKey(thread), node->GetValue(thread))) {
168             JSHandle<JSTaggedValue> key(thread, node->GetKey(thread));
169             JSHandle<JSTaggedValue> value(thread, node->GetValue(thread));
170             Set(thread, hashMap, key, value);
171         }
172         node.Update(node->GetNext(thread));
173     }
174 }
175 
SetAllRBTreeNode(JSThread * thread,JSHandle<JSAPIHashMap> hashMap,JSHandle<RBTreeNode> node)176 void JSAPIHashMap::SetAllRBTreeNode(JSThread *thread, JSHandle<JSAPIHashMap> hashMap, JSHandle<RBTreeNode> node)
177 {
178     ASSERT(node.GetTaggedValue().IsRBTreeNode());
179     JSMutableHandle<JSTaggedValue> key(thread, node->GetKey(thread));
180     JSMutableHandle<JSTaggedValue> value(thread, node->GetValue(thread));
181     if (!hashMap->Replace(thread, key.GetTaggedValue(), value.GetTaggedValue())) {
182         Set(thread, hashMap, key, value);
183     }
184     JSMutableHandle<RBTreeNode> left(thread, node->GetLeft(thread));
185     if (!left.GetTaggedValue().IsHole()) {
186         SetAllRBTreeNode(thread, hashMap, left);
187     }
188     JSMutableHandle<RBTreeNode> right(thread, node->GetRight(thread));
189     if (!right.GetTaggedValue().IsHole()) {
190         SetAllRBTreeNode(thread, hashMap, right);
191     }
192 }
193 
Clear(JSThread * thread)194 void JSAPIHashMap::Clear(JSThread *thread)
195 {
196     TaggedHashArray *hashArray = TaggedHashArray::Cast(GetTable(thread).GetTaggedObject());
197     uint32_t nodeLength = GetSize();
198     if (nodeLength > 0) {
199         hashArray->Clear(thread);
200         SetSize(0);
201     }
202 }
203 
Remove(JSThread * thread,JSHandle<JSAPIHashMap> hashMap,JSTaggedValue key)204 JSTaggedValue JSAPIHashMap::Remove(JSThread *thread, JSHandle<JSAPIHashMap> hashMap, JSTaggedValue key)
205 {
206     if (!TaggedHashArray::IsKey(key)) {
207         return JSTaggedValue::Undefined();
208     }
209 
210     JSHandle<TaggedHashArray> hashArray(thread, hashMap->GetTable(thread));
211     uint32_t nodeNum = hashMap->GetSize();
212     if (nodeNum == 0) {
213         return JSTaggedValue::Undefined();
214     }
215     uint32_t hash = static_cast<uint32_t>(TaggedNode::Hash(thread, key));
216     JSHandle<JSTaggedValue> removeValue(thread, hashArray->RemoveNode(thread, hash, key));
217     if (removeValue->IsHole()) {
218         return JSTaggedValue::Undefined();
219     }
220     hashMap->SetSize(--nodeNum);
221     uint32_t length = hashArray->GetLength();
222     ASSERT_PRINT(length >= TaggedHashArray::DEFAULT_INITIAL_CAPACITY,
223                  "TaggedHashArray length must greater than or equal to the default minimum value");
224 
225     uint32_t index = (length - 1) & hash;
226     JSTaggedValue rootVa = hashArray->Get(thread, index);
227     if (rootVa.IsRBTreeNode()) {
228         uint32_t numTreeNode = RBTreeNode::Count(rootVa);
229         if (numTreeNode < TaggedHashArray::UNTREEIFY_THRESHOLD) {
230             JSHandle<RBTreeNode> root(thread, rootVa);
231             JSHandle<LinkedNode> head = RBTreeNode::Detreeing(thread, root);
232             hashArray->Set(thread, index, head);
233         }
234     }
235     return removeValue.GetTaggedValue();
236 }
237 }
238