/* * Copyright (c) 2024 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/js_api/js_api_bitvector.h" #include "ecmascript/tagged_array-inl.h" #include "ecmascript/shared_objects/concurrent_api_scope.h" namespace panda::ecmascript { using ContainerError = containers::ContainerError; using ErrorFlag = containers::ErrorFlag; bool JSAPIBitVector::Push( JSThread* thread, const JSHandle& bitVector, const JSHandle& value) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); uint32_t length = bitVector->GetLength().GetArrayLength(); JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); if ((length & TAGGED_VALUE_BIT_OFFSET) == 0) { std::bitset increaseSet; if (!value->IsZero()) { increaseSet.set(0); } elements->push_back(increaseSet); } else { SetBit(elements, length, value.GetTaggedValue()); } bitVector->SetLength(thread, JSTaggedValue(++length)); return true; } JSTaggedValue JSAPIBitVector::Pop(JSThread* thread, const JSHandle& bitVector) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); uint32_t lastIndex = bitVector->GetLength().GetArrayLength() - 1; if (lastIndex < 0) { return JSTaggedValue::Undefined(); } JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); JSTaggedValue bit = GetBit(elements, lastIndex); if ((lastIndex & TAGGED_VALUE_BIT_OFFSET) == 0) { elements->pop_back(); } else { SetBit(elements, lastIndex, JSTaggedValue(0)); } bitVector->SetLength(thread, JSTaggedValue(lastIndex)); return bit; } JSTaggedValue JSAPIBitVector::Set(JSThread* thread, const uint32_t index, JSTaggedValue value) { if (index >= GetLength().GetArrayLength()) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (GetLength().GetArrayLength() - 1) << ". Received value is: " << index; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSNativePointer* np = JSNativePointer::Cast(GetNativePointer().GetTaggedObject()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); SetBit(elements, index, value); return JSTaggedValue::Undefined(); } JSTaggedValue JSAPIBitVector::Get(JSThread* thread, const uint32_t index) { if (index >= GetLength().GetArrayLength()) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (GetLength().GetArrayLength() - 1) << ". Received value is: " << index; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSNativePointer* np = JSNativePointer::Cast(GetNativePointer().GetTaggedObject()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); return GetBit(elements, index); } bool JSAPIBitVector::Has(JSThread* thread, const JSHandle& bitVector, const JSHandle& value, const JSHandle& start, const JSHandle& end) { int32_t startIndex = JSTaggedValue::ToInt32(thread, start); int32_t endIndex = JSTaggedValue::ToInt32(thread, end); int32_t length = bitVector->GetLength().GetInt(); int32_t size = length > endIndex ? endIndex : length; if (endIndex < 0 || endIndex > length) { std::ostringstream oss; oss << "The value of \"toIndex\" is out of range. It must be >= 0 && <= " << length << ". Received value is: " << endIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); } if (startIndex < 0 || startIndex >= size) { std::ostringstream oss; oss << "The value of \"fromIndex\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << startIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); } if (length == 0) { return false; } JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); for (int index = startIndex; index <= endIndex; index++) { std::pair pair = ComputeElementIdAndBitId(index); uint32_t elementId = pair.first; uint32_t bitId = pair.second; if ((value->IsZero() && (*elements)[elementId].test(bitId) == 0) || (!value->IsZero() && (*elements)[elementId].test(bitId) != 0)) { return true; } } return false; } bool JSAPIBitVector::Has(const JSTaggedValue& value) const { uint32_t length = GetSize(); if (length == 0) { return false; } JSNativePointer* np = JSNativePointer::Cast(GetNativePointer().GetTaggedObject()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); for (uint32_t index = 0; index < length; index++) { std::pair pair = ComputeElementIdAndBitId(index); uint32_t elementId = pair.first; uint32_t bitId = pair.second; if ((value.IsZero() && (*elements)[elementId].test(bitId) == 0) || (!value.IsZero() && (*elements)[elementId].test(bitId) != 0)) { return true; } } return false; } JSTaggedValue JSAPIBitVector::SetBitsByRange(JSThread* thread, const JSHandle& bitVector, const JSHandle& value, const JSHandle& start, const JSHandle& end) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); int32_t startIndex = JSTaggedValue::ToInt32(thread, start); int32_t endIndex = JSTaggedValue::ToInt32(thread, end); int32_t length = bitVector->GetLength().GetInt(); int32_t size = length > endIndex ? endIndex : length; if (endIndex < 0 || endIndex > length) { std::ostringstream oss; oss << "The value of \"toIndex\" is out of range. It must be >= 0 && <= " << length << ". Received value is: " << endIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (startIndex < 0 || startIndex >= size) { std::ostringstream oss; oss << "The value of \"fromIndex\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << startIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); for (int32_t index = startIndex; index < endIndex; index++) { SetBit(elements, index, value.GetTaggedValue()); } return JSTaggedValue::Undefined(); } JSTaggedValue JSAPIBitVector::GetBitsByRange(JSThread* thread, const JSHandle& bitVector, const JSHandle& start, const JSHandle& end) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); int32_t startIndex = JSTaggedValue::ToInt32(thread, start); int32_t endIndex = JSTaggedValue::ToInt32(thread, end); int32_t length = bitVector->GetLength().GetInt(); int32_t size = length > endIndex ? endIndex : length; if (endIndex < 0 || endIndex > length) { std::ostringstream oss; oss << "The value of \"toIndex\" is out of range. It must be >= 0 && <= " << length << ". Received value is: " << endIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (startIndex < 0 || startIndex >= size) { std::ostringstream oss; oss << "The value of \"fromIndex\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << startIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } int32_t dstLength = endIndex - startIndex; auto factory = thread->GetEcmaVM()->GetFactory(); JSHandle srcNp(thread, bitVector->GetNativePointer()); auto srcElements = reinterpret_cast>*>(srcNp->GetExternalPointer()); JSHandle newBitVector = factory->NewJSAPIBitVector(dstLength); JSHandle dstNp(thread, newBitVector->GetNativePointer()); auto dstElements = reinterpret_cast>*>(dstNp->GetExternalPointer()); for (int32_t index = 0; index < dstLength; index++) { JSTaggedValue value = GetBit(srcElements, index + startIndex); SetBit(dstElements, index, value); } newBitVector->SetLength(thread, JSTaggedValue(dstLength)); newBitVector->SetNativePointer(thread, dstNp); return newBitVector.GetTaggedValue(); } JSTaggedValue JSAPIBitVector::SetAllBits( JSThread* thread, const JSHandle& bitVector, const JSHandle& value) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); int size = static_cast(elements->size()); if (value->IsZero()) { for (int index = 0; index < size; index++) { (*elements)[index] = std::bitset(0); } } else { for (int index = 0; index < size; index++) { (*elements)[index] = std::bitset(UINT64_MAX); } } return JSTaggedValue::Undefined(); } JSTaggedValue JSAPIBitVector::GetBitCountByRange(JSThread* thread, const JSHandle& bitVector, const JSHandle& value, const JSHandle& start, const JSHandle& end) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); int32_t startIndex = JSTaggedValue::ToInt32(thread, start); int32_t endIndex = JSTaggedValue::ToInt32(thread, end); int32_t length = bitVector->GetLength().GetInt(); int32_t size = length > endIndex ? endIndex : length; int32_t count = 0; if (endIndex < 0 || endIndex > length) { std::ostringstream oss; oss << "The value of \"toIndex\" is out of range. It must be >= 0 && <= " << length << ". Received value is: " << endIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (startIndex < 0 || startIndex >= size) { std::ostringstream oss; oss << "The value of \"fromIndex\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << startIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); for (int32_t index = startIndex; index < endIndex; index++) { std::pair pair = ComputeElementIdAndBitId(index); uint32_t elementId = pair.first; uint32_t bitId = pair.second; if ((value->IsZero() && (*elements)[elementId].test(bitId) == 0) || (!value->IsZero() && (*elements)[elementId].test(bitId) != 0)) { count++; } } return JSTaggedValue(count); } int JSAPIBitVector::GetIndexOf(JSThread* thread, const JSHandle& bitVector, const JSHandle& value, const JSHandle& start, const JSHandle& end) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); int32_t startIndex = JSTaggedValue::ToInt32(thread, start); int32_t endIndex = JSTaggedValue::ToInt32(thread, end); int32_t length = bitVector->GetLength().GetInt(); int32_t size = length > endIndex ? endIndex : length; if (endIndex < 0 || endIndex > length) { std::ostringstream oss; oss << "The value of \"toIndex\" is out of range. It must be >= 0 && <= " << length << ". Received value is: " << endIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, -1); } if (startIndex < 0 || startIndex >= size) { std::ostringstream oss; oss << "The value of \"fromIndex\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << startIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, -1); } JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); for (int32_t index = startIndex; index < endIndex; index++) { std::pair pair = ComputeElementIdAndBitId(index); uint32_t elementId = pair.first; uint32_t bitId = pair.second; if ((value->IsZero() && (*elements)[elementId].test(bitId) == 0) || (!value->IsZero() && (*elements)[elementId].test(bitId) != 0)) { return index; } } return -1; } int JSAPIBitVector::GetLastIndexOf(JSThread* thread, const JSHandle& bitVector, const JSHandle& value, const JSHandle& start, const JSHandle& end) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); int32_t startIndex = JSTaggedValue::ToInt32(thread, start); int32_t endIndex = JSTaggedValue::ToInt32(thread, end); int32_t length = bitVector->GetLength().GetInt(); int32_t size = length > endIndex ? endIndex : length; if (endIndex < 0 || endIndex > length) { std::ostringstream oss; oss << "The value of \"toIndex\" is out of range. It must be >= 0 && <= " << length << ". Received value is: " << endIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, -1); } if (startIndex < 0 || startIndex >= size) { std::ostringstream oss; oss << "The value of \"fromIndex\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << startIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, -1); } JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); for (int32_t index = endIndex - 1; index >= startIndex; index--) { std::pair pair = ComputeElementIdAndBitId(index); uint32_t elementId = pair.first; uint32_t bitId = pair.second; if ((value->IsZero() && (*elements)[elementId].test(bitId) == 0) || (!value->IsZero() && (*elements)[elementId].test(bitId) != 0)) { return index; } } return -1; } JSTaggedValue JSAPIBitVector::FlipBitByIndex(JSThread* thread, const JSHandle& bitVector, int index) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); if (index >= bitVector->GetLength().GetInt()) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (bitVector->GetLength().GetInt() - 1) << ". Received value is: " << index; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); std::pair pair = ComputeElementIdAndBitId(index); uint32_t elementId = pair.first; uint32_t bitId = pair.second; (*elements)[elementId].flip(bitId); return JSTaggedValue::Undefined(); } JSTaggedValue JSAPIBitVector::FlipBitsByRange(JSThread* thread, const JSHandle& bitVector, const JSHandle& start, const JSHandle& end) { [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); int32_t startIndex = JSTaggedValue::ToInt32(thread, start); int32_t endIndex = JSTaggedValue::ToInt32(thread, end); int32_t length = bitVector->GetLength().GetInt(); int32_t size = length > endIndex ? endIndex : length; if (endIndex < 0 || endIndex > length) { std::ostringstream oss; oss << "The value of \"toIndex\" is out of range. It must be >= 0 && <= " << length << ". Received value is: " << endIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (startIndex < 0 || startIndex >= size) { std::ostringstream oss; oss << "The value of \"fromIndex\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << startIndex; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); for (int32_t index = startIndex; index < endIndex; index++) { std::pair pair = ComputeElementIdAndBitId(index); uint32_t elementId = pair.first; uint32_t bitId = pair.second; (*elements)[elementId].flip(bitId); } return JSTaggedValue::Undefined(); } void JSAPIBitVector::Resize(JSThread* thread, const JSHandle& bitVector, int newSize) { if (newSize < 0) { std::ostringstream oss; oss << "The value of \"length\" is out of range. It must be >= 0" << ". Received value is: " << newSize; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN(thread, error); } [[maybe_unused]] ConcurrentApiScope scope(thread, JSHandle::Cast(bitVector)); int length = bitVector->GetLength().GetInt(); uint32_t elementsLength = ((length - 1) / BIT_SET_LENGTH) + 1; uint32_t newElementsLength = ((newSize - 1) / BIT_SET_LENGTH) + 1; JSHandle np(thread, bitVector->GetNativePointer()); auto elements = reinterpret_cast>*>(np->GetExternalPointer()); if (elementsLength == newElementsLength && length < newSize) { for (int32_t index = length; index < newSize; index++) { SetBit(elements, index, JSTaggedValue(0)); } } else if (elementsLength < newElementsLength) { std::bitset initBitSet; elements->resize(newElementsLength, initBitSet); } else if (elementsLength > newElementsLength) { elements->resize(newElementsLength); } bitVector->SetLength(thread, JSTaggedValue(newSize)); } JSHandle JSAPIBitVector::OwnKeys(JSThread* thread, const JSHandle& obj) { uint32_t numOfElements = static_cast(obj->GetLength().GetInt()); JSHandle keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(numOfElements); if (numOfElements > 0) { for (uint32_t i = 0; i < numOfElements; ++i) { auto key = base::NumberHelper::IntToEcmaString(thread, i); keyArray->Set(thread, i, key); } } return keyArray; } JSHandle JSAPIBitVector::OwnEnumKeys(JSThread* thread, const JSHandle& obj) { return OwnKeys(thread, obj); } bool JSAPIBitVector::GetOwnProperty( JSThread* thread, const JSHandle& obj, const JSHandle& key) { uint32_t index = 0; if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); CString errorMsg = "The type of \"index\" can not obtain attributes of no-number type. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); } uint32_t length = obj->GetLength().GetArrayLength(); if (index >= length) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be > " << (length - 1) << ". Received value is: " << index; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); } obj->Get(thread, index); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); return true; } JSTaggedValue JSAPIBitVector::GetIteratorObj(JSThread* thread, const JSHandle& obj) { ObjectFactory* factory = thread->GetEcmaVM()->GetFactory(); JSHandle iter(factory->NewJSAPIBitVectorIterator(obj)); return iter.GetTaggedValue(); } OperationResult JSAPIBitVector::GetProperty( JSThread* thread, const JSHandle& obj, const JSHandle& key) { int length = obj->GetLength().GetInt(); int index = key->GetInt(); if (index < 0 || index >= length) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1) << ". Received value is: " << index; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE( thread, error, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); } return OperationResult(thread, obj->Get(thread, index), PropertyMetaData(false)); } bool JSAPIBitVector::SetProperty(JSThread* thread, const JSHandle& obj, const JSHandle& key, const JSHandle& value) { int length = obj->GetLength().GetInt(); int index = key->GetInt(); if (index < 0 || index >= length) { return false; } obj->Set(thread, index, value.GetTaggedValue()); return true; } } // namespace panda::ecmascript