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