1/* 2 * Copyright (c) 2022-2024 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_vector.h" 17 18#include "ecmascript/global_env_constants-inl.h" 19#include "ecmascript/interpreter/interpreter.h" 20#include "ecmascript/js_function.h" 21 22#include <codecvt> 23#include <locale> 24 25namespace panda::ecmascript { 26static const uint32_t MAX_VALUE = 0x7fffffff; 27static const uint32_t MAX_ARRAY_SIZE = MAX_VALUE - 8; 28bool JSAPIVector::Add(JSThread *thread, const JSHandle<JSAPIVector> &vector, const JSHandle<JSTaggedValue> &value) 29{ 30 uint32_t length = vector->GetSize(); 31 GrowCapacity(thread, vector, length + 1); 32 33 TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 34 ASSERT(!elements->IsDictionaryMode()); 35 elements->Set(thread, length, value); 36 vector->SetLength(++length); 37 38 return true; 39} 40 41void JSAPIVector::Insert(JSThread *thread, const JSHandle<JSAPIVector> &vector, 42 const JSHandle<JSTaggedValue> &value, int32_t index) 43{ 44 uint32_t length = vector->GetSize(); 45 if (index < 0 || index > static_cast<int32_t>(length)) { 46 THROW_ERROR(thread, ErrorType::RANGE_ERROR, "the index is out-of-bounds"); 47 } 48 GrowCapacity(thread, vector, length + 1); 49 50 TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 51 ASSERT(!elements->IsDictionaryMode()); 52 for (int32_t i = static_cast<int32_t>(length) - 1; i >= index; i--) { 53 elements->Set(thread, i + 1, elements->Get(i)); 54 } 55 56 elements->Set(thread, index, value); 57 vector->SetLength(++length); 58} 59 60void JSAPIVector::SetLength(JSThread *thread, const JSHandle<JSAPIVector> &vector, uint32_t newSize) 61{ 62 uint32_t len = vector->GetSize(); 63 if (newSize > len) { 64 GrowCapacity(thread, vector, newSize); 65 } 66 vector->SetLength(newSize); 67} 68 69uint32_t JSAPIVector::GetCapacity() 70{ 71 TaggedArray *elementData = TaggedArray::Cast(GetElements().GetTaggedObject()); 72 ASSERT(!elementData->IsDictionaryMode()); 73 return elementData->GetLength(); 74} 75 76void JSAPIVector::IncreaseCapacityTo(JSThread *thread, const JSHandle<JSAPIVector> &vector, int32_t newCapacity) 77{ 78 if (newCapacity < 0) { 79 THROW_ERROR(thread, ErrorType::RANGE_ERROR, "An incorrect size was set"); 80 } 81 82 JSHandle<TaggedArray> elementData(thread, vector->GetElements()); 83 ASSERT(!elementData->IsDictionaryMode()); 84 uint32_t oldCapacity = elementData->GetLength(); 85 uint32_t tempCapacity = static_cast<uint32_t>(newCapacity); 86 if (oldCapacity < tempCapacity) { 87 JSHandle<TaggedArray> newElements = 88 thread->GetEcmaVM()->GetFactory()->CopyArray(elementData, oldCapacity, tempCapacity); 89 vector->SetElements(thread, newElements); 90 } 91} 92 93int32_t JSAPIVector::GetIndexOf(JSThread *thread, const JSHandle<JSAPIVector> &vector, 94 const JSHandle<JSTaggedValue> &obj) 95{ 96 return JSAPIVector::GetIndexFrom(thread, vector, obj, 0); 97} 98 99int32_t JSAPIVector::GetIndexFrom(JSThread *thread, const JSHandle<JSAPIVector> &vector, 100 const JSHandle<JSTaggedValue> &obj, int32_t index) 101{ 102 TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 103 ASSERT(!elements->IsDictionaryMode()); 104 uint32_t length = vector->GetSize(); 105 if (index < 0) { 106 index = 0; 107 } else if (index >= static_cast<int32_t>(length)) { 108 THROW_RANGE_ERROR_AND_RETURN(thread, "no-such-element", -1); 109 } 110 111 JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined()); 112 for (uint32_t i = static_cast<uint32_t>(index); i < length; i++) { 113 value.Update(JSTaggedValue(elements->Get(i))); 114 if (JSTaggedValue::StrictEqual(thread, obj, value)) { 115 return i; 116 } 117 } 118 return -1; 119} 120 121bool JSAPIVector::IsEmpty() const 122{ 123 return GetSize() == 0; 124} 125 126JSTaggedValue JSAPIVector::GetLastElement() 127{ 128 uint32_t length = GetSize(); 129 if (length == 0) { 130 return JSTaggedValue::Undefined(); 131 } 132 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 133 ASSERT(!elements->IsDictionaryMode()); 134 return elements->Get(length - 1); 135} 136 137int32_t JSAPIVector::GetLastIndexOf(JSThread *thread, const JSHandle<JSAPIVector> &vector, 138 const JSHandle<JSTaggedValue> &obj) 139{ 140 int32_t index = static_cast<int32_t>(vector->GetSize()) - 1; 141 if (index < 0) { 142 return -1; // vector isEmpty, defalut return -1 143 } 144 return JSAPIVector::GetLastIndexFrom(thread, vector, obj, index); 145} 146 147int32_t JSAPIVector::GetLastIndexFrom(JSThread *thread, const JSHandle<JSAPIVector> &vector, 148 const JSHandle<JSTaggedValue> &obj, int32_t index) 149{ 150 uint32_t length = vector->GetSize(); 151 if (index >= static_cast<int32_t>(length)) { 152 THROW_RANGE_ERROR_AND_RETURN(thread, "index-out-of-bounds", -1); 153 } else if (index < 0) { 154 index = 0; 155 } 156 TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 157 ASSERT(!elements->IsDictionaryMode()); 158 JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined()); 159 for (int32_t i = index; i >= 0; i--) { 160 value.Update(elements->Get(i)); 161 if (JSTaggedValue::StrictEqual(thread, obj, value)) { 162 return i; 163 } 164 } 165 return -1; 166} 167 168bool JSAPIVector::Remove(JSThread *thread, const JSHandle<JSAPIVector> &vector, const JSHandle<JSTaggedValue> &obj) 169{ 170 int32_t index = GetIndexOf(thread, vector, obj); 171 uint32_t length = vector->GetSize(); 172 if (index >= 0) { 173 JSHandle<TaggedArray> elements(thread, vector->GetElements()); 174 ASSERT(!elements->IsDictionaryMode()); 175 TaggedArray::RemoveElementByIndex(thread, elements, index, length); 176 length--; 177 vector->SetLength(length); 178 return true; 179 } 180 return false; 181} 182 183JSTaggedValue JSAPIVector::RemoveByIndex(JSThread *thread, const JSHandle<JSAPIVector> &vector, int32_t index) 184{ 185 uint32_t length = vector->GetSize(); 186 if (index < 0 || index >= static_cast<int32_t>(length)) { 187 THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); 188 } 189 TaggedArray *resElements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 190 ASSERT(!resElements->IsDictionaryMode()); 191 JSTaggedValue oldValue = resElements->Get(index); 192 193 if (index >= 0) { 194 JSHandle<TaggedArray> elements(thread, vector->GetElements()); 195 ASSERT(!elements->IsDictionaryMode() && length > 0); 196 TaggedArray::RemoveElementByIndex(thread, elements, index, length); 197 vector->SetLength(length - 1); 198 } 199 length--; 200 vector->SetLength(length); 201 return oldValue; 202} 203 204JSTaggedValue JSAPIVector::RemoveByRange(JSThread *thread, const JSHandle<JSAPIVector> &vector, 205 int32_t fromIndex, int32_t toIndex) 206{ 207 int32_t length = static_cast<int32_t>(vector->GetSize()); 208 if (toIndex <= fromIndex) { 209 THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex cannot be less than or equal to toIndex", 210 JSTaggedValue::Exception()); 211 } 212 if (fromIndex < 0 || fromIndex >= length) { 213 THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex or the toIndex is out-of-bounds", 214 JSTaggedValue::Exception()); 215 } 216 217 int32_t endIndex = toIndex >= length ? length : toIndex; 218 TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 219 ASSERT(!elements->IsDictionaryMode()); 220 int32_t numMoved = length - endIndex; 221 for (int32_t i = 0; i < numMoved; i++) { 222 elements->Set(thread, fromIndex + i, elements->Get(endIndex + i)); 223 } 224 225 int32_t newLength = length - (endIndex - fromIndex); 226 elements->Trim(thread, newLength); 227 vector->SetLength(newLength); 228 return JSTaggedValue::True(); 229} 230 231JSHandle<JSAPIVector> JSAPIVector::SubVector(JSThread *thread, const JSHandle<JSAPIVector> &vector, 232 int32_t fromIndex, int32_t toIndex) 233{ 234 int32_t length = static_cast<int32_t>(vector->GetSize()); 235 if (fromIndex < 0 || toIndex < 0 || 236 fromIndex >= length || toIndex >= length) { 237 THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex or the toIndex is out-of-bounds", 238 JSHandle<JSAPIVector>()); 239 } 240 if (toIndex <= fromIndex) { 241 THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex cannot be less than or equal to toIndex", 242 JSHandle<JSAPIVector>()); 243 } 244 245 uint32_t newLength = static_cast<uint32_t>(toIndex - fromIndex); 246 JSHandle<JSAPIVector> subVector = thread->GetEcmaVM()->GetFactory()->NewJSAPIVector(newLength); 247 TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 248 249 subVector->SetLength(newLength); 250 for (uint32_t i = 0; i < newLength; i++) { 251 subVector->Set(thread, i, elements->Get(fromIndex + i)); 252 } 253 254 return subVector; 255} 256 257JSTaggedValue JSAPIVector::ToString(JSThread *thread, const JSHandle<JSAPIVector> &vector) 258{ 259 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 260 std::u16string sepHandle = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes(","); 261 262 uint32_t length = vector->GetSize(); 263 std::u16string concatStr; 264 JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Undefined()); 265 for (uint32_t k = 0; k < length; k++) { 266 std::u16string nextStr; 267 element.Update(Get(thread, vector, k)); 268 if (!element->IsUndefined() && !element->IsNull()) { 269 JSHandle<EcmaString> nextStringHandle = JSTaggedValue::ToString(thread, element); 270 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 271 nextStr = EcmaStringAccessor(nextStringHandle).ToU16String(); 272 } 273 if (k > 0) { 274 concatStr.append(sepHandle); 275 concatStr.append(nextStr); 276 continue; 277 } 278 concatStr.append(nextStr); 279 } 280 281 char16_t *char16tData = concatStr.data(); 282 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData); 283 uint32_t u16strSize = concatStr.size(); 284 return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); 285} 286 287JSTaggedValue JSAPIVector::ForEach(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle, 288 const JSHandle<JSTaggedValue> &callbackFn, 289 const JSHandle<JSTaggedValue> &thisArg) 290{ 291 JSHandle<JSAPIVector> vector = JSHandle<JSAPIVector>::Cast(thisHandle); 292 uint32_t length = vector->GetSize(); 293 JSTaggedValue key = JSTaggedValue::Undefined(); 294 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined()); 295 const uint32_t argsLength = NUM_MANDATORY_JSFUNC_ARGS; 296 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 297 298 for (uint32_t k = 0; k < length; k++) { 299 kValue.Update(Get(thread, vector, k)); 300 key = JSTaggedValue(k); 301 EcmaRuntimeCallInfo *info = 302 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFn, thisArg, undefined, argsLength); 303 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 304 info->SetCallArg(kValue.GetTaggedValue(), key, thisHandle.GetTaggedValue()); 305 JSTaggedValue funcResult = JSFunction::Call(info); 306 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); 307 if (length != vector->GetSize()) { // prevent length change 308 length = vector->GetSize(); 309 } 310 } 311 312 return JSTaggedValue::Undefined(); 313} 314 315JSTaggedValue JSAPIVector::ReplaceAllElements(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle, 316 const JSHandle<JSTaggedValue> &callbackFn, 317 const JSHandle<JSTaggedValue> &thisArg) 318{ 319 JSHandle<JSAPIVector> vector = JSHandle<JSAPIVector>::Cast(thisHandle); 320 uint32_t length = vector->GetSize(); 321 JSTaggedValue key = JSTaggedValue::Undefined(); 322 JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined()); 323 const uint32_t argsLength = NUM_MANDATORY_JSFUNC_ARGS; 324 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 325 326 for (uint32_t k = 0; k < length; k++) { 327 kValue.Update(Get(thread, vector, k)); 328 key = JSTaggedValue(k); 329 EcmaRuntimeCallInfo *info = 330 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFn, thisArg, undefined, argsLength); 331 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 332 info->SetCallArg(kValue.GetTaggedValue(), key, thisHandle.GetTaggedValue()); 333 JSTaggedValue funcResult = JSFunction::Call(info); 334 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); 335 if (length != vector->GetSize()) { // prevent length change 336 length = vector->GetSize(); 337 if (k >= length) { 338 break; 339 } 340 } 341 vector->Set(thread, k, funcResult); 342 } 343 344 return JSTaggedValue::Undefined(); 345} 346 347void JSAPIVector::GrowCapacity(JSThread *thread, const JSHandle<JSAPIVector> &vector, uint32_t minCapacity) 348{ 349 JSHandle<TaggedArray> elementData(thread, vector->GetElements()); 350 ASSERT(!elementData->IsDictionaryMode()); 351 uint32_t curCapacity = elementData->GetLength(); 352 if (minCapacity > curCapacity) { 353 uint32_t oldCapacity = elementData->GetLength(); 354 // 2 : 2 Capacity doubled 355 uint32_t newCapacity = oldCapacity * 2; 356 if (newCapacity < minCapacity) { 357 newCapacity = minCapacity; 358 } 359 360 if (newCapacity > MAX_ARRAY_SIZE) { 361 newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? MAX_VALUE : MAX_ARRAY_SIZE; 362 } 363 JSHandle<TaggedArray> newElements = 364 thread->GetEcmaVM()->GetFactory()->CopyArray(elementData, oldCapacity, newCapacity); 365 366 vector->SetElements(thread, newElements); 367 } 368} 369 370JSTaggedValue JSAPIVector::Get(JSThread *thread, const JSHandle<JSAPIVector> &vector, int32_t index) 371{ 372 uint32_t len = vector->GetSize(); 373 if (index < 0 || index >= static_cast<int32_t>(len)) { 374 THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); 375 } 376 377 TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 378 return elements->Get(index); 379} 380 381JSTaggedValue JSAPIVector::Set(JSThread *thread, int32_t index, const JSTaggedValue &value) 382{ 383 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 384 elements->Set(thread, index, value); 385 return JSTaggedValue::Undefined(); 386} 387 388bool JSAPIVector::Has(const JSTaggedValue &value) const 389{ 390 TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); 391 uint32_t length = GetSize(); 392 if (length == 0) { 393 return false; 394 } 395 396 for (uint32_t i = 0; i < length; i++) { 397 if (JSTaggedValue::SameValue(elements->Get(i), value)) { 398 return true; 399 } 400 } 401 return false; 402} 403 404JSHandle<TaggedArray> JSAPIVector::OwnKeys(JSThread *thread, const JSHandle<JSAPIVector> &obj) 405{ 406 return JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>::Cast(obj)); 407} 408 409JSHandle<TaggedArray> JSAPIVector::OwnEnumKeys(JSThread *thread, const JSHandle<JSAPIVector> &obj) 410{ 411 return JSObject::GetOwnEnumPropertyKeys(thread, JSHandle<JSObject>::Cast(obj)); 412} 413 414bool JSAPIVector::GetOwnProperty(JSThread *thread, const JSHandle<JSAPIVector> &obj, 415 const JSHandle<JSTaggedValue> &key) 416{ 417 uint32_t index = 0; 418 if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { 419 THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); 420 } 421 422 uint32_t length = obj->GetSize(); 423 if (index >= length) { 424 THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); 425 } 426 427 JSAPIVector::Get(thread, obj, index); 428 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); 429 return true; 430} 431 432void JSAPIVector::TrimToCurrentLength(JSThread *thread, const JSHandle<JSAPIVector> &obj) 433{ 434 uint32_t length = obj->GetSize(); 435 uint32_t capacity = obj->GetCapacity(); 436 TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject()); 437 ASSERT(!elements->IsDictionaryMode()); 438 if (capacity > length) { 439 elements->Trim(thread, length); 440 } 441} 442 443void JSAPIVector::Clear(JSThread *thread, const JSHandle<JSAPIVector> &obj) 444{ 445 uint32_t length = obj->GetLength(); 446 JSHandle<TaggedArray> elements(thread, obj->GetElements()); 447 ASSERT(!elements->IsDictionaryMode()); 448 for (uint32_t i = 0; i < length; ++i) { 449 elements->Set(thread, i, JSTaggedValue::Hole()); 450 } 451 obj->SetLength(0); 452} 453 454JSHandle<JSAPIVector> JSAPIVector::Clone(JSThread *thread, const JSHandle<JSAPIVector> &obj) 455{ 456 JSHandle<TaggedArray> srcElements(thread, obj->GetElements()); 457 auto factory = thread->GetEcmaVM()->GetFactory(); 458 JSHandle<JSAPIVector> newVector = factory->NewJSAPIVector(0); 459 460 uint32_t length = obj->GetSize(); 461 newVector->SetLength(length); 462 463 JSHandle<TaggedArray> dstElements = factory->NewAndCopyTaggedArray(srcElements, length, length); 464 newVector->SetElements(thread, dstElements); 465 return newVector; 466} 467 468JSTaggedValue JSAPIVector::GetFirstElement(const JSHandle<JSAPIVector> &vector) 469{ 470 uint32_t length = vector->GetSize(); 471 if (length == 0) { 472 return JSTaggedValue::Undefined(); 473 } 474 TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); 475 return elements->Get(0); 476} 477 478JSTaggedValue JSAPIVector::GetIteratorObj(JSThread *thread, const JSHandle<JSAPIVector> &obj) 479{ 480 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 481 JSHandle<JSAPIVectorIterator> iter(factory->NewJSAPIVectorIterator(obj)); 482 483 return iter.GetTaggedValue(); 484} 485 486OperationResult JSAPIVector::GetProperty(JSThread *thread, const JSHandle<JSAPIVector> &obj, 487 const JSHandle<JSTaggedValue> &key) 488{ 489 uint32_t length = obj->GetSize(); 490 int index = key->GetInt(); 491 if (index < 0 || index >= static_cast<int>(length)) { 492 THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds", 493 OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); 494 } 495 496 return OperationResult(thread, JSAPIVector::Get(thread, obj, index), PropertyMetaData(false)); 497} 498 499bool JSAPIVector::SetProperty(JSThread *thread, const JSHandle<JSAPIVector> &obj, 500 const JSHandle<JSTaggedValue> &key, 501 const JSHandle<JSTaggedValue> &value) 502{ 503 uint32_t length = obj->GetSize(); 504 int index = key->GetInt(); 505 if (index < 0 || index >= static_cast<int>(length)) { 506 return false; 507 } 508 509 obj->Set(thread, index, value.GetTaggedValue()); 510 return true; 511} 512} // namespace panda::ecmascript 513