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_vector.h"
17
18#include "ecmascript/global_env_constants-inl.h"
19#include "ecmascript/interpreter/interpreter.h"
20#include "ecmascript/js_function.h"
21
22#include <codecvt>
23#include <locale>
24
25namespace panda::ecmascript {
26static const uint32_t MAX_VALUE = 0x7fffffff;
27static const uint32_t MAX_ARRAY_SIZE = MAX_VALUE - 8;
28bool JSAPIVector::Add(JSThread *thread, const JSHandle<JSAPIVector> &vector, const JSHandle<JSTaggedValue> &value)
29{
30    uint32_t length = vector->GetSize();
31    GrowCapacity(thread, vector, length + 1);
32
33    TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
34    ASSERT(!elements->IsDictionaryMode());
35    elements->Set(thread, length, value);
36    vector->SetLength(++length);
37
38    return true;
39}
40
41void JSAPIVector::Insert(JSThread *thread, const JSHandle<JSAPIVector> &vector,
42                         const JSHandle<JSTaggedValue> &value, int32_t index)
43{
44    uint32_t length = vector->GetSize();
45    if (index < 0 || index > static_cast<int32_t>(length)) {
46        THROW_ERROR(thread, ErrorType::RANGE_ERROR, "the index is out-of-bounds");
47    }
48    GrowCapacity(thread, vector, length + 1);
49
50    TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
51    ASSERT(!elements->IsDictionaryMode());
52    for (int32_t i = static_cast<int32_t>(length) - 1; i >= index; i--) {
53        elements->Set(thread, i + 1, elements->Get(i));
54    }
55
56    elements->Set(thread, index, value);
57    vector->SetLength(++length);
58}
59
60void JSAPIVector::SetLength(JSThread *thread, const JSHandle<JSAPIVector> &vector, uint32_t newSize)
61{
62    uint32_t len = vector->GetSize();
63    if (newSize > len) {
64        GrowCapacity(thread, vector, newSize);
65    }
66    vector->SetLength(newSize);
67}
68
69uint32_t JSAPIVector::GetCapacity()
70{
71    TaggedArray *elementData = TaggedArray::Cast(GetElements().GetTaggedObject());
72    ASSERT(!elementData->IsDictionaryMode());
73    return elementData->GetLength();
74}
75
76void JSAPIVector::IncreaseCapacityTo(JSThread *thread, const JSHandle<JSAPIVector> &vector, int32_t newCapacity)
77{
78    if (newCapacity < 0) {
79        THROW_ERROR(thread, ErrorType::RANGE_ERROR, "An incorrect size was set");
80    }
81
82    JSHandle<TaggedArray> elementData(thread, vector->GetElements());
83    ASSERT(!elementData->IsDictionaryMode());
84    uint32_t oldCapacity = elementData->GetLength();
85    uint32_t tempCapacity = static_cast<uint32_t>(newCapacity);
86    if (oldCapacity < tempCapacity) {
87        JSHandle<TaggedArray> newElements =
88            thread->GetEcmaVM()->GetFactory()->CopyArray(elementData, oldCapacity, tempCapacity);
89        vector->SetElements(thread, newElements);
90    }
91}
92
93int32_t JSAPIVector::GetIndexOf(JSThread *thread, const JSHandle<JSAPIVector> &vector,
94                                const JSHandle<JSTaggedValue> &obj)
95{
96    return JSAPIVector::GetIndexFrom(thread, vector, obj, 0);
97}
98
99int32_t JSAPIVector::GetIndexFrom(JSThread *thread, const JSHandle<JSAPIVector> &vector,
100                                  const JSHandle<JSTaggedValue> &obj, int32_t index)
101{
102    TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
103    ASSERT(!elements->IsDictionaryMode());
104    uint32_t length = vector->GetSize();
105    if (index < 0) {
106        index = 0;
107    } else if (index >= static_cast<int32_t>(length)) {
108        THROW_RANGE_ERROR_AND_RETURN(thread, "no-such-element", -1);
109    }
110
111    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
112    for (uint32_t i = static_cast<uint32_t>(index); i < length; i++) {
113        value.Update(JSTaggedValue(elements->Get(i)));
114        if (JSTaggedValue::StrictEqual(thread, obj, value)) {
115            return i;
116        }
117    }
118    return -1;
119}
120
121bool JSAPIVector::IsEmpty() const
122{
123    return GetSize() == 0;
124}
125
126JSTaggedValue JSAPIVector::GetLastElement()
127{
128    uint32_t length = GetSize();
129    if (length == 0) {
130        return JSTaggedValue::Undefined();
131    }
132    TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
133    ASSERT(!elements->IsDictionaryMode());
134    return elements->Get(length - 1);
135}
136
137int32_t JSAPIVector::GetLastIndexOf(JSThread *thread, const JSHandle<JSAPIVector> &vector,
138                                    const JSHandle<JSTaggedValue> &obj)
139{
140    int32_t index = static_cast<int32_t>(vector->GetSize()) - 1;
141    if (index < 0) {
142        return -1; // vector isEmpty, defalut return -1
143    }
144    return JSAPIVector::GetLastIndexFrom(thread, vector, obj, index);
145}
146
147int32_t JSAPIVector::GetLastIndexFrom(JSThread *thread, const JSHandle<JSAPIVector> &vector,
148                                      const JSHandle<JSTaggedValue> &obj, int32_t index)
149{
150    uint32_t length = vector->GetSize();
151    if (index >= static_cast<int32_t>(length)) {
152        THROW_RANGE_ERROR_AND_RETURN(thread, "index-out-of-bounds", -1);
153    } else if (index < 0) {
154        index = 0;
155    }
156    TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
157    ASSERT(!elements->IsDictionaryMode());
158    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
159    for (int32_t i = index; i >= 0; i--) {
160        value.Update(elements->Get(i));
161        if (JSTaggedValue::StrictEqual(thread, obj, value)) {
162            return i;
163        }
164    }
165    return -1;
166}
167
168bool JSAPIVector::Remove(JSThread *thread, const JSHandle<JSAPIVector> &vector, const JSHandle<JSTaggedValue> &obj)
169{
170    int32_t index = GetIndexOf(thread, vector, obj);
171    uint32_t length = vector->GetSize();
172    if (index >= 0) {
173        JSHandle<TaggedArray> elements(thread, vector->GetElements());
174        ASSERT(!elements->IsDictionaryMode());
175        TaggedArray::RemoveElementByIndex(thread, elements, index, length);
176        length--;
177        vector->SetLength(length);
178        return true;
179    }
180    return false;
181}
182
183JSTaggedValue JSAPIVector::RemoveByIndex(JSThread *thread, const JSHandle<JSAPIVector> &vector, int32_t index)
184{
185    uint32_t length = vector->GetSize();
186    if (index < 0 || index >= static_cast<int32_t>(length)) {
187        THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception());
188    }
189    TaggedArray *resElements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
190    ASSERT(!resElements->IsDictionaryMode());
191    JSTaggedValue oldValue = resElements->Get(index);
192
193    if (index >= 0) {
194        JSHandle<TaggedArray> elements(thread, vector->GetElements());
195        ASSERT(!elements->IsDictionaryMode() && length > 0);
196        TaggedArray::RemoveElementByIndex(thread, elements, index, length);
197        vector->SetLength(length - 1);
198    }
199    length--;
200    vector->SetLength(length);
201    return oldValue;
202}
203
204JSTaggedValue JSAPIVector::RemoveByRange(JSThread *thread, const JSHandle<JSAPIVector> &vector,
205                                         int32_t fromIndex, int32_t toIndex)
206{
207    int32_t length = static_cast<int32_t>(vector->GetSize());
208    if (toIndex <= fromIndex) {
209        THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex cannot be less than or equal to toIndex",
210                                     JSTaggedValue::Exception());
211    }
212    if (fromIndex < 0 || fromIndex >= length) {
213        THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex or the toIndex is out-of-bounds",
214                                     JSTaggedValue::Exception());
215    }
216
217    int32_t endIndex = toIndex >= length ? length : toIndex;
218    TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
219    ASSERT(!elements->IsDictionaryMode());
220    int32_t numMoved = length - endIndex;
221    for (int32_t i = 0; i < numMoved; i++) {
222        elements->Set(thread, fromIndex + i, elements->Get(endIndex + i));
223    }
224
225    int32_t newLength = length - (endIndex - fromIndex);
226    elements->Trim(thread, newLength);
227    vector->SetLength(newLength);
228    return JSTaggedValue::True();
229}
230
231JSHandle<JSAPIVector> JSAPIVector::SubVector(JSThread *thread, const JSHandle<JSAPIVector> &vector,
232                                             int32_t fromIndex, int32_t toIndex)
233{
234    int32_t length = static_cast<int32_t>(vector->GetSize());
235    if (fromIndex < 0 || toIndex < 0 ||
236        fromIndex >= length || toIndex >= length) {
237        THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex or the toIndex is out-of-bounds",
238                                     JSHandle<JSAPIVector>());
239    }
240    if (toIndex <= fromIndex) {
241        THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex cannot be less than or equal to toIndex",
242                                     JSHandle<JSAPIVector>());
243    }
244
245    uint32_t newLength = static_cast<uint32_t>(toIndex - fromIndex);
246    JSHandle<JSAPIVector> subVector = thread->GetEcmaVM()->GetFactory()->NewJSAPIVector(newLength);
247    TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
248
249    subVector->SetLength(newLength);
250    for (uint32_t i = 0; i < newLength; i++) {
251        subVector->Set(thread, i, elements->Get(fromIndex + i));
252    }
253
254    return subVector;
255}
256
257JSTaggedValue JSAPIVector::ToString(JSThread *thread, const JSHandle<JSAPIVector> &vector)
258{
259    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
260    std::u16string sepHandle = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes(",");
261
262    uint32_t length = vector->GetSize();
263    std::u16string concatStr;
264    JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Undefined());
265    for (uint32_t k = 0; k < length; k++) {
266        std::u16string nextStr;
267        element.Update(Get(thread, vector, k));
268        if (!element->IsUndefined() && !element->IsNull()) {
269            JSHandle<EcmaString> nextStringHandle = JSTaggedValue::ToString(thread, element);
270            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
271            nextStr = EcmaStringAccessor(nextStringHandle).ToU16String();
272        }
273        if (k > 0) {
274            concatStr.append(sepHandle);
275            concatStr.append(nextStr);
276            continue;
277        }
278        concatStr.append(nextStr);
279    }
280
281    char16_t *char16tData = concatStr.data();
282    auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
283    uint32_t u16strSize = concatStr.size();
284    return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
285}
286
287JSTaggedValue JSAPIVector::ForEach(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle,
288                                   const JSHandle<JSTaggedValue> &callbackFn,
289                                   const JSHandle<JSTaggedValue> &thisArg)
290{
291    JSHandle<JSAPIVector> vector = JSHandle<JSAPIVector>::Cast(thisHandle);
292    uint32_t length = vector->GetSize();
293    JSTaggedValue key = JSTaggedValue::Undefined();
294    JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
295    const uint32_t argsLength = NUM_MANDATORY_JSFUNC_ARGS;
296    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
297
298    for (uint32_t k = 0; k < length; k++) {
299        kValue.Update(Get(thread, vector, k));
300        key = JSTaggedValue(k);
301        EcmaRuntimeCallInfo *info =
302            EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFn, thisArg, undefined, argsLength);
303        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
304        info->SetCallArg(kValue.GetTaggedValue(), key, thisHandle.GetTaggedValue());
305        JSTaggedValue funcResult = JSFunction::Call(info);
306        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
307        if (length != vector->GetSize()) {  // prevent length change
308            length = vector->GetSize();
309        }
310    }
311
312    return JSTaggedValue::Undefined();
313}
314
315JSTaggedValue JSAPIVector::ReplaceAllElements(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle,
316                                              const JSHandle<JSTaggedValue> &callbackFn,
317                                              const JSHandle<JSTaggedValue> &thisArg)
318{
319    JSHandle<JSAPIVector> vector = JSHandle<JSAPIVector>::Cast(thisHandle);
320    uint32_t length = vector->GetSize();
321    JSTaggedValue key = JSTaggedValue::Undefined();
322    JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
323    const uint32_t argsLength = NUM_MANDATORY_JSFUNC_ARGS;
324    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
325
326    for (uint32_t k = 0; k < length; k++) {
327        kValue.Update(Get(thread, vector, k));
328        key = JSTaggedValue(k);
329        EcmaRuntimeCallInfo *info =
330            EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFn, thisArg, undefined, argsLength);
331        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
332        info->SetCallArg(kValue.GetTaggedValue(), key, thisHandle.GetTaggedValue());
333        JSTaggedValue funcResult = JSFunction::Call(info);
334        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
335        if (length != vector->GetSize()) {  // prevent length change
336            length = vector->GetSize();
337            if (k >= length) {
338                break;
339            }
340        }
341        vector->Set(thread, k, funcResult);
342    }
343
344    return JSTaggedValue::Undefined();
345}
346
347void JSAPIVector::GrowCapacity(JSThread *thread, const JSHandle<JSAPIVector> &vector, uint32_t minCapacity)
348{
349    JSHandle<TaggedArray> elementData(thread, vector->GetElements());
350    ASSERT(!elementData->IsDictionaryMode());
351    uint32_t curCapacity = elementData->GetLength();
352    if (minCapacity > curCapacity) {
353        uint32_t oldCapacity = elementData->GetLength();
354        // 2 : 2 Capacity doubled
355        uint32_t newCapacity = oldCapacity * 2;
356        if (newCapacity < minCapacity) {
357            newCapacity = minCapacity;
358        }
359
360        if (newCapacity > MAX_ARRAY_SIZE) {
361            newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? MAX_VALUE : MAX_ARRAY_SIZE;
362        }
363        JSHandle<TaggedArray> newElements =
364            thread->GetEcmaVM()->GetFactory()->CopyArray(elementData, oldCapacity, newCapacity);
365
366        vector->SetElements(thread, newElements);
367    }
368}
369
370JSTaggedValue JSAPIVector::Get(JSThread *thread, const JSHandle<JSAPIVector> &vector, int32_t index)
371{
372    uint32_t len = vector->GetSize();
373    if (index < 0 || index >= static_cast<int32_t>(len)) {
374        THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception());
375    }
376
377    TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
378    return elements->Get(index);
379}
380
381JSTaggedValue JSAPIVector::Set(JSThread *thread, int32_t index, const JSTaggedValue &value)
382{
383    TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
384    elements->Set(thread, index, value);
385    return JSTaggedValue::Undefined();
386}
387
388bool JSAPIVector::Has(const JSTaggedValue &value) const
389{
390    TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
391    uint32_t length = GetSize();
392    if (length == 0) {
393        return false;
394    }
395
396    for (uint32_t i = 0; i < length; i++) {
397        if (JSTaggedValue::SameValue(elements->Get(i), value)) {
398            return true;
399        }
400    }
401    return false;
402}
403
404JSHandle<TaggedArray> JSAPIVector::OwnKeys(JSThread *thread, const JSHandle<JSAPIVector> &obj)
405{
406    return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>::Cast(obj));
407}
408
409JSHandle<TaggedArray> JSAPIVector::OwnEnumKeys(JSThread *thread, const JSHandle<JSAPIVector> &obj)
410{
411    return JSObject::GetOwnEnumPropertyKeys(thread, JSHandle<JSObject>::Cast(obj));
412}
413
414bool JSAPIVector::GetOwnProperty(JSThread *thread, const JSHandle<JSAPIVector> &obj,
415                                 const JSHandle<JSTaggedValue> &key)
416{
417    uint32_t index = 0;
418    if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) {
419        THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false);
420    }
421
422    uint32_t length = obj->GetSize();
423    if (index >= length) {
424        THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false);
425    }
426
427    JSAPIVector::Get(thread, obj, index);
428    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
429    return true;
430}
431
432void JSAPIVector::TrimToCurrentLength(JSThread *thread, const JSHandle<JSAPIVector> &obj)
433{
434    uint32_t length = obj->GetSize();
435    uint32_t capacity = obj->GetCapacity();
436    TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
437    ASSERT(!elements->IsDictionaryMode());
438    if (capacity > length) {
439        elements->Trim(thread, length);
440    }
441}
442
443void JSAPIVector::Clear(JSThread *thread, const JSHandle<JSAPIVector> &obj)
444{
445    uint32_t length = obj->GetLength();
446    JSHandle<TaggedArray> elements(thread, obj->GetElements());
447    ASSERT(!elements->IsDictionaryMode());
448    for (uint32_t i = 0; i < length; ++i) {
449        elements->Set(thread, i, JSTaggedValue::Hole());
450    }
451    obj->SetLength(0);
452}
453
454JSHandle<JSAPIVector> JSAPIVector::Clone(JSThread *thread, const JSHandle<JSAPIVector> &obj)
455{
456    JSHandle<TaggedArray> srcElements(thread, obj->GetElements());
457    auto factory = thread->GetEcmaVM()->GetFactory();
458    JSHandle<JSAPIVector> newVector = factory->NewJSAPIVector(0);
459
460    uint32_t length = obj->GetSize();
461    newVector->SetLength(length);
462
463    JSHandle<TaggedArray> dstElements = factory->NewAndCopyTaggedArray(srcElements, length, length);
464    newVector->SetElements(thread, dstElements);
465    return newVector;
466}
467
468JSTaggedValue JSAPIVector::GetFirstElement(const JSHandle<JSAPIVector> &vector)
469{
470    uint32_t length = vector->GetSize();
471    if (length == 0) {
472        return JSTaggedValue::Undefined();
473    }
474    TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject());
475    return elements->Get(0);
476}
477
478JSTaggedValue JSAPIVector::GetIteratorObj(JSThread *thread, const JSHandle<JSAPIVector> &obj)
479{
480    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
481    JSHandle<JSAPIVectorIterator> iter(factory->NewJSAPIVectorIterator(obj));
482
483    return iter.GetTaggedValue();
484}
485
486OperationResult JSAPIVector::GetProperty(JSThread *thread, const JSHandle<JSAPIVector> &obj,
487                                         const JSHandle<JSTaggedValue> &key)
488{
489    uint32_t length = obj->GetSize();
490    int index = key->GetInt();
491    if (index < 0 || index >= static_cast<int>(length)) {
492        THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds",
493                                     OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
494    }
495
496    return OperationResult(thread, JSAPIVector::Get(thread, obj, index), PropertyMetaData(false));
497}
498
499bool JSAPIVector::SetProperty(JSThread *thread, const JSHandle<JSAPIVector> &obj,
500                              const JSHandle<JSTaggedValue> &key,
501                              const JSHandle<JSTaggedValue> &value)
502{
503    uint32_t length = obj->GetSize();
504    int index = key->GetInt();
505    if (index < 0 || index >= static_cast<int>(length)) {
506        return false;
507    }
508
509    obj->Set(thread, index, value.GetTaggedValue());
510    return true;
511}
512} // namespace panda::ecmascript
513