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/builtins/builtins_atomics.h"
17
18#include "ecmascript/base/atomic_helper.h"
19#include "ecmascript/base/typed_array_helper-inl.h"
20#include "libpandabase/utils/time.h"
21#include "ecmascript/checkpoint/thread_state_transition.h"
22
23namespace panda::ecmascript::builtins {
24using NumberHelper = base::NumberHelper;
25using AtomicHelper = base::AtomicHelper;
26using BytesSize = base::BytesSize;
27using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
28
29WaiterList *g_waitLists = Singleton<WaiterList>::GetInstance();
30Mutex *g_mutex = Singleton<Mutex>::GetInstance();
31
32// 25.4.2 Atomics.add ( typedArray, index, value )
33JSTaggedValue BuiltinsAtomics::Sub(EcmaRuntimeCallInfo *argv)
34{
35    ASSERT(argv);
36    JSThread *thread = argv->GetThread();
37    BUILTINS_API_TRACE(thread, Atomics, Sub);
38    [[maybe_unused]] EcmaHandleScope handleScope(thread);
39    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
40    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
41    return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::SubFun());
42}
43
44JSTaggedValue BuiltinsAtomics::Add(EcmaRuntimeCallInfo *argv)
45{
46    ASSERT(argv);
47    JSThread *thread = argv->GetThread();
48    BUILTINS_API_TRACE(thread, Atomics, Add);
49    [[maybe_unused]] EcmaHandleScope handleScope(thread);
50    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
51    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
52    return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::AddFun());
53}
54
55JSTaggedValue BuiltinsAtomics::And(EcmaRuntimeCallInfo *argv)
56{
57    ASSERT(argv);
58    JSThread *thread = argv->GetThread();
59    BUILTINS_API_TRACE(thread, Atomics, And);
60    [[maybe_unused]] EcmaHandleScope handleScope(thread);
61    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
62    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
63    return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::AndFun());
64}
65
66JSTaggedValue BuiltinsAtomics::Or(EcmaRuntimeCallInfo *argv)
67{
68    ASSERT(argv);
69    JSThread *thread = argv->GetThread();
70    BUILTINS_API_TRACE(thread, Atomics, Or);
71    [[maybe_unused]] EcmaHandleScope handleScope(thread);
72    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
73    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
74    return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::OrFun());
75}
76
77JSTaggedValue BuiltinsAtomics::Xor(EcmaRuntimeCallInfo *argv)
78{
79    ASSERT(argv);
80    JSThread *thread = argv->GetThread();
81    BUILTINS_API_TRACE(thread, Atomics, Xor);
82    [[maybe_unused]] EcmaHandleScope handleScope(thread);
83    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
84    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
85    return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::XorFun());
86}
87
88JSTaggedValue BuiltinsAtomics::CompareExchange(EcmaRuntimeCallInfo *argv)
89{
90    ASSERT(argv);
91    JSThread *thread = argv->GetThread();
92    BUILTINS_API_TRACE(thread, Atomics, CompareExchange);
93    [[maybe_unused]] EcmaHandleScope handleScope(thread);
94    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
95    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
96    return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::CompareExchangeFun());
97}
98
99JSTaggedValue BuiltinsAtomics::Exchange(EcmaRuntimeCallInfo *argv)
100{
101    ASSERT(argv);
102    JSThread *thread = argv->GetThread();
103    BUILTINS_API_TRACE(thread, Atomics, Exchange);
104    [[maybe_unused]] EcmaHandleScope handleScope(thread);
105    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
106    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
107    return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::ExchangeFun());
108}
109
110JSTaggedValue BuiltinsAtomics::Store(EcmaRuntimeCallInfo *argv)
111{
112    ASSERT(argv);
113    JSThread *thread = argv->GetThread();
114    BUILTINS_API_TRACE(thread, Atomics, Store);
115    [[maybe_unused]] EcmaHandleScope handleScope(thread);
116    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
117    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
118    JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
119    return AtomicHelper::AtomicStore(thread, typedArray, index, value);
120}
121
122JSTaggedValue BuiltinsAtomics::Load(EcmaRuntimeCallInfo *argv)
123{
124    ASSERT(argv);
125    JSThread *thread = argv->GetThread();
126    BUILTINS_API_TRACE(thread, Atomics, Load);
127    [[maybe_unused]] EcmaHandleScope handleScope(thread);
128    JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0);
129    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
130    return AtomicHelper::AtomicLoad(thread, typedArray, index);
131}
132
133JSTaggedValue BuiltinsAtomics::IsLockFree(EcmaRuntimeCallInfo *argv)
134{
135    ASSERT(argv);
136    JSThread *thread = argv->GetThread();
137    BUILTINS_API_TRACE(thread, Atomics, IsLockFree);
138    [[maybe_unused]] EcmaHandleScope handleScope(thread);
139    JSHandle<JSTaggedValue> sizeTag = GetCallArg(argv, 0);
140    BytesSize size = BytesSize(JSTaggedValue::ToInt32(thread, sizeTag));
141    bool result = false;
142    switch (size) {
143        case BytesSize::ONEBYTES:
144        case BytesSize::TWOBYTES:
145        case BytesSize::FOURBYTES:
146        case BytesSize::EIGHTBYTES:
147            result = true;
148            break;
149        default:
150            result = false;
151            break;
152    }
153    return JSTaggedValue(result);
154}
155
156// 25.4.11 Atomics.wait ( typedArray, index, value, timeout )
157JSTaggedValue BuiltinsAtomics::Wait(EcmaRuntimeCallInfo *argv)
158{
159    ASSERT(argv);
160    JSThread *thread = argv->GetThread();
161    BUILTINS_API_TRACE(thread, Atomics, Wait);
162    [[maybe_unused]] EcmaHandleScope handleScope(thread);
163
164    JSHandle<JSTaggedValue> array = GetCallArg(argv, 0);
165    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
166    JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
167    JSHandle<JSTaggedValue> timeout = GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
168
169    // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
170    JSHandle<JSTaggedValue> arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true));
171    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
172
173    // 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
174    if (!arrayBuffer->IsSharedArrayBuffer()) {
175        THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not  sharedArrayBuffer.",
176                                    JSTaggedValue::Exception());
177    }
178
179    // 3. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).
180    uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index);
181    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
182
183    // 4. If typedArray.[[TypedArrayName]] is "BigInt64Array", let v be ? ToBigInt64(value).
184    // 5. Otherwise, let v be ? ToInt32(value).
185    int64_t v = 0;
186    if (array->IsJSBigInt64Array()) {
187        if (value->IsBoolean()) {
188            value = JSHandle<JSTaggedValue>(thread, JSTaggedValue::ToBigInt64(thread, value));
189            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
190        }
191        v = JSHandle<BigInt>::Cast(value)->ToInt64();
192    } else {
193        v = static_cast<int64_t>(JSTaggedValue::ToInt32(thread, value));
194    }
195    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
196
197    // 6. Let q be ? ToNumber(timeout).
198    // 7. If q is NaN or +∞�, let t be +∞; else if q is -∞�, let t be 0; else let t be max(ℝ(q), 0).
199    double t = 0;
200    if (timeout->IsUndefined()) {
201        t = base::POSITIVE_INFINITY;
202    } else {
203        JSTaggedNumber q = JSTaggedValue::ToNumber(thread, timeout);
204        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
205        t = q.GetNumber();
206        if (NumberHelper::IsNaN(q) || (!NumberHelper::IsFinite(q) && t > 0)) {
207            t = base::POSITIVE_INFINITY;
208        } else if (t < 0) {
209            t = 0;
210        }
211    }
212
213    // 8. Let B be AgentCanSuspend().
214    // 9. If B is false, throw a TypeError exception.
215    if (!thread->GetCurrentEcmaContext()->GetAllowAtomicWait()) {
216        THROW_TYPE_ERROR_AND_RETURN(thread, "vm does not allow wait to block.",
217                                    JSTaggedValue::Exception());
218    }
219    WaitResult res = WaitResult::OK;
220    if (array->IsJSBigInt64Array()) {
221        // AtomicHelper::Wait<int64_t>(thread, arrayBuffer, indexedPosition, v, t);
222        res = DoWait<int64_t>(thread, arrayBuffer, indexedPosition, v, t);
223    } else {
224        // AtomicHelper::Wait<int32_t>(thread, arrayBuffer, indexedPosition, static_cast<int32_t>(v), t);
225        res = DoWait<int32_t>(thread, arrayBuffer, indexedPosition, static_cast<int32_t>(v), t);
226    }
227    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
228    if (res == WaitResult::OK) {
229        return globalConst->GetOkString();
230    } else if (res == WaitResult::NOT_EQ) {
231        return globalConst->GetNotEqualString();
232    }
233    return globalConst->GetTimeoutString();
234}
235
236// 25.4.12 Atomics.notify ( typedArray, index, count )
237JSTaggedValue BuiltinsAtomics::Notify(EcmaRuntimeCallInfo *argv)
238{
239    ASSERT(argv);
240    JSThread *thread = argv->GetThread();
241    BUILTINS_API_TRACE(thread, Atomics, Notify);
242    [[maybe_unused]] EcmaHandleScope handleScope(thread);
243
244    JSHandle<JSTaggedValue> array = GetCallArg(argv, 0);
245    JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
246    JSHandle<JSTaggedValue> count = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
247
248    // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
249    JSHandle<JSTaggedValue> arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true));
250    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
251
252    // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).
253    uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index);
254    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
255
256    // 3. If count is undefined, let c be +∞.
257    // 4. Else,
258    // a. Let intCount be ? ToIntegerOrInfinity(count).
259    // b. Let c be max(intCount, 0).
260    double c = 0;
261    if (count->IsUndefined()) {
262        c = base::POSITIVE_INFINITY;
263    } else {
264        JSTaggedNumber countTemp = JSTaggedValue::ToNumber(thread, count);
265        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
266        c = base::NumberHelper::TruncateDouble(countTemp.GetNumber());
267        c = c < 0 ? 0 : c;
268    }
269    // 6. If IsSharedArrayBuffer(buffer) is false, return +0�.
270    if (!arrayBuffer->IsSharedArrayBuffer()) {
271        return JSTaggedValue(0);
272    }
273    return JSTaggedValue(Signal(arrayBuffer, indexedPosition, c));
274}
275
276template<typename callbackfun>
277JSTaggedValue BuiltinsAtomics::AtomicReadModifyWrite(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray,
278                                                     JSHandle<JSTaggedValue> &index, EcmaRuntimeCallInfo *argv,
279                                                     const callbackfun &op)
280{
281    BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWrite);
282    if (!typedArray->IsTypedArray()) {
283        THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception());
284    }
285    // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
286    JSTaggedValue bufferValue = base::AtomicHelper::ValidateIntegerTypedArray(thread, typedArray);
287    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
288    JSHandle<JSTaggedValue> buffer(thread, bufferValue);
289    // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).
290    uint32_t indexedPosition = base::AtomicHelper::ValidateAtomicAccess(thread, typedArray, index);
291    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
292    // 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
293    JSHandle<JSTaggedValue> arrayTypeName(thread,
294                                          JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName());
295    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(typedArray));
296    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
297    // 7. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to
298    // ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the
299    // buffer to become detached.
300    // 8. Let elementType be the Element Type value in Table 60 for arrayTypeName.
301    DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName);
302    // 9. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op).
303    return AtomicReadModifyWriteCase(thread, buffer.GetTaggedValue(), elementType, indexedPosition, argv, op);
304}
305
306template<typename callbackfun>
307JSTaggedValue BuiltinsAtomics::AtomicReadModifyWriteCase(JSThread *thread, JSTaggedValue arrBuf,
308                                                         DataViewType type, uint32_t indexedPosition,
309                                                         EcmaRuntimeCallInfo *argv, const callbackfun &op)
310{
311    BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWriteCase);
312    JSHandle<JSTaggedValue> arrBufHadle(thread, arrBuf);
313    JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
314    void *pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue());
315    uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
316    uint32_t size = argv->GetArgsNumber();
317    switch (type) {
318        case DataViewType::UINT8: {
319            uint8_t tag = JSTaggedValue::ToInt8(thread, value);
320            pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue());
321            block = reinterpret_cast<uint8_t *>(pointer);
322            return HandleWithUint8(thread, size, block, indexedPosition, argv, op, tag);
323        }
324        case DataViewType::INT8:{
325            int8_t tag = JSTaggedValue::ToInt8(thread, value);
326            pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue());
327            block = reinterpret_cast<uint8_t *>(pointer);
328            return HandleWithInt8(thread, size, block, indexedPosition, argv, op, tag);
329        }
330        case DataViewType::UINT16: {
331            uint16_t tag = JSTaggedValue::ToInt16(thread, value);
332            pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue());
333            block = reinterpret_cast<uint8_t *>(pointer);
334            return HandleWithUint16(thread, size, block, indexedPosition, argv, op, tag);
335        }
336        case DataViewType::INT16: {
337            int16_t tag = JSTaggedValue::ToInt16(thread, value);
338            pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue());
339            block = reinterpret_cast<uint8_t *>(pointer);
340            return HandleWithInt16(thread, size, block, indexedPosition, argv, op, tag);
341        }
342        case DataViewType::UINT32: {
343            uint32_t tag = JSTaggedValue::ToUint32(thread, value);
344            pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue());
345            block = reinterpret_cast<uint8_t *>(pointer);
346            return HandleWithUint32(thread, size, block, indexedPosition, argv, op, tag);
347        }
348        case DataViewType::INT32: {
349            int32_t tag = static_cast<int32_t>(JSTaggedValue::ToUint32(thread, value));
350            pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue());
351            block = reinterpret_cast<uint8_t *>(pointer);
352            return HandleWithInt32(thread, size, block, indexedPosition, argv, op, tag);
353        }
354        case DataViewType::BIGINT64: {
355            int64_t val = 0;
356            bool lossless = true;
357            BigInt::BigIntToInt64(thread, value, &val, &lossless);
358            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
359            return HandleWithBigInt64(thread, size, block, indexedPosition, argv, op, val, lossless);
360        }
361        case DataViewType::BIGUINT64: {
362            uint64_t val = 0;
363            bool lossless = true;
364            BigInt::BigIntToUint64(thread, value, &val, &lossless);
365            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
366            return HandleWithBigUint64(thread, size, block, indexedPosition, argv, op, val, lossless);
367        }
368        default:
369            break;
370    }
371    LOG_ECMA(FATAL) << "this branch is unreachable";
372    UNREACHABLE();
373}
374
375template<typename callbackfun>
376JSTaggedValue BuiltinsAtomics::HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block,
377                                               uint32_t indexedPosition,
378                                               EcmaRuntimeCallInfo *argv, const callbackfun &op, uint8_t &tag)
379{
380    BUILTINS_API_TRACE(thread, Atomics, HandleWithUint8);
381    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
382    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
383    uint8_t arg[ARGS_NUMBER] = {0};
384    arg[0] = tag;
385    if (size == 3) { // the number of parameters is 3
386        auto result = op((block + indexedPosition), arg, ARGS_NUMBER);
387        return BuiltinsBase::GetTaggedInt(result);
388    }
389    JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
390    uint8_t newTag = JSTaggedValue::ToUint8(thread, newValue);
391    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
392    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
393    arg[1] = newTag;
394    auto result = op((block + indexedPosition), arg, ARGS_NUMBER);
395    return JSTaggedValue(result);
396}
397
398template<typename callbackfun>
399JSTaggedValue BuiltinsAtomics::HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block,
400                                              uint32_t indexedPosition,
401                                              EcmaRuntimeCallInfo *argv, const callbackfun &op, int8_t &tag)
402{
403    BUILTINS_API_TRACE(thread, Atomics, HandleWithInt8);
404    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
405    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
406    int8_t arg[ARGS_NUMBER] = {0};
407    arg[0] = tag;
408    if (size == 3) { // the number of parameters is 3
409        auto result = op(reinterpret_cast<int8_t *>(block + indexedPosition), arg, ARGS_NUMBER);
410        return BuiltinsBase::GetTaggedInt(result);
411    }
412    JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
413    int8_t newTag = JSTaggedValue::ToInt8(thread, newValue);
414    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
415    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
416    arg[1] = newTag;
417    auto result = op(reinterpret_cast<int8_t *>(block + indexedPosition), arg, ARGS_NUMBER);
418    return JSTaggedValue(result);
419}
420
421template<typename callbackfun>
422JSTaggedValue BuiltinsAtomics::HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block,
423                                                uint32_t indexedPosition,
424                                                EcmaRuntimeCallInfo *argv, const callbackfun &op, uint16_t &tag)
425{
426    BUILTINS_API_TRACE(thread, Atomics, HandleWithUint16);
427    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
428    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
429    uint16_t arg[ARGS_NUMBER] = {0};
430    arg[0] = tag;
431    if (size == 3) { // the number of parameters is 3
432        auto result = op(reinterpret_cast<uint16_t *>(block + indexedPosition), arg, ARGS_NUMBER);
433        return BuiltinsBase::GetTaggedInt(result);
434    }
435    JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
436    uint16_t newTag = JSTaggedValue::ToUint16(thread, newValue);
437    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
438    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
439    arg[1] = newTag;
440    auto result = op(reinterpret_cast<uint16_t *>(block + indexedPosition), arg, ARGS_NUMBER);
441    return JSTaggedValue(result);
442}
443
444template<typename callbackfun>
445JSTaggedValue BuiltinsAtomics::HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block,
446                                               uint32_t indexedPosition,
447                                               EcmaRuntimeCallInfo *argv, const callbackfun &op, int16_t &tag)
448{
449    BUILTINS_API_TRACE(thread, Atomics, HandleWithInt16);
450    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
451    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
452    int16_t arg[ARGS_NUMBER] = {0};
453    arg[0] = tag;
454    if (size == 3) { // the number of parameters is 3
455        auto result = op(reinterpret_cast<int16_t *>(block + indexedPosition), arg, ARGS_NUMBER);
456        return BuiltinsBase::GetTaggedInt(result);
457    }
458    JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
459    int16_t newTag = JSTaggedValue::ToInt16(thread, newValue);
460    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
461    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
462    arg[1] = newTag;
463    auto result = op(reinterpret_cast<int16_t *>(block + indexedPosition), arg, ARGS_NUMBER);
464    return JSTaggedValue(result);
465}
466
467template<typename callbackfun>
468JSTaggedValue BuiltinsAtomics::HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block,
469                                                uint32_t indexedPosition,
470                                                EcmaRuntimeCallInfo *argv, const callbackfun &op, uint32_t &tag)
471{
472    BUILTINS_API_TRACE(thread, Atomics, HandleWithUint32);
473    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
474    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
475    uint32_t arg[ARGS_NUMBER] = {0};
476    arg[0] = tag;
477    if (size == 3) { // the number of parameters is 3
478        auto result = op(reinterpret_cast<uint32_t *>(block + indexedPosition), arg, ARGS_NUMBER);
479        return JSTaggedValue(result);
480    }
481    JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
482    uint32_t newTag = JSTaggedValue::ToUint32(thread, newValue);
483    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
484    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
485    arg[1] = newTag;
486    auto result = op(reinterpret_cast<uint32_t *>(block + indexedPosition), arg, ARGS_NUMBER);
487    return JSTaggedValue(result);
488}
489
490template<typename callbackfun>
491JSTaggedValue BuiltinsAtomics::HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block,
492                                               uint32_t indexedPosition,
493                                               EcmaRuntimeCallInfo *argv, const callbackfun &op, int32_t &tag)
494{
495    BUILTINS_API_TRACE(thread, Atomics, HandleWithInt32);
496    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
497    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
498    int32_t arg[ARGS_NUMBER] = {0};
499    arg[0] = tag;
500    if (size == 3) { // the number of parameters is 3
501        auto result = op(reinterpret_cast<int32_t *>(block + indexedPosition), arg, ARGS_NUMBER);
502        return BuiltinsBase::GetTaggedInt(result);
503    }
504    JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
505    int32_t newTag = JSTaggedValue::ToInt32(thread, newValue);
506    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
507    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
508    arg[1] = newTag;
509    auto result = op(reinterpret_cast<int32_t *>(block + indexedPosition), arg, ARGS_NUMBER);
510    return JSTaggedValue(result);
511}
512
513template<typename callbackfun>
514JSTaggedValue BuiltinsAtomics::HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block,
515                                                  uint32_t indexedPosition,
516                                                  EcmaRuntimeCallInfo *argv, const callbackfun &op,
517                                                  int64_t &tag, bool &lossless)
518{
519    BUILTINS_API_TRACE(thread, Atomics, HandleWithBigInt64);
520    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
521    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
522    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
523    int64_t arg[ARGS_NUMBER] = {0};
524    arg[0] = tag;
525    if (size == 3) { // the number of parameters is 3
526        auto result = op(reinterpret_cast<int64_t *>(block + indexedPosition), arg, ARGS_NUMBER);
527        return BigInt::Int64ToBigInt(thread, result).GetTaggedValue();
528    }
529    JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
530    int64_t newVal = 0;
531    BigInt::BigIntToInt64(thread, newValue, &newVal, &lossless);
532    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
533    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
534    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
535    arg[1] = newVal;
536    auto result = op(reinterpret_cast<int64_t *>(block + indexedPosition), arg, ARGS_NUMBER);
537    return BigInt::Int64ToBigInt(thread, result).GetTaggedValue();
538}
539
540template<typename callbackfun>
541JSTaggedValue BuiltinsAtomics::HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block,
542                                                   uint32_t indexedPosition,
543                                                   EcmaRuntimeCallInfo *argv, const callbackfun &op,
544                                                   uint64_t &tag, bool &lossless)
545{
546    BUILTINS_API_TRACE(thread, Atomics, HandleWithBigUint64);
547    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
548    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
549    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
550    uint64_t arg[ARGS_NUMBER] = {0};
551    arg[0] = tag;
552    if (size == 3) { // the number of parameters is 3
553        auto result = op(reinterpret_cast<uint64_t *>(block + indexedPosition), arg, ARGS_NUMBER);
554        return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue();
555    }
556    JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
557    uint64_t newVal = 0;
558    BigInt::BigIntToUint64(thread, newValue, &newVal, &lossless);
559    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
560    BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
561    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
562    arg[1] = newVal;
563    auto result = op(reinterpret_cast<uint64_t *>(block + indexedPosition), arg, ARGS_NUMBER);
564    return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue();
565}
566
567template <typename T>
568WaitResult BuiltinsAtomics::DoWait(JSThread *thread, JSHandle<JSTaggedValue> &arrayBuffer,
569                                   size_t index, T execpt, double timeout)
570{
571    BUILTINS_API_TRACE(thread, Atomics, DoWait);
572    MutexGuard lockGuard(g_mutex);
573    void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrayBuffer.GetTaggedValue());
574    ASSERT(buffer != nullptr);
575    WaiterListNode *node = thread->GetCurrentEcmaContext()->GetWaiterListNode();
576    node->date_ = buffer;
577    node->index_ = index;
578    node->waitPointer_ = reinterpret_cast<int8_t*>(buffer) + index;
579    node->waiting_ = true;
580    std::atomic<T> *atomicValue = reinterpret_cast<std::atomic<T> *>(node->waitPointer_);
581    T value = atomicValue->load();
582    if (value != execpt) {
583        return WaitResult::NOT_EQ;
584    }
585    g_waitLists->AddNode(node);
586    uint64_t currentTime = 0;
587    uint64_t timeoutTime = 0;
588    bool hasTimeout = timeout != base::POSITIVE_INFINITY;
589    if (hasTimeout) {
590        currentTime = time::GetCurrentTimeInMillis();
591        timeoutTime = currentTime + static_cast<uint64_t>(timeout);
592    }
593    WaitResult res = WaitResult::OK;
594    ThreadNativeScope nativeScope(thread);
595    while (true) {
596        if (!node->waiting_) {
597            res = WaitResult::OK;
598            break;
599        }
600        if (hasTimeout) {
601            currentTime = time::GetCurrentTimeInMillis();
602            if (currentTime >= timeoutTime) {
603                res = WaitResult::TIME_OUT;
604                break;
605            }
606            uint64_t untilTime = timeoutTime - currentTime;
607            ASSERT(untilTime != 0);
608
609            node->cond_.TimedWait(g_mutex, untilTime);
610        } else {
611            node->cond_.Wait(g_mutex);
612        }
613    }
614    g_waitLists->DeleteNode(node);
615    node->waiting_ = false;
616    return res;
617}
618
619uint32_t BuiltinsAtomics::Signal(JSHandle<JSTaggedValue> &arrayBuffer, const size_t &index, double wakeCount)
620{
621    void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrayBuffer.GetTaggedValue());
622    ASSERT(buffer != nullptr);
623    MutexGuard lockGuard(g_mutex);
624    auto &locationListMap = g_waitLists->locationListMap_;
625    auto iter = locationListMap.find(reinterpret_cast<int8_t *>(buffer) + index);
626    if (iter == locationListMap.end()) {
627        return 0;
628    }
629    WaiterListNode *node = iter->second.pHead;
630    uint32_t wokenUpCount = 0;
631    while (node != nullptr && wakeCount > 0.0) {
632        if (!node->waiting_) {
633            node = node->next_;
634            continue;
635        }
636        if (buffer == node->date_) {
637            ASSERT(index == node->index_);
638            node->waiting_ = false;
639
640            WaiterListNode *oldNode = node;
641            node = node->next_;
642            oldNode->cond_.Signal();
643            if (wakeCount != base::POSITIVE_INFINITY) {
644                wakeCount--;
645            }
646            wokenUpCount++;
647            continue;
648        }
649        node = node->next_;
650    }
651    return wokenUpCount;
652}
653} // namespace panda::ecmascript::builtins
654