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_deque.h" 17 18#include "ecmascript/containers/containers_errors.h" 19#include "ecmascript/js_tagged_value-inl.h" 20#include "ecmascript/tagged_array-inl.h" 21 22namespace panda::ecmascript { 23using ContainerError = containers::ContainerError; 24using ErrorFlag = containers::ErrorFlag; 25class JSAPIDequelterator; 26 27void JSAPIDeque::InsertFront(JSThread *thread, const JSHandle<JSAPIDeque> &deque, const JSHandle<JSTaggedValue> &value) 28{ 29 JSHandle<TaggedArray> elements(thread, deque->GetElements()); 30 ASSERT(!elements->IsDictionaryMode()); 31 uint32_t capacity = elements->GetLength(); 32 uint32_t first = deque->GetFirst(); 33 uint32_t last = deque->GetLast(); 34 ASSERT(capacity != 0); 35 if ((first + capacity - 1) % capacity == last) { 36 elements = GrowCapacity(thread, deque, capacity, first, last); 37 ASSERT(!elements->IsDictionaryMode()); 38 deque->SetLast(capacity - 1); 39 first = 0; 40 } 41 capacity = elements->GetLength(); 42 ASSERT(capacity != 0); 43 first = (first + capacity - 1) % capacity; 44 elements->Set(thread, first, value); 45 deque->SetFirst(first); 46} 47 48void JSAPIDeque::InsertEnd(JSThread *thread, const JSHandle<JSAPIDeque> &deque, const JSHandle<JSTaggedValue> &value) 49{ 50 JSHandle<TaggedArray> elements(thread, deque->GetElements()); 51 ASSERT(!elements->IsDictionaryMode()); 52 uint32_t capacity = elements->GetLength(); 53 uint32_t first = deque->GetFirst(); 54 uint32_t last = deque->GetLast(); 55 ASSERT(capacity != 0); 56 if (first == (last + 1) % capacity) { 57 elements = GrowCapacity(thread, deque, capacity, first, last); 58 ASSERT(!elements->IsDictionaryMode()); 59 deque->SetFirst(0); 60 last = capacity - 1; 61 } 62 elements->Set(thread, last, value); 63 capacity = elements->GetLength(); 64 ASSERT(capacity != 0); 65 last = (last + 1) % capacity; 66 deque->SetLast(last); 67} 68 69JSTaggedValue JSAPIDeque::GetFront() 70{ 71 if (JSAPIDeque::IsEmpty()) { 72 return JSTaggedValue::Undefined(); 73 } 74 75 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 76 ASSERT(!elements->IsDictionaryMode()); 77 return elements->Get(GetFirst()); 78} 79 80JSTaggedValue JSAPIDeque::GetTail() 81{ 82 if (JSAPIDeque::IsEmpty()) { 83 return JSTaggedValue::Undefined(); 84 } 85 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 86 ASSERT(!elements->IsDictionaryMode()); 87 uint32_t capacity = elements->GetLength(); 88 uint32_t last = GetLast(); 89 ASSERT(capacity != 0); 90 return elements->Get((last + capacity - 1) % capacity); 91} 92 93JSHandle<TaggedArray> JSAPIDeque::GrowCapacity(JSThread *thread, const JSHandle<JSAPIDeque> &deque, 94 uint32_t oldCapacity, uint32_t first, uint32_t last) 95{ 96 JSHandle<TaggedArray> oldElements(thread, deque->GetElements()); 97 ASSERT(!oldElements->IsDictionaryMode()); 98 uint32_t newCapacity = ComputeCapacity(oldCapacity); 99 uint32_t size = deque->GetSize(); 100 JSHandle<TaggedArray> newElements = 101 thread->GetEcmaVM()->GetFactory()->CopyDeque(oldElements, newCapacity, size, first, last); 102 deque->SetElements(thread, newElements); 103 return newElements; 104} 105 106JSTaggedValue JSAPIDeque::PopFirst() 107{ 108 if (JSAPIDeque::IsEmpty()) { 109 return JSTaggedValue::Undefined(); 110 } 111 uint32_t first = GetFirst(); 112 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 113 ASSERT(!elements->IsDictionaryMode()); 114 uint32_t capacity = elements->GetLength(); 115 JSTaggedValue firstElement = elements->Get(first); 116 ASSERT(capacity != 0); 117 first = (first + 1) % capacity; 118 SetFirst(first); 119 return firstElement; 120} 121 122JSTaggedValue JSAPIDeque::PopLast() 123{ 124 if (JSAPIDeque::IsEmpty()) { 125 return JSTaggedValue::Undefined(); 126 } 127 uint32_t last = GetLast(); 128 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 129 ASSERT(!elements->IsDictionaryMode()); 130 uint32_t capacity = elements->GetLength(); 131 ASSERT(capacity != 0); 132 last = (last + capacity - 1) % capacity; 133 JSTaggedValue lastElement = elements->Get(last); 134 SetLast(last); 135 return lastElement; 136} 137 138bool JSAPIDeque::IsEmpty() 139{ 140 uint32_t first = GetFirst(); 141 uint32_t last = GetLast(); 142 return first == last; 143} 144 145uint32_t JSAPIDeque::GetSize() const 146{ 147 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 148 uint32_t capacity = elements->GetLength(); 149 uint32_t first = GetFirst(); 150 uint32_t last = GetLast(); 151 ASSERT(capacity != 0); 152 return (last - first + capacity) % capacity; 153} 154 155JSTaggedValue JSAPIDeque::Get(const uint32_t index) 156{ 157 ASSERT(index < GetSize()); 158 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 159 uint32_t capacity = elements->GetLength(); 160 uint32_t first = GetFirst(); 161 ASSERT(capacity != 0); 162 uint32_t curIndex = (first + index) % capacity; 163 return elements->Get(curIndex); 164} 165 166JSTaggedValue JSAPIDeque::Set(JSThread *thread, const uint32_t index, JSTaggedValue value) 167{ 168 uint32_t length = static_cast<uint32_t>(GetSize()); 169 if (index < 0 || index >= length) { 170 return JSTaggedValue::False(); 171 } 172 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 173 uint32_t capacity = elements->GetLength(); 174 uint32_t first = GetFirst(); 175 ASSERT(capacity != 0); 176 uint32_t curIndex = (first + index) % capacity; 177 elements->Set(thread, curIndex, value); 178 return JSTaggedValue::True(); 179} 180 181bool JSAPIDeque::Has(JSTaggedValue value) const 182{ 183 uint32_t first = GetFirst(); 184 uint32_t last = GetLast(); 185 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 186 uint32_t capacity = elements->GetLength(); 187 uint32_t index = first; 188 while (index != last) { 189 if (JSTaggedValue::SameValue(elements->Get(index), value)) { 190 return true; 191 } 192 ASSERT(capacity != 0); 193 index = (index + 1) % capacity; 194 } 195 return false; 196} 197 198JSHandle<TaggedArray> JSAPIDeque::OwnKeys(JSThread *thread, const JSHandle<JSAPIDeque> &deque) 199{ 200 uint32_t length = deque->GetSize(); 201 202 JSHandle<TaggedArray> oldElements(thread, deque->GetElements()); 203 ASSERT(!oldElements->IsDictionaryMode()); 204 uint32_t oldCapacity = oldElements->GetLength(); 205 uint32_t newCapacity = ComputeCapacity(oldCapacity); 206 uint32_t firstIndex = deque->GetFirst(); 207 uint32_t lastIndex = deque->GetLast(); 208 JSHandle<TaggedArray> newElements = 209 thread->GetEcmaVM()->GetFactory()->CopyDeque(oldElements, newCapacity, length, firstIndex, lastIndex); 210 deque->SetFirst(0); 211 deque->SetLast(length); 212 deque->SetElements(thread, newElements); 213 214 return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>::Cast(deque)); 215} 216 217JSHandle<TaggedArray> JSAPIDeque::OwnEnumKeys(JSThread *thread, const JSHandle<JSAPIDeque> &deque) 218{ 219 uint32_t length = deque->GetSize(); 220 221 JSHandle<TaggedArray> oldElements(thread, deque->GetElements()); 222 ASSERT(!oldElements->IsDictionaryMode()); 223 uint32_t oldCapacity = oldElements->GetLength(); 224 uint32_t newCapacity = ComputeCapacity(oldCapacity); 225 uint32_t firstIndex = deque->GetFirst(); 226 uint32_t lastIndex = deque->GetLast(); 227 JSHandle<TaggedArray> newElements = 228 thread->GetEcmaVM()->GetFactory()->CopyDeque(oldElements, newCapacity, length, firstIndex, lastIndex); 229 deque->SetFirst(0); 230 deque->SetLast(length); 231 deque->SetElements(thread, newElements); 232 233 return JSObject::GetOwnEnumPropertyKeys(thread, JSHandle<JSObject>::Cast(deque)); 234} 235 236bool JSAPIDeque::GetOwnProperty(JSThread *thread, const JSHandle<JSAPIDeque> &deque, 237 const JSHandle<JSTaggedValue> &key) 238{ 239 uint32_t index = 0; 240 if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { 241 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); 242 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); 243 CString errorMsg = 244 "The type of \"key\" can not obtain attributes of no-number type. Received value is: " 245 + ConvertToString(*result); 246 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); 247 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); 248 } 249 250 uint32_t length = deque->GetSize(); 251 if (index >= length) { 252 ASSERT(length > 0); 253 std::ostringstream oss; 254 oss << "The value of \"index\" is out of range. It must be > " << (length - 1) 255 << ". Received value is: " << index; 256 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); 257 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); 258 } 259 260 deque->Get(index); 261 return true; 262} 263 264JSTaggedValue JSAPIDeque::GetIteratorObj(JSThread *thread, const JSHandle<JSAPIDeque> &deque) 265{ 266 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 267 JSHandle<JSAPIDequeIterator> iter(factory->NewJSAPIDequeIterator(deque)); 268 269 return iter.GetTaggedValue(); 270} 271 272OperationResult JSAPIDeque::GetProperty(JSThread *thread, const JSHandle<JSAPIDeque> &obj, 273 const JSHandle<JSTaggedValue> &key) 274{ 275 int length = static_cast<int>(obj->GetSize()); 276 if (length == 0) { 277 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "Container is empty"); 278 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread, 279 JSTaggedValue::Exception(), 280 PropertyMetaData(false))); 281 } 282 JSHandle<JSTaggedValue> indexKey = key; 283 if (indexKey->IsDouble()) { 284 // Math.floor(1) will produce TaggedDouble, we need to cast into TaggedInt 285 // For integer which is greater than INT32_MAX, it will remain TaggedDouble 286 indexKey = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(indexKey->GetDouble())); 287 } 288 if (!indexKey->IsInt()) { 289 CString errorMsg = "The type of \"index\" must be small integer."; 290 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); 291 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, 292 OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); 293 } 294 295 int index = indexKey->GetInt(); 296 if (index < 0 || index >= length) { 297 std::ostringstream oss; 298 oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1) 299 << ". Received value is: " << index; 300 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); 301 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread, 302 JSTaggedValue::Exception(), 303 PropertyMetaData(false))); 304 } 305 306 return OperationResult(thread, obj->Get(index), PropertyMetaData(false)); 307} 308 309bool JSAPIDeque::SetProperty(JSThread *thread, const JSHandle<JSAPIDeque> &obj, 310 const JSHandle<JSTaggedValue> &key, 311 const JSHandle<JSTaggedValue> &value) 312{ 313 int length = static_cast<int>(obj->GetSize()); 314 JSHandle<JSTaggedValue> indexKey = key; 315 if (indexKey->IsDouble()) { 316 // Math.floor(1) will produce TaggedDouble, we need to cast into TaggedInt 317 // For integer which is greater than INT32_MAX, it will remain TaggedDouble 318 indexKey = JSHandle<JSTaggedValue>(thread, JSTaggedValue::TryCastDoubleToInt32(indexKey->GetDouble())); 319 } 320 if (!indexKey->IsInt()) { 321 return false; 322 } 323 324 int index = indexKey->GetInt(); 325 if (index < 0 || index >= length) { 326 return false; 327 } 328 329 obj->Set(thread, index, value.GetTaggedValue()); 330 return true; 331} 332} // namespace panda::ecmascript 333