/* * 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/base/atomic_helper.h" #include "ecmascript/base/typed_array_helper-inl.h" namespace panda::ecmascript::base { using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; JSTaggedValue AtomicHelper::ValidateIntegerTypedArray(JSThread *thread, JSHandle typedArray, bool waitable) { // 1. If waitable is not present, set waitable to false. // 2. Let buffer be ? ValidateTypedArray(typedArray). JSTaggedValue buffer = TypedArrayHelper::ValidateTypedArray(thread, typedArray); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle bufferHandle(thread, buffer); // 3. Let typeName be typedArray.[[TypedArrayName]]. // 4. Let type be the Element Type value in Table 60 for typeName. JSHandle typeName(thread, JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName()); DataViewType type = JSTypedArray::GetTypeFromName(thread, typeName); // 5. If waitable is true, then // a. If typeName is not "Int32Array" or "BigInt64Array", throw a TypeError exception. // 6. Else, // a. If ! IsUnclampedIntegerElementType(type) is false and ! IsBigIntElementType(type) is false, // throw a TypeError exception. if (waitable) { if (!(type == DataViewType::INT32 || type == DataViewType::BIGINT64)) { THROW_TYPE_ERROR_AND_RETURN(thread, "The typeName is not Int32Array/BigInt64Array.", JSTaggedValue::Exception()); } } else { if (!(BuiltinsArrayBuffer::IsUnclampedIntegerElementType(type) || BuiltinsArrayBuffer::IsBigIntElementType(type))) { THROW_TYPE_ERROR_AND_RETURN(thread, "The typedArray type is not UnclampedInteger/BigInt.", JSTaggedValue::Exception()); } } // 7. Return buffer. return bufferHandle.GetTaggedValue(); } uint32_t AtomicHelper::ValidateAtomicAccess(JSThread *thread, const JSHandle typedArray, JSHandle requestIndex) { // 1. Assert: typedArray is an Object that has a [[ViewedArrayBuffer]] internal slot. ASSERT(typedArray->IsECMAObject() && typedArray->IsTypedArray()); // 2. Let length be typedArray.[[ArrayLength]]. JSHandle typedArrayObj(typedArray); JSHandle srcObj(typedArray); uint32_t length = srcObj->GetArrayLength(); // 3. Let accessIndex be ? ToIndex(requestIndex). JSTaggedNumber accessIndex = JSTaggedValue::ToIndex(thread, requestIndex); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); uint64_t index = base::NumberHelper::DoubleToUInt64(accessIndex.GetNumber()); // 4. If accessIndex ≥ length, throw a RangeError exception. if (index >= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "Index is overflow.", 0); } // 5. Let arrayTypeName be typedArray.[[TypedArrayName]]. // 6. Let elementSize be the Element Size value specified in Table 60 for arrayTypeName. // 7. Let offset be typedArray.[[ByteOffset]]. JSHandle arrayTypeName(thread, JSTypedArray::Cast(*typedArrayObj)->GetTypedArrayName()); DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName); uint32_t elementSize = TypedArrayHelper::GetSizeFromType(elementType); uint32_t offset = srcObj->GetByteOffset(); // 8. Return (accessIndex × elementSize) + offset. ASSERT(index * elementSize + offset <= UINT32_MAX); uint32_t allOffset = static_cast(index * elementSize + offset); return allOffset; } JSTaggedValue AtomicHelper::AtomicStore(JSThread *thread, const JSHandle &typedArray, JSHandle index, JSHandle &value) { JSTaggedValue bufferValue = ValidateIntegerTypedArray(thread, typedArray); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle buffer(thread, bufferValue); uint32_t indexedPosition = ValidateAtomicAccess(thread, typedArray, index); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle arrayTypeName(thread, JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName()); DataViewType type = JSTypedArray::GetTypeFromName(thread, arrayTypeName); JSHandle bufferTag; if (type == DataViewType::BIGUINT64 || type == DataViewType::BIGINT64) { JSTaggedValue integerValue = JSTaggedValue::ToBigInt(thread, value); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bufferTag = JSHandle(thread, integerValue); } else { JSTaggedNumber integerValue = JSTaggedValue::ToInteger(thread, value); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bufferTag = JSHandle(thread, integerValue); } BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(typedArray)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); BuiltinsArrayBuffer::SetValueInBuffer(thread, buffer.GetTaggedValue(), indexedPosition, type, bufferTag, true); return bufferTag.GetTaggedValue(); } JSTaggedValue AtomicHelper::AtomicLoad(JSThread *thread, const JSHandle &typedArray, JSHandle index) { JSTaggedValue bufferValue = ValidateIntegerTypedArray(thread, typedArray); JSHandle buffer(thread, bufferValue); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint32_t indexedPosition = ValidateAtomicAccess(thread, typedArray, index); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(typedArray)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle arrayTypeName(thread, JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName()); DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName); return BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer.GetTaggedValue(), indexedPosition, elementType, true); } } // panda::ecmascript::base