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
19 namespace panda::ecmascript {
20 using ContainerError = containers::ContainerError;
21 using ErrorFlag = containers::ErrorFlag;
Add(JSThread * thread,const JSHandle<JSAPIQueue> & queue,const JSHandle<JSTaggedValue> & value)22 void JSAPIQueue::Add(JSThread *thread, const JSHandle<JSAPIQueue> &queue, const JSHandle<JSTaggedValue> &value)
23 {
24 uint32_t length = queue->GetLength(thread).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
GrowCapacity(const JSThread * thread,const JSHandle<JSAPIQueue> & obj,uint32_t capacity)38 JSHandle<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(thread));
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
GetFirst(JSThread * thread,const JSHandle<JSAPIQueue> & queue)66 JSTaggedValue JSAPIQueue::GetFirst(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
67 {
68 if (queue->GetLength(thread).GetArrayLength() < 1) {
69 return JSTaggedValue::Undefined();
70 }
71
72 uint32_t index = queue->GetFront();
73
74 JSHandle<TaggedArray> elements(thread, queue->GetElements(thread));
75 ASSERT(!elements->IsDictionaryMode());
76 return elements->Get(thread, index);
77 }
78
Pop(JSThread * thread,const JSHandle<JSAPIQueue> & queue)79 JSTaggedValue JSAPIQueue::Pop(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
80 {
81 uint32_t length = queue->GetLength(thread).GetArrayLength();
82 if (length < 1) {
83 return JSTaggedValue::Undefined();
84 }
85
86 JSHandle<TaggedArray> elements(thread, queue->GetElements(thread));
87 ASSERT(!elements->IsDictionaryMode());
88 uint32_t front = queue->GetFront();
89
90 JSTaggedValue value = elements->Get(thread, 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
Get(JSThread * thread,const uint32_t index)99 JSTaggedValue JSAPIQueue::Get(JSThread *thread, const uint32_t index)
100 {
101 uint32_t length = GetSize(thread);
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(thread).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(thread, curIndex);
117 }
118
Set(JSThread * thread,const uint32_t index,JSTaggedValue value)119 JSTaggedValue JSAPIQueue::Set(JSThread *thread, const uint32_t index, JSTaggedValue value)
120 {
121 if (GetLength(thread).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(thread).GetArrayLength()) {
126 std::ostringstream oss;
127 oss << "The value of \"Set property index\" is out of range. It must be >= 0 && <= "
128 << (GetLength(thread).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(thread).GetTaggedObject());
134 elements->Set(thread, index, value);
135 return JSTaggedValue::Undefined();
136 }
137
Has(const JSThread * thread,JSTaggedValue value) const138 bool JSAPIQueue::Has(const JSThread *thread, JSTaggedValue value) const
139 {
140 uint32_t begin = GetCurrentFront();
141 uint32_t end = GetCurrentTail();
142 TaggedArray *elements = TaggedArray::Cast(GetElements(thread).GetTaggedObject());
143 uint32_t capacity = elements->GetLength();
144
145 uint32_t index = begin;
146 while (index != end) {
147 if (JSTaggedValue::SameValue(thread, elements->Get(thread, 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(thread).GetArrayLength();
159
160 JSHandle<TaggedArray> oldElements(thread, obj->GetElements(thread));
161 uint32_t oldCapacity = oldElements->GetLength();
162 uint32_t newCapacity = ComputeCapacity(oldCapacity);
163 uint32_t front = obj->GetFront();
164 uint32_t tail = obj->GetTail();
165 JSHandle<TaggedArray> newElements =
166 thread->GetEcmaVM()->GetFactory()->CopyQueue(oldElements, newCapacity, front, tail);
167 obj->SetFront(0);
168 obj->SetTail(length);
169 obj->SetElements(thread, newElements);
170
171 return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>::Cast(obj));
172 }
173
OwnEnumKeys(JSThread * thread,const JSHandle<JSAPIQueue> & obj)174 JSHandle<TaggedArray> JSAPIQueue::OwnEnumKeys(JSThread *thread, const JSHandle<JSAPIQueue> &obj)
175 {
176 uint32_t length = obj->GetLength(thread).GetArrayLength();
177
178 JSHandle<TaggedArray> oldElements(thread, obj->GetElements(thread));
179 ASSERT(!oldElements->IsDictionaryMode());
180 uint32_t oldCapacity = oldElements->GetLength();
181 uint32_t newCapacity = ComputeCapacity(oldCapacity);
182 uint32_t front = obj->GetFront();
183 uint32_t tail = obj->GetTail();
184 JSHandle<TaggedArray> newElements =
185 thread->GetEcmaVM()->GetFactory()->CopyQueue(oldElements, newCapacity, front, tail);
186 obj->SetFront(0);
187 obj->SetTail(length);
188 obj->SetElements(thread, newElements);
189
190 return JSObject::GetOwnEnumPropertyKeys(thread, JSHandle<JSObject>::Cast(obj));
191 }
192
GetOwnProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key)193 bool JSAPIQueue::GetOwnProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
194 const JSHandle<JSTaggedValue> &key)
195 {
196 uint32_t index = 0;
197 if (UNLIKELY(!JSTaggedValue::ToElementIndex(thread, key.GetTaggedValue(), &index))) {
198 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, key.GetTaggedValue());
199 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
200 CString errorMsg =
201 "The type of \"index\" can not obtain attributes of no-number type. Received value is: "
202 + ConvertToString(thread, *result);
203 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
204 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
205 }
206
207 uint32_t length = obj->GetLength(thread).GetArrayLength();
208 if (length == 0) {
209 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
210 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
211 }
212 if (index >= length) {
213 ASSERT(length > 0);
214 std::ostringstream oss;
215 oss << "The value of \"index\" is out of range. It must be > " << (length - 1)
216 << ". Received value is: " << index;
217 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
218 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
219 }
220
221 obj->Get(thread, index);
222 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
223 return true;
224 }
225
GetProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key)226 OperationResult JSAPIQueue::GetProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
227 const JSHandle<JSTaggedValue> &key)
228 {
229 int32_t length = static_cast<int32_t>(obj->GetSize(thread));
230 JSHandle<JSTaggedValue> indexKey = key;
231 if (indexKey->IsDouble()) {
232 // Math.floor(1) will produce TaggedDouble, we need to cast into TaggedInt
233 // For integer which is greater than INT32_MAX, it will remain TaggedDouble
234 indexKey = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(indexKey->GetDouble()));
235 }
236 if (!indexKey->IsInt()) {
237 CString errorMsg = "The type of \"index\" must be small integer.";
238 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
239 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error,
240 OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
241 }
242
243 int index = indexKey->GetInt();
244 if (index < 0 || index >= length) {
245 std::ostringstream oss;
246 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1)
247 << ". Received value is: " << index;
248 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
249 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread,
250 JSTaggedValue::Exception(),
251 PropertyMetaData(false)));
252 }
253
254 return OperationResult(thread, obj->Get(thread, static_cast<uint32_t>(index)), PropertyMetaData(false));
255 }
256
SetProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)257 bool JSAPIQueue::SetProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
258 const JSHandle<JSTaggedValue> &key,
259 const JSHandle<JSTaggedValue> &value)
260 {
261 uint32_t length = obj->GetSize(thread);
262 double index = key->GetNumber();
263 if (index < 0 || static_cast<uint32_t>(index) >= length) {
264 return false;
265 }
266
267 obj->Set(thread, static_cast<uint32_t>(index), value.GetTaggedValue());
268 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
269 return true;
270 }
271
GetArrayLength(JSThread * thread,const JSHandle<JSAPIQueue> & queue)272 uint32_t JSAPIQueue::GetArrayLength(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
273 {
274 uint32_t begin = queue->GetCurrentFront();
275 uint32_t end = queue->GetCurrentTail();
276 JSHandle<TaggedArray> elements(thread, queue->GetElements(thread));
277 ASSERT(!elements->IsDictionaryMode());
278 uint32_t elementsSize = elements->GetLength();
279 ASSERT(elementsSize != 0);
280 uint32_t length = (end - begin + elementsSize) % elementsSize;
281 return length;
282 }
283
GetNextPosition(JSThread * thread,uint32_t current)284 uint32_t JSAPIQueue::GetNextPosition(JSThread *thread, uint32_t current)
285 {
286 uint32_t next = 0;
287 TaggedArray *elements = TaggedArray::Cast(GetElements(thread).GetTaggedObject());
288 uint32_t elementsSize = elements->GetLength();
289 ASSERT(elementsSize != 0);
290 next = (current + 1) % elementsSize;
291 return next;
292 }
293 } // namespace panda::ecmascript
294