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
18 #include "ecmascript/containers/containers_errors.h"
19 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
20 #include "ecmascript/js_object.h"
21 #include "ecmascript/js_tagged_value.h"
22 #include "ecmascript/object_factory.h"
23
24 namespace panda::ecmascript {
25 using ContainerError = containers::ContainerError;
26 using ErrorFlag = containers::ErrorFlag;
Add(JSThread * thread,const JSHandle<JSAPIQueue> & queue,const JSHandle<JSTaggedValue> & value)27 void JSAPIQueue::Add(JSThread *thread, const JSHandle<JSAPIQueue> &queue, const JSHandle<JSTaggedValue> &value)
28 {
29 uint32_t length = queue->GetLength().GetArrayLength();
30 JSHandle<TaggedArray> elements = GrowCapacity(thread, queue, length + 1);
31
32 ASSERT(!elements->IsDictionaryMode());
33 uint32_t tail = queue->GetTail();
34
35 elements->Set(thread, tail, value);
36 queue->SetLength(thread, JSTaggedValue(++length));
37
38 uint32_t elementsSize = elements->GetLength();
39 ASSERT(elementsSize != 0);
40 queue->SetTail((tail + 1) % elementsSize);
41 }
42
GrowCapacity(const JSThread * thread,const JSHandle<JSAPIQueue> & obj,uint32_t capacity)43 JSHandle<TaggedArray> JSAPIQueue::GrowCapacity(const JSThread *thread, const JSHandle<JSAPIQueue> &obj,
44 uint32_t capacity)
45 {
46 JSHandle<TaggedArray> newElements;
47 uint32_t front = obj->GetFront();
48 uint32_t tail = obj->GetTail();
49 JSHandle<TaggedArray> oldElements(thread, obj->GetElements());
50 ASSERT(!oldElements->IsDictionaryMode());
51 uint32_t oldLength = oldElements->GetLength();
52 uint32_t newCapacity = 0;
53 if (oldLength == 0) {
54 newCapacity = ComputeCapacity(capacity);
55 newElements = thread->GetEcmaVM()->GetFactory()->CopyArray(oldElements, oldLength, newCapacity);
56 } else if ((tail + 1) % oldLength == front) {
57 newCapacity = ComputeCapacity(capacity);
58 newElements = thread->GetEcmaVM()->GetFactory()->CopyQueue(oldElements, newCapacity, front, tail);
59 front = 0;
60 tail = oldLength - 1;
61 } else {
62 return oldElements;
63 }
64
65 obj->SetElements(thread, newElements);
66 obj->SetFront(front);
67 obj->SetTail(tail);
68 return newElements;
69 }
70
GetFirst(JSThread * thread,const JSHandle<JSAPIQueue> & queue)71 JSTaggedValue JSAPIQueue::GetFirst(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
72 {
73 if (queue->GetLength().GetArrayLength() < 1) {
74 return JSTaggedValue::Undefined();
75 }
76
77 uint32_t index = queue->GetFront();
78
79 JSHandle<TaggedArray> elements(thread, queue->GetElements());
80 ASSERT(!elements->IsDictionaryMode());
81 return elements->Get(index);
82 }
83
Pop(JSThread * thread,const JSHandle<JSAPIQueue> & queue)84 JSTaggedValue JSAPIQueue::Pop(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
85 {
86 uint32_t length = queue->GetLength().GetArrayLength();
87 if (length < 1) {
88 return JSTaggedValue::Undefined();
89 }
90
91 JSHandle<TaggedArray> elements(thread, queue->GetElements());
92 ASSERT(!elements->IsDictionaryMode());
93 uint32_t front = queue->GetFront();
94
95 JSTaggedValue value = elements->Get(front);
96 queue->SetLength(thread, JSTaggedValue(length - 1));
97 uint32_t elementsSize = elements->GetLength();
98 ASSERT(elementsSize != 0);
99 queue->SetFront((front + 1) % elementsSize);
100
101 return value;
102 }
103
Get(JSThread * thread,const uint32_t index)104 JSTaggedValue JSAPIQueue::Get(JSThread *thread, const uint32_t index)
105 {
106 uint32_t length = GetSize();
107 if (index >= length) {
108 std::ostringstream oss;
109 oss << "The value of \"Get property index\" is out of range. It must be >= 0 && <= "
110 << (length - 1) << ". Received value is: " << index;
111 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
112 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
113 }
114
115 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
116 uint32_t capacity = elements->GetLength();
117 uint32_t front = GetCurrentFront();
118 ASSERT(capacity != 0);
119 uint32_t curIndex = (front + index) % capacity;
120 return elements->Get(curIndex);
121 }
122
Set(JSThread * thread,const uint32_t index,JSTaggedValue value)123 JSTaggedValue JSAPIQueue::Set(JSThread *thread, const uint32_t index, JSTaggedValue value)
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
Has(JSTaggedValue value) const138 bool 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
OwnKeys(JSThread * thread,const JSHandle<JSAPIQueue> & obj)156 JSHandle<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
OwnEnumKeys(JSThread * thread,const JSHandle<JSAPIQueue> & obj)175 JSHandle<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
GetOwnProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key)194 bool 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 (index >= length) {
210 std::ostringstream oss;
211 oss << "The value of \"index\" is out of range. It must be > " << (length - 1)
212 << ". Received value is: " << index;
213 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
214 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
215 }
216
217 obj->Get(thread, index);
218 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
219 return true;
220 }
221
GetProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key)222 OperationResult JSAPIQueue::GetProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
223 const JSHandle<JSTaggedValue> &key)
224 {
225 int length = static_cast<int>(obj->GetLength().GetArrayLength());
226 int index = key->GetInt();
227 if (index < 0 || index >= length) {
228 std::ostringstream oss;
229 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1)
230 << ". Received value is: " << index;
231 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
232 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread,
233 JSTaggedValue::Exception(),
234 PropertyMetaData(false)));
235 }
236
237 return OperationResult(thread, obj->Get(thread, index), PropertyMetaData(false));
238 }
239
SetProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)240 bool JSAPIQueue::SetProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
241 const JSHandle<JSTaggedValue> &key,
242 const JSHandle<JSTaggedValue> &value)
243 {
244 int length = static_cast<int>(obj->GetLength().GetArrayLength());
245 int index = key->GetInt();
246 if (index < 0 || index >= length) {
247 return false;
248 }
249
250 obj->Set(thread, index, value.GetTaggedValue());
251 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
252 return true;
253 }
254
GetArrayLength(JSThread * thread,const JSHandle<JSAPIQueue> & queue)255 uint32_t JSAPIQueue::GetArrayLength(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
256 {
257 uint32_t begin = queue->GetCurrentFront();
258 uint32_t end = queue->GetCurrentTail();
259 JSHandle<TaggedArray> elements(thread, queue->GetElements());
260 ASSERT(!elements->IsDictionaryMode());
261 uint32_t elementsSize = elements->GetLength();
262 ASSERT(elementsSize != 0);
263 uint32_t length = (end - begin + elementsSize) % elementsSize;
264 return length;
265 }
266
GetNextPosition(uint32_t current)267 uint32_t JSAPIQueue::GetNextPosition(uint32_t current)
268 {
269 uint32_t next = 0;
270 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
271 uint32_t elementsSize = elements->GetLength();
272 ASSERT(elementsSize != 0);
273 next = (current + 1) % elementsSize;
274 return next;
275 }
276 } // namespace panda::ecmascript
277