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