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