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_queue.h"
17#include "ecmascript/interpreter/fast_runtime_stub-inl.h"
18
19namespace panda::ecmascript {
20using ContainerError = containers::ContainerError;
21using ErrorFlag = containers::ErrorFlag;
22void JSAPIQueue::Add(JSThread *thread, const JSHandle<JSAPIQueue> &queue, const JSHandle<JSTaggedValue> &value)
23{
24    uint32_t length = queue->GetLength().GetArrayLength();
25    JSHandle<TaggedArray> elements = GrowCapacity(thread, queue, length + 1);
26
27    ASSERT(!elements->IsDictionaryMode());
28    uint32_t tail = queue->GetTail();
29
30    elements->Set(thread, tail, value);
31    queue->SetLength(thread, JSTaggedValue(++length));
32
33    uint32_t elementsSize = elements->GetLength();
34    ASSERT(elementsSize != 0);
35    queue->SetTail((tail + 1) % elementsSize);
36}
37
38JSHandle<TaggedArray> JSAPIQueue::GrowCapacity(const JSThread *thread, const JSHandle<JSAPIQueue> &obj,
39                                               uint32_t capacity)
40{
41    JSHandle<TaggedArray> newElements;
42    uint32_t front = obj->GetFront();
43    uint32_t tail = obj->GetTail();
44    JSHandle<TaggedArray> oldElements(thread, obj->GetElements());
45    ASSERT(!oldElements->IsDictionaryMode());
46    uint32_t oldLength = oldElements->GetLength();
47    uint32_t newCapacity = 0;
48    if (oldLength == 0) {
49        newCapacity = ComputeCapacity(capacity);
50        newElements = thread->GetEcmaVM()->GetFactory()->CopyArray(oldElements, oldLength, newCapacity);
51    } else if ((tail + 1) % oldLength == front) {
52        newCapacity = ComputeCapacity(capacity);
53        newElements = thread->GetEcmaVM()->GetFactory()->CopyQueue(oldElements, newCapacity, front, tail);
54        front = 0;
55        tail = oldLength - 1;
56    } else {
57        return oldElements;
58    }
59
60    obj->SetElements(thread, newElements);
61    obj->SetFront(front);
62    obj->SetTail(tail);
63    return newElements;
64}
65
66JSTaggedValue JSAPIQueue::GetFirst(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
67{
68    if (queue->GetLength().GetArrayLength() < 1) {
69        return JSTaggedValue::Undefined();
70    }
71
72    uint32_t index = queue->GetFront();
73
74    JSHandle<TaggedArray> elements(thread, queue->GetElements());
75    ASSERT(!elements->IsDictionaryMode());
76    return elements->Get(index);
77}
78
79JSTaggedValue JSAPIQueue::Pop(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
80{
81    uint32_t length = queue->GetLength().GetArrayLength();
82    if (length < 1) {
83        return JSTaggedValue::Undefined();
84    }
85
86    JSHandle<TaggedArray> elements(thread, queue->GetElements());
87    ASSERT(!elements->IsDictionaryMode());
88    uint32_t front = queue->GetFront();
89
90    JSTaggedValue value = elements->Get(front);
91    queue->SetLength(thread, JSTaggedValue(length - 1));
92    uint32_t elementsSize = elements->GetLength();
93    ASSERT(elementsSize != 0);
94    queue->SetFront((front + 1) % elementsSize);
95
96    return value;
97}
98
99JSTaggedValue JSAPIQueue::Get(JSThread *thread, const uint32_t index)
100{
101    uint32_t length = GetSize();
102    if (index >= length) {
103        ASSERT(length > 0);
104        std::ostringstream oss;
105        oss << "The value of \"Get property index\" is out of range. It must be >= 0 && <= "
106            << (length - 1) << ". Received value is: " << index;
107        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
108        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
109    }
110
111    TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
112    uint32_t capacity = elements->GetLength();
113    uint32_t front = GetCurrentFront();
114    ASSERT(capacity != 0);
115    uint32_t curIndex = (front + index) % capacity;
116    return elements->Get(curIndex);
117}
118
119JSTaggedValue JSAPIQueue::Set(JSThread *thread, const uint32_t index, JSTaggedValue value)
120{
121    if (GetLength().GetArrayLength() == 0) {
122        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
123        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
124    }
125    if (index < 0 || index >= GetLength().GetArrayLength()) {
126        std::ostringstream oss;
127        oss << "The value of \"Set property index\" is out of range. It must be >= 0 && <= "
128            << (GetLength().GetArrayLength() - 1) << ". Received value is: " << index;
129        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
130        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
131    }
132
133    TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
134    elements->Set(thread, index, value);
135    return JSTaggedValue::Undefined();
136}
137
138bool JSAPIQueue::Has(JSTaggedValue value) const
139{
140    uint32_t begin = GetCurrentFront();
141    uint32_t end = GetCurrentTail();
142    TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
143    uint32_t capacity = elements->GetLength();
144
145    uint32_t index = begin;
146    while (index != end) {
147        if (JSTaggedValue::SameValue(elements->Get(index), value)) {
148            return true;
149        }
150        ASSERT(capacity != 0);
151        index = (index + 1) % capacity;
152    }
153    return false;
154}
155
156JSHandle<TaggedArray> JSAPIQueue::OwnKeys(JSThread *thread, const JSHandle<JSAPIQueue> &obj)
157{
158    uint32_t length = obj->GetLength().GetArrayLength();
159
160    JSHandle<TaggedArray> oldElements(thread, obj->GetElements());
161    ASSERT(!oldElements->IsDictionaryMode());
162    uint32_t oldCapacity = oldElements->GetLength();
163    uint32_t newCapacity = ComputeCapacity(oldCapacity);
164    uint32_t front = obj->GetFront();
165    uint32_t tail = obj->GetTail();
166    JSHandle<TaggedArray> newElements =
167        thread->GetEcmaVM()->GetFactory()->CopyQueue(oldElements, newCapacity, front, tail);
168    obj->SetFront(0);
169    obj->SetTail(length);
170    obj->SetElements(thread, newElements);
171
172    return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>::Cast(obj));
173}
174
175JSHandle<TaggedArray> JSAPIQueue::OwnEnumKeys(JSThread *thread, const JSHandle<JSAPIQueue> &obj)
176{
177    uint32_t length = obj->GetLength().GetArrayLength();
178
179    JSHandle<TaggedArray> oldElements(thread, obj->GetElements());
180    ASSERT(!oldElements->IsDictionaryMode());
181    uint32_t oldCapacity = oldElements->GetLength();
182    uint32_t newCapacity = ComputeCapacity(oldCapacity);
183    uint32_t front = obj->GetFront();
184    uint32_t tail = obj->GetTail();
185    JSHandle<TaggedArray> newElements =
186        thread->GetEcmaVM()->GetFactory()->CopyQueue(oldElements, newCapacity, front, tail);
187    obj->SetFront(0);
188    obj->SetTail(length);
189    obj->SetElements(thread, newElements);
190
191    return JSObject::GetOwnEnumPropertyKeys(thread, JSHandle<JSObject>::Cast(obj));
192}
193
194bool JSAPIQueue::GetOwnProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
195                                const JSHandle<JSTaggedValue> &key)
196{
197    uint32_t index = 0;
198    if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) {
199        JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, key.GetTaggedValue());
200        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
201        CString errorMsg =
202            "The type of \"index\" can not obtain attributes of no-number type. Received value is: "
203            + ConvertToString(*result);
204        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
205        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
206    }
207
208    uint32_t length = obj->GetLength().GetArrayLength();
209    if (length == 0) {
210        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
211        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
212    }
213    if (index >= length) {
214        ASSERT(length > 0);
215        std::ostringstream oss;
216        oss << "The value of \"index\" is out of range. It must be > " << (length - 1)
217            << ". Received value is: " << index;
218        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
219        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
220    }
221
222    obj->Get(thread, index);
223    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
224    return true;
225}
226
227OperationResult JSAPIQueue::GetProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
228                                        const JSHandle<JSTaggedValue> &key)
229{
230    int32_t length = static_cast<int32_t>(obj->GetSize());
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 index = indexKey->GetInt();
245    if (index < 0 || index >= length) {
246        std::ostringstream oss;
247        oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1)
248            << ". Received value is: " << index;
249        JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
250        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread,
251                                                                        JSTaggedValue::Exception(),
252                                                                        PropertyMetaData(false)));
253    }
254
255    return OperationResult(thread, obj->Get(thread, static_cast<uint32_t>(index)), PropertyMetaData(false));
256}
257
258bool JSAPIQueue::SetProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
259                             const JSHandle<JSTaggedValue> &key,
260                             const JSHandle<JSTaggedValue> &value)
261{
262    uint32_t length = obj->GetSize();
263    double index = key->GetNumber();
264    if (index < 0 || static_cast<uint32_t>(index) >= length) {
265        return false;
266    }
267
268    obj->Set(thread, static_cast<uint32_t>(index), value.GetTaggedValue());
269    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
270    return true;
271}
272
273uint32_t JSAPIQueue::GetArrayLength(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
274{
275    uint32_t begin = queue->GetCurrentFront();
276    uint32_t end = queue->GetCurrentTail();
277    JSHandle<TaggedArray> elements(thread, queue->GetElements());
278    ASSERT(!elements->IsDictionaryMode());
279    uint32_t elementsSize = elements->GetLength();
280    ASSERT(elementsSize != 0);
281    uint32_t length = (end - begin + elementsSize) % elementsSize;
282    return length;
283}
284
285uint32_t JSAPIQueue::GetNextPosition(uint32_t current)
286{
287    uint32_t next = 0;
288    TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
289    uint32_t elementsSize = elements->GetLength();
290    ASSERT(elementsSize != 0);
291    next = (current + 1) % elementsSize;
292    return next;
293}
294} // namespace panda::ecmascript
295