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 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
160 JSHandle<TaggedArray> keys = factory->NewTaggedArray(length);
161
162 for (uint32_t i = 0; i < length; i++) {
163 keys->Set(thread, i, JSTaggedValue(i));
164 }
165
166 return keys;
167 }
168
GetOwnProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key)169 bool JSAPIQueue::GetOwnProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
170 const JSHandle<JSTaggedValue> &key)
171 {
172 uint32_t index = 0;
173 if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) {
174 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, key.GetTaggedValue());
175 CString errorMsg =
176 "The type of \"index\" can not obtain attributes of no-number type. Received value is: "
177 + ConvertToString(*result);
178 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str());
179 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
180 }
181
182 uint32_t length = obj->GetLength().GetArrayLength();
183 if (index >= length) {
184 std::ostringstream oss;
185 oss << "The value of \"index\" is out of range. It must be > " << (length - 1)
186 << ". Received value is: " << index;
187 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
188 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
189 }
190
191 obj->Get(thread, index);
192 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
193 return true;
194 }
195
GetProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key)196 OperationResult JSAPIQueue::GetProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
197 const JSHandle<JSTaggedValue> &key)
198 {
199 int length = static_cast<int>(obj->GetLength().GetArrayLength());
200 int index = key->GetInt();
201 if (index < 0 || index >= length) {
202 std::ostringstream oss;
203 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1)
204 << ". Received value is: " << index;
205 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
206 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread,
207 JSTaggedValue::Exception(),
208 PropertyMetaData(false)));
209 }
210
211 return OperationResult(thread, obj->Get(thread, index), PropertyMetaData(false));
212 }
213
SetProperty(JSThread * thread,const JSHandle<JSAPIQueue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)214 bool JSAPIQueue::SetProperty(JSThread *thread, const JSHandle<JSAPIQueue> &obj,
215 const JSHandle<JSTaggedValue> &key,
216 const JSHandle<JSTaggedValue> &value)
217 {
218 int length = static_cast<int>(obj->GetLength().GetArrayLength());
219 int index = key->GetInt();
220 if (index < 0 || index >= length) {
221 return false;
222 }
223
224 obj->Set(thread, index, value.GetTaggedValue());
225 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
226 return true;
227 }
228
GetArrayLength(JSThread * thread,const JSHandle<JSAPIQueue> & queue)229 uint32_t JSAPIQueue::GetArrayLength(JSThread *thread, const JSHandle<JSAPIQueue> &queue)
230 {
231 uint32_t begin = queue->GetCurrentFront();
232 uint32_t end = queue->GetCurrentTail();
233 JSHandle<TaggedArray> elements(thread, queue->GetElements());
234 ASSERT(!elements->IsDictionaryMode());
235 uint32_t elementsSize = elements->GetLength();
236 ASSERT(elementsSize != 0);
237 uint32_t length = (end - begin + elementsSize) % elementsSize;
238 return length;
239 }
240
GetNextPosition(uint32_t current)241 uint32_t JSAPIQueue::GetNextPosition(uint32_t current)
242 {
243 uint32_t next = 0;
244 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject());
245 uint32_t elementsSize = elements->GetLength();
246 ASSERT(elementsSize != 0);
247 next = (current + 1) % elementsSize;
248 return next;
249 }
250 } // namespace panda::ecmascript
251