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