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/base/atomic_helper.h"
17#include "ecmascript/base/typed_array_helper-inl.h"
18
19namespace panda::ecmascript::base {
20using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
21
22JSTaggedValue AtomicHelper::ValidateIntegerTypedArray(JSThread *thread, JSHandle<JSTaggedValue> typedArray,
23                                                      bool waitable)
24{
25    // 1. If waitable is not present, set waitable to false.
26    // 2. Let buffer be ? ValidateTypedArray(typedArray).
27    JSTaggedValue buffer = TypedArrayHelper::ValidateTypedArray(thread, typedArray);
28    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
29
30    JSHandle<JSTaggedValue> bufferHandle(thread, buffer);
31
32    // 3. Let typeName be typedArray.[[TypedArrayName]].
33    // 4. Let type be the Element Type value in Table 60 for typeName.
34    JSHandle<JSTaggedValue> typeName(thread, JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName());
35    DataViewType type = JSTypedArray::GetTypeFromName(thread, typeName);
36
37    // 5. If waitable is true, then
38    // a. If typeName is not "Int32Array" or "BigInt64Array", throw a TypeError exception.
39    // 6. Else,
40    // a. If ! IsUnclampedIntegerElementType(type) is false and ! IsBigIntElementType(type) is false,
41    // throw a  TypeError exception.
42    if (waitable) {
43        if (!(type == DataViewType::INT32 || type == DataViewType::BIGINT64)) {
44            THROW_TYPE_ERROR_AND_RETURN(thread, "The typeName is not Int32Array/BigInt64Array.",
45                                        JSTaggedValue::Exception());
46        }
47    } else {
48        if (!(BuiltinsArrayBuffer::IsUnclampedIntegerElementType(type) ||
49              BuiltinsArrayBuffer::IsBigIntElementType(type))) {
50            THROW_TYPE_ERROR_AND_RETURN(thread, "The typedArray type is not UnclampedInteger/BigInt.",
51                                        JSTaggedValue::Exception());
52        }
53    }
54    // 7. Return buffer.
55    return bufferHandle.GetTaggedValue();
56}
57
58uint32_t AtomicHelper::ValidateAtomicAccess(JSThread *thread, const JSHandle<JSTaggedValue> typedArray,
59                                            JSHandle<JSTaggedValue> requestIndex)
60{
61    // 1. Assert: typedArray is an Object that has a [[ViewedArrayBuffer]] internal slot.
62    ASSERT(typedArray->IsECMAObject() && typedArray->IsTypedArray());
63    // 2. Let length be typedArray.[[ArrayLength]].
64    JSHandle<JSObject> typedArrayObj(typedArray);
65    JSHandle<JSTypedArray> srcObj(typedArray);
66    int32_t length = static_cast<int32_t>(srcObj->GetArrayLength());
67
68    // 3. Let accessIndex be ? ToIndex(requestIndex).
69    JSTaggedNumber accessIndex = JSTaggedValue::ToIndex(thread, requestIndex);
70    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
71    int32_t index = base::NumberHelper::DoubleInRangeInt32(accessIndex.GetNumber());
72
73    // 4. Assert: accessIndex ≥ 0.
74    ASSERT(index >= 0);
75
76    // 5. If accessIndex ≥ length, throw a RangeError exception.
77    if (index >= length) {
78        THROW_RANGE_ERROR_AND_RETURN(thread, "Index is overflow.", 0);
79    }
80
81    // 6. Let arrayTypeName be typedArray.[[TypedArrayName]].
82    // 7. Let elementSize be the Element Size value specified in Table 60 for arrayTypeName.
83    // 8. Let offset be typedArray.[[ByteOffset]].
84    JSHandle<JSTaggedValue> arrayTypeName(thread, JSTypedArray::Cast(*typedArrayObj)->GetTypedArrayName());
85    DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName);
86    uint32_t elementSize = TypedArrayHelper::GetSizeFromType(elementType);
87    uint32_t offset = srcObj->GetByteOffset();
88    // 9. Return (accessIndex × elementSize) + offset.
89    ASSERT((static_cast<size_t>(index) * static_cast<size_t>(elementSize) +
90        static_cast<size_t>(offset)) <= static_cast<size_t>(UINT32_MAX));
91    uint32_t allOffset = static_cast<uint32_t>(index) * elementSize + offset;
92    return allOffset;
93}
94
95JSTaggedValue AtomicHelper::AtomicStore(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray,
96                                        JSHandle<JSTaggedValue> index, JSHandle<JSTaggedValue> &value)
97{
98    JSTaggedValue bufferValue = ValidateIntegerTypedArray(thread, typedArray);
99    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
100    JSHandle<JSTaggedValue> buffer(thread, bufferValue);
101    uint32_t indexedPosition = ValidateAtomicAccess(thread, typedArray, index);
102    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
103    JSHandle<JSTaggedValue> arrayTypeName(thread,
104                                          JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName());
105    DataViewType type = JSTypedArray::GetTypeFromName(thread, arrayTypeName);
106    JSHandle<JSTaggedValue> bufferTag;
107    if (type == DataViewType::BIGUINT64 || type == DataViewType::BIGINT64) {
108        JSTaggedValue integerValue = JSTaggedValue::ToBigInt(thread, value);
109        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
110        bufferTag = JSHandle<JSTaggedValue>(thread, integerValue);
111    } else {
112        JSTaggedNumber integerValue = JSTaggedValue::ToInteger(thread, value);
113        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
114        bufferTag = JSHandle<JSTaggedValue>(thread, integerValue);
115    }
116    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(typedArray));
117    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
118    BuiltinsArrayBuffer::SetValueInBuffer(thread, buffer.GetTaggedValue(), indexedPosition, type, bufferTag, true);
119    return bufferTag.GetTaggedValue();
120}
121
122JSTaggedValue AtomicHelper::AtomicLoad(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray,
123                                       JSHandle<JSTaggedValue> index)
124{
125    JSTaggedValue bufferValue = ValidateIntegerTypedArray(thread, typedArray);
126    JSHandle<JSTaggedValue> buffer(thread, bufferValue);
127    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
128    uint32_t indexedPosition = ValidateAtomicAccess(thread, typedArray, index);
129    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
130    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(typedArray));
131    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
132    JSHandle<JSTaggedValue> arrayTypeName(thread,
133                                          JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName());
134    DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName);
135    return BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer.GetTaggedValue(),
136                                                   indexedPosition, elementType, true);
137}
138}  // panda::ecmascript::base
139
140