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