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