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_plain_array.h"
17
18 #include "ecmascript/containers/containers_errors.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/js_function.h"
21 #include <codecvt>
22
23 namespace panda::ecmascript {
24 using ContainerError = containers::ContainerError;
25 using ErrorFlag = containers::ErrorFlag;
Add(JSThread * thread,const JSHandle<JSAPIPlainArray> & obj,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> value)26 void JSAPIPlainArray::Add(JSThread *thread, const JSHandle<JSAPIPlainArray> &obj, JSHandle<JSTaggedValue> key,
27 JSHandle<JSTaggedValue> value)
28 {
29 JSHandle<TaggedArray> keyArray(thread, obj->GetKeys(thread));
30 JSHandle<TaggedArray> valueArray(thread, obj->GetValues(thread));
31 uint32_t size = obj->GetLength();
32 int32_t index = obj->BinarySearch(thread, *keyArray, 0, size, key.GetTaggedValue().GetNumber());
33 if (index >= 0) {
34 keyArray->Set(thread, index, key);
35 valueArray->Set(thread, index, value);
36 return;
37 }
38 index ^= 0xFFFFFFFF;
39 if (index < static_cast<int32_t>(size)) {
40 obj->AdjustArray(thread, *keyArray, index, size, true);
41 obj->AdjustArray(thread, *valueArray, index, size, true);
42 }
43 uint32_t capacity = valueArray->GetLength();
44 if (size + 1 >= capacity) {
45 uint32_t newCapacity = capacity << 1U;
46 keyArray =
47 thread->GetEcmaVM()->GetFactory()->CopyArray(keyArray, capacity, newCapacity);
48 valueArray =
49 thread->GetEcmaVM()->GetFactory()->CopyArray(valueArray, capacity, newCapacity);
50 obj->SetKeys(thread, keyArray);
51 obj->SetValues(thread, valueArray);
52 }
53 keyArray->Set(thread, index, key);
54 valueArray->Set(thread, index, value);
55 size++;
56 obj->SetLength(size);
57 }
58
CreateSlot(const JSThread * thread,const uint32_t capacity)59 JSHandle<TaggedArray> JSAPIPlainArray::CreateSlot(const JSThread *thread, const uint32_t capacity)
60 {
61 ASSERT_PRINT(capacity > 0, "size must be a non-negative integer");
62 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
63 JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(capacity, JSTaggedValue::Hole());
64 return taggedArray;
65 }
66
AdjustForward(JSThread * thread,int32_t index,int32_t forwardSize)67 bool JSAPIPlainArray::AdjustForward(JSThread *thread, int32_t index, int32_t forwardSize)
68 {
69 uint32_t size = GetLength();
70 TaggedArray *keys = TaggedArray::Cast(GetKeys(thread).GetTaggedObject());
71 TaggedArray *values = TaggedArray::Cast(GetValues(thread).GetTaggedObject());
72 AdjustPrimitiveArray(keys, index + forwardSize, index);
73 AdjustArray(thread, values, index + forwardSize, index, false);
74 size = size - static_cast<uint32_t>(forwardSize);
75 SetLength(size);
76 return true;
77 }
78
AdjustPrimitiveArray(TaggedArray * srcArray,int32_t fromIndex,int32_t toIndex)79 void JSAPIPlainArray::AdjustPrimitiveArray(TaggedArray *srcArray, int32_t fromIndex, int32_t toIndex)
80 {
81 uint32_t size = GetLength();
82 auto srcPtr = reinterpret_cast<JSTaggedType *>(
83 ToUintPtr(srcArray->GetData()) + fromIndex * JSTaggedValue::TaggedTypeSize());
84 auto dstPtr = reinterpret_cast<JSTaggedType *>(
85 ToUintPtr(srcArray->GetData()) + toIndex * JSTaggedValue::TaggedTypeSize());
86 // move Array element from srcPtr to dstPtr
87 for (uint32_t count = size - fromIndex; count > 0; --count) {
88 *dstPtr = *srcPtr;
89 ++srcPtr;
90 ++dstPtr;
91 }
92 for (uint32_t count = fromIndex - toIndex; count > 0; --count) {
93 *dstPtr = JSTaggedValue::Hole().GetRawData();
94 ++dstPtr;
95 }
96 }
97
AdjustArray(JSThread * thread,TaggedArray * srcArray,int32_t fromIndex,int32_t toIndex,bool direction)98 void JSAPIPlainArray::AdjustArray(JSThread *thread, TaggedArray *srcArray, int32_t fromIndex,
99 int32_t toIndex, bool direction)
100 {
101 uint32_t size = GetLength();
102 ASSERT(size > 0);
103 uint32_t idx = size - 1;
104 if (direction) {
105 while (fromIndex < toIndex) {
106 JSTaggedValue value = srcArray->Get(thread, idx);
107 srcArray->Set(thread, idx + 1, value);
108 idx--;
109 fromIndex++;
110 }
111 } else {
112 if (srcArray->IsYoungAndNotMarking(thread)) {
113 AdjustPrimitiveArray(srcArray, fromIndex, toIndex);
114 } else {
115 auto srcPtr = reinterpret_cast<JSTaggedType *>(
116 ToUintPtr(srcArray->GetData()) + fromIndex * JSTaggedValue::TaggedTypeSize());
117 uint32_t dstIndex = toIndex;
118 for (uint32_t count = size - fromIndex; count > 0; --count) {
119 srcArray->Set(thread, dstIndex, JSTaggedValue(*srcPtr));
120 ++srcPtr;
121 ++dstIndex;
122 }
123 for (uint32_t count = fromIndex - toIndex; count > 0; --count) {
124 srcArray->Set(thread, dstIndex, JSTaggedValue::Hole());
125 ++dstIndex;
126 }
127 }
128 }
129 }
130
BinarySearch(JSThread * thread,TaggedArray * array,int32_t fromIndex,int32_t toIndex,int32_t key)131 int32_t JSAPIPlainArray::BinarySearch(JSThread *thread, TaggedArray *array, int32_t fromIndex, int32_t toIndex,
132 int32_t key)
133 {
134 int32_t low = fromIndex;
135 int32_t high = toIndex - 1;
136 while (low <= high) {
137 int32_t mid = static_cast<int32_t>(static_cast<uint32_t>(low + high) >> 1U);
138 int32_t midVal = static_cast<int32_t>(array->Get(thread, mid).GetNumber());
139 if (midVal < key) {
140 low = mid + 1;
141 } else {
142 if (midVal <= key) {
143 return mid;
144 }
145 high = mid - 1;
146 }
147 }
148 return -(low + 1);
149 }
150
Clear(JSThread * thread)151 void JSAPIPlainArray::Clear(JSThread *thread)
152 {
153 TaggedArray *keys = TaggedArray::Cast(GetKeys(thread).GetTaggedObject());
154 TaggedArray *values = TaggedArray::Cast(GetValues(thread).GetTaggedObject());
155 uint32_t size = GetLength();
156 for (uint32_t index = 0; index < size; index++) {
157 keys->Set(thread, index, JSTaggedValue::Hole());
158 values->Set(thread, index, JSTaggedValue::Hole());
159 }
160 SetLength(0);
161 }
162
RemoveRangeFrom(JSThread * thread,int32_t index,int32_t batchSize)163 JSTaggedValue JSAPIPlainArray::RemoveRangeFrom(JSThread *thread, int32_t index, int32_t batchSize)
164 {
165 int32_t size = static_cast<int32_t>(GetLength());
166 if (size <= 0) {
167 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
168 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
169 }
170 if (index < 0 || index >= size) {
171 std::ostringstream oss;
172 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1)
173 << ". Received value is: " << index;
174 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
175 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
176 }
177 if (batchSize < 1) {
178 std::ostringstream oss;
179 oss << "The value of \"size\" is out of range. It must be > 0" << ". Received value is: " << batchSize;
180 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
181 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
182 }
183 int32_t safeSize = (size - (index + batchSize)) < 0 ? size - index : batchSize;
184 AdjustForward(thread, index, safeSize);
185 return JSTaggedValue(safeSize);
186 }
187
Set(JSThread * thread,const JSHandle<JSAPIPlainArray> & obj,const uint32_t index,JSTaggedValue value)188 JSTaggedValue JSAPIPlainArray::Set(JSThread *thread, const JSHandle<JSAPIPlainArray> &obj,
189 const uint32_t index, JSTaggedValue value)
190 {
191 JSHandle<JSTaggedValue> key(thread, JSTaggedValue(index));
192 JSHandle<JSTaggedValue> valueHandle(thread, value);
193 JSAPIPlainArray::Add(thread, obj, key, valueHandle);
194 return JSTaggedValue::Undefined();
195 }
196
GetOwnProperty(JSThread * thread,const JSHandle<JSAPIPlainArray> & obj,const JSHandle<JSTaggedValue> & key)197 bool JSAPIPlainArray::GetOwnProperty(JSThread *thread, const JSHandle<JSAPIPlainArray> &obj,
198 const JSHandle<JSTaggedValue> &key)
199 {
200 TaggedArray *keyArray = TaggedArray::Cast(obj->GetKeys(thread).GetTaggedObject());
201 uint32_t size = obj->GetLength();
202 if (size == 0) {
203 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
204 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
205 }
206 int32_t index = obj->BinarySearch(thread, keyArray, 0, size, key.GetTaggedValue().GetInt());
207 if (index < 0 || index >= static_cast<int32_t>(size)) {
208 ASSERT(size > 0);
209 std::ostringstream oss;
210 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1)
211 << ". Received value is: " << index;
212 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
213 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
214 }
215
216 obj->Get(thread, key.GetTaggedValue());
217 return true;
218 }
219
GetProperty(JSThread * thread,const JSHandle<JSAPIPlainArray> & obj,const JSHandle<JSTaggedValue> & key)220 OperationResult JSAPIPlainArray::GetProperty(JSThread *thread, const JSHandle<JSAPIPlainArray> &obj,
221 const JSHandle<JSTaggedValue> &key)
222 {
223 TaggedArray *keyArray = TaggedArray::Cast(obj->GetKeys(thread).GetTaggedObject());
224 uint32_t size = obj->GetLength();
225 if (size == 0) {
226 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
227 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread,
228 JSTaggedValue::Exception(),
229 PropertyMetaData(false)));
230 }
231 JSHandle<JSTaggedValue> indexKey = key;
232 if (indexKey->IsDouble()) {
233 // Math.floor(1) will produce TaggedDouble, we need to cast into TaggedInt
234 // For integer which is greater than INT32_MAX, it will remain TaggedDouble
235 indexKey = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(indexKey->GetDouble()));
236 }
237 if (!indexKey->IsInt()) {
238 CString errorMsg = "The type of \"index\" must be small integer.";
239 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
240 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error,
241 OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
242 }
243
244 int keyVal = indexKey->GetInt();
245 int32_t index = obj->BinarySearch(thread, keyArray, 0, size, keyVal);
246 if (index < 0 || index >= static_cast<int32_t>(size)) {
247 std::ostringstream oss;
248 ASSERT(size > 0);
249 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1)
250 << ". Received value is: " << index;
251 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
252 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread,
253 JSTaggedValue::Exception(),
254 PropertyMetaData(false)));
255 }
256
257 return OperationResult(thread, obj->Get(thread, JSTaggedValue(index)), PropertyMetaData(false));
258 }
259
SetProperty(JSThread * thread,const JSHandle<JSAPIPlainArray> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)260 bool JSAPIPlainArray::SetProperty(JSThread *thread, const JSHandle<JSAPIPlainArray> &obj,
261 const JSHandle<JSTaggedValue> &key,
262 const JSHandle<JSTaggedValue> &value)
263 {
264 TaggedArray *keyArray = TaggedArray::Cast(obj->GetKeys(thread).GetTaggedObject());
265 uint32_t size = obj->GetLength();
266 JSHandle<JSTaggedValue> indexKey = key;
267 if (indexKey->IsDouble()) {
268 // Math.floor(1) will produce TaggedDouble, we need to cast into TaggedInt
269 // For integer which is greater than INT32_MAX, it will remain TaggedDouble
270 indexKey = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(indexKey->GetDouble()));
271 }
272 if (!indexKey->IsInt()) {
273 return false;
274 }
275 int32_t index = obj->BinarySearch(thread, keyArray, 0, size, indexKey->GetInt());
276 if (index < 0 || index >= static_cast<int32_t>(size)) {
277 return false;
278 }
279
280 obj->Set(thread, obj, index, value.GetTaggedValue());
281 return true;
282 }
283
Clone(JSThread * thread,const JSHandle<JSAPIPlainArray> & obj)284 JSHandle<JSAPIPlainArray> JSAPIPlainArray::Clone(JSThread *thread, const JSHandle<JSAPIPlainArray> &obj)
285 {
286 JSHandle<TaggedArray> srckeys(thread, obj->GetKeys(thread));
287 JSHandle<TaggedArray> srcvalues(thread, obj->GetValues(thread));
288 auto factory = thread->GetEcmaVM()->GetFactory();
289 JSHandle<JSAPIPlainArray> newPlainArray = factory->NewJSAPIPlainArray(0);
290
291 uint32_t length = obj->GetLength();
292 newPlainArray->SetLength(length);
293 JSHandle<TaggedArray> srcKeyArray(thread, obj->GetKeys(thread));
294 JSHandle<TaggedArray> srcValueArray(thread, obj->GetValues(thread));
295
296 JSHandle<TaggedArray> dstKeyArray = factory->NewAndCopyTaggedArray(srcKeyArray, length, length);
297 JSHandle<TaggedArray> dstValueArray = factory->NewAndCopyTaggedArray(srcValueArray, length, length);
298
299 newPlainArray->SetKeys(thread, dstKeyArray);
300 newPlainArray->SetValues(thread, dstValueArray);
301 return newPlainArray;
302 }
303
Has(JSThread * thread,const int32_t key)304 bool JSAPIPlainArray::Has(JSThread *thread, const int32_t key)
305 {
306 uint32_t size = GetLength();
307 TaggedArray *keyArray = TaggedArray::Cast(GetKeys(thread).GetTaggedObject());
308 int32_t index = BinarySearch(thread, keyArray, 0, size, key);
309 if (index < 0) {
310 return false;
311 }
312 return true;
313 }
314
Get(JSThread * thread,const JSTaggedValue key)315 JSTaggedValue JSAPIPlainArray::Get(JSThread *thread, const JSTaggedValue key)
316 {
317 uint32_t size = GetLength();
318 TaggedArray *keyArray = TaggedArray::Cast(GetKeys(thread).GetTaggedObject());
319 int32_t index = BinarySearch(thread, keyArray, 0, size, key.GetNumber());
320 if (index < 0) {
321 return JSTaggedValue::Undefined();
322 }
323 TaggedArray *values = TaggedArray::Cast(GetValues(thread).GetTaggedObject());
324 return values->Get(thread, index);
325 }
326
GetIteratorObj(JSThread * thread,const JSHandle<JSAPIPlainArray> & obj,IterationKind kind)327 JSHandle<JSTaggedValue> JSAPIPlainArray::GetIteratorObj(JSThread *thread, const JSHandle<JSAPIPlainArray> &obj,
328 IterationKind kind)
329 {
330 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
331 JSHandle<JSTaggedValue> iter =
332 JSHandle<JSTaggedValue>::Cast(factory->NewJSAPIPlainArrayIterator(obj, kind));
333 return iter;
334 }
335
ForEach(JSThread * thread,const JSHandle<JSTaggedValue> & thisHandle,const JSHandle<JSTaggedValue> & callbackFn,const JSHandle<JSTaggedValue> & thisArg)336 JSTaggedValue JSAPIPlainArray::ForEach(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle,
337 const JSHandle<JSTaggedValue> &callbackFn,
338 const JSHandle<JSTaggedValue> &thisArg)
339 {
340 JSAPIPlainArray *plainarray = JSAPIPlainArray::Cast(thisHandle->GetTaggedObject());
341 uint32_t length = plainarray->GetLength();
342 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
343 JSHandle<TaggedArray> keyArray(thread, plainarray->GetKeys(thread));
344 JSHandle<TaggedArray> valueArray(thread, plainarray->GetValues(thread));
345 for (uint32_t k = 0; k < length; k++) {
346 JSTaggedValue kValue = valueArray->Get(thread, k);
347 JSTaggedValue key = keyArray->Get(thread, k);
348 EcmaRuntimeCallInfo *info =
349 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFn, thisArg, undefined, 3); // 3: three args
350 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
351 info->SetCallArg(kValue, key, thisHandle.GetTaggedValue());
352 JSTaggedValue funcResult = JSFunction::Call(info);
353 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
354 }
355 return JSTaggedValue::Undefined();
356 }
357
ToString(JSThread * thread,const JSHandle<JSAPIPlainArray> & plainarray)358 JSTaggedValue JSAPIPlainArray::ToString(JSThread *thread, const JSHandle<JSAPIPlainArray> &plainarray)
359 {
360 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
361 std::u16string sepStr = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes(",");
362 std::u16string colonStr = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes(":");
363
364 uint32_t length = plainarray->GetLength();
365 std::u16string concatStr = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes("");
366 std::u16string concatStrNew = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes("");
367 JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
368 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
369 for (uint32_t k = 0; k < length; k++) {
370 std::u16string valueStr;
371 valueHandle.Update(plainarray->GetValueAt(thread, k));
372 if (!valueHandle->IsUndefined() && !valueHandle->IsNull()) {
373 JSHandle<EcmaString> valueStringHandle = JSTaggedValue::ToString(thread, valueHandle);
374 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
375 valueStr = EcmaStringAccessor(valueStringHandle).ToU16String(thread);
376 }
377
378 std::u16string nextStr;
379 keyHandle.Update(plainarray->GetKeyAt(thread, k));
380 if (!keyHandle->IsUndefined() && !keyHandle->IsNull()) {
381 JSHandle<EcmaString> keyStringHandle = JSTaggedValue::ToString(thread, keyHandle);
382 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
383 nextStr = EcmaStringAccessor(keyStringHandle).ToU16String(thread);
384 }
385
386 nextStr.append(colonStr);
387 nextStr.append(valueStr);
388 if (k > 0) {
389 concatStr.append(sepStr);
390 concatStr.append(nextStr);
391 continue;
392 }
393 concatStr.append(nextStr);
394 }
395
396 char16_t *char16tData = concatStr.data();
397 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
398 uint32_t u16strSize = concatStr.size();
399 return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
400 }
401
GetIndexOfKey(JSThread * thread,int32_t key)402 JSTaggedValue JSAPIPlainArray::GetIndexOfKey(JSThread *thread, int32_t key)
403 {
404 uint32_t size = GetLength();
405 TaggedArray *keyArray = TaggedArray::Cast(GetKeys(thread).GetTaggedObject());
406 int32_t index = BinarySearch(thread, keyArray, 0, size, key);
407 if (index < 0) {
408 return JSTaggedValue(-1);
409 }
410 return JSTaggedValue(index);
411 }
412
TryFastGetIndexOfValue(JSThread * thread,TaggedArray * values,JSTaggedValue value)413 JSTaggedValue JSAPIPlainArray::TryFastGetIndexOfValue(JSThread *thread, TaggedArray *values, JSTaggedValue value)
414 {
415 uint32_t size = GetLength();
416 for (uint32_t i = 0; i < size; ++i) {
417 JSTaggedValue currVal = values->Get(thread, i);
418 if (currVal.IsInt() && (currVal == value)) {
419 return JSTaggedValue(i);
420 }
421 }
422 return JSTaggedValue(-1);
423 }
424
GetIndexOfValue(JSThread * thread,JSTaggedValue value)425 JSTaggedValue JSAPIPlainArray::GetIndexOfValue(JSThread *thread, JSTaggedValue value)
426 {
427 TaggedArray *values = TaggedArray::Cast(GetValues(thread).GetTaggedObject());
428 if (value.IsInt()) {
429 return TryFastGetIndexOfValue(thread, values, value);
430 } else {
431 uint32_t size = GetLength();
432 for (uint32_t i = 0; i < size; ++i) {
433 if (JSTaggedValue::SameValue(thread, values->Get(thread, i), value)) {
434 return JSTaggedValue(i);
435 }
436 }
437 }
438 return JSTaggedValue(-1);
439 }
440
IsEmpty()441 bool JSAPIPlainArray::IsEmpty()
442 {
443 uint32_t length = GetLength();
444 return length == 0;
445 }
446
GetKeyAt(JSThread * thread,int32_t index)447 JSTaggedValue JSAPIPlainArray::GetKeyAt(JSThread *thread, int32_t index)
448 {
449 uint32_t size = GetLength();
450 TaggedArray *keyArray = TaggedArray::Cast(GetKeys(thread).GetTaggedObject());
451 if (index < 0 || index >= static_cast<int32_t>(size)) {
452 return JSTaggedValue::Undefined();
453 }
454 return keyArray->Get(thread, index);
455 }
456
GetValueAt(JSThread * thread,int32_t index)457 JSTaggedValue JSAPIPlainArray::GetValueAt(JSThread *thread, int32_t index)
458 {
459 uint32_t size = GetLength();
460 if (size == 0) {
461 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
462 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
463 }
464 if (index < 0 || index >= static_cast<int32_t>(size)) {
465 ASSERT(size > 0);
466 std::ostringstream oss;
467 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1)
468 << ". Received value is: " << index;
469 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
470 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
471 }
472 TaggedArray *values = TaggedArray::Cast(GetValues(thread).GetTaggedObject());
473 return values->Get(thread, index);
474 }
475
Remove(JSThread * thread,JSTaggedValue key)476 JSTaggedValue JSAPIPlainArray::Remove(JSThread *thread, JSTaggedValue key)
477 {
478 uint32_t size = GetLength();
479 TaggedArray *keyArray = TaggedArray::Cast(GetKeys(thread).GetTaggedObject());
480 int32_t index = BinarySearch(thread, keyArray, 0, size, key.GetNumber());
481 if (index < 0 || index >= static_cast<int32_t>(size)) {
482 return JSTaggedValue::Undefined();
483 }
484 TaggedArray *values = TaggedArray::Cast(GetValues(thread).GetTaggedObject());
485 JSTaggedValue value = values->Get(thread, index);
486 AdjustForward(thread, index, 1); // 1 means the length of array
487 return value;
488 }
489
RemoveAt(JSThread * thread,JSTaggedValue index)490 JSTaggedValue JSAPIPlainArray::RemoveAt(JSThread *thread, JSTaggedValue index)
491 {
492 uint32_t size = GetLength();
493 int32_t seat = index.GetNumber();
494 if (seat < 0 || static_cast<uint32_t>(seat) >= size) {
495 return JSTaggedValue::Undefined();
496 }
497 TaggedArray *values = TaggedArray::Cast(GetValues(thread).GetTaggedObject());
498 JSTaggedValue value = values->Get(thread, seat);
499 AdjustForward(thread, seat, 1);
500 return value;
501 }
502
SetValueAt(JSThread * thread,JSTaggedValue index,JSTaggedValue value)503 bool JSAPIPlainArray::SetValueAt(JSThread *thread, JSTaggedValue index, JSTaggedValue value)
504 {
505 uint32_t size = GetLength();
506 if (size == 0) {
507 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
508 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
509 }
510 int32_t seat = index.GetNumber();
511 if (seat < 0 || static_cast<uint32_t>(seat) >= size) {
512 std::ostringstream oss;
513 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1)
514 << ". Received value is: " << seat;
515 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
516 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
517 }
518 TaggedArray *values = TaggedArray::Cast(GetValues(thread).GetTaggedObject());
519 values->Set(thread, seat, value);
520 return true;
521 }
522 } // namespace panda::ecmascript
523