• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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->GetEcmaVM()->GetAgentCanSuspend()) {
216         THROW_TYPE_ERROR_AND_RETURN(thread, "vm does not allow wait to block.",
217                                     JSTaggedValue::Exception());
218     }
219 
220     WaitResult res = WaitResult::OK;
221     if (array->IsJSBigInt64Array()) {
222         // AtomicHelper::Wait<int64_t>(thread, arrayBuffer, indexedPosition, v, t);
223         res = DoWait<int64_t>(thread, arrayBuffer, indexedPosition, v, t);
224     } else {
225         // AtomicHelper::Wait<int32_t>(thread, arrayBuffer, indexedPosition, static_cast<int32_t>(v), t);
226         res = DoWait<int32_t>(thread, arrayBuffer, indexedPosition, static_cast<int32_t>(v), t);
227     }
228     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
229     if (res == WaitResult::OK) {
230         return globalConst->GetOkString();
231     } else if (res == WaitResult::NOT_EQ) {
232         return globalConst->GetNotEqualString();
233     }
234     return globalConst->GetTimeoutString();
235 }
236 
237 // 25.4.12 Atomics.notify ( typedArray, index, count )
Notify(EcmaRuntimeCallInfo * argv)238 JSTaggedValue BuiltinsAtomics::Notify(EcmaRuntimeCallInfo *argv)
239 {
240     ASSERT(argv);
241     JSThread *thread = argv->GetThread();
242     BUILTINS_API_TRACE(thread, Atomics, Notify);
243     [[maybe_unused]] EcmaHandleScope handleScope(thread);
244 
245     JSHandle<JSTaggedValue> array = GetCallArg(argv, 0);
246     JSHandle<JSTaggedValue> index = GetCallArg(argv, 1);
247     JSHandle<JSTaggedValue> count = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
248 
249     // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
250     JSHandle<JSTaggedValue> arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true));
251     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
252 
253     // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).
254     uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index);
255     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
256 
257     // 3. If count is undefined, let c be +∞.
258     // 4. Else,
259     // a. Let intCount be ? ToIntegerOrInfinity(count).
260     // b. Let c be max(intCount, 0).
261     double c = 0;
262     if (count->IsUndefined()) {
263         c = base::POSITIVE_INFINITY;
264     } else {
265         JSTaggedNumber countTemp = JSTaggedValue::ToNumber(thread, count);
266         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
267         c = base::NumberHelper::TruncateDouble(countTemp.GetNumber());
268         c = c < 0 ? 0 : c;
269     }
270     // 6. If IsSharedArrayBuffer(buffer) is false, return +0��.
271     if (!arrayBuffer->IsSharedArrayBuffer()) {
272         return JSTaggedValue(0);
273     }
274     return JSTaggedValue(Signal(thread, arrayBuffer, indexedPosition, c));
275 }
276 
277 template<typename callbackfun>
AtomicReadModifyWrite(JSThread * thread,const JSHandle<JSTaggedValue> & typedArray,JSHandle<JSTaggedValue> & index,EcmaRuntimeCallInfo * argv,const callbackfun & op)278 JSTaggedValue BuiltinsAtomics::AtomicReadModifyWrite(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray,
279                                                      JSHandle<JSTaggedValue> &index, EcmaRuntimeCallInfo *argv,
280                                                      const callbackfun &op)
281 {
282     BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWrite);
283     if (!typedArray->IsTypedArray()) {
284         THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception());
285     }
286     // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
287     JSTaggedValue bufferValue = base::AtomicHelper::ValidateIntegerTypedArray(thread, typedArray);
288     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
289     JSHandle<JSTaggedValue> buffer(thread, bufferValue);
290     // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).
291     uint32_t indexedPosition = base::AtomicHelper::ValidateAtomicAccess(thread, typedArray, index);
292     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
293     // 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
294     JSHandle<JSTaggedValue> arrayTypeName(thread,
295         JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName(thread));
296     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(typedArray));
297     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
298     // 7. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to
299     // ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the
300     // buffer to become detached.
301     // 8. Let elementType be the Element Type value in Table 60 for arrayTypeName.
302     DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName);
303     // 9. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op).
304     return AtomicReadModifyWriteCase(thread, buffer.GetTaggedValue(), elementType, indexedPosition, argv, op);
305 }
306 
307 template<typename callbackfun>
AtomicReadModifyWriteCase(JSThread * thread,JSTaggedValue arrBuf,DataViewType type,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op)308 JSTaggedValue BuiltinsAtomics::AtomicReadModifyWriteCase(JSThread *thread, JSTaggedValue arrBuf,
309                                                          DataViewType type, uint32_t indexedPosition,
310                                                          EcmaRuntimeCallInfo *argv, const callbackfun &op)
311 {
312     BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWriteCase);
313     JSHandle<JSTaggedValue> arrBufHadle(thread, arrBuf);
314     JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
315     void *pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue());
316     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
317     uint32_t size = argv->GetArgsNumber();
318     switch (type) {
319         case DataViewType::UINT8: {
320             uint8_t tag = JSTaggedValue::ToInt8(thread, value);
321             pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue());
322             block = reinterpret_cast<uint8_t *>(pointer);
323             return HandleWithUint8(thread, size, block, indexedPosition, argv, op, tag);
324         }
325         case DataViewType::INT8:{
326             int8_t tag = JSTaggedValue::ToInt8(thread, value);
327             pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue());
328             block = reinterpret_cast<uint8_t *>(pointer);
329             return HandleWithInt8(thread, size, block, indexedPosition, argv, op, tag);
330         }
331         case DataViewType::UINT16: {
332             uint16_t tag = JSTaggedValue::ToInt16(thread, value);
333             pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue());
334             block = reinterpret_cast<uint8_t *>(pointer);
335             return HandleWithUint16(thread, size, block, indexedPosition, argv, op, tag);
336         }
337         case DataViewType::INT16: {
338             int16_t tag = JSTaggedValue::ToInt16(thread, value);
339             pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue());
340             block = reinterpret_cast<uint8_t *>(pointer);
341             return HandleWithInt16(thread, size, block, indexedPosition, argv, op, tag);
342         }
343         case DataViewType::UINT32: {
344             uint32_t tag = JSTaggedValue::ToUint32(thread, value);
345             pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue());
346             block = reinterpret_cast<uint8_t *>(pointer);
347             return HandleWithUint32(thread, size, block, indexedPosition, argv, op, tag);
348         }
349         case DataViewType::INT32: {
350             int32_t tag = static_cast<int32_t>(JSTaggedValue::ToUint32(thread, value));
351             pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue());
352             block = reinterpret_cast<uint8_t *>(pointer);
353             return HandleWithInt32(thread, size, block, indexedPosition, argv, op, tag);
354         }
355         case DataViewType::BIGINT64: {
356             int64_t val = 0;
357             bool lossless = true;
358             BigInt::BigIntToInt64(thread, value, &val, &lossless);
359             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
360             return HandleWithBigInt64(thread, size, block, indexedPosition, argv, op, val, lossless);
361         }
362         case DataViewType::BIGUINT64: {
363             uint64_t val = 0;
364             bool lossless = true;
365             BigInt::BigIntToUint64(thread, value, &val, &lossless);
366             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
367             return HandleWithBigUint64(thread, size, block, indexedPosition, argv, op, val, lossless);
368         }
369         default:
370             break;
371     }
372     LOG_ECMA(FATAL) << "this branch is unreachable";
373     UNREACHABLE();
374 }
375 
376 template<typename callbackfun>
HandleWithUint8(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op,uint8_t & tag)377 JSTaggedValue BuiltinsAtomics::HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block,
378                                                uint32_t indexedPosition,
379                                                EcmaRuntimeCallInfo *argv, const callbackfun &op, uint8_t &tag)
380 {
381     BUILTINS_API_TRACE(thread, Atomics, HandleWithUint8);
382     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
383     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
384     uint8_t arg[ARGS_NUMBER] = {0};
385     arg[0] = tag;
386     if (size == 3) { // the number of parameters is 3
387         auto result = op((block + indexedPosition), arg, ARGS_NUMBER);
388         return BuiltinsBase::GetTaggedInt(result);
389     }
390     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
391     uint8_t newTag = JSTaggedValue::ToUint8(thread, newValue);
392     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
393     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
394     arg[1] = newTag;
395     auto result = op((block + indexedPosition), arg, ARGS_NUMBER);
396     return JSTaggedValue(result);
397 }
398 
399 template<typename callbackfun>
HandleWithInt8(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op,int8_t & tag)400 JSTaggedValue BuiltinsAtomics::HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block,
401                                               uint32_t indexedPosition,
402                                               EcmaRuntimeCallInfo *argv, const callbackfun &op, int8_t &tag)
403 {
404     BUILTINS_API_TRACE(thread, Atomics, HandleWithInt8);
405     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
406     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
407     int8_t arg[ARGS_NUMBER] = {0};
408     arg[0] = tag;
409     if (size == 3) { // the number of parameters is 3
410         auto result = op(reinterpret_cast<int8_t *>(block + indexedPosition), arg, ARGS_NUMBER);
411         return BuiltinsBase::GetTaggedInt(result);
412     }
413     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
414     int8_t newTag = JSTaggedValue::ToInt8(thread, newValue);
415     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
416     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
417     arg[1] = newTag;
418     auto result = op(reinterpret_cast<int8_t *>(block + indexedPosition), arg, ARGS_NUMBER);
419     return JSTaggedValue(result);
420 }
421 
422 template<typename callbackfun>
HandleWithUint16(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op,uint16_t & tag)423 JSTaggedValue BuiltinsAtomics::HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block,
424                                                 uint32_t indexedPosition,
425                                                 EcmaRuntimeCallInfo *argv, const callbackfun &op, uint16_t &tag)
426 {
427     BUILTINS_API_TRACE(thread, Atomics, HandleWithUint16);
428     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
429     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
430     uint16_t arg[ARGS_NUMBER] = {0};
431     arg[0] = tag;
432     if (size == 3) { // the number of parameters is 3
433         auto result = op(reinterpret_cast<uint16_t *>(block + indexedPosition), arg, ARGS_NUMBER);
434         return BuiltinsBase::GetTaggedInt(result);
435     }
436     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
437     uint16_t newTag = JSTaggedValue::ToUint16(thread, newValue);
438     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
439     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
440     arg[1] = newTag;
441     auto result = op(reinterpret_cast<uint16_t *>(block + indexedPosition), arg, ARGS_NUMBER);
442     return JSTaggedValue(result);
443 }
444 
445 template<typename callbackfun>
HandleWithInt16(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op,int16_t & tag)446 JSTaggedValue BuiltinsAtomics::HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block,
447                                                uint32_t indexedPosition,
448                                                EcmaRuntimeCallInfo *argv, const callbackfun &op, int16_t &tag)
449 {
450     BUILTINS_API_TRACE(thread, Atomics, HandleWithInt16);
451     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
452     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
453     int16_t arg[ARGS_NUMBER] = {0};
454     arg[0] = tag;
455     if (size == 3) { // the number of parameters is 3
456         auto result = op(reinterpret_cast<int16_t *>(block + indexedPosition), arg, ARGS_NUMBER);
457         return BuiltinsBase::GetTaggedInt(result);
458     }
459     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
460     int16_t newTag = JSTaggedValue::ToInt16(thread, newValue);
461     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
462     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
463     arg[1] = newTag;
464     auto result = op(reinterpret_cast<int16_t *>(block + indexedPosition), arg, ARGS_NUMBER);
465     return JSTaggedValue(result);
466 }
467 
468 template<typename callbackfun>
HandleWithUint32(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op,uint32_t & tag)469 JSTaggedValue BuiltinsAtomics::HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block,
470                                                 uint32_t indexedPosition,
471                                                 EcmaRuntimeCallInfo *argv, const callbackfun &op, uint32_t &tag)
472 {
473     BUILTINS_API_TRACE(thread, Atomics, HandleWithUint32);
474     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
475     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
476     uint32_t arg[ARGS_NUMBER] = {0};
477     arg[0] = tag;
478     if (size == 3) { // the number of parameters is 3
479         auto result = op(reinterpret_cast<uint32_t *>(block + indexedPosition), arg, ARGS_NUMBER);
480         return JSTaggedValue(result);
481     }
482     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
483     uint32_t newTag = JSTaggedValue::ToUint32(thread, newValue);
484     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
485     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
486     arg[1] = newTag;
487     auto result = op(reinterpret_cast<uint32_t *>(block + indexedPosition), arg, ARGS_NUMBER);
488     return JSTaggedValue(result);
489 }
490 
491 template<typename callbackfun>
HandleWithInt32(JSThread * thread,uint32_t size,uint8_t * block,uint32_t indexedPosition,EcmaRuntimeCallInfo * argv,const callbackfun & op,int32_t & tag)492 JSTaggedValue BuiltinsAtomics::HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block,
493                                                uint32_t indexedPosition,
494                                                EcmaRuntimeCallInfo *argv, const callbackfun &op, int32_t &tag)
495 {
496     BUILTINS_API_TRACE(thread, Atomics, HandleWithInt32);
497     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
498     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
499     int32_t arg[ARGS_NUMBER] = {0};
500     arg[0] = tag;
501     if (size == 3) { // the number of parameters is 3
502         auto result = op(reinterpret_cast<int32_t *>(block + indexedPosition), arg, ARGS_NUMBER);
503         return BuiltinsBase::GetTaggedInt(result);
504     }
505     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
506     int32_t newTag = JSTaggedValue::ToInt32(thread, newValue);
507     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
508     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
509     arg[1] = newTag;
510     auto result = op(reinterpret_cast<int32_t *>(block + indexedPosition), arg, ARGS_NUMBER);
511     return JSTaggedValue(result);
512 }
513 
514 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)515 JSTaggedValue BuiltinsAtomics::HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block,
516                                                   uint32_t indexedPosition,
517                                                   EcmaRuntimeCallInfo *argv, const callbackfun &op,
518                                                   int64_t &tag, bool &lossless)
519 {
520     BUILTINS_API_TRACE(thread, Atomics, HandleWithBigInt64);
521     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
522     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
523     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
524     int64_t arg[ARGS_NUMBER] = {0};
525     arg[0] = tag;
526     if (size == 3) { // the number of parameters is 3
527         auto result = op(reinterpret_cast<int64_t *>(block + indexedPosition), arg, ARGS_NUMBER);
528         return BigInt::Int64ToBigInt(thread, result).GetTaggedValue();
529     }
530     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
531     int64_t newVal = 0;
532     BigInt::BigIntToInt64(thread, newValue, &newVal, &lossless);
533     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
534     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
535     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
536     arg[1] = newVal;
537     auto result = op(reinterpret_cast<int64_t *>(block + indexedPosition), arg, ARGS_NUMBER);
538     return BigInt::Int64ToBigInt(thread, result).GetTaggedValue();
539 }
540 
541 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)542 JSTaggedValue BuiltinsAtomics::HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block,
543                                                    uint32_t indexedPosition,
544                                                    EcmaRuntimeCallInfo *argv, const callbackfun &op,
545                                                    uint64_t &tag, bool &lossless)
546 {
547     BUILTINS_API_TRACE(thread, Atomics, HandleWithBigUint64);
548     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
549     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
550     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
551     uint64_t arg[ARGS_NUMBER] = {0};
552     arg[0] = tag;
553     if (size == 3) { // the number of parameters is 3
554         auto result = op(reinterpret_cast<uint64_t *>(block + indexedPosition), arg, ARGS_NUMBER);
555         return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue();
556     }
557     JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
558     uint64_t newVal = 0;
559     BigInt::BigIntToUint64(thread, newValue, &newVal, &lossless);
560     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
561     BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0)));
562     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
563     arg[1] = newVal;
564     auto result = op(reinterpret_cast<uint64_t *>(block + indexedPosition), arg, ARGS_NUMBER);
565     return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue();
566 }
567 
568 template <typename T>
DoWait(JSThread * thread,JSHandle<JSTaggedValue> & arrayBuffer,size_t index,T execpt,double timeout)569 WaitResult BuiltinsAtomics::DoWait(JSThread *thread, JSHandle<JSTaggedValue> &arrayBuffer,
570                                    size_t index, T execpt, double timeout)
571 {
572     BUILTINS_API_TRACE(thread, Atomics, DoWait);
573     MutexGuard lockGuard(g_mutex);
574     void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrayBuffer.GetTaggedValue());
575     ASSERT(buffer != nullptr);
576     WaiterListNode *node = thread->GetEcmaVM()->GetWaiterListNode();
577     node->date_ = buffer;
578     node->index_ = index;
579     node->waitPointer_ = reinterpret_cast<int8_t*>(buffer) + index;
580     node->waiting_ = true;
581     std::atomic<T> *atomicValue = reinterpret_cast<std::atomic<T> *>(node->waitPointer_);
582     T value = atomicValue->load();
583     if (value != execpt) {
584         return WaitResult::NOT_EQ;
585     }
586     g_waitLists->AddNode(node);
587     uint64_t currentTime = 0;
588     uint64_t timeoutTime = 0;
589     bool hasTimeout = timeout != base::POSITIVE_INFINITY;
590     if (hasTimeout) {
591         currentTime = time::GetCurrentTimeInMillis();
592         timeoutTime = currentTime + static_cast<uint64_t>(timeout);
593     }
594     WaitResult res = WaitResult::OK;
595     ThreadNativeScope nativeScope(thread);
596     while (true) {
597         if (!node->waiting_) {
598             res = WaitResult::OK;
599             break;
600         }
601         if (hasTimeout) {
602             currentTime = time::GetCurrentTimeInMillis();
603             if (currentTime >= timeoutTime) {
604                 res = WaitResult::TIME_OUT;
605                 break;
606             }
607             uint64_t untilTime = timeoutTime - currentTime;
608             ASSERT(untilTime != 0);
609 
610             node->cond_.TimedWait(g_mutex, untilTime);
611         } else {
612             node->cond_.Wait(g_mutex);
613         }
614     }
615     g_waitLists->DeleteNode(node);
616     node->waiting_ = false;
617     return res;
618 }
619 
Signal(JSThread * thread,JSHandle<JSTaggedValue> & arrayBuffer,const size_t & index,double wakeCount)620 uint32_t BuiltinsAtomics::Signal(JSThread *thread, JSHandle<JSTaggedValue> &arrayBuffer,
621                                  const size_t &index, double wakeCount)
622 {
623     void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrayBuffer.GetTaggedValue());
624     ASSERT(buffer != nullptr);
625     MutexGuard lockGuard(g_mutex);
626     auto &locationListMap = g_waitLists->locationListMap_;
627     auto iter = locationListMap.find(reinterpret_cast<int8_t *>(buffer) + index);
628     if (iter == locationListMap.end()) {
629         return 0;
630     }
631     WaiterListNode *node = iter->second.pHead;
632     uint32_t wokenUpCount = 0;
633     while (node != nullptr && wakeCount > 0.0) {
634         if (!node->waiting_) {
635             node = node->next_;
636             continue;
637         }
638         if (buffer == node->date_) {
639             ASSERT(index == node->index_);
640             node->waiting_ = false;
641 
642             WaiterListNode *oldNode = node;
643             node = node->next_;
644             oldNode->cond_.Signal();
645             if (wakeCount != base::POSITIVE_INFINITY) {
646                 wakeCount--;
647             }
648             wokenUpCount++;
649             continue;
650         }
651         node = node->next_;
652     }
653     return wokenUpCount;
654 }
655 } // namespace panda::ecmascript::builtins
656