/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/containers/containers_arraylist.h" #include "ecmascript/containers/containers_errors.h" #include "ecmascript/base/array_helper.h" #include "ecmascript/base/number_helper.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/js_api/js_api_arraylist.h" #include "ecmascript/js_api/js_api_arraylist_iterator.h" #include "ecmascript/js_array.h" #include "ecmascript/js_function.h" #include "ecmascript/js_iterator.h" #include "ecmascript/object_factory.h" #include "ecmascript/tagged_array-inl.h" namespace panda::ecmascript::containers { JSTaggedValue ContainersArrayList::ArrayListConstructor(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Constructor); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle newTarget = GetNewTarget(argv); if (newTarget->IsUndefined()) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR, "The ArrayList's constructor cannot be directly invoked."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle constructor = GetConstructor(argv); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle newTaggedArray = factory->NewTaggedArray(JSAPIArrayList::DEFAULT_CAPACITY_LENGTH); obj->SetElements(thread, newTaggedArray); return obj.GetTaggedValue(); } JSTaggedValue ContainersArrayList::Add(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Add); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The add method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); return GetTaggedBoolean(JSAPIArrayList::Add(thread, JSHandle::Cast(self), value)); } JSTaggedValue ContainersArrayList::Insert(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Insert); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The insert method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSAPIArrayList::Insert(thread, JSHandle::Cast(self), value, JSTaggedValue::ToUint32(thread, index)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue::Undefined(); } JSTaggedValue ContainersArrayList::Clear(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Clear); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The clear method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSAPIArrayList::Clear(thread, JSHandle::Cast(self)); return JSTaggedValue::True(); } JSTaggedValue ContainersArrayList::Clone(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Clone); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR, "The clone method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle newArrayList = JSAPIArrayList::Clone(thread, JSHandle::Cast(self)); return newArrayList.GetTaggedValue(); } JSTaggedValue ContainersArrayList::Has(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Has); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR, "The has method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); bool isHas = JSHandle::Cast(self)->Has(value.GetTaggedValue()); return GetTaggedBoolean(isHas); } JSTaggedValue ContainersArrayList::GetCapacity(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetCapacity); JSThread *thread = argv->GetThread(); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The getCapacity method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } uint32_t capacity = JSAPIArrayList::GetCapacity(thread, JSHandle::Cast(self)); return JSTaggedValue(capacity); } JSTaggedValue ContainersArrayList::IncreaseCapacityTo(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, IncreaseCapacityTo); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The increaseCapacityTo method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle newCapacity = GetCallArg(argv, 0); if (!newCapacity->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, newCapacity); CString errorMsg = "The type of \"newCapacity\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSAPIArrayList::IncreaseCapacityTo(thread, JSHandle::Cast(self), JSTaggedValue::ToUint32(thread, newCapacity)); return JSTaggedValue::True(); } JSTaggedValue ContainersArrayList::TrimToCurrentLength(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, TrimToCurrentLength); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The trimToCurrentLength method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSAPIArrayList::TrimToCurrentLength(thread, JSHandle::Cast(self)); return JSTaggedValue::True(); } JSTaggedValue ContainersArrayList::Get(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Get); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The get method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); JSTaggedValue element = JSHandle::Cast(self)->Get(thread, JSTaggedValue::ToUint32(thread, value)); return element; } JSTaggedValue ContainersArrayList::GetIndexOf(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetIndexOf); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The getIndexOf method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); return JSTaggedValue(JSAPIArrayList::GetIndexOf(thread, JSHandle::Cast(self), value)); } JSTaggedValue ContainersArrayList::IsEmpty(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, IsEmpty); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The isEmpty method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } return JSTaggedValue(JSAPIArrayList::IsEmpty(JSHandle::Cast(self))); } JSTaggedValue ContainersArrayList::GetLastIndexOf(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetLastIndexOf); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The getLastIndexOf method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); return JSTaggedValue(JSAPIArrayList::GetLastIndexOf(thread, JSHandle::Cast(self), value)); } JSTaggedValue ContainersArrayList::RemoveByIndex(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, RemoveByIndex); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The removeByIndex method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); if (!value->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSTaggedValue result = JSAPIArrayList::RemoveByIndex(thread, JSHandle::Cast(self), JSTaggedValue::ToUint32(thread, value)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return result; } JSTaggedValue ContainersArrayList::Remove(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Remove); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The remove method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); bool isRemove = JSAPIArrayList::Remove(thread, JSHandle::Cast(self), value); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return GetTaggedBoolean(isRemove); } JSTaggedValue ContainersArrayList::RemoveByRange(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, RemoveByRange); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, BIND_ERROR, "The removeByRange method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle startIndex = GetCallArg(argv, 0); JSHandle endIndex = GetCallArg(argv, 1); if (!startIndex->IsInteger()) { std::ostringstream oss; JSHandle result = JSTaggedValue::ToString(thread, startIndex); CString errorMsg = "The type of \"fromIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (!endIndex->IsInteger()) { std::ostringstream oss; JSHandle result = JSTaggedValue::ToString(thread, endIndex); CString errorMsg = "The type of \"toIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSTaggedValue result = JSAPIArrayList::RemoveByRange(thread, JSHandle::Cast(self), startIndex, endIndex); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return result; } JSTaggedValue ContainersArrayList::ReplaceAllElements(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, ReplaceAllElements); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The replaceAllElements method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle thisArgHandle = GetCallArg(argv, 1); return JSAPIArrayList::ReplaceAllElements(thread, self, callbackFnHandle, thisArgHandle); } JSTaggedValue ContainersArrayList::Set(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Set); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The set method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle index = GetCallArg(argv, 0); JSHandle value = GetCallArg(argv, 1); JSHandle::Cast(self)->Set(thread, JSTaggedValue::ToUint32(thread, index), value.GetTaggedValue()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue::Undefined(); } JSTaggedValue ContainersArrayList::SubArrayList(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, SubArrayList); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The subArrayList method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value1 = GetCallArg(argv, 0); JSHandle value2 = GetCallArg(argv, 1); if (!value1->IsInteger()) { std::ostringstream oss; JSHandle result = JSTaggedValue::ToString(thread, value1); CString errorMsg = "The type of \"fromIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (!value2->IsInteger()) { std::ostringstream oss; JSHandle result = JSTaggedValue::ToString(thread, value2); CString errorMsg = "The type of \"toIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSTaggedValue newArrayList = JSAPIArrayList::SubArrayList(thread, JSHandle::Cast(self), value1, value2); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return newArrayList; } JSTaggedValue ContainersArrayList::Sort(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Array, Sort); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The sort method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); CString errorMsg = "The type of \"comparator\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle elements(thread, JSHandle::Cast(self)->GetElements()); JSMutableHandle presentValue(thread, JSTaggedValue::Undefined()); JSMutableHandle middleValue(thread, JSTaggedValue::Undefined()); JSMutableHandle previousValue(thread, JSTaggedValue::Undefined()); uint32_t length = JSHandle::Cast(self)->GetLength().GetArrayLength(); for (uint32_t i = 1; i < length; i++) { uint32_t beginIndex = 0; uint32_t endIndex = i; presentValue.Update(elements->Get(i)); while (beginIndex < endIndex) { uint32_t middleIndex = (beginIndex + endIndex) / 2; // 2 : half middleValue.Update(elements->Get(middleIndex)); int32_t compareResult = base::ArrayHelper::SortCompare(thread, callbackFnHandle, middleValue, presentValue); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (compareResult > 0) { endIndex = middleIndex; } else { beginIndex = middleIndex + 1; } } if (endIndex >= 0 && endIndex < i) { for (uint32_t j = i; j > endIndex; j--) { previousValue.Update(elements->Get(j - 1)); elements->Set(thread, j, previousValue.GetTaggedValue()); } elements->Set(thread, endIndex, presentValue.GetTaggedValue()); } } return JSTaggedValue::Undefined(); } JSTaggedValue ContainersArrayList::GetSize(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetSize); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The getSize method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } return JSTaggedValue(JSHandle::Cast(self)->GetSize()); } JSTaggedValue ContainersArrayList::ConvertToArray(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, ConvertToArray); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The convertToArray method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle arrayList = JSHandle::Cast(self); auto factory = thread->GetEcmaVM()->GetFactory(); JSHandle array = factory->NewJSArray(); uint32_t length = arrayList->GetLength().GetArrayLength(); array->SetArrayLength(thread, length); JSHandle srcElements(thread, arrayList->GetElements()); JSHandle dstElements = factory->NewAndCopyTaggedArray(srcElements, length, length); array->SetElements(thread, dstElements); return array.GetTaggedValue(); } JSTaggedValue ContainersArrayList::ForEach(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, ForEach); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The forEach method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle thisArgHandle = GetCallArg(argv, 1); return JSAPIArrayList::ForEach(thread, self, callbackFnHandle, thisArgHandle); } JSTaggedValue ContainersArrayList::GetIteratorObj(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), ArrayList, GetIteratorObj); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIArrayList()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIArrayList()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The Symbol.iterator method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSTaggedValue values = JSAPIArrayList::GetIteratorObj(thread, JSHandle::Cast(self)); return values; } } // namespace panda::ecmascript::containers