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().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());
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().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
Pop(JSThread * thread,const JSHandle<JSAPIQueue> & queue)79 JSTaggedValue 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 elements->Set(thread, front, JSTaggedValue::Hole());
95 queue->SetFront((front + 1) % elementsSize);
96
97 return value;
98 }
99
Get(JSThread * thread,const uint32_t index)100 JSTaggedValue JSAPIQueue::Get(JSThread *thread, const uint32_t index)
101 {
102 uint32_t length = GetSize();
103 if (index >= length) {
104 ASSERT(length > 0);
105 std::ostringstream oss;
106 oss << "The value of \"Get property index\" is out of range. It must be >= 0 && <= "
107 << (length - 1) << ". Received value is: " << index;
108 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
109 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
110 }
111
112 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
113 uint32_t capacity = elements->GetLength();
114 uint32_t front = GetCurrentFront();
115 ASSERT(capacity != 0);
116 uint32_t curIndex = (front + index) % capacity;
117 return elements->Get(curIndex);
118 }
119
Set(JSThread * thread,const uint32_t index,JSTaggedValue value)120 JSTaggedValue JSAPIQueue::Set(JSThread *thread, const uint32_t index, JSTaggedValue value)
121 {
122 if (GetLength().GetArrayLength() == 0) {
123 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty");
124 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
125 }
126 if (index < 0 || index >= GetLength().GetArrayLength()) {
127 std::ostringstream oss;
128 oss << "The value of \"Set property index\" is out of range. It must be >= 0 && <= "
129 << (GetLength().GetArrayLength() - 1) << ". Received value is: " << index;
130 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
131 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
132 }
133
134 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
135 elements->Set(thread, index, value);
136 return JSTaggedValue::Undefined();
137 }
138
Has(JSTaggedValue value) const139 bool JSAPIQueue::Has(JSTaggedValue value) const
140 {
141 uint32_t begin = GetCurrentFront();
142 uint32_t end = GetCurrentTail();
143 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
144 uint32_t capacity = elements->GetLength();
145
146 uint32_t index = begin;
147 while (index != end) {
148 if (JSTaggedValue::SameValue(elements->Get(index), value)) {
149 return true;
150 }
151 ASSERT(capacity != 0);
152 index = (index + 1) % capacity;
153 }
154 return false;
155 }
156
OwnKeys(JSThread * thread,const JSHandle<JSAPIQueue> & obj)157 JSHandle<TaggedArray> JSAPIQueue::OwnKeys(JSThread *thread, const JSHandle<JSAPIQueue> &obj)
158 {
159 uint32_t length = obj->GetLength().GetArrayLength();
160
161 JSHandle<TaggedArray> oldElements(thread, obj->GetElements());
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 (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
GetProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key)227 OperationResult 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
SetProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)258 bool 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
GetArrayLength(JSThread * thread,const JSHandle<JSAPIQueue> & queue)273 uint32_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
GetNextPosition(uint32_t current)285 uint32_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