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