• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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