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 
23 namespace panda::ecmascript::builtins {
24 using NumberHelper = base::NumberHelper;
25 using AtomicHelper = base::AtomicHelper;
26 using BytesSize = base::BytesSize;
27 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
28 
29 WaiterList *g_waitLists = Singleton<WaiterList>::GetInstance();
30 Mutex *g_mutex = Singleton<Mutex>::GetInstance();
31 
32 // 25.4.2 Atomics.add ( typedArray, index, value )
Sub(EcmaRuntimeCallInfo *argv)33 JSTaggedValue 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 
Add(EcmaRuntimeCallInfo *argv)44 JSTaggedValue 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 
And(EcmaRuntimeCallInfo *argv)55 JSTaggedValue 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 
Or(EcmaRuntimeCallInfo *argv)66 JSTaggedValue 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 
Xor(EcmaRuntimeCallInfo *argv)77 JSTaggedValue 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 
CompareExchange(EcmaRuntimeCallInfo *argv)88 JSTaggedValue 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 
Exchange(EcmaRuntimeCallInfo *argv)99 JSTaggedValue 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 
Store(EcmaRuntimeCallInfo *argv)110 JSTaggedValue 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 
Load(EcmaRuntimeCallInfo *argv)122 JSTaggedValue 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 
IsLockFree(EcmaRuntimeCallInfo *argv)133 JSTaggedValue 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 )
Wait(EcmaRuntimeCallInfo *argv)157 JSTaggedValue 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 )
Notify(EcmaRuntimeCallInfo *argv)237 JSTaggedValue 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 
276 template<typename callbackfun>
AtomicReadModifyWrite(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray, JSHandle<JSTaggedValue> &index, EcmaRuntimeCallInfo *argv, const callbackfun &op)277 JSTaggedValue 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 
306 template<typename callbackfun>
AtomicReadModifyWriteCase(JSThread *thread, JSTaggedValue arrBuf, DataViewType type, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op)307 JSTaggedValue 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 
375 template<typename callbackfun>
HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint8_t &tag)376 JSTaggedValue 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 
398 template<typename callbackfun>
HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int8_t &tag)399 JSTaggedValue 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 
421 template<typename callbackfun>
HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint16_t &tag)422 JSTaggedValue 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 
444 template<typename callbackfun>
HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int16_t &tag)445 JSTaggedValue 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 
467 template<typename callbackfun>
HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint32_t &tag)468 JSTaggedValue 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 
490 template<typename callbackfun>
HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int32_t &tag)491 JSTaggedValue 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 
513 template<typename callbackfun>
HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int64_t &tag, bool &lossless)514 JSTaggedValue 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 
540 template<typename callbackfun>
HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint64_t &tag, bool &lossless)541 JSTaggedValue 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 
567 template <typename T>
DoWait(JSThread *thread, JSHandle<JSTaggedValue> &arrayBuffer, size_t index, T execpt, double timeout)568 WaitResult 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 
Signal(JSHandle<JSTaggedValue> &arrayBuffer, const size_t &index, double wakeCount)619 uint32_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