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