1 /*
2 * Copyright (c) 2022-2024 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_linked_list.h"
17
18 #include "ecmascript/containers/containers_errors.h"
19 namespace panda::ecmascript {
20 using ContainerError = containers::ContainerError;
21 using ErrorFlag = containers::ErrorFlag;
Insert(JSThread * thread,const JSHandle<JSAPILinkedList> & list,const JSHandle<JSTaggedValue> & value,const int index)22 JSTaggedValue JSAPILinkedList::Insert(JSThread *thread, const JSHandle<JSAPILinkedList> &list,
23 const JSHandle<JSTaggedValue> &value, const int index)
24 {
25 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
26 int nodeLength = doubleList->Length();
27 if (index < 0 || index > nodeLength) {
28 std::ostringstream oss;
29 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << nodeLength
30 << ". Received value is: " << index;
31 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
32 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
33 }
34 JSTaggedValue newList = TaggedDoubleList::Insert(thread, doubleList, value, index);
35 list->SetDoubleList(thread, newList);
36 return JSTaggedValue::True();
37 }
38
Clear(JSThread * thread)39 void JSAPILinkedList::Clear(JSThread *thread)
40 {
41 TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList(thread).GetTaggedObject());
42 if (doubleList->NumberOfNodes() > 0) {
43 doubleList->Clear(thread);
44 }
45 }
46
Clone(JSThread * thread,const JSHandle<JSAPILinkedList> & list)47 JSHandle<JSAPILinkedList> JSAPILinkedList::Clone(JSThread *thread, const JSHandle<JSAPILinkedList> &list)
48 {
49 JSTaggedValue doubleListTaggedValue = list->GetDoubleList(thread);
50 JSHandle<TaggedDoubleList> srcDoubleList(thread, doubleListTaggedValue);
51 JSHandle<TaggedArray> srcTaggedArray(thread, doubleListTaggedValue);
52 ASSERT(!srcDoubleList->IsDictionaryMode());
53 uint32_t numberOfNodes = static_cast<uint32_t>(srcDoubleList->NumberOfNodes());
54 uint32_t numberOfDeletedNodes = static_cast<uint32_t>(srcDoubleList->NumberOfDeletedNodes());
55 uint32_t effectiveCapacity = TaggedDoubleList::ELEMENTS_START_INDEX +
56 (numberOfNodes + numberOfDeletedNodes + 1) * TaggedDoubleList::ENTRY_SIZE;
57 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
58 JSHandle<JSAPILinkedList> newLinkedList = factory->NewJSAPILinkedList();
59 JSHandle<TaggedArray> dstTaggedArray = factory->NewAndCopyTaggedArray(srcTaggedArray,
60 effectiveCapacity, effectiveCapacity);
61 newLinkedList->SetDoubleList(thread, dstTaggedArray.GetTaggedValue());
62 return newLinkedList;
63 }
64
RemoveFirst(JSThread * thread,const JSHandle<JSAPILinkedList> & list)65 JSTaggedValue JSAPILinkedList::RemoveFirst(JSThread *thread, const JSHandle<JSAPILinkedList> &list)
66 {
67 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
68 int nodeLength = doubleList->Length();
69 if (nodeLength <= 0) {
70 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::IS_EMPTY_ERROR, "Container is empty");
71 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
72 }
73 return doubleList->RemoveFirst(thread);
74 }
75
RemoveLast(JSThread * thread,const JSHandle<JSAPILinkedList> & list)76 JSTaggedValue JSAPILinkedList::RemoveLast(JSThread *thread, const JSHandle<JSAPILinkedList> &list)
77 {
78 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
79 int nodeLength = doubleList->Length();
80 if (nodeLength <= 0) {
81 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::IS_EMPTY_ERROR, "Container is empty");
82 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
83 }
84 return doubleList->RemoveLast(thread);
85 }
86
RemoveByIndex(JSThread * thread,JSHandle<JSAPILinkedList> & list,const int index)87 JSTaggedValue JSAPILinkedList::RemoveByIndex(JSThread *thread, JSHandle<JSAPILinkedList> &list, const int index)
88 {
89 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
90 int nodeLength = doubleList->Length();
91 if (nodeLength <= 0) {
92 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
93 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
94 }
95 if (index < 0 || index >= nodeLength) {
96 int size = (nodeLength > 0) ? (nodeLength - 1) : 0;
97 std::ostringstream oss;
98 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << size
99 << ". Received value is: " << index;
100 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
101 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
102 }
103 return doubleList->RemoveByIndex(thread, index);
104 }
105
Remove(JSThread * thread,const JSTaggedValue & element)106 JSTaggedValue JSAPILinkedList::Remove(JSThread *thread, const JSTaggedValue &element)
107 {
108 TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList(thread).GetTaggedObject());
109 int nodeLength = doubleList->Length();
110 if (nodeLength < 0) {
111 return JSTaggedValue::False();
112 }
113 return doubleList->Remove(thread, element);
114 }
115
RemoveFirstFound(JSThread * thread,JSHandle<JSAPILinkedList> & list,const JSTaggedValue & element)116 JSTaggedValue JSAPILinkedList::RemoveFirstFound(JSThread *thread, JSHandle<JSAPILinkedList> &list,
117 const JSTaggedValue &element)
118 {
119 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
120 int nodeLength = doubleList->Length();
121 if (nodeLength <= 0) {
122 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::IS_EMPTY_ERROR, "Container is empty");
123 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
124 }
125 return TaggedDoubleList::RemoveFirstFound(thread, doubleList, element);
126 }
127
RemoveLastFound(JSThread * thread,JSHandle<JSAPILinkedList> & list,const JSTaggedValue & element)128 JSTaggedValue JSAPILinkedList::RemoveLastFound(JSThread *thread, JSHandle<JSAPILinkedList> &list,
129 const JSTaggedValue &element)
130 {
131 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
132 int nodeLength = doubleList->Length();
133 if (nodeLength < 0) {
134 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::IS_EMPTY_ERROR, "Container is empty");
135 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
136 }
137 return TaggedDoubleList::RemoveLastFound(thread, doubleList, element);
138 }
139
Add(JSThread * thread,const JSHandle<JSAPILinkedList> & list,const JSHandle<JSTaggedValue> & value)140 void JSAPILinkedList::Add(JSThread *thread, const JSHandle<JSAPILinkedList> &list, const JSHandle<JSTaggedValue> &value)
141 {
142 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
143 JSTaggedValue newLinkedList = TaggedDoubleList::Add(thread, doubleList, value);
144 list->SetDoubleList(thread, newLinkedList);
145 }
146
AddFirst(JSThread * thread,const JSHandle<JSAPILinkedList> & list,const JSHandle<JSTaggedValue> & value)147 void JSAPILinkedList::AddFirst(JSThread *thread, const JSHandle<JSAPILinkedList> &list,
148 const JSHandle<JSTaggedValue> &value)
149 {
150 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
151 JSTaggedValue newLinkedList = TaggedDoubleList::AddFirst(thread, doubleList, value);
152 list->SetDoubleList(thread, newLinkedList);
153 }
154
GetFirst(const JSThread * thread)155 JSTaggedValue JSAPILinkedList::GetFirst(const JSThread *thread)
156 {
157 JSTaggedValue res = TaggedDoubleList::Cast(GetDoubleList(thread).GetTaggedObject())->GetFirst(thread);
158 if (res.IsHole()) {
159 return JSTaggedValue::Undefined();
160 }
161 return res;
162 }
163
GetLast(const JSThread * thread)164 JSTaggedValue JSAPILinkedList::GetLast(const JSThread *thread)
165 {
166 JSTaggedValue res = TaggedDoubleList::Cast(GetDoubleList(thread).GetTaggedObject())->GetLast(thread);
167 if (res.IsHole()) {
168 return JSTaggedValue::Undefined();
169 }
170 return res;
171 }
172
Set(JSThread * thread,const JSHandle<JSAPILinkedList> & list,const int index,const JSHandle<JSTaggedValue> & value)173 JSTaggedValue JSAPILinkedList::Set(JSThread *thread, const JSHandle<JSAPILinkedList> &list,
174 const int index, const JSHandle<JSTaggedValue> &value)
175 {
176 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
177 int nodeLength = doubleList->Length();
178 if (nodeLength <= 0) {
179 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
180 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
181 }
182 if (index < 0 || index >= nodeLength) {
183 std::ostringstream oss;
184 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (nodeLength - 1)
185 << ". Received value is: " << index;
186 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
187 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
188 }
189 TaggedDoubleList::Set(thread, doubleList, index, value);
190 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
191 return value.GetTaggedValue();
192 }
193
Has(const JSThread * thread,const JSTaggedValue & element)194 bool JSAPILinkedList::Has(const JSThread *thread, const JSTaggedValue &element)
195 {
196 TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList(thread).GetTaggedObject());
197 return doubleList->Has(thread, element);
198 }
199
Get(const JSThread * thread,const int index)200 JSTaggedValue JSAPILinkedList::Get(const JSThread *thread, const int index)
201 {
202 TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList(thread).GetTaggedObject());
203 int nodeLength = doubleList->Length();
204 if (index < 0 || index >= nodeLength) {
205 return JSTaggedValue::Undefined();
206 }
207 return doubleList->Get(thread, index);
208 }
209
GetIndexOf(const JSThread * thread,const JSTaggedValue & element)210 JSTaggedValue JSAPILinkedList::GetIndexOf(const JSThread *thread, const JSTaggedValue &element)
211 {
212 TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList(thread).GetTaggedObject());
213 return JSTaggedValue(doubleList->GetIndexOf(thread, element));
214 }
215
GetLastIndexOf(const JSThread * thread,const JSTaggedValue & element)216 JSTaggedValue JSAPILinkedList::GetLastIndexOf(const JSThread *thread, const JSTaggedValue &element)
217 {
218 TaggedDoubleList *doubleList = TaggedDoubleList::Cast(GetDoubleList(thread).GetTaggedObject());
219 return JSTaggedValue(doubleList->GetLastIndexOf(thread, element));
220 }
221
ConvertToArray(const JSThread * thread,const JSHandle<JSAPILinkedList> & list)222 JSTaggedValue JSAPILinkedList::ConvertToArray(const JSThread *thread, const JSHandle<JSAPILinkedList> &list)
223 {
224 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
225 return TaggedDoubleList::ConvertToArray(thread, doubleList);
226 }
227
OwnKeys(JSThread * thread,const JSHandle<JSAPILinkedList> & list)228 JSHandle<TaggedArray> JSAPILinkedList::OwnKeys(JSThread *thread, const JSHandle<JSAPILinkedList> &list)
229 {
230 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread).GetTaggedObject());
231 return TaggedDoubleList::OwnKeys(thread, doubleList);
232 }
233
GetOwnProperty(JSThread * thread,const JSHandle<JSAPILinkedList> & list,const JSHandle<JSTaggedValue> & key)234 bool JSAPILinkedList::GetOwnProperty(JSThread *thread, const JSHandle<JSAPILinkedList> &list,
235 const JSHandle<JSTaggedValue> &key)
236 {
237 uint32_t index = 0;
238 if (UNLIKELY(!JSTaggedValue::ToElementIndex(thread, key.GetTaggedValue(), &index))) {
239 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, key.GetTaggedValue());
240 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
241 CString errorMsg =
242 "The type of \"index\" can not obtain attributes of no-number type. Received value is: "
243 + ConvertToString(thread, *result);
244 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
245 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
246 }
247 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
248 uint32_t length = static_cast<uint32_t>(doubleList->Length());
249 if (length == 0) {
250 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
251 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
252 }
253 if (index >= length) {
254 ASSERT(length > 0);
255 std::ostringstream oss;
256 oss << "The value of \"index\" is out of range. It must be > " << (length - 1)
257 << ". Received value is: " << index;
258 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
259 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
260 }
261
262 list->Get(thread, index);
263 return true;
264 }
265
GetProperty(JSThread * thread,const JSHandle<JSAPILinkedList> & list,const JSHandle<JSTaggedValue> & key)266 OperationResult JSAPILinkedList::GetProperty(JSThread *thread, const JSHandle<JSAPILinkedList> &list,
267 const JSHandle<JSTaggedValue> &key)
268 {
269 JSHandle<TaggedDoubleList> doubleList(thread, list->GetDoubleList(thread));
270 int nodeLength = doubleList->Length();
271 JSHandle<JSTaggedValue> indexKey = key;
272 if (indexKey->IsDouble()) {
273 // Math.floor(1) will produce TaggedDouble, we need to cast into TaggedInt
274 // For integer which is greater than INT32_MAX, it will remain TaggedDouble
275 indexKey = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(indexKey->GetDouble()));
276 }
277 if (!indexKey->IsInt()) {
278 CString errorMsg = "The type of \"index\" must be small integer.";
279 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
280 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error,
281 OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
282 }
283
284 int index = indexKey->GetInt();
285 if (index < 0 || index >= nodeLength) {
286 std::ostringstream oss;
287 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (nodeLength - 1)
288 << ". Received value is: " << index;
289 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
290 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread,
291 JSTaggedValue::Exception(),
292 PropertyMetaData(false)));
293 }
294
295 return OperationResult(thread, doubleList->Get(thread, index), PropertyMetaData(false));
296 }
297
SetProperty(JSThread * thread,const JSHandle<JSAPILinkedList> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)298 bool JSAPILinkedList::SetProperty(JSThread *thread, const JSHandle<JSAPILinkedList> &obj,
299 const JSHandle<JSTaggedValue> &key,
300 const JSHandle<JSTaggedValue> &value)
301 {
302 JSHandle<TaggedDoubleList> doubleList(thread, obj->GetDoubleList(thread));
303 int nodeLength = doubleList->Length();
304 JSHandle<JSTaggedValue> indexKey = key;
305 if (indexKey->IsDouble()) {
306 indexKey = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(indexKey->GetDouble()));
307 }
308 if (!indexKey->IsInt()) {
309 return false;
310 }
311 int index = indexKey->GetInt();
312 if (index < 0 || index >= nodeLength) {
313 return false;
314 }
315
316 TaggedDoubleList::Set(thread, doubleList, index, value);
317 return true;
318 }
319 } // namespace panda::ecmascript
320